---
name: Tutti Workspace App Factory
slug: tutti-workspace-app-factory
category: DevOps
description: Tutti Workspace App Factory creates, converts, or repairs a Tutti workspace app as a self-contained publishable package or a Chrome-style local debug app. Use it for factory handoffs, standalone generation, repair flows, manifests, bootstrap scripts, and validation.
github: "https://github.com/tutti-os/tutti/tree/main/services/tuttid/service/workspace/app_factory_reference"
language: TypeScript
stars: 3282
forks: 328
install: "npx degit https://github.com/tutti-os/tutti/tree/main/services/tuttid/service/workspace/app_factory_reference ~/.claude/skills/app_factory_reference"
installs_to: ~/.claude/skills/app_factory_reference
source_path: services/tuttid/service/workspace/app_factory_reference/SKILL.md
collection_size: 8
category_size: 798
collection_url: "https://dirskills.com/collections/tutti-os/tutti"
added: 2026-08-17T07:08:31.748Z
last_synced: 2026-08-17T07:08:31.748Z
canonical_url: "https://dirskills.com/skills/tutti-workspace-app-factory"
---

# Tutti Workspace App Factory

Tutti Workspace App Factory creates, converts, or repairs a Tutti workspace app as a self-contained publishable package or a Chrome-style local debug app. Use it for factory handoffs, standalone generation, repair flows, manifests, bootstrap scripts, and validation.

**Install:**

```bash
npx degit https://github.com/tutti-os/tutti/tree/main/services/tuttid/service/workspace/app_factory_reference ~/.claude/skills/app_factory_reference
```

## README

# Tutti Workspace App Factory

Use this skill to create, convert, or repair one Tutti workspace app. Choose one output mode before editing:

- **Publishable package**: create a self-contained app under `package/`, runnable by the Tutti custom app runtime and safe to copy into a workspace app archive.
- **Local debug app**: create a small `.tutti/dev-app/` wrapper that launches the user's existing source tree through the Chrome-style "Load unpacked" flow.

When the user selected a directory in App Center and Tutti reports that it cannot be loaded, treat the task as local debug repair. Adapt the selected project by creating or fixing `.tutti/dev-app/`; do not create a zip wrapper or copy the repository into `package/` unless the user explicitly asks for release packaging.

For a full agent-enabled Tutti app repository with `apps/web`, `apps/server`, `packages/shared`, `@tutti-os/agent-acp-kit`, kit-owned `TUTTI_CLI` agent/composer discovery, dynamic local agent runtimes, MCP tool gateways, and an app-owned package builder, use `$tutti-agent-workspace-app` first. Return to this skill for the final package contract and validation. Do not invent managed-agent credential, cwd, JSB fallback, request-body credential, `CODEX_HOME`, CLI argv, app ID, token, or provider alias behavior in this factory skill; the agent app skill keeps those concerns in server-side kit calls.

If the user request needs local agent or local LLM execution, the Tutti agent catalog, or app-owned MCP/tooling, load `$tutti-agent-workspace-app` and read its `references/dynamic-agent-providers.md` and `references/agent-acp-kit.md`. Agent-enabled apps must use a Node server and `@tutti-os/agent-acp-kit`; do not implement app-owned local agent execution by shelling out to raw `$TUTTI_CLI agent ...` commands, provider-specific command families, or session polling.

## Version Check And Update Reminder

Before generating, repairing, or validating an app package, perform a best-effort freshness check unless the user asks to skip network access or the environment is offline.

1. Fetch the current published skill body:

   ```text
   https://raw.githubusercontent.com/tutti-os/tutti-agent-skills/main/skills/tutti-workspace-app-factory/SKILL.md
   ```

2. Compare the fetched content with the local `SKILL.md` that loaded this skill. Prefer a content hash comparison when filesystem access is available. If the local skill path is unavailable, fetch the latest repository commit instead:

   ```text
   https://api.github.com/repos/tutti-os/tutti-agent-skills/commits?per_page=1
   ```

