@uniqbit/mate-core 0.18.5 → 1.0.0-canary.0

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 (209) hide show
  1. package/claude-plugin/hooks/companion-instructions.mjs +9 -0
  2. package/claude-plugin/hooks/hooks.json +12 -2
  3. package/package.json +10 -2
  4. package/src/brand/art.generated.ts +69 -0
  5. package/src/brand/index.ts +12 -0
  6. package/src/brand/palette.ts +19 -0
  7. package/src/brand/svg.generated.ts +8 -0
  8. package/src/brand/terminal.ts +83 -0
  9. package/src/brand/types.ts +33 -0
  10. package/src/cli/commands/artifact/artifact.ts +8 -0
  11. package/src/cli/commands/artifact/delivery/archive.ts +117 -0
  12. package/src/cli/commands/artifact/delivery/branch-flags.ts +55 -0
  13. package/src/cli/commands/artifact/delivery/environment.ts +104 -0
  14. package/src/cli/commands/artifact/delivery/link.ts +154 -0
  15. package/src/cli/commands/artifact/delivery/output.ts +58 -0
  16. package/src/cli/commands/artifact/finish/command.ts +33 -3
  17. package/src/cli/commands/artifact/finish/engine.ts +47 -3
  18. package/src/cli/commands/artifact/finish/finisher.ts +10 -0
  19. package/src/cli/commands/artifact/finish/openspec.ts +177 -138
  20. package/src/cli/commands/artifact/finish/secret-scan.ts +43 -0
  21. package/src/cli/commands/artifact/pending/command.ts +31 -3
  22. package/src/cli/commands/artifact/pending/discovery.ts +21 -57
  23. package/src/cli/commands/cap/graphify.ts +52 -3
  24. package/src/cli/commands/cap/index-cmd.ts +8 -1
  25. package/src/cli/commands/cap/openspec.ts +13 -25
  26. package/src/cli/commands/companion/open.ts +56 -39
  27. package/src/cli/commands/companion/update.ts +75 -42
  28. package/src/cli/commands/doctor.ts +176 -3
  29. package/src/cli/commands/plugin/install.ts +9 -4
  30. package/src/cli/commands/plugin/plugin.ts +3 -2
  31. package/src/cli/commands/report/adapter.ts +11 -2
  32. package/src/cli/commands/report/contract.ts +21 -0
  33. package/src/cli/commands/report/highlight.ts +34 -4
  34. package/src/cli/commands/report/markdown.ts +110 -0
  35. package/src/cli/commands/report/palette.ts +118 -0
  36. package/src/cli/commands/report/renderer.ts +456 -153
  37. package/src/cli/commands/report/types.ts +16 -0
  38. package/src/cli/commands/setup.ts +7 -15
  39. package/src/cli/commands/studio/client-asset.ts +16 -0
  40. package/src/cli/commands/studio/openspec-cli.ts +10 -58
  41. package/src/cli/commands/studio/routes.ts +2 -0
  42. package/src/cli/commands/studio/selection.ts +19 -1
  43. package/src/cli/commands/studio/server.ts +42 -1
  44. package/src/cli/commands/studio/views/client.ts +6 -616
  45. package/src/cli/commands/studio/views/dashboard/changes.tsx +5 -10
  46. package/src/cli/commands/studio/views/document.tsx +137 -69
  47. package/src/cli/commands/studio/views/markdown.tsx +250 -0
  48. package/src/cli/commands/studio/views/palette.ts +134 -0
  49. package/src/cli/commands/studio/views/reports/index.tsx +2 -7
  50. package/src/cli/commands/studio/views/skills/index.tsx +43 -62
  51. package/src/cli/commands/studio/views/specs/index.tsx +7 -15
  52. package/src/cli/commands/studio/views/studio-client/actions.ts +51 -0
  53. package/src/cli/commands/studio/views/studio-client/companion.ts +13 -0
  54. package/src/cli/commands/studio/views/studio-client/index.ts +32 -0
  55. package/src/cli/commands/studio/views/studio-client/keys.ts +3 -0
  56. package/src/cli/commands/studio/views/studio-client/main.ts +3 -0
  57. package/src/cli/commands/studio/views/studio-client/page.ts +146 -0
  58. package/src/cli/commands/studio/views/studio-client/panels.ts +57 -0
  59. package/src/cli/commands/studio/views/studio-client/reports.ts +122 -0
  60. package/src/cli/commands/studio/views/studio-client/theme.ts +63 -0
  61. package/src/cli/commands/studio/views/studio-client/vault-editor.ts +166 -0
  62. package/src/cli/commands/studio/views/studio-client/vault-pane.ts +202 -0
  63. package/src/cli/commands/studio/views/studio-client/vault-tree.ts +258 -0
  64. package/src/cli/commands/studio/views/studio-client.generated.ts +2 -0
  65. package/src/cli/commands/studio/views/styles.ts +217 -206
  66. package/src/cli/commands/studio/views/vault/state.ts +3 -5
  67. package/src/cli/commands/studio/views/workflow/delivery-path.tsx +370 -0
  68. package/src/cli/commands/studio/views/workflow/graph.ts +220 -0
  69. package/src/cli/commands/studio/views/workflow/index.tsx +2 -3
  70. package/src/cli/commands/studio/views/workflow/steps.ts +228 -287
  71. package/src/cli/commands/wrap.ts +5 -13
  72. package/src/cli/main.ts +4 -1
  73. package/src/cli/parse-flags.ts +25 -7
  74. package/src/cli/setup-selector.tsx +38 -162
  75. package/src/cli/usage.ts +4 -0
  76. package/src/hooks/companion-instructions.ts +190 -0
  77. package/src/hooks/hook-io.ts +63 -0
  78. package/src/hooks/session-banner.ts +21 -29
  79. package/src/hooks/session-guidance.ts +9 -17
  80. package/src/hooks/session-prepare.ts +115 -28
  81. package/src/hooks/validate-artifact-path.ts +82 -24
  82. package/src/index.ts +4 -0
  83. package/src/lib/change-layout.ts +154 -0
  84. package/src/lib/components/wizard-header.tsx +3 -1
  85. package/src/lib/context-mode-package.ts +24 -11
  86. package/src/lib/delivery/archive-change.ts +154 -0
  87. package/src/lib/delivery/branches.ts +108 -0
  88. package/src/lib/delivery/environment.ts +29 -0
  89. package/src/lib/delivery/forge.ts +279 -0
  90. package/src/lib/delivery/overlap.ts +202 -0
  91. package/src/lib/delivery/record-delivery.ts +196 -0
  92. package/src/lib/delivery/record.ts +343 -0
  93. package/src/lib/delivery/refresh.ts +174 -0
  94. package/src/lib/delivery/repository-access.ts +97 -0
  95. package/src/lib/delivery/stages.ts +111 -0
  96. package/src/lib/delivery/token.ts +58 -0
  97. package/src/lib/frontmatter.ts +18 -0
  98. package/src/lib/opencode-plugin-package.ts +5 -5
  99. package/src/lib/openspec-cli.ts +116 -0
  100. package/src/lib/openspec-scopes.ts +86 -0
  101. package/src/lib/orchestrator/adapters/claude-args.ts +50 -0
  102. package/src/lib/orchestrator/adapters/claude.ts +48 -40
  103. package/src/lib/orchestrator/adapters/opencode.ts +2 -0
  104. package/src/lib/orchestrator/editor.ts +9 -9
  105. package/src/lib/orchestrator/openspec-store.ts +14 -54
  106. package/src/lib/orchestrator/projection-claude-commands.ts +36 -0
  107. package/src/lib/orchestrator/projection-claude-links.ts +159 -0
  108. package/src/lib/orchestrator/projection-claude-skills.ts +19 -137
  109. package/src/lib/orchestrator/projection-entries.ts +45 -14
  110. package/src/lib/orchestrator/projection-runtime-documents.ts +15 -6
  111. package/src/lib/orchestrator/projection-types.ts +3 -0
  112. package/src/lib/orchestrator/refresh-working-claude-links.ts +87 -0
  113. package/src/lib/orchestrator/session-prepare.ts +45 -5
  114. package/src/lib/orchestrator/setup-compatibilities.ts +18 -75
  115. package/src/lib/orchestrator/types.ts +2 -2
  116. package/src/lib/orchestrator/wrap-pass.ts +22 -1
  117. package/src/lib/package-paths.ts +8 -0
  118. package/src/opencode/companion-hooks.ts +64 -5
  119. package/src/opencode/companion-policy.ts +37 -18
  120. package/src/opencode/projected-guidance.ts +4 -1
  121. package/src/opencode/session-prepare.ts +2 -1
  122. package/src/opencode/tui.tsx +4 -2
  123. package/src/runtime/artifact-refusal.ts +63 -0
  124. package/src/runtime/claude-delivery-plan.ts +89 -7
  125. package/src/runtime/commit-attribution-guard.ts +181 -0
  126. package/src/runtime/companion-guidance.ts +15 -3
  127. package/src/runtime/companion-instructions.ts +340 -0
  128. package/src/runtime/error-message.ts +4 -0
  129. package/src/runtime/index.ts +5 -0
  130. package/src/runtime/managed-block.ts +117 -0
  131. package/src/runtime/markdown-scan.ts +266 -0
  132. package/src/runtime/policy.ts +15 -1
  133. package/src/runtime/projected-flag.ts +7 -0
  134. package/src/runtime/projected-guidance.ts +1 -0
  135. package/src/runtime/runtime-document-manifest.ts +62 -11
  136. package/src/runtime/secret-guard.ts +148 -0
  137. package/src/runtime/secret-patterns.ts +164 -0
  138. package/src/runtime/secret-scan.ts +90 -0
  139. package/src/runtime/session-kind.ts +13 -0
  140. package/src/runtime/shell-heredoc.ts +81 -0
  141. package/src/runtime/wrap-state.ts +36 -2
  142. package/src/runtime/write-atomic.ts +64 -0
  143. package/src/templates/capabilities/openspec-cap/mate-minimal/schema.yaml +8 -6
  144. package/src/templates/capabilities/openspec-cap/mate-minimal/templates/spec.md +3 -5
  145. package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +7 -9
  146. package/src/templates/capabilities/openspec-cap/mate-v1/templates/spec.md +3 -5
  147. package/src/templates/capabilities/openspec-cap/openspec-conventions.yaml +10 -12
  148. package/src/templates/mate-skills/THIRD-PARTY-NOTICES.md +40 -0
  149. package/src/templates/mate-skills/agents/mate-apply-change/SKILL.md +76 -0
  150. package/src/templates/mate-skills/agents/mate-apply-change/references/openspec-apply-change.md +198 -0
  151. package/src/templates/mate-skills/agents/mate-archive-change/SKILL.md +59 -0
  152. package/src/templates/mate-skills/{claude/mate-openspec-backfill → agents/mate-backfill-spec}/SKILL.md +4 -4
  153. package/src/templates/mate-skills/agents/mate-clarify-intent/SKILL.md +95 -0
  154. package/src/templates/mate-skills/agents/mate-explore-change/SKILL.md +50 -0
  155. package/src/templates/mate-skills/agents/mate-explore-change/references/openspec-explore.md +348 -0
  156. package/src/templates/mate-skills/agents/mate-grill-me/SKILL.md +3 -3
  157. package/src/templates/mate-skills/agents/mate-interview-me/SKILL.md +5 -5
  158. package/src/templates/mate-skills/agents/mate-propose-change/SKILL.md +103 -0
  159. package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-propose.md +167 -0
  160. package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-update-change.md +101 -0
  161. package/src/templates/mate-skills/{claude/mate-artifact-publish → agents/mate-publish-artifact}/SKILL.md +91 -23
  162. package/src/templates/mate-skills/agents/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
  163. package/src/templates/mate-skills/agents/mate-show-me/SKILL.md +74 -16
  164. package/src/templates/mate-skills/agents/mate-simplify-code/SKILL.md +74 -68
  165. package/src/templates/mate-skills/claude/mate-apply-change/SKILL.md +76 -0
  166. package/src/templates/mate-skills/claude/mate-apply-change/references/openspec-apply-change.md +198 -0
  167. package/src/templates/mate-skills/claude/mate-archive-change/SKILL.md +59 -0
  168. package/src/templates/mate-skills/{agents/mate-openspec-backfill → claude/mate-backfill-spec}/SKILL.md +4 -4
  169. package/src/templates/mate-skills/claude/mate-clarify-intent/SKILL.md +95 -0
  170. package/src/templates/mate-skills/claude/mate-explore-change/SKILL.md +50 -0
  171. package/src/templates/mate-skills/claude/mate-explore-change/references/openspec-explore.md +348 -0
  172. package/src/templates/mate-skills/claude/mate-grill-me/SKILL.md +3 -3
  173. package/src/templates/mate-skills/claude/mate-interview-me/SKILL.md +5 -5
  174. package/src/templates/mate-skills/claude/mate-propose-change/SKILL.md +103 -0
  175. package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-propose.md +167 -0
  176. package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-update-change.md +101 -0
  177. package/src/templates/mate-skills/{agents/mate-artifact-publish → claude/mate-publish-artifact}/SKILL.md +91 -23
  178. package/src/templates/mate-skills/claude/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
  179. package/src/templates/mate-skills/claude/mate-show-me/SKILL.md +74 -16
  180. package/src/templates/mate-skills/claude/mate-simplify-code/SKILL.md +74 -68
  181. package/src/templates/mate-skills/openspec-references.ts +140 -0
  182. package/src/templates/root/TEMPLATE_AGENTS.md +2 -2
  183. package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +3727 -320
  184. package/src/tools/setup/capabilities/context-mode.ts +25 -5
  185. package/src/tools/setup/capabilities/graphify-gate.ts +31 -0
  186. package/src/tools/setup/capabilities/graphify-secrets.ts +255 -0
  187. package/src/tools/setup/capabilities/graphify.ts +12 -0
  188. package/src/tools/setup/capabilities/openspec.ts +151 -270
  189. package/src/tools/setup/capabilities/tokensave.ts +36 -3
  190. package/src/tools/setup/context-services.ts +9 -17
  191. package/src/tools/setup/dynamic-plugins/install.ts +24 -28
  192. package/src/tools/setup/engine.ts +97 -21
  193. package/src/tools/setup/managed-output.ts +51 -0
  194. package/src/tools/setup/mate.ts +86 -11
  195. package/src/tools/setup/plugin.ts +35 -0
  196. package/src/tools/setup/plugins/guidance.ts +7 -4
  197. package/src/tools/setup/providers/agent-file-sections.ts +4 -2
  198. package/src/tools/setup/providers/claude-attribution.ts +65 -0
  199. package/src/tools/setup/providers/claude-plugin-folders.ts +625 -0
  200. package/src/tools/setup/providers/claude-plugin-hooks.ts +39 -19
  201. package/src/tools/setup/providers/claude.ts +284 -8
  202. package/src/tools/setup/providers/opencode.ts +180 -1
  203. package/src/tools/setup/utils.ts +14 -4
  204. package/src/tools/setup.ts +126 -10
  205. package/wrappers/bin/graphify +52 -4
  206. package/wrappers/bin/openspec +33 -9
  207. package/wrappers/lib/graphify-gate.mjs +8 -0
  208. package/src/cli/commands/studio/views/workflow/transcript.tsx +0 -190
  209. package/src/tools/setup/runtime-documents.ts +0 -291
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  type GitModeSelection,
3
- type OpenSpecSchemaSelection,
4
3
  listSetupCapabilityCompatibilities,
