@clossys/launcher 0.1.5 → 0.3.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 (114) hide show
  1. package/README.md +213 -20
  2. package/contracts/conversation-contract.md +41 -0
  3. package/dist/apply-plan-cli.d.ts +7 -0
  4. package/dist/apply-plan-cli.d.ts.map +1 -0
  5. package/dist/apply-plan-cli.js +96 -0
  6. package/dist/apply-plan-cli.js.map +1 -0
  7. package/dist/apply-plan.d.ts +79 -0
  8. package/dist/apply-plan.d.ts.map +1 -0
  9. package/dist/apply-plan.js +129 -0
  10. package/dist/apply-plan.js.map +1 -0
  11. package/dist/check-cli.d.ts.map +1 -1
  12. package/dist/check-cli.js +3 -0
  13. package/dist/check-cli.js.map +1 -1
  14. package/dist/cli.d.ts +2 -1
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +37 -11
  17. package/dist/cli.js.map +1 -1
  18. package/dist/contract.d.ts +28 -0
  19. package/dist/contract.d.ts.map +1 -0
  20. package/dist/contract.js +78 -0
  21. package/dist/contract.js.map +1 -0
  22. package/dist/core.d.ts +38 -6
  23. package/dist/core.d.ts.map +1 -1
  24. package/dist/core.js +289 -42
  25. package/dist/core.js.map +1 -1
  26. package/dist/doctor-cli.d.ts +4 -0
  27. package/dist/doctor-cli.d.ts.map +1 -0
  28. package/dist/doctor-cli.js +32 -0
  29. package/dist/doctor-cli.js.map +1 -0
  30. package/dist/doctor.d.ts +28 -0
  31. package/dist/doctor.d.ts.map +1 -0
  32. package/dist/doctor.js +68 -0
  33. package/dist/doctor.js.map +1 -0
  34. package/dist/host.d.ts.map +1 -1
  35. package/dist/host.js +3 -0
  36. package/dist/host.js.map +1 -1
  37. package/dist/hosts.d.ts +14 -0
  38. package/dist/hosts.d.ts.map +1 -0
  39. package/dist/hosts.js +61 -0
  40. package/dist/hosts.js.map +1 -0
  41. package/dist/index.d.ts +15 -2
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +7 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/inventory-adoption.d.ts +20 -0
  46. package/dist/inventory-adoption.d.ts.map +1 -0
  47. package/dist/inventory-adoption.js +67 -0
  48. package/dist/inventory-adoption.js.map +1 -0
  49. package/dist/manifest.d.ts +20 -0
  50. package/dist/manifest.d.ts.map +1 -0
  51. package/dist/manifest.js +106 -0
  52. package/dist/manifest.js.map +1 -0
  53. package/dist/model-profile.d.ts +46 -0
  54. package/dist/model-profile.d.ts.map +1 -0
  55. package/dist/model-profile.js +98 -0
  56. package/dist/model-profile.js.map +1 -0
  57. package/dist/product-repository.d.ts +26 -0
  58. package/dist/product-repository.d.ts.map +1 -0
  59. package/dist/product-repository.js +49 -0
  60. package/dist/product-repository.js.map +1 -0
  61. package/dist/skills.d.ts +20 -1
  62. package/dist/skills.d.ts.map +1 -1
  63. package/dist/skills.js +98 -7
  64. package/dist/skills.js.map +1 -1
  65. package/dist/types.d.ts +57 -1
  66. package/dist/types.d.ts.map +1 -1
  67. package/model-profiles/claude-code.json +10 -0
  68. package/model-profiles/codex.json +10 -0
  69. package/model-profiles/cursor.json +10 -0
  70. package/package.json +9 -5
  71. package/skeleton/README.md +5 -0
  72. package/skeleton/package.json +1 -1
  73. package/skill/SKILL.md +41 -0
  74. package/skill-catalogue/advisor/SKILL.md +46 -8
  75. package/skill-catalogue/architect/SKILL.md +0 -11
  76. package/skill-catalogue/bouncer/SKILL.md +0 -11
  77. package/skill-catalogue/builder/SKILL.md +0 -11
  78. package/skill-catalogue/butler/SKILL.md +0 -11
  79. package/skill-catalogue/controller/SKILL.md +6 -9
  80. package/skill-catalogue/customer/SKILL.md +83 -0
  81. package/skill-catalogue/designer/SKILL.md +18 -9
  82. package/skill-catalogue/giver/SKILL.md +0 -11
  83. package/skill-catalogue/influencer/SKILL.md +0 -11
  84. package/skill-catalogue/inspector/SKILL.md +1 -12
  85. package/skill-catalogue/integrator/SKILL.md +0 -11
  86. package/skill-catalogue/keeper/SKILL.md +0 -11
  87. package/skill-catalogue/launcher/SKILL.md +0 -12
  88. package/skill-catalogue/locksmith/SKILL.md +0 -11
  89. package/skill-catalogue/messenger/SKILL.md +0 -11
  90. package/skill-catalogue/observer/SKILL.md +0 -11
  91. package/skill-catalogue/publisher/SKILL.md +18 -10
  92. package/skill-catalogue/starter/SKILL.md +0 -11
  93. package/skill-catalogue/strategist/SKILL.md +35 -12
  94. package/skill-catalogue/writer/SKILL.md +7 -9
  95. package/src/apply-plan-cli.ts +94 -0
  96. package/src/apply-plan.ts +172 -0
  97. package/src/check-cli.ts +3 -0
  98. package/src/cli.ts +45 -10
  99. package/src/contract.ts +81 -0
  100. package/src/core.ts +337 -37
  101. package/src/doctor-cli.ts +33 -0
  102. package/src/doctor.ts +145 -0
  103. package/src/host.ts +3 -0
  104. package/src/hosts.ts +79 -0
  105. package/src/index.ts +33 -0
  106. package/src/inventory-adoption.ts +85 -0
  107. package/src/manifest.ts +103 -0
  108. package/src/model-profile.ts +148 -0
  109. package/src/product-repository.ts +73 -0
  110. package/src/skills.ts +113 -8
  111. package/src/types.ts +58 -1
  112. package/CHANGELOG.md +0 -64
  113. /package/skeleton/{.clossys → clossys/.state}/inventory.json +0 -0
  114. /package/skeleton/{.clossys → clossys/.state}/workspace.json +0 -0
