@passioncode-ai/passioncode 0.1.53 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +72 -23
  3. package/SECURITY.md +23 -3
  4. package/bin/passioncode.js +71 -6
  5. package/family.json +2 -2
  6. package/lib/catalog.js +91 -0
  7. package/lib/doctor.js +288 -0
  8. package/lib/launcher.js +123 -58
  9. package/lib/mcp-probe.js +91 -0
  10. package/lib/openpgp.js +209 -0
  11. package/lib/release-key.asc +10 -0
  12. package/lib/releases.js +211 -0
  13. package/lib/setup.js +238 -0
  14. package/package.json +6 -3
  15. package/payload/.claude-plugin/marketplace.json +3 -3
  16. package/payload/manifest.json +10 -9
  17. package/payload/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
  18. package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +1 -1
  19. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +3 -2
  20. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md +5 -5
  21. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +26 -0
  22. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +182 -0
  23. package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +1 -1
  24. package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +1 -1
  25. package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +1 -1
  26. package/payload/plugins/passioncode/.claude-plugin/plugin.json +2 -2
  27. package/payload/plugins/passioncode/hooks/session-start.js +13 -1
  28. package/payload/plugins/passioncode/skills/setting-up-passioncode/SKILL.md +109 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1 - 2026-10-13
4
+
5
+ ### Changed
6
+
7
+ - **Fabric Agent Adapter is pinned at `v0.30.0`** (it was `v0.29.0`). The Node service kit now installs and uninstalls a launchd job the way the Python kit does: `launchdPlist`, `launchdInstall` and `launchdUninstall`. A service's plist comes out the same from either kit. The service probe (`check_service.py`) gains a rule, `lifecycle.supervisor-marker`: it fails a launchd job whose plist does not tell the service that launchd supervises it. Without that marker, a second copy that finds the lock held exits at once and launchd restarts it every few seconds.
8
+
9
+ ## 0.2.0 - 2026-10-13
10
+
11
+ ### Added
12
+
13
+ - **`setup` (alias `onboard`): one command for a new computer and for one that is already set up.**
14
+ It checks everything first, then installs the PassionCode.ai apps that are missing (Fabric
15
+ Switchboard, Fabric Dashboards, Project Observatory, Fabric Inbox, and Fabric as an early
16
+ preview when Docker and the Supabase CLI are present), installs the skills, registers every
17
+ app's MCP server for every coding agent present, repairs what is wrong, checks again and prints
18
+ a before → after table with one list of the steps that stay yours. On a healthy computer it
19
+ changes nothing and exits 0. In a terminal it asks one question per app; `--yes`, `--with`,
20
+ `--without`, `--no-apps`, `--no-open`, `--dry-run` and `--json` serve agents and scripts.
21
+ - **No unverified byte is installed.** Each release's `SHA256SUMS.asc` is verified against the
22
+ PassionCode.ai release key bundled in the package (pinned by fingerprint, no gpg needed); the
23
+ download must match the signed checksum and GitHub's digest; the app must pass `codesign`,
24
+ carry team `KJ35UYYL22`, have its notarization ticket stapled and pass Gatekeeper. An outdated
25
+ or damaged app is reinstalled only when it is not running, and the old one goes to the Trash.
26
+ - **`doctor`: a read-only check of the whole setup** — prerequisites, the skill set, each app's
27
+ version, signature and team, each agent's MCP entries with one real proving call per server,
28
+ and the family's Fabric services. Every line names its state and its fix; exit 0 means no check
29
+ failed. `doctor --fix` runs setup's repairs without installing new apps.
30
+ - **`update` registers Fabric Switchboard's MCP server (`switchboard mcp`)** next to Fabric
31
+ Dashboards', under the same never-clobber, reversible rules; an entry counts as the same only
32
+ with the same command and arguments. **`setup` also registers Fabric's (`fabric-mcp --runner
33
+ <agent>`)**: an early preview, it is never added by a background update (ADR-0004).
34
+ - **Safer installs and removals**: an app is copied beside its target and swapped in with two
35
+ renames, after a second check that nothing runs from it; uninstall keeps a Codex entry that
36
+ gained settings since it was written; a dry run copies no config file.
37
+ - **The `setting-up-passioncode` skill**: "set me up" or "check my setup" makes any coding agent
38
+ run setup or doctor, finish the steps an agent may take and hand you only sign-ins, consents
39
+ and secrets typed into a product's own hidden prompt. The README and passioncode.ai/start/
40
+ carry a one-paragraph prompt to paste into Claude Code or Codex.
41
+ - **Session start names the problems of the last setup check** while it has any; it never runs
42
+ the check itself.
43
+
3
44
  ## 0.1.53 - 2026-10-13
4
45
 
5
46
  ### Changed
package/README.md CHANGED
@@ -1,38 +1,65 @@
1
1
  # PassionCode.ai launcher
2
2
 
3
- The **PassionCode.ai launcher** (`@passioncode-ai/passioncode` on npm) installs and updates the
4
- PassionCode.ai skills and plugins listed in [`family.json`](family.json) — the Fabric Agent Adapter,
5
- Observatory Log and the organization's working rules — for Claude Code and every other agent on your machine, as one set.
6
- It is how a teammate's agent gets the tools that build for Fabric, PassionCode.ai's CEO AI agent,
7
- and it works on its own: no Fabric install, no account, no key.
3
+ The **PassionCode.ai launcher** (`@passioncode-ai/passioncode` on npm) sets up PassionCode.ai on a
4
+ computer with one command: the apps (Fabric, Fabric Switchboard, Fabric Dashboards, Fabric Inbox,
5
+ Project Observatory) from their signed releases, their MCP servers for every coding agent on it,
6
+ and the PassionCode.ai skills listed in [`family.json`](family.json). Run the same command on a
7
+ computer that is already set up, and it checks everything, repairs what is wrong and tells you
8
+ what changed.
8
9
 
9
10
  ```bash