5
4
  } from "../lib/orchestrator/setup-compatibilities";
6
5
  import type { CapabilityConfig } from "../lib/orchestrator/types";
@@ -63,6 +62,31 @@ export function parseFlags(
63
62
  return flags;
64
63
  }
65
64
 
65
+ /** Tokens that are neither flags nor flag values, read with the rules of {@link parseFlags}. */
66
+ export function positionalArgs(
67
+ argv: string[],
68
+ booleanFlags: BooleanFlagSet = NO_BOOLEAN_FLAGS,
69
+ ): string[] {
70
+ const names: string[] = [];
71
+ for (let index = 0; index < argv.length; index += 1) {
72
+ const token = argv[index];
73
+ if (!token.startsWith("--")) names.push(token);
74
+ else if (token.includes("=") || booleanFlags.has(token.slice(2))) continue;
75
+ else if (argv[index + 1] && !argv[index + 1].startsWith("--")) index += 1;
76
+ }
77
+ return names;
78
+ }
79
+
80
+ /** A flag's value when it was given one, so a bare or repeated flag reads as absent. */
81
+ export function stringFlag(value: CliFlags[string] | undefined): string | undefined {
82
+ return typeof value === "string" ? value : undefined;
83
+ }
84
+
85
+ /** The first of `names` given bare or more than once, which {@link stringFlag} would read as absent. */
86
+ export function malformedValueFlag(flags: CliFlags, names: readonly string[]): string | undefined {
87
+ return names.find((name) => flags[name] !== undefined && typeof flags[name] !== "string");
88
+ }
89
+
66
90
  export function parseAllowedAgents(flags: CliFlags): string[] | undefined {
67
91
  const allowedAgents = flags["allowed-agent"];
68
92
  return Array.isArray(allowedAgents)
@@ -95,12 +119,6 @@ export function parseCapabilities(flags: CliFlags): CapabilityConfig[] | undefin
95
119
  return names.map((name) => index.get(name) ?? { name });
96
120
  }