package/README.md CHANGED
@@ -8,6 +8,46 @@ The hub inventories where Foundry packages are installed and coordinates
8
8
  engagement. It is not a product application and does not receive a dump of
9
9
  the catalogue.
10
10
 
11
+ ## Layout: the `clossys/` folder
12
+
13
+ Every apply writes to one visible `clossys/` folder in the target repository —
14
+ never a hidden dot-folder for anything a person might want to see. A generated
15
+ `README.md` at the root of `clossys/` is an index of which `clossys/<role>/` folders are active and what
16
+ each holds; it is rewritten on every run, never hand-edited. `clossys/.state/`
17
+ holds machine files only — the hub marker, the inventory, and the skills
18
+ manifest (below) — visible so it is easy to find, but still not a place to
19
+ edit by hand. Other roles' folders (`clossys/strategist/`, `clossys/writer/`,
20
+ and so on) are written by their own packages, not by launcher.
21
+
22
+ A hub created before this layout existed kept its marker and inventory under
23
+ a hidden `.clossys/`. Resume detects that automatically and migrates both
24
+ files to `clossys/.state/`, removing the old directory, and reports the move
25
+ in the health report. If somehow both a `.clossys/` and a `clossys/.state/`
26
+ hub state exist at once, launcher refuses rather than guessing which one is
27
+ current — remove one and resume again.
28
+
29
+ ## Conversation contract
30
+
31
+ Every composed skill carries one shared conversation contract: lead with a
32
+ plain-language status, always state a recommendation, ask exactly one
33
+ question with the recommended option labelled first, and say what happens
34
+ next. Launcher packs this contract at build time and injects it into each
35
+ composed skill in place of that skill's own "how we work together" and "one
36
+ question at a time" sections, at the same position — so an installed or
37
+ catalogue skill's own wording never has to drift from it.
38
+
39
+ ## Skills manifest and freshness
40
+
41
+ Every apply writes a skills manifest recording each composed skill's source
42
+ (`installed` or `catalogue`), version, and a content digest. The health
43
+ report states how many composed skills are out of date against the live
44
+ `@clossys/launcher` version (catalogue-sourced skills are only ever as fresh
45
+ as the launcher release that packed them) and how many were retired this
46
+ run. Retirement means: a skill this directory's own manifest previously
47
+ listed is no longer composed (its source disappeared), so launcher removes
48
+ its composed output and host discovery links — and only that. It never
49
+ touches a skill it did not itself write.
50
+
11
51
  ## Health report and staleness
12
52
 
13
53
  After create, resume, or appoint — and on every resume — the command prints
@@ -16,11 +56,17 @@ a read-only health report. It scans all four dependency buckets
16
56
  `peerDependencies`) for the Advisor pin and for extra `@clossys/*` names.
17
57
  When the live registry version is known, each pin is graded against it: a
18
58
  pin older than live is a `stale pin` finding and marks the report
19
- **degraded**. Exit stays 0 on resume (the report is advisory); adopt prints
20
- the same report and an unparseable pin-versus-live comparison is noted as
21
- indeterminate rather than stale. `checkInventoryEntries()` additionally
22
- validates hub inventory ids read-only, marking ids whose repository no
23
- longer resolves (skipped with a note when `gh` is unavailable).
59
+ **degraded**. The report is also degraded when Advisor is missing, dual-pinned,
60
+ or present in any bucket other than `devDependencies`, and when apply skipped
61
+ one or more inventoried roster targets (missing sibling clone, origin mismatch,
62
+ and similar — the same `skill roster skipped` lines in the report). Per-package
63
+ skill sources missing from the catalogue are noted but do not by themselves mark
64
+ degraded. Exit stays 0 on resume
65
+ (the report is advisory); adopt prints the same report and an unparseable
66
+ pin-versus-live comparison is noted as indeterminate rather than stale.
67
+ `checkInventoryEntries()` additionally validates hub inventory ids read-only,
68
+ marking ids whose repository no longer resolves (skipped with a note when
69
+ `gh` is unavailable).
24
70
 
25
71
 
26
72
  ## Install
@@ -31,13 +77,25 @@ The get-started command is the package name:
31
77
  npx @clossys/launcher
32
78
  ```
33
79
 
80
+ `npx` caches the resolved version, so a plain `npx @clossys/launcher` can
81
+ keep running an old one. Because a catalogue-sourced skill is only ever as
82
+ fresh as the launcher release that packed it (see "Skills manifest and
83
+ freshness" above), run:
84
+
85
+ ```bash
86
+ npx @clossys/launcher@latest
87
+ ```
88
+
89
+ when the health report says a skill is out of date, or whenever you want to
90
+ be sure you are on the current release.
91
+
34
92
  Public npm reads are credentialless. Packages publish to
35
93
  `https://registry.npmjs.org`. Do not add a token or private registry