10
- npx @passioncode-ai/passioncode@latest update
11
+ npx @passioncode-ai/passioncode@latest setup # a new computer: set up · an existing one: check and repair
12
+ npx @passioncode-ai/passioncode@latest doctor # check only; changes nothing
11
13
  ```
12
14
 
13
15
  | Member | Skills |
14
16
  |---|---|
15
17
  | Fabric Agent Adapter | `creating-fabric-agents`, `adapting-projects-to-fabric`, `building-fabric-services` |
16
18
  | Observatory Log | `explaining-changes`, `handling-secrets`, `tracking-resources` (+ its session hooks) |
17
- | passioncode | `working-in-passioncode` (the organisation's working rules, for every contributor's agent) and the once-a-day update check |
19
+ | passioncode | `setting-up-passioncode` (set up and check a computer), `working-in-passioncode` (the organisation's working rules, for every contributor's agent) and the once-a-day update check |
18
20
 
19
21
  Only PassionCode.ai products are members. Agents that people build for themselves stay
20
22
  out of it, even when they implement Fabric protocols.
21
23
 
24
+ ## Set it up with your coding agent
25
+
26
+ Paste this into Claude Code, Codex or another coding agent:
27
+
28
+ > Set up PassionCode.ai on this computer, or check and repair it if it is already set up. Run
29
+ > `npx --yes @passioncode-ai/passioncode@latest setup --yes --json` (it needs Node 18 or newer)
30
+ > and read its JSON: it installs the missing apps from their signed releases, the skills and
31
+ > every MCP server, repairs whatever is wrong, and checks everything again. Do not download or
32
+ > install anything by hand, and never stop or kill a running app. Then run each product
33
+ > onboarding step it lists that an agent may run, never ask me for a key or password in the
34
+ > chat (the tools open their own hidden prompts), and finish with
35
+ > `npx --yes @passioncode-ai/passioncode@latest doctor`. Tell me briefly what you set up or
36
+ > fixed, what still fails, and the numbered steps only I can do, such as signing in, and
37
+ > remind me to restart you so the new skills and MCP servers load.
38
+
39
+ Once the skills are installed, "set me up" or "check my setup" is enough: the
40
+ `setting-up-passioncode` skill knows the rest. What stays yours: signing in, consenting, typing a
41
+ secret into a product's own hidden prompt, starting Docker for Fabric, and restarting your agent.
42
+
22
43
  ## Quick start for a new teammate
23
44
 
24
45
  1. **Install** (Node 18 or newer; the Claude Code CLI on `PATH` for the plugins):
25
- `npx @passioncode-ai/passioncode@latest update`, then restart your agents. Check it with
26
- `npx @passioncode-ai/passioncode@latest status` (every member `claude: plugin · hub: n/n`) and
27
- `claude plugin list` (the plugins listed in `family.json`, under `@passioncode`).
28
- Always call the scoped package shown above.
46
+ `npx @passioncode-ai/passioncode@latest setup`, then restart your agents. In a terminal it asks
47
+ one question per app; `--yes` accepts the plan, `--with`/`--without <ids>` choose apps
48
+ (`switchboard`, `dashboards`, `observatory`, `inbox`, `fabric`), `--no-apps` sets up the skills
49
+ and MCP only, `--dry-run` shows the plan. Apps are installed on macOS; on other systems setup
50
+ names each download page. Skills only: `npx @passioncode-ai/passioncode@latest update`.
29
51
  2. **Configure:** nothing to set, no account and no key. Background updates are on by default;
30
- `npx @passioncode-ai/passioncode@latest config set update.auto off` turns them off.
31
- 3. **MCP:** none today, and not planned. The launcher installs skills and session hooks and is
32
- driven by its CLI (`update`, `status`, `restore`, `uninstall`, `config`); it neither serves nor
33
- calls an MCP server. The products it installs for, and the ones that serve MCP (Fabric
34
- Switchboard, Fabric Inbox, Fabric Dashboards), document their own registration and proving
35
- call; the Fabric Agent Adapter's quick start builds and calls one.
52
+ `npx @passioncode-ai/passioncode@latest config set update.auto off` turns them off. Each app's
53
+ own onboarding (sign-ins, Fabric Inbox's server, Observatory's projects) is listed by `setup`.
54
+ 3. **MCP:** the launcher serves no MCP server of its own. It registers the apps' servers —
55
+ `fabric-dashboards`, `fabric` (`fabric-mcp --runner <agent>`) and `switchboard`
56
+ (`switchboard mcp`) — for Claude Code, Codex CLI, Gemini CLI and Kimi Code, and `doctor` proves
57
+ each one with a real call: `initialize`, `tools/list`, then `list_services`,
58
+ `switchboard_accounts` or `observatory_status`. Fabric's server is never started by doctor,
59
+ because it enrols a session as it starts: doctor checks its entry, and your agent's first
60
+ `fabric_whoami` is the proving call. Fabric is an early preview, so only `setup` registers it,
61
+ never a background update. Project Observatory and Fabric Inbox register their
62
+ own servers through their onboarding; `doctor` checks those too.
36
63
  4. **Develop:** clone, then `npm test` — there are no dependencies and no lockfile, so there is
37
64
  nothing to install (`npm ci` refuses to run here). `npm run vendor` builds `payload/` from the
38
65
  members pinned in `family.json`; `claude plugin validate ./payload --strict` checks it. Start
@@ -55,12 +82,34 @@ out of it, even when they implement Fabric protocols.
55
82
  `~/.passioncode/quarantine/` — `npx @passioncode-ai/passioncode@latest restore` puts them back.
56
83
  - Each version is an immutable release under `~/.passioncode/releases/<version>`; an update
57
84
  keeps the release it replaced for rollback and removes older ones.
58
- - When the Fabric Dashboards app is installed, `update` also registers its MCP server
59
- (`fabric-dashboards`) for every agent that is present — Claude Code (through
60
- `claude mcp add --scope user`), Codex CLI (`~/.codex/config.toml`), Gemini CLI
61
- (`~/.gemini/settings.json`) and Kimi Code (`~/.kimi-code/mcp.json`). An entry that
62
- already points at the same command is left alone; one that points elsewhere is reported
63
- and kept. `restore` and `uninstall` remove what the launcher added.
85
+ - `update` (and therefore `setup`) registers the MCP server inside each installed app — Fabric
86
+ Dashboards (`fabric-dashboards`), Fabric Switchboard (`switchboard`, unless Claude Code already
87
+ gets it from the `switchboard` plugin) and, from `setup` only because it is an early preview,
88
+ Fabric (`fabric`, with each agent's own `--runner`) —
89
+ for every agent that is present: Claude Code (through `claude mcp add --scope user`), Codex CLI
90
+ (`~/.codex/config.toml`), Gemini CLI (`~/.gemini/settings.json`) and Kimi Code
91
+ (`~/.kimi-code/mcp.json`). An entry that already exists is left alone; one that points elsewhere
92
+ is reported and kept. `restore` and `uninstall` remove what the launcher added
93
+ ([ADR-0003](docs/adr/0003-fabric-dashboards-mcp-registration.md),
94
+ [ADR-0004](docs/adr/0004-setup-installs-verified-apps-and-registers-their-mcp.md)).
95
+
96
+ ## Setup and doctor
97
+
98
+ `setup` checks first, then installs the absent apps you chose, repairs what the checks found,
99
+ checks again and prints a before → after table and the steps that stay yours. On a healthy
100
+ computer it changes nothing and exits 0. An app is installed only after its release's
101
+ `SHA256SUMS.asc` verifies against the PassionCode.ai release key bundled in this package
102
+ (fingerprint `63B3 0DC3 24BD 6974 87AA 3194 4FAF B8AE C803 B6A7`), the file matches the signed
103
+ checksum and GitHub's digest, and the app passes `codesign`, carries team `KJ35UYYL22`, has its
104
+ notarization ticket stapled and is accepted by Gatekeeper. An outdated or damaged app is
105
+ reinstalled only when it is not running; the old one goes to the Trash. A family service that
106
+ does not answer is started through launchd (`launchctl kickstart`, never a kill).
107
+
108
+ `doctor` changes nothing. It reads prerequisites, the skill set, each app's version, signature
109
+ and team, each agent's MCP entries with one proving call per server, and the Fabric services of
110
+ the family; each line names its state and its fix. Exit codes: **0** no check failed, **1** a
111
+ check (or a setup step) failed, **2** bad arguments. `doctor --fix` runs setup's repairs without
112
+ installing new apps. Details: the [CLI contract](docs/reference/cli.md#setup-and-doctor).
64
113
 
65
114
  ## Staying current
66
115
 
package/SECURITY.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Report vulnerabilities privately to contact@passioncode.ai. Do not open an issue that
4
4
  carries credentials or a working exploit.
5
5
 
6
- What `passioncode update` touches: `~/.passioncode/` (releases, state, quarantine,
6
+ What `passioncode update` touches (setup and doctor: [below](#what-setup-and-doctor-touch)): `~/.passioncode/` (releases, state, quarantine,
7
7
  logs); Claude Code's plugin registry through the `claude plugin` CLI only;
8
8
  `~/.agents/skills/<member skill>` and `<agent channel>/<member skill>` links; and
9
9
  `~/.claude/skills/<member skill>`, which it moves into the quarantine once the plugin
@@ -11,8 +11,9 @@ that replaces it is verified. For a member's old single-member marketplace
11
11
  (`legacyMarketplaces` in `family.json`), once that plugin is verified and nothing else
12
12
  installed comes from it, it also moves the `extraKnownMarketplaces` entry out of
13
13
  `~/.claude/settings.json` (a copy of the file is kept in the quarantine) and runs
14
- `claude plugin marketplace remove <name>`. When the Fabric Dashboards app is installed it
15
- also registers a `fabric-dashboards` MCP entry for every agent that is present: through
14
+ `claude plugin marketplace remove <name>`. For each installed app with an MCP server in its
15
+ bundle (Fabric Dashboards, Fabric, Fabric Switchboard) it also registers that entry for every
16
+ agent that is present: through
16
17
  `claude mcp add --scope user` for Claude Code (it never writes `~/.claude.json` itself),
17
18
  and in `~/.codex/config.toml`, `~/.gemini/settings.json` and `~/.kimi-code/mcp.json`,
18
19
  copying each existing file into the quarantine first; an entry that already exists is
@@ -26,6 +27,25 @@ records. Each update removes the launcher's own older releases (its output, rein
26
27
  from npm) and keeps the one it replaced for rollback. A failed removal
27
28
  returns a nonzero exit code. See the [CLI recovery contract](docs/reference/cli.md).
28
29
 
30
+ ## What `setup` and `doctor` touch
31
+
32
+ `doctor` reads: `~/.claude.json`, `~/.codex/config.toml`, `~/.gemini/settings.json`,
33
+ `~/.kimi-code/mcp.json` (entry names, commands and arguments; environment values are passed to
34
+ the server it starts and never printed), the Fabric service descriptors (never their token
35
+ files), the apps' `Info.plist`, and the GitHub releases API. It starts each registered stdio MCP
36
+ server for one proving call and stops it (never Fabric's, which enrols a session as it starts), and writes only `~/.passioncode/doctor.json` and a
37
+ release cache.
38
+
39
+ `setup` also downloads releases from GitHub into `~/.passioncode/downloads/` (removed after
40
+ each app), copies a verified app into `/Applications`, moves an app it replaces into `~/.Trash/`,
41
+ runs `update` (above), runs `launchctl kickstart gui/<uid>/<label>` for a family service whose
42
+ descriptor names launchd and that does not answer, and opens the apps it installed. It never
43
+ stops or kills a process, never replaces a running app, never asks for or stores a secret, and
44
+ never installs a byte whose signature, checksum, team or notarization does not verify. The trust
45
+ root is the release key bundled at `lib/release-key.asc`, pinned by fingerprint in
46
+ `lib/openpgp.js`; a new key needs a launcher release. `PASSIONCODE_RELEASE_KEY` replaces it for
47
+ tests and is named in setup's output whenever it is set.
48
+
29
49
  ## The self-update and who it trusts
30
50
 
31
51
  The SessionStart hook reads `~/.passioncode/state.json` and reuses a check for up to
@@ -17,19 +17,84 @@ function cachedNpmStatus({ latest, checkedAt, installed }) {
17
17
  return `npm latest (cached) ${latest} (${checked}${stale ? '; stale: older than installed' : ''})`;
18
18
  }
19
19
 
20
- const HELP = `passioncode — the PassionCode.ai skill set (family.json), for every agent on this machine
20
+ const HELP = `passioncode — set up and check PassionCode.ai on this machine: the apps, their MCP servers, the skills
21
21
 
22
- npx @passioncode-ai/passioncode@latest update [--dry-run] [--json] install or update the whole set
22
+ npx @passioncode-ai/passioncode@latest setup [--yes] [--with ids] [--without ids] [--no-apps] [--no-open] [--dry-run] [--json]
23
+ a new machine: install and configure everything; an existing one: check, repair, check again
24
+ npx @passioncode-ai/passioncode@latest doctor [--fix] [--json] check everything (read-only); --fix repairs as setup does
25
+ npx @passioncode-ai/passioncode@latest update [--dry-run] [--json] install or update the skill set and the apps' MCP registrations
23
26
  npx @passioncode-ai/passioncode@latest status [--json] what is installed where
24
27
  npx @passioncode-ai/passioncode@latest restore put back what the last update moved aside
25
28
  npx @passioncode-ai/passioncode@latest config set update.auto on|off background updates at session start
26
29
  npx @passioncode-ai/passioncode@latest uninstall remove the set (quarantined items stay restorable)
27
30
 
28
- Claude Code gets plugins from the local "passioncode" marketplace; other agents get
29
- the same skills through ~/.agents/skills. Restart your agent after an update.`;
31
+ App ids: switchboard, dashboards, observatory, inbox, fabric. Exit codes: 0 nothing failed,
32
+ 1 a check or a step failed, 2 bad arguments. Claude Code gets plugins from the local
33
+ "passioncode" marketplace; other agents get the same skills through ~/.agents/skills.
34
+ Restart your agent after a change.`;
30
35
 
31
- function main(argv) {
36
+ const IDS = ['switchboard', 'dashboards', 'observatory', 'inbox', 'fabric'];
37
+
38
+ /** Flags of setup and doctor: booleans, plus --with/--without taking a comma list. Null when invalid. */
39
+ function parseSetupArgs(cmd, rest) {
40
+ const allowed = cmd === 'doctor' ? ['--fix', '--json', '--offline'] : ['--yes', '-y', '--json', '--offline', '--no-apps', '--no-open', '--dry-run'];
41
+ const o = { flags: new Set(), withIds: null, withoutIds: [] };
42
+ for (let i = 0; i < rest.length; i++) {
43
+ const arg = rest[i];
44
+ const m = /^--(with|without)(?:=(.*))?$/.exec(arg);
45
+ if (m && cmd !== 'doctor') {
46
+ const value = m[2] !== undefined ? m[2] : rest[++i];
47
+ const ids = String(value || '').split(',').map((x) => x.trim()).filter(Boolean);
48
+ if (!ids.length || ids.some((x) => !IDS.includes(x))) return null;
49
+ if (m[1] === 'with') o.withIds = [...(o.withIds || []), ...ids]; else o.withoutIds.push(...ids);
50
+ } else if (allowed.includes(arg)) o.flags.add(arg === '-y' ? '--yes' : arg);
51
+ else return null;
52
+ }
53
+ return o;
54
+ }
55
+
56
+ function asker() {
57
+ if (!process.stdin.isTTY || !process.stderr.isTTY) return null;
58
+ const readline = require('readline');
59
+ return (question) => new Promise((resolve) => {
60
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
61
+ rl.question(question, (answer) => { rl.close(); resolve(answer); });
62
+ });
63
+ }
64
+
65
+ async function setupOrDoctor(cmd, rest) {
66
+ const o = parseSetupArgs(cmd, rest);
67
+ if (!o) {
68
+ process.stderr.write(`passioncode: unsupported arguments for ${cmd}; run passioncode help. App ids: ${IDS.join(', ')}.\n`);
69
+ return 2;
70
+ }
71
+ const json = o.flags.has('--json');
72
+ const online = !o.flags.has('--offline');
73
+ if (cmd === 'doctor' && !o.flags.has('--fix')) {
74
+ const D = require('../lib/doctor');
75
+ const r = await D.doctor({ online });
76
+ process.stdout.write(json ? JSON.stringify(r, null, 2) + '\n' : D.render(r) + '\n');
77
+ return r.ok ? 0 : 1;
78
+ }
79
+ const S = require('../lib/setup');
80
+ const repairOnly = cmd === 'doctor';
81
+ const log = json ? () => {} : (line) => process.stderr.write(`${line}\n`);
82
+ const r = await S.setup({
83
+ yes: repairOnly || o.flags.has('--yes'), withIds: o.withIds, withoutIds: o.withoutIds, noApps: o.flags.has('--no-apps'),
84
+ noOpen: o.flags.has('--no-open'), dryRun: o.flags.has('--dry-run'), repairOnly, online, ask: json ? null : asker(), log,
85
+ });
86
+ process.stdout.write(json ? JSON.stringify(r, null, 2) + '\n' : S.render(r, { repairOnly }) + '\n');
87
+ return r.planOnly ? 0 : r.ok ? 0 : 1;
88
+ }
89
+
90
+ async function main(argv) {
32
91
  const [cmd = 'help', ...rest] = argv;
92
+ if (cmd === 'setup' || cmd === 'onboard' || cmd === 'doctor') {
93
+ try { return await setupOrDoctor(cmd === 'onboard' ? 'setup' : cmd, rest); } catch (error) {
94
+ process.stderr.write(`passioncode: ${error.message}\n`);
95
+ return 1;
96
+ }
97
+ }
33
98
  const json = rest.includes('--json');
34
99
  const quiet = rest.includes('--quiet');
35
100
  const out = (value, text) => process.stdout.write(json ? JSON.stringify(value, null, 2) + '\n' : text + '\n');
@@ -89,4 +154,4 @@ function main(argv) {
89
154
  }
90
155
  }
91
156
 
92
- process.exit(main(process.argv.slice(2)));
157
+ main(process.argv.slice(2)).then((code) => { process.exitCode = code; });
package/family.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "name": "fabric-agent-adapter",
9
9
  "displayName": "Fabric Agent Adapter",
10
10
  "repo": "passioncode-ai/fabric-agent-adapter",
11
- "ref": "v0.29.0",
11
+ "ref": "v0.30.0",
12
12
  "kind": "plugin",
13
13
  "path": "plugins/fabric-agent-adapter",
14
14
  "legacyPluginIds": [
@@ -37,7 +37,7 @@
37
37
  "displayName": "PassionCode.ai",
38
38
  "kind": "self",
39
39
  "path": "plugin/passioncode",
40
- "description": "The organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check that keeps the PassionCode.ai set current."
40
+ "description": "Set up and check PassionCode.ai on a computer with one command (setting-up-passioncode), the organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check that keeps the PassionCode.ai set current."
41
41
  }
42
42
  ]
43
43
  }
package/lib/catalog.js ADDED
@@ -0,0 +1,91 @@
1
+ 'use strict';
2
+ /**
3
+ * The PassionCode.ai apps `setup` installs and `doctor` checks: where each is released, which
4
+ * asset is its macOS build, where it lives once installed, its MCP server and the one read-only
5
+ * call that proves the server answers, and the product's own onboarding step. Public facts only;
6
+ * each is the product's own documentation (named per entry).
7
+ */
8
+ const path = require('path');
9
+
10
+ const TEAM_ID = 'KJ35UYYL22'; // Developer ID Application team of every PassionCode.ai macOS build
11
+ const SITE = 'https://passioncode.ai';
12
+
13
+ /** Agents the launcher registers MCP servers for: `cli` is detected by its command, the rest by a directory. */
14
+ const AGENTS = [
15
+ { id: 'claude', name: 'Claude Code', fabricRunner: 'claude-code' },
16
+ { id: 'codex', name: 'Codex CLI', fabricRunner: 'codex' },
17
+ { id: 'gemini', name: 'Gemini CLI', fabricRunner: null }, // fabric-mcp has no Gemini runner
18
+ { id: 'kimi', name: 'Kimi Code', fabricRunner: 'kimi-code' },
19
+ ];
20
+
21
+ const PRODUCTS = [
22
+ {
23
+ // fabric-switchboard docs/INSTALL.md, docs/AGENT-ONBOARDING.md
24
+ id: 'switchboard', name: 'Fabric Switchboard', repo: 'passioncode-ai/fabric-switchboard',
25
+ app: 'Fabric Switchboard.app', page: `${SITE}/switchboard/`,
26
+ asset: (v) => `Fabric-Switchboard-${v}-macos-universal.zip`,
27
+ mcp: {
28
+ name: 'switchboard', bin: 'Contents/MacOS/switchboard', args: () => ['mcp'],
29
+ claudePlugin: 'switchboard@', prove: { tool: 'switchboard_accounts', args: {} },
30
+ },
31
+ guide: 'Fabric Switchboard: your agent runs `switchboard onboard --status --json` and follows it; you sign in to each coding-agent account in the app.',
32
+ },
33
+ {
34
+ // fabric-dashboards README "Install", src/core/setup.ts
35
+ id: 'dashboards', name: 'Fabric Dashboards', repo: 'passioncode-ai/fabric-dashboards',
36
+ app: 'Fabric Dashboards.app', page: `${SITE}/dashboards/`,
37
+ asset: (v) => `Fabric-Dashboards-${v}-mac.zip`,
38
+ mcp: { name: 'fabric-dashboards', bin: 'Contents/Resources/bin/fabric-dashboards-mcp', args: () => [], prove: { tool: 'list_services', args: {} } },
39
+ guide: 'Fabric Dashboards: open it once; its first session is a Setup console your coding agent runs.',
40
+ },
41
+ {
42
+ // project-observatory-dashboard docs/AGENT-ONBOARDING.md (`full onboard`)
43
+ id: 'observatory', name: 'Project Observatory', repo: 'passioncode-ai/project-observatory-dashboard',
44
+ app: 'Project Observatory.app', page: `${SITE}/observatory/`,
45
+ asset: (v) => `ProjectObservatory-${v}-macos.zip`,
46
+ serviceIds: ['project-observatory'],
47
+ mcp: { name: 'observatory', external: true, prove: { tool: 'observatory_status', args: {} } },
48
+ guide: 'Project Observatory: your agent runs `project-observatory full onboard` and follows it (it registers the `observatory` MCP server); secrets go in through its hidden prompt, never the chat.',
49
+ },
50
+ {
51
+ // fabric-inbox docs/agents/onboard.md (B-79)
52
+ id: 'inbox', name: 'Fabric Inbox', repo: 'passioncode-ai/fabric-inbox',
53
+ app: 'Fabric Inbox.app', page: `${SITE}/inbox/`,
54
+ asset: (v) => `Fabric-Inbox-${v}-mac.zip`,
55
+ mcp: { name: 'fabric-inbox', external: true, remote: true },
56
+ guide: 'Fabric Inbox: your agent follows its onboard guide (github.com/passioncode-ai/fabric-inbox, docs/agents/onboard.md) to create your mail server; you enter the Cloudflare token in its hidden prompt, sign in with the emailed code and press Allow.',
57
+ },
58
+ {
59
+ // fabric apps/desktop/resources/bin/fabric-mcp, src/main/fabricMcpMain.ts
60
+ id: 'fabric', name: 'Fabric', repo: 'passioncode-ai/fabric', preview: true, prerelease: true,
61
+ app: 'Fabric.app', page: `${SITE}/fabric/`,
62
+ asset: (v) => `Fabric-${v}-universal.dmg`,
63
+ needs: ['docker', 'supabase'],
64
+ mcp: {
65
+ name: 'fabric', bin: 'Contents/Resources/bin/fabric-mcp',
66
+ args: (agent) => (agent.fabricRunner ? ['--runner', agent.fabricRunner] : null),
67
+ // fabric-mcp enrols a session with Fabric's hub as soon as it starts (fabricMcpMain.ts runs
68
+ // proxy.run at start), so doctor never starts it: it checks the entry and the binary, and the
69
+ // agent's own `fabric_whoami` is the proving call.
70
+ prove: null, spawn: false,
71
+ },
72
+ guide: 'Fabric (early preview): open it and finish its first run; Docker or OrbStack must be running. Then your agent calls `fabric_whoami` once to prove its MCP server.',
73
+ },
74
+ ];
75
+
76
+ function appsDir() { return process.env.PASSIONCODE_APPLICATIONS || '/Applications'; }
77
+ function appPath(product) { return path.join(appsDir(), product.app); }
78
+
79
+ /** The stdio command of a product's MCP server for one agent, or null (not bundled / no runner). */
80
+ function mcpCommand(product, agent) {
81
+ const m = product.mcp;
82
+ if (!m || m.external || !m.bin) return null;
83
+ const override = product.id === 'dashboards' ? process.env.PASSIONCODE_FABRIC_DASHBOARDS_MCP : null;
84
+ const command = override || path.join(appPath(product), m.bin);
85
+ const args = m.args(agent);
86
+ return args ? { command, args } : null;
87
+ }
88
+
89
+ function byId(id) { return PRODUCTS.find((p) => p.id === id) || null; }
90
+
91
+ module.exports = { PRODUCTS, AGENTS, TEAM_ID, SITE, appsDir, appPath, mcpCommand, byId };