3. If the published skill differs from the local copy, state this at the start of the reply before doing other work:

   ```text
   This Tutti skill has a newer version available. I can continue with the currently loaded copy, but updating first is recommended so the latest manifest rules and runtime guidance are used.

   Update the Codex plugin marketplace:
   codex plugin marketplace upgrade tutti-agent-skills

   Update the direct skill install:
   npx --yes skills add tutti-os/tutti-agent-skills
   ```

4. If the user has explicitly asked to update local installs, or if the current task is itself about keeping the skill current, run the update commands that are available in the environment before continuing. Tell the user that newly installed skill content may take effect only after the next skill reload or new session.

5. If the freshness check fails because network access, GitHub, `codex`, or `npx` is unavailable, mention the check was skipped or failed briefly and continue the requested Tutti app work.

## Required Context

If the current working directory contains `context.json`, or the task includes `mention://workspace-app-factory/create` or `mention://workspace-app-factory`, operate in Tutti factory handoff mode. Read `context.json` before writing files, then follow its metadata, output rules, workspace context, and constraints exactly. Do not copy the context file into generated app outputs.

If `context.json` is absent, operate in standalone mode. Treat the current working directory as the app authoring workspace. If the user asks for local debugging, Load unpacked support, or repair of a selected project directory, create or update `.tutti/dev-app/`. Otherwise, if the directory already contains an app or repository, adapt it into a self-contained Tutti package under `package/`; if it does not, create a new self-contained package under `package/`. Infer missing metadata conservatively from the user request.

For publishable packages, the package root is the only generated app output directory; files outside it are scratch or coordination files and will not be published. For local debugging, `.tutti/dev-app/` is the generated dev app directory and the surrounding project source remains owned by the user repository.

## Mention Contract

Treat a `mention://workspace-app-factory/create` or `mention://workspace-app-factory` link as the factory handoff. In handoff mode, use the exact metadata, output rules, workspace context, and constraints from `context.json` as authoritative.

Before writing files, read these bundled references:

- `references/manifest-contract.md` for `tutti.app.json`.
- `references/cli-manifest-contract.md` for `tutti.cli.json` when exposing app capabilities to the Tutti ecosystem.
- `references/runtime-env.md` for runtime environment variables and storage ownership.
- `references/i18n-harness.md` when the app has localized metadata, user-facing in-app copy, or an existing localization system.
- `references/tutti-cli-commands.md` when the generated app runtime should call, combine, or expose local Tutti CLI capabilities.
- `references/validation-checklist.md` for completion checks.

Read `references/demos/simple-node-static-app/` only when you need a concrete complete package shape. Do not copy its demo app id, display name, description, or tags unless the user explicitly asks for the demo itself.

## Output Contract

For a publishable package, create or update these files under `output.packageRoot` from the context in handoff mode, or under `package/` in standalone mode:

- `tutti.app.json`: valid JSON manifest matching `references/manifest-contract.md`.
- `tutti.cli.json`: CLI manifest matching `references/cli-manifest-contract.md`, required when the user asks to connect the app to the Tutti ecosystem; otherwise create it only when `tutti.app.json` declares `cli.manifest`.
- `bootstrap.sh`: backward-compatible executable shell entrypoint that starts the app server with no arguments.
- Platform-native binaries under `bin/<platform>/` when the package supports Windows or uses the `standalone` profile; keep all supported artifacts in the same fat package and select them from `bootstrap.sh` using `TUTTI_PLATFORM`.
- `AGENTS.md`: package-local guidance describing layout, runtime command, endpoints, data storage, and modification rules.
- `locales/<locale>/manifest.json`: manifest metadata localization files, only when the user asks for localized app metadata.
- App-owned locale dictionaries or an i18n helper/harness when the app has user-facing in-app copy in more than one language.
- App implementation files and assets needed for the requested behavior.

For a local debug app, create or update these files under `.tutti/dev-app/` instead of `package/`:

- `tutti.app.json`: local debug manifest.
- `tutti.cli.json`: CLI manifest matching `references/cli-manifest-contract.md`, only when the app exposes capabilities or `tutti.app.json` declares `cli.manifest`.
- `bootstrap.sh`: executable shell entrypoint that reads the host-injected port and starts the source dev server.
- `AGENTS.md`: dev-app guidance describing the project root, dev/watch command, host/port contract, source hot-reload ownership, and how to reload from App Center.
- Optional app assets referenced by the manifest.

Keep `.tutti/dev-app/` small. It should describe and launch the local app, not copy the whole project. Tutti Desktop can load either the `.tutti/dev-app/` directory directly or the project root that contains it.

If the task supplies exact metadata such as `appId`, version, display name, or description, copy those values exactly into `tutti.app.json`. If metadata is missing, choose conservative defaults:

- `schemaVersion`: `tutti.app.manifest.v1`
- `version`: `0.1.0`
- `appId`: `app_` plus a lowercase hyphenated slug from the app name or request
- `description`: one concise sentence describing actual app behavior
- `icon`: package-local asset, preferably `{"type":"asset","src":"icon.svg"}`
- `runtime.bootstrap`: `bootstrap.sh`
- `runtime.bootstrap`: the single cross-platform package entrypoint. Keep it POSIX-compatible and use `TUTTI_PLATFORM` when a fat package needs to select a platform-native binary.
- `runtime.healthcheckPath`: `/healthz`
- `localizationInfo`: omit unless the user asks for localized app metadata; when needed, follow `references/manifest-contract.md` and create each referenced locale file.

If the user asks to connect the app to the Tutti ecosystem, expose at least one app capability through `tutti.cli.json` and declare it from `tutti.app.json`. If the app has a UI, include a business-level open command when there is a meaningful target to open, such as `open-project`, `open-file`, `open-run`, or `open-context`. The command should own the full self-open flow: accept stable domain identifiers, validate them, map them to an app-owned origin-root route, and request opening this same app through `$TUTTI_CLI` with an argv list equivalent to `--json app open --app-id "$TUTTI_APP_ID" --route ...`. `--json` is the CLI machine-readable output flag. Do not return route parameters for a caller or agent to interpret and then call `app open`; that makes the integration chain too indirect. Do not expose raw frontend route construction as the public contract; callers should invoke the app's business open command without needing to know the app's internal router.

## Runtime Rules

Build a small local HTTP app. Default newly generated apps to a Node server. Use Python only when adapting an existing Python project or when the user explicitly requests Python. Agent-enabled apps must use a Node server because `@tutti-os/agent-acp-kit` is Node-only.

The runtime must:

- Bind `$TUTTI_APP_HOST:$TUTTI_APP_PORT`, defaulting the host to `127.0.0.1` only when the host variable is absent.
- Fail startup with a clear error when `$TUTTI_APP_PORT` is absent. Do not guess, reserve, or hard-code a fallback port; the daemon owns port allocation.
- Serve the manifest healthcheck path with a 2xx response.
- Treat `$TUTTI_APP_PACKAGE_DIR` as read-only after startup.
- Write durable app artifacts and non-database state only under `$TUTTI_APP_DATA_DIR`.
- Write active SQLite databases, WAL/SHM files, indexes, and other database-managed files only under `$TUTTI_APP_DATABASE_DIR`. The directory is host-local and durable for the installation; apps may create multiple databases beneath it.
- Write scratch/runtime files only under `$TUTTI_APP_RUNTIME_DIR`.
- Write logs only under `$TUTTI_APP_LOG_DIR` when backend/server-side file logs are needed.
- Store reusable app-managed binaries only under `$TUTTI_APP_TOOLCHAIN_ROOT`.
- Prefer `window.tuttiExternal?.logs?.write?.()` for browser-side diagnostics in Tutti Desktop; reserve `$TUTTI_APP_LOG_DIR` for backend process logs.
- Do not expect a workspace-root environment variable. Use `$TUTTI_WORKSPACE_ID` and `$TUTTI_CLI` for explicit workspace-scoped capabilities, and treat caller-supplied absolute file paths as opaque inputs rather than deriving a root from them.
- Launch Python with `$TUTTI_APP_PYTHON` and Node with `$TUTTI_APP_NODE`; use `$TUTTI_APP_NPM` for npm install/build work.
- When the app exposes an open command, support the routed pages in the app runtime itself: direct navigation to the route must render the intended page, and an already-mounted frontend should handle repeated open intents through `window.tuttiExternal?.workspace?.onLaunchIntent?.(...)`.
- When the generated app calls another local Tutti capability at runtime, use `$TUTTI_CLI` and follow `references/tutti-cli-commands.md`.
- Read the current UI locale from the optional host-injected app context when localized in-app copy is needed. Do not pass locale in the launch URL query.
- Keep localized in-app copy behind stable keys and use the harness pattern in `references/i18n-harness.md` so future edits can check locale parity.
- Use CSS `prefers-color-scheme` / `matchMedia("(prefers-color-scheme: dark)")` for dark/light rendering. Do not pass theme in the launch URL query.
- When exposing app-owned files through references or generated content, return reference-list `location` objects scoped to `app-data-relative` or `app-package-relative`. Do not emit, persist, or instruct clients to open direct `.tutti` / `.tutti-dev` app state paths such as `$TUTTI_STATE_DIR/apps/...`; the daemon resolves valid locations before desktop clients open files.