36
94
  mapping for `@clossys`. Pin an exact version once you depend on the library
37
95
  API:
38
96
 
39
97
  ```bash
40
- npm install --save-dev --save-exact @clossys/launcher@0.1.5
98
+ npm install --save-dev --save-exact @clossys/launcher@0.2.0
41
99
  ```
42
100
 
43
101
  ## Talking to the team
@@ -54,11 +112,20 @@ Voices are how you talk in a coding agent; they are not engagement engines. The
54
112
  `@clossys-advisor` in chat is its hiring and compatibility voice. Use
55
113
  `@clossys-advisor` and `@clossys-<package>` in the hub or in any inventoried
56
114
  product repository. Each voice can talk even when that npm package is not pinned
57
- in that repo. Launcher health notes missing catalogue sources but apply continues.
115
+ in that repo. When composing into a checkout, the launcher reads each skill body
116
+ from that checkout's installed `@clossys/<package>/skill/SKILL.md` when present,
117
+ then from the packed catalogue or a sibling monorepo source. Launcher health
118
+ notes missing catalogue sources but apply continues.
58
119
 
59
120
  Run `npx @clossys/launcher` again from the hub for a health report and to
60
- refresh composed voices on sibling inventoried clones. It does not
61
- `gh repo clone` missing inventory entries—that is not how you talk to the team.
121
+ refresh composed voices on sibling inventoried clones. By default it does
122
+ not `gh repo clone` missing inventory entries -- that is not how you talk to
123
+ the team. `launcher --clone-missing` is the one explicit, approved
124
+ exception (#1179): on resume only, it clones every inventoried repository
125
+ not yet sitting beside the hub, using `cloneMissingInventoryRepositories()`,
126
+ and only those -- an id skipped for any other reason (wrong account, the
127
+ Foundry supplier tree, a mismatched git origin) is left exactly as skipped,
128
+ never attempted.
62
129
 
63
130
  ## How to run it
64
131
 
@@ -72,9 +139,9 @@ silent fallback.
72
139
 
73
140
  | Current directory | What happens |
74
141
  | --- | --- |
75
- | Empty | Creates `{owner}/workspace` from the in-package skeleton, or clones that hub if it already exists. |
76
- | Already a hub (generated marker; packed template `skeleton/.clossys/workspace.json`) | Resumes. No new repository. `--inventory` here is refused with a pointer to the appointed hub's own `.clossys/inventory.json`. |
77
- | Any other GitHub repository you control | Appoints it as the account hub. Keeps the existing name and files. Refuses when the working tree has uncommitted changes (`git status --porcelain` non-empty) — the refusal names the offending remote host when the origin is not on github.com. Refuses when `CLOSSYS_OWNER` names a different account than the repository's github.com origin owner. Writes the hub marker. Leaves an existing `@clossys/advisor` pin in whichever bucket it already occupies; pins live Advisor in `devDependencies` only when missing. Refuses if the generated hub inventory is missing or empty (packed template `skeleton/.clossys/inventory.json`; that generated path does not ship) unless `--inventory <path>` supplies a populated document — or, when the on-disk inventory is already populated and `--inventory` is also supplied, merges the two by repository id (on-disk order first, new ids appended, first occurrence of an id wins). Does not rewrite the lockfile or dump the catalogue. Prints a read-only health report. |
142
+ | Empty | Creates `{owner}/workspace` from the in-package skeleton (package name `@owner/workspace`), or clones that hub if it already exists. |
143
+ | Already a hub (generated marker; packed template `skeleton/clossys/.state/workspace.json`) | Resumes. No new repository. `--inventory` here is refused with a pointer to the appointed hub's own `clossys/.state/inventory.json`. A legacy `.clossys/` hub state is migrated automatically; see "Layout" above. |
144
+ | Any other GitHub repository you control | Appoints it as the account hub. Keeps existing product files. Refuses when the working tree has uncommitted changes (`git status --porcelain` non-empty) — the refusal names the offending remote host when the origin is not on github.com. Refuses when `CLOSSYS_OWNER` names a different account than the repository's github.com origin owner. Writes the hub marker. Pins live `@clossys/advisor` in `devDependencies`, relocating and upgrading any pin left in another bucket. A dedicated `{owner}/workspace` checkout is named `@owner/workspace`; a product repository keeps its package name. Always reads the public Advisor version (needed to pin live and to grade resume health). Refuses if the generated hub inventory is missing or empty (packed template `skeleton/clossys/.state/inventory.json`; that generated path does not ship) unless `--inventory <path>` supplies a populated document — or, when the on-disk inventory is already populated and `--inventory` is also supplied, merges the two by repository id (on-disk order first, new ids appended, first occurrence of an id wins). Does not rewrite the lockfile or dump the catalogue. Prints a read-only health report. |
78
145
 
79
146
  It does not have to be a brand-new exclusive repository, and it does not
80
147
  have to already match a Foundry layout. Informal "workspace-looking" trees
@@ -85,17 +152,23 @@ not install packages.
85
152
 
86
153
  Do not run this inside the Foundry supplier tree.
87
154
 
88
- Open the resulting folder in your coding agent. Advisor stays read-only
89
- until you approve a next action. The same command resumes later.
155
+ Open the resulting folder in your coding agent. Talk in ordinary
156
+ sentences. Advisor stays read-only until you approve a next action. A
157
+ yes in chat is permission for that one step only; it is not a lasting
158
+ grant and it does not write git unless a file is saved later. The same
159
+ command resumes later.
90
160
 
91
161
  ## CLI
92
162
 
93
163
  ```bash
94
164
  launcher
95
165
  launcher --inventory path/to/inventory.json
166
+ launcher --clone-missing
96
167
  launcher --help
97
168
  launcher-check --help
98
169
  launcher-check --input observation.json
170
+ launcher-doctor
171
+ launcher-apply-plan --plan plan.json --brief brief.json --repo ./product-checkout
99
172
  ```
100
173
 
101
174
  Exit codes preserve the ternary:
@@ -113,21 +186,137 @@ Exit codes preserve the ternary:
113
186
  | Export | Description |
114
187
  | --- | --- |
115
188
  | `planWorkspace()` | Decides create, resume, or adopt from a cwd observation. Optional `{ inventoryPath }` is the only way to appoint without a populated on-disk inventory. |
116
- | `applyWorkspacePlan()` | Copies the in-package skeleton or hub marker through a host port and returns a `WorkspaceApplyResult` with health. Composes the same skill voices on the hub and on inventoried sibling checkouts beside it; refreshes stale hub guidance on every path, including resume. Optional `{ skillCatalogueRoot, launcherPackageRoot }` selects where skill bodies are read. |
117
- | `observeWorkspace()` | Reads `gh`, git remotes, cwd, inventory classification, and the public Advisor version. |
189
+ | `applyWorkspacePlan()` | Copies the in-package skeleton or hub marker through a host port and returns a `WorkspaceApplyResult` with health. Composes the same skill voices (with the shared conversation contract injected) on the hub and on inventoried sibling checkouts beside it; refreshes stale hub guidance and the generated `clossys/` README on every path, including resume; migrates a legacy `.clossys/` hub state automatically. Optional `{ skillCatalogueRoot, launcherPackageRoot, contractPath, liveLauncherVersion }` selects where skill and contract bodies are read and grades skill-manifest staleness. |
190
+ | `observeWorkspace()` | Reads `gh`, git remotes, cwd, inventory classification, hub-state migration status, and the public Advisor version. |
118
191
  | `readInventoryRepositories()` | Reads repository ids from a `schemaVersion: 1` inventory document. |
119
- | `launcherPackageRootFromModule()` | Resolves this package's root from `import.meta.url` so apply can find the packed skill catalogue. |
192
+ | `readLiveLauncherVersion()` | Reads the public `@clossys/launcher` registry version, used only to grade catalogue-sourced skill staleness. |
193
+ | `launcherPackageRootFromModule()` | Resolves this package's root from `import.meta.url` so apply can find the packed skill catalogue and contract. |
120
194
  | `parseGitHubRemote()` | Parses a github.com remote and rejects any other host. |
121
- | `isHubDocument()` | Type guard for the generated hub marker (packed template: `skeleton/.clossys/workspace.json`). |
195
+ | `isHubDocument()` | Type guard for the generated hub marker (packed template: `skeleton/clossys/.state/workspace.json`). |
122
196
  | `inspectInventory()` | Classifies inventory JSON as missing, empty, or populated. |
123
- | `reportHubHealth()` | Read-only pin and inventory report. Does not install or uninstall. |
197
+ | `reportHubHealth()` | Read-only pin, inventory, migration, and skills-manifest report. Does not install or uninstall. |
124
198
  | `formatHubHealth()` | Human lines plus a `health:` JSON line for the same report. |
125
199
  | `hasAdvisorPin()` | True when a manifest already pins Advisor in any dependency bucket. |
126
200
  | `checkInventoryEntries()` | Read-only inventory id validation through `gh repo view` (batched; skips with a note when `gh` is unavailable). |
127
201
  | `DEFAULT_REPOSITORY_NAME` | Default new-hub repository name (`workspace`). Used only when creating, never when appointing. |
202
+ | `CLOSSYS_DIR_REL` | Relative path of the one visible per-repository Clossys folder (`clossys`). |
203
+ | `STATE_DIR_REL` | Relative path of the machine-state folder (`clossys/.state`). |
128
204
  | `WORKSPACE_MARKER_REL` | Relative path of the hub marker. |
129
205
  | `WORKSPACE_INVENTORY_REL` | Relative path of the hub inventory. |
130
- | `CommandResult` / `CwdObservation` / `DependencyBucket` / `HubDocument` / `HubHealthReport` / `InventoryObservation` / `InventoryValidationEntry` / `InventoryValidationReport` / `PinFinding` / `PinGrade` / `ApplyWorkspaceOptions` / `WorkspaceApplyResult` / `WorkspaceDecision` / `WorkspaceHost` / `WorkspaceObservation` / `WorkspacePlan` / `WorkspaceRefusal` / `WorkspaceState` | Typed host, observation, plan, health, and outcome contracts. |
206
+ | `CLOSSYS_README_REL` | Relative path of the generated index README at the root of `clossys/`. |
207
+ | `LEGACY_STATE_DIR_REL` / `LEGACY_WORKSPACE_MARKER_REL` / `LEGACY_WORKSPACE_INVENTORY_REL` | Pre-#1171 `.clossys/` paths, kept only so resume can detect and migrate them. |
208
+ | `CommandResult` / `CwdObservation` / `DependencyBucket` / `HubDocument` / `HubHealthReport` / `HubMigrationState` / `InventoryObservation` / `InventoryValidationEntry` / `InventoryValidationReport` / `PinFinding` / `PinGrade` / `SkillManifestDocument` / `SkillManifestEntry` / `SkillsManifestSummary` / `ApplyWorkspaceOptions` / `WorkspaceApplyResult` / `WorkspaceDecision` / `WorkspaceHost` / `WorkspaceObservation` / `WorkspacePlan` / `WorkspaceRefusal` / `WorkspaceState` | Typed host, observation, plan, health, and outcome contracts. |
209
+ | `cloneMissingInventoryRepositories()` | Explicit, approved action (#1179): clones every inventoried repository `resolveSisterCloneTargets` skipped for "not beside the hub", and only those. Returns a `CloneMissingOutcome[]`. |
210
+ | `runDoctorChecks()` | Read-only prerequisite checks in fix-in-this-order sequence: git, `gh`, signed in, Node.js, npm, then the advisory coding-agent step. Returns a `DoctorReport`. |
211
+ | `renderDoctorReport()` | Renders a `DoctorReport` one step at a time, the way `launcher-doctor` prints it. |
212
+ | `checkCloudSessionBootstrap()` | Read-only: the three product-repository-layout.json cloud-session-bootstrap checks against a directory. Returns a `CloudBootstrapReport`. |
213
+ | `reportInventoryDrift()` | Compares a declared external inventory against the launcher-written one; reports external-only, launcher-only, and agreeing repository ids. Returns an `InventoryDriftReport`. |
214
+ | `detectLinkedHosts()` | Read-only: which of `claude-code`, `cursor`, `codex` can currently discover skills in a directory. |
215
+ | `serializeHostRecord()` / `parseHostRecord()` | Round-trip `clossys/.state/hosts.json` (`HOSTS_REL`). |
216
+ | `parsePreferences()` | Reads `clossys/preferences.json`'s budget stance; defaults to `"balanced"` on absence or malformed input. |
217
+ | `readHostModelProfile()` | Reads a packed `model-profiles/<host>.json`; returns `undefined`, never throws, on a missing or malformed file. |
218
+ | `resolveModelForTier()` | Resolves a tier and budget preference to one model name for a host, reporting `belowFloor` rather than silently substituting a weaker tier's model. |
219
+ | `validateAdvisorPlan()` / `validateEngagementBrief()` | Shape validation against the #1175 "Plan file contract" for `clossys/advisor/plan.json` and `clossys/brief.json`. |
220
+ | `isPlanApproved()` | True only when a plan's most recent decision (by timestamp) has `chosen === "approved"`. |
221
+ | `applyEngagementBrief()` | Writes `clossys/brief.json` into a repository directory once both the plan and the brief validate; refuses and writes nothing otherwise. |
222
+ | `CloneMissingOutcome` / `DoctorCheckHost` / `DoctorReport` / `DoctorStepId` / `DoctorStepResult` / `CloudBootstrapCheck` / `CloudBootstrapReport` / `ExternalInventoryDeclaration` / `InventoryDriftReport` / `DiscoveredHost` / `HostRecord` / `BudgetPreference` / `HostModelProfile` / `HostTierMapping` / `ModelResolution` / `PreferencesDocument` / `ReasoningTier` / `SupportedHost` / `AdvisorPlan` / `ApplyBriefResult` / `BlockerKind` / `EngagementBrief` / `EngagementBriefRole` / `PlanBlocker` / `PlanDecision` / `ValidationResult` | Typed contracts for the sections above. |
223
+
224
+ ## Doctor
225
+
226
+ `launcher-doctor` (installed alongside `@clossys/launcher`) is read-only and
227
+ never writes anything (#1220). It checks the prerequisites a client needs
228
+ before the hub even exists -- git, the GitHub command-line tool, whether
229
+ you are signed in, Node.js, and npm -- and reports the first thing that is
230
+ missing, in plain language, with the one next action to take. Run it again
231
+ after fixing that one thing; it always reports the next thing, never a dump
232
+ of everything at once. A missing coding agent is reported but never blocks
233
+ the verdict: `runDoctorChecks()` marks it advisory, since Launcher cannot
234
+ detect every host and it is a one-time choice, not a step to fix in
235
+ sequence. `renderDoctorReport()` renders the report the way the CLI prints
236
+ it.
237
+
238
+ ## Product repositories
239
+
240
+ `docs/contracts/product-repository-layout.json` (this repository's own contract; it does not ship in the published package) extends the account hub's
241
+ `clossys/` layout to a product repository: `apps/*`, workspace wiring so
242
+ `@clossys/*` packages install as exact pinned versions, `AGENTS.md` /
243
+ `CLAUDE.md` pointers, and the CI Starter proof (#1215). A cloud agent
244
+ session (browser plus GitHub, no local setup) can pick up a product
245
+ repository too -- `checkCloudSessionBootstrap()` verifies exactly what that
246
+ session needs: a resolvable `package.json` plus `package-lock.json` pair,
247
+ an `AGENTS.md` that mentions `clossys/`, and a hub marker at the same
248
+ relative path as the packed template `skeleton/clossys/.state/workspace.json`.
249
+ It never runs `npm ci` itself and never mutates anything; it only reports
250
+ which of the three is missing.
251
+
252
+ ## Inventory: adopting an existing source
253
+
254
+ When an account already keeps a repository inventory in its own control
255
+ plane, launcher reads it as the source of truth instead of writing a
256
+ second, diverging one (#1216). Declare it by hand-editing the hub marker (the packed template
257
+ `skeleton/clossys/.state/workspace.json`; the generated path does not
258
+ ship) to add an `externalInventory: { path, shape }` field (a `"foundry"`
259
+ or `"custom"` shape). Every `launcher` run
260
+ (create, resume, or appoint) then calls `reportInventoryDrift()`
261
+ automatically and prints the result in hub health output when the marker
262
+ declares one: three sets, all three even when one is empty -- ids only in
263
+ the external source, ids only in launcher's own inventory, and ids both
264
+ agree on. A `"custom"` shape is reported indeterminate rather than guessed
265
+ at -- launcher has no mapping for a non-foundry inventory shape yet. It
266
+ never merges the two silently; writing the reconciled set is left as a
267
+ separate, explicit apply step for a follow-up.
268
+
269
+ ## Hosts
270
+
271
+ Every `launcher` run (create, resume, or appoint) records which
272
+ coding-agent hosts a directory could already discover skills through,
273
+ *before* that run composes skills and stamps every host's discovery path
274
+ (#1180): `claude-code` and `cursor` are detected by their own discovery
275
+ symlink (`.claude/skills`, `.cursor/skills`); `codex` is detected by the
276
+ presence of `.agents/skills` itself, since Codex reads repository skills
277
+ from that path directly and needs no separate discovery link (verified
278
+ against developers.openai.com/codex/skills, 2026-09-22). The snapshot is
279
+ written to `clossys/.state/hosts.json` (`HOSTS_REL`, via
280
+ `serializeHostRecord()` / `parseHostRecord()`) for the hub and for every
281
+ sibling clone launcher composes skills into -- so a consumer such as
282
+ Advisor's next-action phrasing can name the client's actual tool instead
283
+ of guessing.
284
+
285
+ ## Model guidance
286
+
287
+ Packages declare what a step demands -- a reasoning tier (`light` /
288
+ `standard` / `deep`) -- never a model name (#1219). Launcher ships the
289
+ tier-to-model mapping per host in `model-profiles/<host>.json`, dated and
290
+ re-verified against that host's real current models;
291
+ `readHostModelProfile()` reads one. `parsePreferences()` reads
292
+ `clossys/preferences.json`'s budget stance (`cost-conscious` / `balanced` /
293
+ `max-quality`, defaulting to `balanced` when absent or malformed) --
294
+ Advisor asks the question and writes the file; this package only resolves
295
+ against it. `resolveModelForTier()` combines a profile, a tier, and a
296
+ preference into one `ModelResolution`, and reports `belowFloor` rather than
297
+ silently substituting a weaker model when a caller-supplied hard floor
298
+ tier cannot be met.
299
+
300
+ ## Applying an approved plan
301
+
302
+ `launcher-apply-plan --plan <plan.json> --brief <brief.json> --repo <dir>`
303
+ writes `clossys/brief.json` into a staffed repository once the plan is
304
+ approved (#1178). `validateAdvisorPlan()` and `validateEngagementBrief()`
305
+ check both files against the exact shapes recorded on issue #1175's "Plan
306
+ file contract"; `isPlanApproved()` reads a plan's most recent decision (by
307
+ timestamp, not array position) and requires it to be `"approved"` --
308
+ absence of any decision is never treated as approval.
309
+ `applyEngagementBrief()` refuses, and writes nothing, unless both checks
310
+ pass, then writes the brief byte-identically -- it never re-authors its
311
+ prose. This package does not compute a brief's content (that is
312
+ `@clossys/advisor`'s `EngagementBrief`, landing in #1193) and does not
313
+ decide whether a plan should be approved (that is Advisor's job); it only
314
+ validates the two landed shapes and writes the one file. Multi-repository
315
+ orchestration -- branch creation, exact package installs, adding Starter's
316
+ caller workflow, and opening one pull request per repository -- is
317
+ deferred: the landed contract does not yet specify how a plan's approved
318
+ roles map to inventory repository ids or to install/remove/relocate work
319
+ items.
131
320
 
132
321
  ## Why this is not Advisor, Starter, Builder, installer, creator, or a connector
133
322
 
@@ -170,3 +359,7 @@ repository.
170
359
  ## Licence
171
360
 
172
361
  MIT.
362
+
363
+ ## Changelog
364
+
365
+ Release notes for every version are in the [changelog](https://github.com/clossys/foundry/blob/main/docs/changelogs/launcher.md), kept in the public repository rather than in the installed package.
@@ -0,0 +1,41 @@
1
+ # Conversation contract
2
+
3
+ One conversation contract for every Foundry role (#1182). Every `packages/*/skill/SKILL.md`
4
+ carried a byte-identical `## How we work together` / `## One question at a time`
5
+ pair with no gate enforcing it; this file is the single source those sections
6
+ now come from. `@clossys/launcher` packs this file at build time and, when it
7
+ composes a skill, replaces that skill's own `## How we work together` and
8
+ `## One question at a time` sections (if present) with the block below, at
9
+ the same position. Packages keep their own role content; only this shared
10
+ block is owned here. A later, separate migration removes the duplicated
11
+ block from each package's own `skill/SKILL.md` on its next natural version
12
+ bump — no package edit is needed for the contract to take effect, because
13
+ composition already replaces it.
14
+
15
+ Everything from the heading below to the end of this file is the injected
16
+ block, verbatim.
17
+
18
+ ## How we work together
19
+
20
+ Before anything else, a role reads `clossys/brief.json` to learn why it is
21
+ staffed here and what its goals are. If the brief is absent, or does not
22
+ staff this role, it says so in plain language and routes the client to
23
+ `@clossys-advisor`, rather than improvising a mandate.
24
+
25
+ Every reply has four parts:
26
+
27
+ 1. **Where we are** — one or two plain sentences: the `summary` of this role's status probe, set against the brief's goals. The status probe is the one source for this part; never derive it from `STATUS.md` or any other file. If the role has no status probe, or it cannot measure yet, say so plainly.
28
+ 2. **My recommendation** — what we would do, with a one-line reason. Always stated.
29
+ 3. **Your call** — one question with 2-4 options, the recommended option listed first and labelled, "something else" as the only free-text path. Use the host's multiple-choice control when one exists; otherwise numbered picks.
30
+ 4. **What happens next** — what happens if the client takes the recommendation.
31
+
32
+ Rules:
33
+
34
+ - Ask only what only the client can know: business facts, audience, taste, authority, risk appetite.
35
+ - Decide craft yourself and state it (for example, "I'm using a four-step type scale; say if you want otherwise").
36
+ - Never ask for ids, slugs, paths, versions, commands, or tool choices.
37
+ - Translate machine states into plain language; no exit codes or check names in the default reply.
38
+ - One decision per turn; no forms.
39
+ - Push back once, plainly, when a choice goes against the recommendation. Refuse, with the reason, when a choice breaks a hard rule.
40
+ - Read-only until approval, one approved step at a time; chat agreement by itself is never authorization.
41
+ - Invoked with the `loop` keyword, run exactly one iteration of the five stages defined in `@clossys/controller`'s shipped `contracts/role-loop-archetypes.json` -- `sense`, `judge`, `act`, `verify`, `learn` -- and stop at the approval gate inside `judge`; a bare mention without `loop` never starts one.
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { createNodeHost } from "./host.js";
3
+ export declare const APPLY_PLAN_USAGE = "Usage: launcher-apply-plan --plan <plan.json> --brief <brief.json> --repo <directory>\n\nWrites clossys/brief.json into <directory> from the given brief, once the\ngiven plan's most recent decision is \"approved\". Refuses, and writes\nnothing, otherwise.\n\nDeterministic mechanics only: this does not decide whether a plan should be\napproved (that is Advisor's job) and does not compute the brief's content\n(that is @clossys/advisor's EngagementBrief, #1193) -- it validates the\nexact shapes recorded on issue #1175 and writes the one file.\n\nExit codes: 0 = applied, 1 = refused (not approved, or a shape does not\nvalidate), 2 = a given file could not be read as JSON.";
4
+ export declare class ApplyPlanInputError extends Error {
5
+ }
6
+ export declare function main(argv: readonly string[], host: ReturnType<typeof createNodeHost>): number;
7
+ //# sourceMappingURL=apply-plan-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-plan-cli.d.ts","sourceRoot":"","sources":["../src/apply-plan-cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG3C,eAAO,MAAM,gBAAgB,yqBAY0B,CAAC;AAExD,qBAAa,mBAAoB,SAAQ,KAAK;CAAG;AAgCjD,wBAAgB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,GAAG,MAAM,CAgC7F"}
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import { isDirectInvocation } from "./cli.js";
3
+ import { createNodeHost } from "./host.js";
4
+ import { applyEngagementBrief, validateAdvisorPlan, validateEngagementBrief } from "./apply-plan.js";
5
+ export const APPLY_PLAN_USAGE = `Usage: launcher-apply-plan --plan <plan.json> --brief <brief.json> --repo <directory>
6
+
7
+ Writes clossys/brief.json into <directory> from the given brief, once the
8
+ given plan's most recent decision is "approved". Refuses, and writes
9
+ nothing, otherwise.
10
+
11
+ Deterministic mechanics only: this does not decide whether a plan should be
12
+ approved (that is Advisor's job) and does not compute the brief's content
13
+ (that is @clossys/advisor's EngagementBrief, #1193) -- it validates the
14
+ exact shapes recorded on issue #1175 and writes the one file.
15
+
16
+ Exit codes: 0 = applied, 1 = refused (not approved, or a shape does not
17
+ validate), 2 = a given file could not be read as JSON.`;
18
+ export class ApplyPlanInputError extends Error {
19
+ }
20
+ function parseArgs(argv) {
21
+ if (argv.length === 1 && (argv[0] === "--help" || argv[0] === "-h"))
22
+ return { help: true };
23
+ const flags = new Map();
24
+ for (let index = 0; index < argv.length; index += 2) {
25
+ const name = argv[index];
26
+ const value = argv[index + 1];
27
+ if ((name !== "--plan" && name !== "--brief" && name !== "--repo") || value === undefined) {
28
+ throw new ApplyPlanInputError("usage: launcher-apply-plan --plan <path> --brief <path> --repo <directory>");
29
+ }
30
+ flags.set(name, value);
31
+ }
32
+ const planPath = flags.get("--plan");
33
+ const briefPath = flags.get("--brief");
34
+ const repoDirectory = flags.get("--repo");
35
+ if (planPath === undefined || briefPath === undefined || repoDirectory === undefined) {
36
+ throw new ApplyPlanInputError("--plan, --brief, and --repo are all required");
37
+ }
38
+ return { help: false, planPath, briefPath, repoDirectory };
39
+ }
40
+ function readJson(readText, path, label) {
41
+ const raw = readText(path);
42
+ if (raw === null)
43
+ throw new ApplyPlanInputError(`${label} could not be read: ${path}`);
44
+ try {
45
+ return JSON.parse(raw);
46
+ }
47
+ catch {
48
+ throw new ApplyPlanInputError(`${label} is not valid JSON: ${path}`);
49
+ }
50
+ }
51
+ export function main(argv, host) {
52
+ const parsed = parseArgs(argv);
53
+ if (parsed.help) {
54
+ console.log(APPLY_PLAN_USAGE);
55
+ return 0;
56
+ }
57
+ let planRaw;
58
+ let briefRaw;
59
+ try {
60
+ planRaw = readJson(host.readText, parsed.planPath, "--plan");
61
+ briefRaw = readJson(host.readText, parsed.briefPath, "--brief");
62
+ }
63
+ catch (cause) {
64
+ console.error(`launcher-apply-plan: ${cause instanceof Error ? cause.message : String(cause)}`);
65
+ return 2;
66
+ }
67
+ const planValidation = validateAdvisorPlan(planRaw);
68
+ if (!planValidation.valid) {
69
+ console.error(`launcher-apply-plan: --plan does not validate: ${planValidation.reason}`);
70
+ return 1;
71
+ }
72
+ const briefValidation = validateEngagementBrief(briefRaw);
73
+ if (!briefValidation.valid) {
74
+ console.error(`launcher-apply-plan: --brief does not validate: ${briefValidation.reason}`);
75
+ return 1;
76
+ }
77
+ const result = applyEngagementBrief(host, parsed.repoDirectory, planRaw, briefRaw, "clossys/brief.json");
78
+ if (result.state === "refused") {
79
+ console.error(`launcher-apply-plan: refused -- ${result.reason}`);
80
+ return 1;
81
+ }
82
+ console.log(`wrote ${result.path}`);
83
+ return 0;
84
+ }
85
+ function run() {
86
+ try {
87
+ process.exitCode = main(process.argv.slice(2), createNodeHost());
88
+ }
89
+ catch (cause) {
90
+ console.error(`launcher-apply-plan: ${cause instanceof Error ? cause.message : String(cause)}`);
91
+ process.exitCode = 2;
92
+ }
93
+ }
94
+ if (isDirectInvocation(import.meta.url, process.argv[1]))
95
+ run();
96
+ //# sourceMappingURL=apply-plan-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-plan-cli.js","sourceRoot":"","sources":["../src/apply-plan-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import type { WorkspaceHost } from "./types.js";
2
+ export interface EngagementBriefRole {
3
+ readonly role: string;
4
+ readonly why: string;
5
+ readonly goal: {
6
+ readonly metric: string;
7
+ readonly direction: "increase" | "decrease";
8
+ };
9
+ readonly inputsFrom: readonly string[];
10
+ readonly outputsTo: readonly string[];
11
+ }
12
+ export interface EngagementBrief {
13
+ readonly schemaVersion: 1;
14
+ readonly problem: string;
15
+ readonly roles: readonly EngagementBriefRole[];
16
+ readonly sequence: readonly string[];
17
+ readonly deliverables: readonly string[];
18
+ }
19
+ export type BlockerKind = "missing-input" | "missing-authority" | "failing-evidence" | "unavailable-environment" | "contradiction";
20
+ export interface PlanBlocker {
21
+ readonly kind: BlockerKind;
22
+ readonly description: string;
23
+ readonly owner: string;
24
+ readonly dueDate?: string;
25
+ }
26
+ export interface PlanDecision {
27
+ readonly at: string;
28
+ readonly recommended: string;
29
+ readonly chosen: string;
30
+ readonly by: string;
31
+ }
32
+ export interface AdvisorPlan {
33
+ readonly schemaVersion: 1;
34
+ readonly asOf: string;
35
+ readonly mandate: {
36
+ readonly problem: string;
37
+ readonly primaryProblemId: string;
38
+ readonly roles: readonly string[];
39
+ };
40
+ readonly whereWeAre: readonly string[];
41
+ readonly recommendedNext: {
42
+ readonly action: string;
43
+ readonly owner: string;
44
+ readonly due: string;
45
+ } | null;
46
+ readonly decisions: readonly PlanDecision[];
47
+ readonly blockers: readonly PlanBlocker[];
48
+ }
49
+ export type ValidationResult = {
50
+ readonly valid: true;
51
+ } | {
52
+ readonly valid: false;
53
+ readonly reason: string;
54
+ };
55
+ /** Validates an EngagementBrief's shape exactly against the #1175 contract. Never mutates, never re-derives content. */
56
+ export declare function validateEngagementBrief(value: unknown): ValidationResult;
57
+ /** Validates an AdvisorPlan's shape exactly against the #1175 contract. */
58
+ export declare function validateAdvisorPlan(value: unknown): ValidationResult;
59
+ /**
60
+ * The plan is approved when its most recent decision (by `at`) records
61
+ * chosen === "approved". No decisions, or a most-recent decision that
62
+ * isn't "approved", is not approved -- this never assumes approval from
63
+ * absence.
64
+ */
65
+ export declare function isPlanApproved(plan: AdvisorPlan): boolean;
66
+ export type ApplyBriefResult = {
67
+ readonly state: "applied";
68
+ readonly path: string;
69
+ } | {
70
+ readonly state: "refused";
71
+ readonly reason: string;
72
+ };
73
+ /**
74
+ * Writes clossys/brief.json into `repositoryDirectory`, byte-identically
75
+ * from the validated brief -- never re-authors its prose. Refuses (does
76
+ * not write) unless both the plan is approved and the brief validates.
77
+ */
78
+ export declare function applyEngagementBrief(host: WorkspaceHost, repositoryDirectory: string, plan: AdvisorPlan, brief: EngagementBrief, briefRelPath: string): ApplyBriefResult;
79
+ //# sourceMappingURL=apply-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-plan.d.ts","sourceRoot":"","sources":["../src/apply-plan.ts"],"names":[],"mappings":"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"}