97
121
 
98
- export function parseOpenSpecSchema(flags: CliFlags): OpenSpecSchemaSelection | undefined {
99
- const schema = flags["openspec-schema"];
100
- if (schema === "mate-v1" || schema === "default") return schema;
101
- return undefined;
102
- }
103
-
104
122
  export function parseGitMode(flags: CliFlags): GitModeSelection | undefined {
105
123
  const gitMode = flags["git-mode"];
106
124
  if (gitMode === "auto" || gitMode === "default") return gitMode;
@@ -9,14 +9,10 @@ import { getRequiredPluginIds, withRequiredSelections } from "../tools/setup/pol
9
9
 
10
10
  import {
11
11
  type GitModeSelection,
12
- type OpenSpecSchemaSelection,
13
12
  type SetupSelections,
14
- applyOpenSpecSchemaSelection,
15
13
  DEFAULT_GIT_MODE_SELECTION,
16
- getOpenSpecSchemaSelection,
17
14
  listSetupCapabilityCompatibilities,
18
15
  listSetupGitModeCompatibilities,
19
- listSetupOpenSpecSchemaCompatibilities,
20
16
  listSetupPackageManagerCompatibilities,
21
17
  listSetupProviderCompatibilities,
22
18
  } from "../lib/orchestrator/setup-compatibilities";
@@ -28,16 +24,14 @@ interface SetupSelectorOptions {
28
24
  stderr?: NodeJS.WriteStream;
29
25
  }
30
26
 
31
- export type WizardStep = "providers" | "capabilities" | "openspecSchema" | "gitMode";
27
+ export type WizardStep = "providers" | "capabilities" | "gitMode";
32
28
 
33
29
  interface SetupSelectorProps {
34
30
  activeIndex: number;
35
31
  currentStep: number;
36
- steps: WizardStep[];
37
32
  selectedProviders: Set<string>;
38
33
  selectedPackageManagers: Set<string>;
39
34
  selectedCapabilities: Set<string>;
40
- selectedOpenSpecSchema: OpenSpecSchemaSelection;
41
35
  selectedGitMode: GitModeSelection;
42
36
  onMounted?: () => void;
43
37
  }
@@ -47,7 +41,7 @@ export const setupSelectorDeps = {
47
41
  };
48
42
 
49
43
  export interface SetupSelectorItem {
50
- kind: "provider" | "packageManager" | "capability" | "openspecSchema" | "gitMode";
44
+ kind: "provider" | "packageManager" | "capability" | "gitMode";
51
45
  id: string;
52
46
  label: string;
53
47
  description: string;
@@ -62,48 +56,17 @@ export interface SetupSelectorState {
62
56
  selectedProviders: Set<string>;
63
57
  selectedPackageManagers: Set<string>;
64
58
  selectedCapabilities: Set<string>;
65
- selectedOpenSpecSchema: OpenSpecSchemaSelection;
66
59
  selectedGitMode: GitModeSelection;
67
60
  }
68
61
 
69
- const BASE_WIZARD_STEPS: readonly Exclude<WizardStep, "openspecSchema">[] = [
70
- "providers",
71
- "capabilities",
72
- "gitMode",
73
- ];
62
+ export const WIZARD_STEPS: readonly WizardStep[] = ["providers", "capabilities", "gitMode"];
74
63
 
75
64
  const STEP_DISPLAY_NAMES: Record<WizardStep, string> = {
76
65
  providers: "Agents",
77
66
  capabilities: "Capabilities",
78
- openspecSchema: "OpenSpec Schema Profile",
79
67
  gitMode: "Git Auto Mode",
80
68
  };
81
69
 
82
- export const OPENSPEC_SCHEMA_SKIP_MESSAGE = "OpenSpec not selected - skipping";
83
-
84
- function hasOpenSpecSelected(selectedCapabilities: Set<string>): boolean {
85
- return selectedCapabilities.has("openspec");
86
- }
87
-
88
- export function getOpenSpecSchemaStepMessage(
89
- step: WizardStep,
90
- selectedCapabilities: Set<string>,
91
- ): string | null {
92
- return step === "openspecSchema" && !hasOpenSpecSelected(selectedCapabilities)
93
- ? OPENSPEC_SCHEMA_SKIP_MESSAGE
94
- : null;
95
- }
96
-
97
- export function buildWizardSteps(options: {
98
- selectedCapabilities: Set<string>;
99
- lockOpenSpecSchemaStep?: boolean;
100
- }): WizardStep[] {
101
- if (!hasOpenSpecSelected(options.selectedCapabilities) && !options.lockOpenSpecSchemaStep) {
102
- return [...BASE_WIZARD_STEPS];
103
- }
104
- return ["providers", "capabilities", "openspecSchema", "gitMode"];
105
- }
106
-
107
70
  export function parseSelectorInput(
108
71
  input: string,
109
72
  ): "up" | "down" | "toggle" | "submit" | "cancel" | "back" | null {
@@ -147,9 +110,7 @@ function markRequiredItems(items: SetupSelectorItem[]): SetupSelectorItem[] {
147
110
  );
148
111
  }
149
112
 
150
- export function getSelectableItems(
151
- activeOpenSpecSchema?: OpenSpecSchemaSelection,
152
- ): SetupSelectorItem[] {
113
+ export function getSelectableItems(): SetupSelectorItem[] {
153
114
  const baseItems: SetupSelectorItem[] = [
154
115
  ...listSetupProviderCompatibilities().map((entry) => ({
155
116
  kind: "provider" as const,
@@ -177,12 +138,6 @@ export function getSelectableItems(
177
138
  return markRequiredItems([
178
139
  ...baseItems,
179
140
  ...getDistributionSelectorItems(knownIds),
180
- ...listSetupOpenSpecSchemaCompatibilities(activeOpenSpecSchema).map((entry) => ({
181
- kind: "openspecSchema" as const,
182
- id: entry.id,
183
- label: entry.label,
184
- description: entry.description,
185
- })),
186
141
  ...listSetupGitModeCompatibilities().map((entry) => ({
187
142
  kind: "gitMode" as const,
188
143
  id: entry.id,
@@ -192,17 +147,13 @@ export function getSelectableItems(
192
147
  ]);
193
148
  }
194
149
 
195
- export function getItemsForStep(
196
- step: WizardStep,
197
- activeOpenSpecSchema?: OpenSpecSchemaSelection,
198
- ): SetupSelectorItem[] {
150
+ export function getItemsForStep(step: WizardStep): SetupSelectorItem[] {
199
151
  const kindMap: Record<WizardStep, SetupSelectorItem["kind"]> = {
200
152
  providers: "provider",
201
153
  capabilities: "capability",
202
- openspecSchema: "openspecSchema",
203
154
  gitMode: "gitMode",
204
155
  };
205
- return getSelectableItems(activeOpenSpecSchema).filter((item) => item.kind === kindMap[step]);
156
+ return getSelectableItems().filter((item) => item.kind === kindMap[step]);
206
157
  }
207
158
 
208
159
  export function getDisabledCapabilityIds(selectedPackageManagers: Set<string>): Set<string> {
@@ -260,7 +211,6 @@ export function toggleSelectableItem(
260
211
  selectedProviders,
261
212
  selectedPackageManagers,
262
213
  selectedCapabilities,
263
- selectedOpenSpecSchema: state.selectedOpenSpecSchema,
264
214
  selectedGitMode: state.selectedGitMode,
265
215
  };
266
216
  }
@@ -269,40 +219,30 @@ export function selectRadioItem(
269
219
  state: SetupSelectorState,
270
220
  current: SetupSelectorItem,
271
221
  ): SetupSelectorState {
272
- if (current.kind !== "openspecSchema" && current.kind !== "gitMode") return state;
222
+ if (current.kind !== "gitMode") return state;
273
223
  return {
274
224
  selectedProviders: new Set(state.selectedProviders),
275
225
  selectedPackageManagers: new Set(state.selectedPackageManagers),
276
226
  selectedCapabilities: new Set(state.selectedCapabilities),
277
- selectedOpenSpecSchema:
278
- current.kind === "openspecSchema"
279
- ? (current.id as OpenSpecSchemaSelection)
280
- : state.selectedOpenSpecSchema,
281
- selectedGitMode:
282
- current.kind === "gitMode" ? (current.id as GitModeSelection) : state.selectedGitMode,
227
+ selectedGitMode: current.id as GitModeSelection,
283
228
  };
284
229
  }
285
230
 
286
231
  function getInitialActiveIndexForStep(
287
232
  step: WizardStep,
288
- selectedOpenSpecSchema: OpenSpecSchemaSelection,
289
233
  selectedGitMode: GitModeSelection = DEFAULT_GIT_MODE_SELECTION,
290
234
  ): number {
291
- if (step !== "openspecSchema" && step !== "gitMode") return 0;
292
- const items = getItemsForStep(step, selectedOpenSpecSchema);
293
- const selected = step === "gitMode" ? selectedGitMode : selectedOpenSpecSchema;
294
- const index = items.findIndex((item) => item.id === selected);
235
+ if (step !== "gitMode") return 0;
236
+ const index = getItemsForStep(step).findIndex((item) => item.id === selectedGitMode);
295
237
  return index >= 0 ? index : 0;
296
238
  }
297
239
 
298
240
  function SetupSelectorApp({
299
241
  activeIndex,
300
242
  currentStep,
301
- steps,
302
243
  selectedProviders,
303
244
  selectedPackageManagers,
304
245
  selectedCapabilities,
305
- selectedOpenSpecSchema,
306
246
  selectedGitMode,
307
247
  onMounted,
308
248
  }: SetupSelectorProps) {
@@ -310,26 +250,18 @@ function SetupSelectorApp({
310
250
  onMounted?.();
311
251
  }, [onMounted]);
312
252
 
313
- const totalSteps = steps.length;
314
- const stepKey = steps[currentStep];
253
+ const totalSteps = WIZARD_STEPS.length;
254
+ const stepKey = WIZARD_STEPS[currentStep];
315
255
  const stepName = STEP_DISPLAY_NAMES[stepKey];
316
256
  const disabledCapabilityIds = getDisabledCapabilityIds(selectedPackageManagers);
317
- const openSpecSkipMessage = getOpenSpecSchemaStepMessage(stepKey, selectedCapabilities);
318
- const skipMessage = openSpecSkipMessage;
319
- const showOpenSpecSkipMessage = skipMessage !== null;
320
- const navigableItems = showOpenSpecSkipMessage
321
- ? []
322
- : getItemsForStep(stepKey, selectedOpenSpecSchema);
323
- const isRadioStep =
324
- (stepKey === "openspecSchema" || stepKey === "gitMode") && !showOpenSpecSkipMessage;
257
+ const navigableItems = getItemsForStep(stepKey);
258
+ const isRadioStep = stepKey === "gitMode";
325
259
  const isLastStep = currentStep === totalSteps - 1;
326
260
  const enterHint = isLastStep ? "enter confirm" : "enter next";
327
261
  const backHint = currentStep > 0 ? " · ← back" : "";
328
- const footerHint = showOpenSpecSkipMessage
329
- ? `${enterHint}${backHint} · q cancel`
330
- : isRadioStep
331
- ? `↑↓ navigate · enter select · ${enterHint}${backHint} · q cancel`
332
- : `↑↓ navigate · space toggle · ${enterHint}${backHint} · q cancel`;
262
+ const footerHint = isRadioStep
263
+ ? `↑↓ navigate · enter select · ${enterHint}${backHint} · q cancel`
264
+ : `↑↓ navigate · space toggle · ${enterHint}${backHint} · q cancel`;
333
265
 
334
266
  const menuItems: SelectMenuItem[] = navigableItems.flatMap((item): SelectMenuItem[] => {
335
267
  if (item.kind === "provider") {
@@ -356,16 +288,6 @@ function SetupSelectorApp({
356
288
  },
357
289
  ];
358
290
  }
359
- if (item.kind === "openspecSchema") {
360
- return [
361
- {
362
- key: `openspecSchema:${item.id}`,
363
- label: item.label,
364
- description: item.description,
365
- indicator: renderIndicator(selectedOpenSpecSchema === item.id, { mode: "radio" }),
366
- },
367
- ];
368
- }
369
291
  if (item.kind === "gitMode") {
370
292
  return [
371
293
  {
@@ -384,15 +306,11 @@ function SetupSelectorApp({
384
306
  <WizardHeader title={stepName} step={currentStep} totalSteps={totalSteps} />
385
307
  {currentStep === 0 && <Text dimColor>Always installing: bun (node) · uv (python)</Text>}
386
308
  <Box marginTop={1} flexDirection="column">
387
- {showOpenSpecSkipMessage ? (
388
- <Text dimColor>{skipMessage}</Text>
389
- ) : (
390
- <SelectMenu
391
- items={menuItems}
392
- activeIndex={activeIndex}
393
- mode={isRadioStep ? "radio" : "multi"}
394
- />
395
- )}
309
+ <SelectMenu
310
+ items={menuItems}
311
+ activeIndex={activeIndex}
312
+ mode={isRadioStep ? "radio" : "multi"}
313
+ />
396
314
  </Box>
397
315
  <WizardFooter hint={footerHint} />
398
316
  </Box>
@@ -426,11 +344,7 @@ export async function selectSetupCompatibilities(
426
344
  let selectedProviders = new Set(initialSelections.allowedAgents);
427
345
  let selectedPackageManagers = new Set(initialSelections.packageManagers);
428
346
  let selectedCapabilities = new Set(initialSelections.capabilities.map((entry) => entry.name));
429
- let selectedOpenSpecSchema =
430
- initialSelections.selectedOpenSpecSchema ??
431
- getOpenSpecSchemaSelection(initialSelections.capabilities);
432
347
  let selectedGitMode = initialSelections.selectedGitMode;
433
- let openSpecSchemaStepLocked = hasOpenSpecSelected(selectedCapabilities);
434
348
  let rendered: ReturnType<typeof inkRender> | undefined;
435
349
 
436
350
  const cleanup = () => {
@@ -449,31 +363,17 @@ export async function selectSetupCompatibilities(
449
363
  resolve(value);
450
364
  };
451
365
 
452
- const getSteps = () =>
453
- buildWizardSteps({
454
- selectedCapabilities,
455
- lockOpenSpecSchemaStep: openSpecSchemaStepLocked,
456
- });
457
-
458
- const getCurrentStep = () => getSteps()[currentStep];
459
-
460
- const getCurrentItems = () => {
461
- const step = getCurrentStep();
462
- return step === "openspecSchema" && !hasOpenSpecSelected(selectedCapabilities)
463
- ? []
464
- : getItemsForStep(step, selectedOpenSpecSchema);
465
- };
366
+ const getCurrentStep = () => WIZARD_STEPS[currentStep];
367
+ const getCurrentItems = () => getItemsForStep(getCurrentStep());
466
368
 
467
369
  const rerender = () => {
468
370
  rendered?.rerender(
469
371
  <SetupSelectorApp
470
372
  activeIndex={activeIndex}
471
373
  currentStep={currentStep}
472
- steps={getSteps()}
473
374
  selectedProviders={selectedProviders}
474
375
  selectedPackageManagers={selectedPackageManagers}
475
376
  selectedCapabilities={selectedCapabilities}
476
- selectedOpenSpecSchema={selectedOpenSpecSchema}
477
377
  selectedGitMode={selectedGitMode}
478
378
  />,
479
379
  );
@@ -510,14 +410,13 @@ export async function selectSetupCompatibilities(
510
410
  if (action === "toggle") {
511
411
  const stepItems = getCurrentItems();
512
412
  const current = stepItems[activeIndex];
513
- if (!current || current.kind === "openspecSchema") return;
413
+ if (!current) return;
514
414
 
515
415
  const next = toggleSelectableItem(
516
416
  {
517
417
  selectedProviders,
518
418
  selectedPackageManagers,
519
419
  selectedCapabilities,
520
- selectedOpenSpecSchema,
521
420
  selectedGitMode,
522
421
  },
523
422
  current,
@@ -525,7 +424,6 @@ export async function selectSetupCompatibilities(
525
424
  selectedProviders = next.selectedProviders;
526
425
  selectedPackageManagers = next.selectedPackageManagers;
527
426
  selectedCapabilities = next.selectedCapabilities;
528
- selectedOpenSpecSchema = next.selectedOpenSpecSchema;
529
427
  selectedGitMode = next.selectedGitMode;
530
428
  activeIndex = Math.max(0, Math.min(activeIndex, getCurrentItems().length - 1));
531
429
  rerender();
@@ -534,20 +432,14 @@ export async function selectSetupCompatibilities(
534
432
 
535
433
  if (action === "submit") {
536
434
  const stepKey = getCurrentStep();
537
- if (
538
- (stepKey === "openspecSchema" && hasOpenSpecSelected(selectedCapabilities)) ||
539
- stepKey === "gitMode"
540
- ) {
435
+ if (stepKey === "gitMode") {
541
436
  const current = getCurrentItems()[activeIndex];
542
- const selectedRadioValue =
543
- stepKey === "gitMode" ? selectedGitMode : selectedOpenSpecSchema;
544
- if (current && current.id !== selectedRadioValue) {
437
+ if (current && current.id !== selectedGitMode) {
545
438
  const next = selectRadioItem(
546
439
  {
547
440
  selectedProviders,
548
441
  selectedPackageManagers,
549
442
  selectedCapabilities,
550
- selectedOpenSpecSchema,
551
443
  selectedGitMode,
552
444
  },
553
445
  current,
@@ -555,25 +447,15 @@ export async function selectSetupCompatibilities(
555
447
  selectedProviders = next.selectedProviders;
556
448
  selectedPackageManagers = next.selectedPackageManagers;
557
449
  selectedCapabilities = next.selectedCapabilities;
558
- selectedOpenSpecSchema = next.selectedOpenSpecSchema;
559
450
  selectedGitMode = next.selectedGitMode;
560
451
  rerender();
561
452
  return;
562
453
  }
563
454
  }
564
455
 
565
- if (stepKey === "capabilities" && hasOpenSpecSelected(selectedCapabilities)) {
566
- openSpecSchemaStepLocked = true;
567
- }
568
-
569
- const steps = getSteps();
570
- if (currentStep < steps.length - 1) {
456
+ if (currentStep < WIZARD_STEPS.length - 1) {
571
457
  currentStep++;
572
- activeIndex = getInitialActiveIndexForStep(
573
- steps[currentStep],
574
- selectedOpenSpecSchema,
575
- selectedGitMode,
576
- );
458
+ activeIndex = getInitialActiveIndexForStep(WIZARD_STEPS[currentStep], selectedGitMode);
577
459
  rerender();
578
460
  return;
579
461
  }
@@ -583,19 +465,15 @@ export async function selectSetupCompatibilities(
583
465
  doResolve({
584
466
  allowedAgents: [...selectedProviders],
585
467
  packageManagers: [...selectedPackageManagers],
586
- capabilities: applyOpenSpecSchemaSelection(
587
- [
588
- ...listSetupCapabilityCompatibilities()
589
- .filter((entry) => selectedCapabilities.has(entry.capability.name))
590
- .map((entry) => entry.capability),
591
- // Distribution-registered capabilities without a builtin entry.
592
- ...[...selectedCapabilities]
593
- .filter((id) => !builtinCapabilityIds.has(id))
594
- .map((id) => ({ name: id })),
595
- ],
596
- selectedOpenSpecSchema,
597
- ),
598
- selectedOpenSpecSchema,
468
+ capabilities: [
469
+ ...listSetupCapabilityCompatibilities()
470
+ .filter((entry) => selectedCapabilities.has(entry.capability.name))
471
+ .map((entry) => entry.capability),
472
+ // Distribution-registered capabilities without a builtin entry.
473
+ ...[...selectedCapabilities]
474
+ .filter((id) => !builtinCapabilityIds.has(id))
475
+ .map((id) => ({ name: id })),
476
+ ],
599
477
  selectedGitMode,
600
478
  });
601
479
  rendered?.unmount();
@@ -624,11 +502,9 @@ export async function selectSetupCompatibilities(
624
502
  <SetupSelectorApp
625
503
  activeIndex={activeIndex}
626
504
  currentStep={currentStep}
627
- steps={getSteps()}
628
505
  selectedProviders={selectedProviders}
629
506
  selectedPackageManagers={selectedPackageManagers}
630
507
  selectedCapabilities={selectedCapabilities}
631
- selectedOpenSpecSchema={selectedOpenSpecSchema}
632
508
  selectedGitMode={selectedGitMode}
633
509
  onMounted={onMounted}
634
510
  />,
package/src/cli/usage.ts CHANGED
@@ -30,6 +30,10 @@ export function usage(): string {
30
30
  ` ${n} artifact publish <anchor|change-name> [--type openspec] [--no-push] [--json]`,
31
31
  " target: a dated archive anchor, or a change name matching exactly one archive",
32
32
  ` ${n} artifact pending [--json]`,
33
+ ` ${n} artifact link <change>... [--repository <org/repo>] [--branch <b> [--direct]]`,
34
+ " [--not-applicable <reason>] [--pr <url>] [--refresh]",
35
+ " [--confirm-merged|--confirm-open|--confirm-dropped] [--json]",
36
+ ` ${n} artifact archive <change> [--repository <org/repo>] [--branch <b> [--direct]|--not-applicable <reason>|--skip-branch] [--json]`,
33
37
  ` ${n} doctor`,
34
38
  ` ${n} report [--days N] [--input FILE|-] [--json]`,
35
39
  ` ${n} studio [--writable] [--terminal] [--detach-timeout <minutes>]`,
@@ -0,0 +1,190 @@
1
+ /** Delivers the Companion Instructions to a Claude main thread or subagent. */
2
+ import { errorMessage } from "../runtime/error-message";
3
+ import { planClaudeInstructions } from "../runtime/claude-delivery-plan";
4
+ import {
5
+ COMPANION_INSTRUCTIONS_BUDGET,
6
+ buildCompanionInstructions,
7
+ companionInstructionsUnavailableLine,
8
+ type CompanionInstructionsInput,
9
+ type InstructionReader,
10
+ } from "../runtime/companion-instructions";
11
+ import { MATE_ENV, hasLaunchEnvironment } from "../runtime/env";
12
+ import { FRAMEWORK_NAME } from "../runtime/framework";
13
+ import { PROJECTED_FLAG } from "../runtime/projected-flag";
14
+ import { resolveProjection } from "../runtime/projection";
15
+ import { managedSessionKind, type SessionKind } from "../runtime/session-kind";
16
+ import { hookOutcome, runHook, type HookCall, type HookEnv } from "./hook-io";
17
+
18
+ export interface InstructionHookPayload {
19
+ hookSpecificOutput?: { hookEventName: InstructionEvent; additionalContext: string };
20
+ systemMessage?: string;
21
+ }
22
+
23
+ export interface InstructionHookInput {
24
+ hook_event_name?: string;
25
+ agent_type?: string;
26
+ cwd?: string;
27
+ }
28
+
29
+ /** A hook run, with the shim arguments already read. */
30
+ export interface InstructionHookCall extends Omit<HookCall, "argv"> {
31
+ /** The copy a wrap carries, marked `--projected`. */
32
+ projected: boolean;
33
+ /** Where the projection resolves; the event's `cwd` when unset. */
34
+ directory?: string;
35
+ }
36
+
37
+ export interface InstructionHookDeps {
38
+ read?: InstructionReader;
39
+ resolveProjection?: typeof resolveProjection;
40
+ }
41
+
42
+ /**
43
+ * Agents never given a copy, since Claude Code's matchers cannot exclude them:
44
+ * Explore and Plan skip project memory by design, and a fork inherits its
45
+ * parent's context, which already holds the instructions.
46
+ */
47
+ const SKIPPED_AGENTS = new Set(["Explore", "Plan", "fork"]);
48
+
49
+ type InstructionEvent = "SessionStart" | "SubagentStart";
50
+
51
+ const INPUT_FIELDS = ["hook_event_name", "agent_type", "cwd"] as const;
52
+
53
+ function normalizeInput(raw: unknown): InstructionHookInput {
54
+ const input: InstructionHookInput = {};
55
+ if (!raw || typeof raw !== "object") return input;
56
+ for (const field of INPUT_FIELDS) {
57
+ const value = (raw as Record<string, unknown>)[field];
58
+ if (typeof value === "string") input[field] = value;
59
+ }
60
+ return input;
61
+ }
62
+
63
+ function sessionKind(env: HookEnv): SessionKind {
64
+ if (!hasLaunchEnvironment(env)) return "unmanaged";
65
+ return managedSessionKind(Boolean(env[MATE_ENV.repositoryPath]));
66
+ }
67
+
68
+ /**
69
+ * A copy runs only because the bundled plugin or a Claude wrap installed it, so
70
+ * a session with no launch environment is planned as Claude-wrapped without
71
+ * reading the Checkout's manifest: a linked worktree runs the main checkout's
72
+ * wrapped settings and has none of its own.
73
+ */
74
+ function ownsEvent(event: InstructionEvent, kind: SessionKind, projected: boolean): boolean {
75
+ const owners = planClaudeInstructions(kind, true);
76
+ if (event === "SessionStart") return owners.main === "wrap-session-hook" && projected;
77
+ if (owners.subagents === "plugin-subagent-hook") return !projected;
78
+ if (owners.subagents === "wrap-subagent-hook") return projected;
79
+ return false;
80
+ }
81
+
82
+ /** A launch sets every path; a partial one is not completed from a projection, which may name another repository. */
83
+ function launchReaderInput(env: HookEnv): CompanionInstructionsInput | null {
84
+ const companionPath = env[MATE_ENV.companionPath];
85
+ const id = env[MATE_ENV.repositoryId];
86
+ const repoPath = env[MATE_ENV.repositoryPath];
87
+ const wrapperBinPath = env[MATE_ENV.wrapperBinPath];
88
+ if (!companionPath || !id || !repoPath || !wrapperBinPath) return null;
89
+ return { companionPath, repository: { id, path: repoPath }, wrapperBinPath };
90
+ }
91
+
92
+ /** The environment always wins: a managed session never reads the projection. */
93
+ function readerInput(
94
+ env: HookEnv,
95
+ kind: SessionKind,
96
+ cwd: string,
97
+ resolve: typeof resolveProjection,
98
+ ): CompanionInstructionsInput | null {
99
+ if (kind !== "unmanaged") return launchReaderInput(env);
100
+ const projection = resolve(cwd);
101
+ if (!projection) return null;
102
+ return {
103
+ companionPath: projection.companionPath,
104
+ /** A linked worktree on a projection copied from the main checkout names itself, not that checkout. */
105
+ repository: { id: projection.repositoryId, path: projection.repoRoot },
106
+ wrapperBinPath: projection.wrapperBinPath,
107
+ };
108
+ }
109
+
110
+ function oversizeMessage(source: string): string {
111
+ const marker = FRAMEWORK_NAME.toUpperCase();
112
+ const budget = COMPANION_INSTRUCTIONS_BUDGET.toLocaleString("en-US");
113
+ return `${FRAMEWORK_NAME}: the ${marker}:COMPANION block in ${source} leaves no room within the ${budget}-character budget, so the instructions exceed it; Claude Code may deliver them as a file preview. Shorten the block.`;
114
+ }
115
+
116
+ function evaluate(
117
+ { env, cwd, projected, directory }: InstructionHookCall,
118
+ input: InstructionHookInput,
119
+ deps: InstructionHookDeps,
120
+ ): InstructionHookPayload | null {
121
+ const resolve = deps.resolveProjection ?? resolveProjection;
122
+ const read = deps.read ?? buildCompanionInstructions;
123
+ const event = input.hook_event_name;
124
+ if (event !== "SessionStart" && event !== "SubagentStart") return null;
125
+ if (event === "SubagentStart" && SKIPPED_AGENTS.has(input.agent_type ?? "")) return null;
126
+
127
+ const kind = sessionKind(env);
128
+ if (!ownsEvent(event, kind, projected)) return null;
129
+
130
+ const reader = readerInput(env, kind, directory || input.cwd || cwd, resolve);
131
+ if (!reader) return null;
132
+
133
+ const result = read(reader);
134
+ if (result.kind === "unavailable") {
135
+ const line = companionInstructionsUnavailableLine(result);
136
+ return instructionOutput(event, line, line);
137
+ }
138
+ const warning = result.oversized ? oversizeMessage(result.source) : undefined;
139
+ return instructionOutput(event, result.text, warning);
140
+ }
141
+
142
+ function instructionOutput(
143
+ event: InstructionEvent,
144
+ additionalContext: string,
145
+ systemMessage?: string,
146
+ ): InstructionHookPayload {
147
+ return {
148
+ hookSpecificOutput: { hookEventName: event, additionalContext },
149
+ ...(systemMessage ? { systemMessage } : {}),
150
+ };
151
+ }
152
+
153
+ function failure(error: unknown): InstructionHookPayload {
154
+ return {
155
+ systemMessage: `${FRAMEWORK_NAME}: companion instructions hook failed: ${errorMessage(error)}`,
156
+ };
157
+ }
158
+
159
+ /**
160
+ * Its own hook output, apart from the Agent Guidance's: Claude Code caps each
161
+ * output at 10,000 characters. A missing `AGENTS.md` is stated as a fact, never
162
+ * as an instruction to stop: a SessionStart hook cannot block, and a pause
163
+ * would stall a `-p` or SDK session.
164
+ */
165
+ export function instructionHookPayload(
166
+ call: InstructionHookCall,
167
+ deps: InstructionHookDeps = {},
168
+ ): InstructionHookPayload | null {
169
+ try {
170
+ return evaluate(call, normalizeInput(call.input), deps);
171
+ } catch (error) {
172
+ return failure(error);
173
+ }
174
+ }
175
+
176
+ /** Plugin-shim entry; exits zero even when reading its input fails. */
177
+ export function run(argv: readonly string[] = process.argv.slice(2)): Promise<number> {
178
+ return runHook(
179
+ (input) =>
180
+ hookOutcome(
181
+ instructionHookPayload({
182
+ env: process.env,
183
+ cwd: process.cwd(),
184
+ projected: argv.includes(PROJECTED_FLAG),
185
+ input,
186
+ }),
187
+ ),
188
+ (error) => hookOutcome(failure(error)),
189
+ );
190
+ }