## Agent Runtime Integration

For a full agent-enabled app repository, prefer `$tutti-agent-workspace-app` first. When this skill still needs to package or repair an app that already uses `@tutti-os/agent-acp-kit`, keep the app in control of agent policy:

- Keep the generic `@tutti-os/agent-acp-kit` runtime path product-neutral. Tutti-specific agent catalog, composer, and skill discovery stays behind the explicit `@tutti-os/agent-acp-kit/tutti` subpath; app code owns only product policy and DTO projection.
- Use a Node server for the app host process. Do not start with a Python server and plan to migrate later.
- To give the app's local agent runs access to Tutti's dynamic CLI skills, prefer the `@tutti-os/agent-acp-kit/tutti` helper instead of hand-writing `$TUTTI_CLI agent tutti-cli-skill-bundle` execution and response parsing in each app.
- Use `loadTuttiAgentSkillContext(...)` from the app host process. Pass the selected agent target id, run id, workspace cwd, and optional Tutti CLI command configuration such as `commandEnvNames`; provider is derived from the selected catalog entry.
- Pass `tuttiContext.skillManifest` into `runtime.run({ ..., skillManifest })`, merging it with app-owned skills when needed.
- Treat `tuttiContext.recommendedSystemPrompt?.content` as advisory raw prompt content. The app may merge it into its own `systemPrompt`, edit it, place it elsewhere, or ignore it. Do not inject it silently, and do not reintroduce duplicated CLI parsing unless the installed kit lacks the helper.
- Keep run-scoped app tools and MCP credentials app-owned. Do not pass broad Tutti daemon credentials or app secrets directly to the agent process.

Agent app main flows must call `loadTuttiAgentCatalog` and lazy `loadTuttiAgentComposerOptions` from `@tutti-os/agent-acp-kit/tutti`. Do not use the deprecated provider-catalog projection because it cannot represent multiple agents sharing a provider. The kit automatically uses `TUTTI_CLI` inside Tutti and standalone runtime detection when the CLI is absent. App code must not pass a mode, app ID, daemon URL, token, provider alias map, or CLI arguments. Follow `$tutti-agent-workspace-app` and its `references/dynamic-agent-providers.md`. Show every returned agent, persist exact agent target ids, keep unavailable agents disabled with a reason, and treat provider as derived runtime metadata. A configured CLI failure is explicit; never synthesize a fixed catalog.

Do not assume a Tutti API token, browser extension, daemon internals, or
undocumented desktop APIs. Generated apps may consume the documented
`window.tuttiExternal` browser surfaces described in `references/runtime-env.md`.
Use `agentActivity` only when the app intentionally orchestrates the official
host-owned Agent GUI runtime, such as a provider test lab; do not use it to
rebuild an app-owned Agent runtime. Apps that own their Agent policy or runtime
must still follow `$tutti-agent-workspace-app` and
`@tutti-os/agent-acp-kit`.

## Dependency Rules

Avoid startup-time package installation. If dependencies or build artifacts are necessary, add an executable `prepare.sh` and keep `bootstrap.sh` focused on launching the prepared app. `prepare.sh` may use `$TUTTI_APP_PYTHON`, `$TUTTI_APP_NODE`, and `$TUTTI_APP_NPM` for install and build steps.

Generated apps must not rely on system `python`, `python3`, `node`, or `npm` commands. Use the explicit managed runtime environment variables instead.

Keep generated apps small and inspectable. Do not add frameworks, background workers, databases, or network services unless they are required by the user request. Use Node built-ins for small apps; for larger Node servers with many routes, middleware, schemas, streaming, or WebSocket needs, Fastify is a good default and Express is acceptable when it already matches the project. For React/Tailwind UI apps, use shadcn/ui components and Tailwind CSS utilities instead of hand-rolled component markup and ad hoc CSS. Do not migrate a small vanilla app solely to use shadcn/ui unless the user explicitly asks for that frontend stack.

## Local Debug Workflow

Use this workflow when the user asks for local app debugging, load-unpacked behavior, or direct development against an existing Next/Vite/Node/Python repository.

1. Create `.tutti/dev-app/tutti.app.json`, `.tutti/dev-app/bootstrap.sh`, and `.tutti/dev-app/AGENTS.md`.
2. Keep the formal release package path separate: a future release/import package must still be self-contained under `package/`.
3. In `bootstrap.sh`, read `$TUTTI_APP_HOST` and `$TUTTI_APP_PORT`; exit with a clear error if the port is missing. The daemon owns port allocation.
4. If the app server lives in the project root, compute it from the dev app directory, for example `PROJECT_ROOT="$(cd "$TUTTI_APP_PACKAGE_DIR/../.." && pwd)"`, then `cd "$PROJECT_ROOT"` before launching.
5. Translate the project's known dev/watch command explicitly. For example, run Vite with host and port flags, run Next with `-H "$TUTTI_APP_HOST" -p "$TUTTI_APP_PORT"`, or run backend servers through their watch mode such as `tsx watch`, `nodemon`, `uvicorn --reload`, `air`, or `cargo watch`. Do not depend on daemon-side framework detection.
6. Treat source hot-reload as the project dev server's responsibility. The Tutti host does not watch the user's project root and should not be expected to restart on normal frontend or backend source edits. If a server-side project lacks a watch/dev command, add or document a project-owned one such as `dev:tutti` rather than adding daemon-side source watching.
7. Treat `.tutti/dev-app/` files as host contract configuration. Changes to `tutti.app.json`, `tutti.cli.json`, `bootstrap.sh`, assets, or dev-app `AGENTS.md` require App Center's local-dev Reload action so the daemon rereads the manifest and restarts the runtime when needed.
8. Use `$TUTTI_APP_NODE` and `$TUTTI_APP_NPM` for Node-based dev servers. Do not call system `node`, `npm`, `pnpm`, or `yarn` directly from `bootstrap.sh` unless the user explicitly owns that dependency and accepts the portability tradeoff.
9. Document the source project entrypoint, the dev/watch command, which edits hot-reload through the project dev server, which edits require App Center Reload, and the fact that Tutti Desktop loads the project root or `.tutti/dev-app/` in `.tutti/dev-app/AGENTS.md`.
10. Run `scripts/check_local_dev_app.py <project-root-or-.tutti/dev-app>` from this skill after creating or repairing `.tutti/dev-app/`. Fix every reported failure before saying the local debug repair is complete.
11. Tell the user to retry App Center's Load unpacked action on the project root or `.tutti/dev-app/`. Do not auto-open a Tutti app window.

The old zip/wrapper conversion approach is a fallback for compatibility or release packaging work only. Do not recommend it for normal local debugging; prefer `.tutti/dev-app/` plus Load unpacked.

## Conversion Workflow

When converting an existing repository in
