@uniqbit/mate-core 0.18.4 → 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 (231) hide show
  1. package/claude-plugin/hooks/companion-instructions.mjs +9 -0
  2. package/claude-plugin/hooks/hooks.json +12 -2
  3. package/opencode-plugin/src/add-dir.ts +76 -10
  4. package/opencode-plugin/src/companion.ts +1 -8
  5. package/opencode-plugin/src/server.ts +2 -15
  6. package/package.json +10 -2
  7. package/src/brand/art.generated.ts +69 -0
  8. package/src/brand/index.ts +12 -0
  9. package/src/brand/palette.ts +19 -0
  10. package/src/brand/svg.generated.ts +8 -0
  11. package/src/brand/terminal.ts +83 -0
  12. package/src/brand/types.ts +33 -0
  13. package/src/cli/commands/artifact/artifact.ts +8 -0
  14. package/src/cli/commands/artifact/delivery/archive.ts +117 -0
  15. package/src/cli/commands/artifact/delivery/branch-flags.ts +55 -0
  16. package/src/cli/commands/artifact/delivery/environment.ts +104 -0
  17. package/src/cli/commands/artifact/delivery/link.ts +154 -0
  18. package/src/cli/commands/artifact/delivery/output.ts +58 -0
  19. package/src/cli/commands/artifact/finish/command.ts +33 -3
  20. package/src/cli/commands/artifact/finish/engine.ts +47 -3
  21. package/src/cli/commands/artifact/finish/finisher.ts +10 -0
  22. package/src/cli/commands/artifact/finish/openspec.ts +200 -129
  23. package/src/cli/commands/artifact/finish/secret-scan.ts +43 -0
  24. package/src/cli/commands/artifact/pending/command.ts +31 -3
  25. package/src/cli/commands/artifact/pending/discovery.ts +21 -57
  26. package/src/cli/commands/cap/graphify.ts +52 -3
  27. package/src/cli/commands/cap/index-cmd.ts +8 -1
  28. package/src/cli/commands/cap/openspec.ts +13 -25
  29. package/src/cli/commands/companion/open.ts +56 -39
  30. package/src/cli/commands/companion/update.ts +75 -42
  31. package/src/cli/commands/doctor.ts +257 -78
  32. package/src/cli/commands/install.ts +0 -8
  33. package/src/cli/commands/plugin/install.ts +9 -4
  34. package/src/cli/commands/plugin/plugin.ts +3 -2
  35. package/src/cli/commands/report/adapter.ts +11 -2
  36. package/src/cli/commands/report/contract.ts +21 -0
  37. package/src/cli/commands/report/highlight.ts +34 -4
  38. package/src/cli/commands/report/markdown.ts +110 -0
  39. package/src/cli/commands/report/palette.ts +118 -0
  40. package/src/cli/commands/report/renderer.ts +456 -153
  41. package/src/cli/commands/report/types.ts +16 -0
  42. package/src/cli/commands/setup.ts +7 -15
  43. package/src/cli/commands/studio/client-asset.ts +16 -0
  44. package/src/cli/commands/studio/openspec-cli.ts +10 -58
  45. package/src/cli/commands/studio/routes.ts +2 -0
  46. package/src/cli/commands/studio/selection.ts +19 -1
  47. package/src/cli/commands/studio/server.ts +42 -1
  48. package/src/cli/commands/studio/terminal-launch.ts +7 -16
  49. package/src/cli/commands/studio/views/client.ts +6 -616
  50. package/src/cli/commands/studio/views/dashboard/changes.tsx +5 -10
  51. package/src/cli/commands/studio/views/document.tsx +137 -69
  52. package/src/cli/commands/studio/views/markdown.tsx +250 -0
  53. package/src/cli/commands/studio/views/palette.ts +134 -0
  54. package/src/cli/commands/studio/views/reports/index.tsx +2 -7
  55. package/src/cli/commands/studio/views/skills/index.tsx +43 -62
  56. package/src/cli/commands/studio/views/specs/index.tsx +7 -15
  57. package/src/cli/commands/studio/views/studio-client/actions.ts +51 -0
  58. package/src/cli/commands/studio/views/studio-client/companion.ts +13 -0
  59. package/src/cli/commands/studio/views/studio-client/index.ts +32 -0
  60. package/src/cli/commands/studio/views/studio-client/keys.ts +3 -0
  61. package/src/cli/commands/studio/views/studio-client/main.ts +3 -0
  62. package/src/cli/commands/studio/views/studio-client/page.ts +146 -0
  63. package/src/cli/commands/studio/views/studio-client/panels.ts +57 -0
  64. package/src/cli/commands/studio/views/studio-client/reports.ts +122 -0
  65. package/src/cli/commands/studio/views/studio-client/theme.ts +63 -0
  66. package/src/cli/commands/studio/views/studio-client/vault-editor.ts +166 -0
  67. package/src/cli/commands/studio/views/studio-client/vault-pane.ts +202 -0
  68. package/src/cli/commands/studio/views/studio-client/vault-tree.ts +258 -0
  69. package/src/cli/commands/studio/views/studio-client.generated.ts +2 -0
  70. package/src/cli/commands/studio/views/styles.ts +217 -206
  71. package/src/cli/commands/studio/views/vault/state.ts +3 -5
  72. package/src/cli/commands/studio/views/workflow/delivery-path.tsx +370 -0
  73. package/src/cli/commands/studio/views/workflow/graph.ts +220 -0
  74. package/src/cli/commands/studio/views/workflow/index.tsx +2 -3
  75. package/src/cli/commands/studio/views/workflow/steps.ts +228 -287
  76. package/src/cli/commands/wrap.ts +9 -42
  77. package/src/cli/main.ts +4 -1
  78. package/src/cli/parse-flags.ts +25 -7
  79. package/src/cli/setup-selector.tsx +38 -162
  80. package/src/cli/usage.ts +5 -2
  81. package/src/hooks/companion-instructions.ts +190 -0
  82. package/src/hooks/hook-io.ts +63 -0
  83. package/src/hooks/session-banner.ts +21 -29
  84. package/src/hooks/session-guidance.ts +10 -36
  85. package/src/hooks/session-prepare.ts +115 -28
  86. package/src/hooks/validate-artifact-path.ts +82 -24
  87. package/src/index.ts +4 -0
  88. package/src/lib/change-layout.ts +154 -0
  89. package/src/lib/components/wizard-header.tsx +3 -1
  90. package/src/lib/context-mode-package.ts +24 -11
  91. package/src/lib/delivery/archive-change.ts +154 -0
  92. package/src/lib/delivery/branches.ts +108 -0
  93. package/src/lib/delivery/environment.ts +29 -0
  94. package/src/lib/delivery/forge.ts +279 -0
  95. package/src/lib/delivery/overlap.ts +202 -0
  96. package/src/lib/delivery/record-delivery.ts +196 -0
  97. package/src/lib/delivery/record.ts +343 -0
  98. package/src/lib/delivery/refresh.ts +174 -0
  99. package/src/lib/delivery/repository-access.ts +97 -0
  100. package/src/lib/delivery/stages.ts +111 -0
  101. package/src/lib/delivery/token.ts +58 -0
  102. package/src/lib/frontmatter.ts +18 -0
  103. package/src/lib/opencode-plugin-package.ts +5 -5
  104. package/src/lib/openspec-cli.ts +116 -0
  105. package/src/lib/openspec-scopes.ts +86 -0
  106. package/src/lib/orchestrator/adapters/claude-args.ts +50 -0
  107. package/src/lib/orchestrator/adapters/claude.ts +60 -162
  108. package/src/lib/orchestrator/adapters/opencode.ts +9 -24
  109. package/src/lib/orchestrator/config-store.ts +2 -70
  110. package/src/lib/orchestrator/editor.ts +9 -9
  111. package/src/lib/orchestrator/openspec-store.ts +14 -54
  112. package/src/lib/orchestrator/projection-claude-commands.ts +36 -0
  113. package/src/lib/orchestrator/projection-claude-links.ts +159 -0
  114. package/src/lib/orchestrator/projection-claude-skills.ts +19 -190
  115. package/src/lib/orchestrator/projection-entries.ts +45 -15
  116. package/src/lib/orchestrator/projection-runtime-documents.ts +15 -6
  117. package/src/lib/orchestrator/projection-types.ts +3 -2
  118. package/src/lib/orchestrator/refresh-working-claude-links.ts +87 -0
  119. package/src/lib/orchestrator/session-prepare.ts +49 -87
  120. package/src/lib/orchestrator/setup-compatibilities.ts +18 -75
  121. package/src/lib/orchestrator/types.ts +2 -2
  122. package/src/lib/orchestrator/wrap-pass.ts +22 -88
  123. package/src/lib/package-paths.ts +8 -0
  124. package/src/opencode/companion-hooks.ts +64 -5
  125. package/src/opencode/companion-policy.ts +37 -18
  126. package/src/opencode/projected-guidance.ts +4 -1
  127. package/src/opencode/session-prepare.ts +2 -1
  128. package/src/opencode/tui.tsx +4 -2
  129. package/src/runtime/artifact-refusal.ts +63 -0
  130. package/src/runtime/claude-delivery-plan.ts +89 -7
  131. package/src/runtime/commit-attribution-guard.ts +181 -0
  132. package/src/runtime/companion-guidance.ts +15 -3
  133. package/src/runtime/companion-instructions.ts +340 -0
  134. package/src/runtime/error-message.ts +4 -0
  135. package/src/runtime/guidance.ts +0 -17
  136. package/src/runtime/index.ts +5 -0
  137. package/src/runtime/managed-block.ts +117 -0
  138. package/src/runtime/markdown-scan.ts +266 -0
  139. package/src/runtime/policy.ts +16 -15
  140. package/src/runtime/projected-flag.ts +7 -0
  141. package/src/runtime/projected-guidance.ts +1 -0
  142. package/src/runtime/runtime-document-manifest.ts +62 -28
  143. package/src/runtime/secret-guard.ts +148 -0
  144. package/src/runtime/secret-patterns.ts +164 -0
  145. package/src/runtime/secret-scan.ts +90 -0
  146. package/src/runtime/session-kind.ts +13 -0
  147. package/src/runtime/shell-heredoc.ts +81 -0
  148. package/src/runtime/wrap-state.ts +36 -6
  149. package/src/runtime/write-atomic.ts +64 -0
  150. package/src/templates/capabilities/openspec-cap/mate-minimal/schema.yaml +8 -6
  151. package/src/templates/capabilities/openspec-cap/mate-minimal/templates/spec.md +3 -5
  152. package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +7 -9
  153. package/src/templates/capabilities/openspec-cap/mate-v1/templates/spec.md +3 -5
  154. package/src/templates/capabilities/openspec-cap/openspec-conventions.yaml +10 -12
  155. package/src/templates/mate-skills/THIRD-PARTY-NOTICES.md +40 -0
  156. package/src/templates/mate-skills/agents/mate-apply-change/SKILL.md +76 -0
  157. package/src/templates/mate-skills/agents/mate-apply-change/references/openspec-apply-change.md +198 -0
  158. package/src/templates/mate-skills/agents/mate-archive-change/SKILL.md +59 -0
  159. package/src/templates/mate-skills/{claude/mate-openspec-backfill → agents/mate-backfill-spec}/SKILL.md +4 -4
  160. package/src/templates/mate-skills/agents/mate-clarify-intent/SKILL.md +95 -0
  161. package/src/templates/mate-skills/agents/mate-explore-change/SKILL.md +50 -0
  162. package/src/templates/mate-skills/agents/mate-explore-change/references/openspec-explore.md +348 -0
  163. package/src/templates/mate-skills/agents/mate-grill-me/SKILL.md +3 -3
  164. package/src/templates/mate-skills/agents/mate-interview-me/SKILL.md +5 -5
  165. package/src/templates/mate-skills/agents/mate-propose-change/SKILL.md +103 -0
  166. package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-propose.md +167 -0
  167. package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-update-change.md +101 -0
  168. package/src/templates/mate-skills/agents/{mate-artifact-publish → mate-publish-artifact}/SKILL.md +91 -23
  169. package/src/templates/mate-skills/agents/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
  170. package/src/templates/mate-skills/agents/mate-show-me/SKILL.md +74 -16
  171. package/src/templates/mate-skills/agents/mate-simplify-code/SKILL.md +74 -68
  172. package/src/templates/mate-skills/claude/mate-apply-change/SKILL.md +76 -0
  173. package/src/templates/mate-skills/claude/mate-apply-change/references/openspec-apply-change.md +198 -0
  174. package/src/templates/mate-skills/claude/mate-archive-change/SKILL.md +59 -0
  175. package/src/templates/mate-skills/{agents/mate-openspec-backfill → claude/mate-backfill-spec}/SKILL.md +4 -4
  176. package/src/templates/mate-skills/claude/mate-clarify-intent/SKILL.md +95 -0
  177. package/src/templates/mate-skills/claude/mate-explore-change/SKILL.md +50 -0
  178. package/src/templates/mate-skills/claude/mate-explore-change/references/openspec-explore.md +348 -0
  179. package/src/templates/mate-skills/claude/mate-grill-me/SKILL.md +3 -3
  180. package/src/templates/mate-skills/claude/mate-interview-me/SKILL.md +5 -5
  181. package/src/templates/mate-skills/claude/mate-propose-change/SKILL.md +103 -0
  182. package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-propose.md +167 -0
  183. package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-update-change.md +101 -0
  184. package/src/templates/mate-skills/claude/{mate-artifact-publish → mate-publish-artifact}/SKILL.md +91 -23
  185. package/src/templates/mate-skills/claude/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
  186. package/src/templates/mate-skills/claude/mate-show-me/SKILL.md +74 -16
  187. package/src/templates/mate-skills/claude/mate-simplify-code/SKILL.md +74 -68
  188. package/src/templates/mate-skills/openspec-references.ts +140 -0
  189. package/src/templates/root/TEMPLATE_AGENTS.md +2 -2
  190. package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +3727 -320
  191. package/src/tools/setup/capabilities/context-mode.ts +25 -5
  192. package/src/tools/setup/capabilities/graphify-gate.ts +31 -0
  193. package/src/tools/setup/capabilities/graphify-secrets.ts +255 -0
  194. package/src/tools/setup/capabilities/graphify.ts +12 -0
  195. package/src/tools/setup/capabilities/openspec.ts +273 -270
  196. package/src/tools/setup/capabilities/tokensave.ts +36 -3
  197. package/src/tools/setup/context-services.ts +9 -17
  198. package/src/tools/setup/dynamic-plugins/audiences.ts +7 -39
  199. package/src/tools/setup/dynamic-plugins/declarations.ts +1 -1
  200. package/src/tools/setup/dynamic-plugins/hydrate.ts +4 -7
  201. package/src/tools/setup/dynamic-plugins/install.ts +24 -28
  202. package/src/tools/setup/engine.ts +93 -148
  203. package/src/tools/setup/managed-output.ts +51 -0
  204. package/src/tools/setup/mate.ts +86 -11
  205. package/src/tools/setup/plugin.ts +37 -17
  206. package/src/tools/setup/plugins/gitignore.ts +1 -7
  207. package/src/tools/setup/plugins/guidance.ts +7 -4
  208. package/src/tools/setup/providers/agent-file-sections.ts +4 -2
  209. package/src/tools/setup/providers/claude-attribution.ts +65 -0
  210. package/src/tools/setup/providers/claude-plugin-folders.ts +625 -0
  211. package/src/tools/setup/providers/claude-plugin-hooks.ts +39 -19
  212. package/src/tools/setup/providers/claude.ts +285 -34
  213. package/src/tools/setup/providers/opencode.ts +180 -11
  214. package/src/tools/setup/registry.ts +2 -24
  215. package/src/tools/setup/surface-target.ts +4 -13
  216. package/src/tools/setup/utils.ts +14 -4
  217. package/src/tools/setup.ts +131 -18
  218. package/wrappers/bin/graphify +52 -4
  219. package/wrappers/bin/openspec +33 -9
  220. package/wrappers/lib/graphify-gate.mjs +8 -0
  221. package/opencode-plugin/src/audience-layer.ts +0 -91
  222. package/src/cli/commands/studio/views/workflow/transcript.tsx +0 -190
  223. package/src/runtime/audience-layer.ts +0 -84
  224. package/src/runtime/opencode-audience-layer.ts +0 -176
  225. package/src/tools/setup/audience-isolation.fixture.ts +0 -325
  226. package/src/tools/setup/audience-layer.ts +0 -207
  227. package/src/tools/setup/audience-registrations.ts +0 -52
  228. package/src/tools/setup/contribution-ledger.ts +0 -207
  229. package/src/tools/setup/isolation-diagnostics.ts +0 -139
  230. package/src/tools/setup/runtime-documents.ts +0 -291
  231. package/src/tools/setup/upgrade-sweep.ts +0 -153
@@ -28,6 +28,7 @@ export type ProjectionEntryId =
28
28
  | "openspec-store-exclude"
29
29
  | "openspec-store-pointer"
30
30
  | "claude-skill-links"
31
+ | "claude-command-links"
31
32
  | "workspace-document"
32
33
  | "capability-excludes"
33
34
  | "claude-working-settings"
@@ -41,6 +42,8 @@ export type ProjectionEntryId =
41
42
  export interface ProjectionInput {
42
43
  repoPath: string;
43
44
  companionPath?: string;
45
+ /** Every companion folder the workspace document lists, in order; `[companionPath]` when absent. */
46
+ workspaceCompanionPaths?: string[];
44
47
  repository?: LinkedRepository;
45
48
  source?: CompanionSource;
46
49
  config?: FrameworkConfig;
@@ -50,8 +53,6 @@ export interface ProjectionInput {
50
53
  runtimeDocuments?: readonly RenderedRuntimeDocument[];
51
54
  /** Restricts runtime-tagged entries to these runtimes; every runtime when absent. */
52
55
  runtimes?: readonly WrapRuntime[];
53
- /** The layer directory of the audience the checkout is wrapped for; `null` or absent for base only. */
54
- audienceLayerDir?: string | null;
55
56
  }
56
57
 
57
58
  export interface ProjectionRemovalInput {
@@ -0,0 +1,87 @@
1
+ import { readWrapState } from "../../runtime/wrap-state";
2
+ import { GlobalConfigStore } from "./global-config-store";
3
+ import { reconcileWorkingRepoClaudeCommandLinks } from "./projection-claude-commands";
4
+ import { reconcileWorkingRepoClaudeSkillLinks } from "./projection-claude-skills";
5
+ import { readCompanionRegistry } from "./repository-link";
6
+ import type { FrameworkConfig, LinkedRepository } from "./types";
7
+ import { errorMessage } from "../../runtime/error-message";
8
+
9
+ export interface LinkRefreshResult {
10
+ /** Null when the companion's list of linked repositories could not be read. */
11
+ repository: LinkedRepository | null;
12
+ outcome: "written" | "current" | "failed";
13
+ error?: string;
14
+ }
15
+
16
+ export interface LinkRefreshDeps {
17
+ readRegistry: (companionPath: string) => Promise<LinkedRepository[]>;
18
+ isWrappedForClaude: (repoPath: string) => Promise<boolean>;
19
+ registeredCompanionPaths: () => Promise<string[]>;
20
+ reconcileSkillLinks: typeof reconcileWorkingRepoClaudeSkillLinks;
21
+ reconcileCommandLinks: typeof reconcileWorkingRepoClaudeCommandLinks;
22
+ }
23
+
24
+ const linkRefreshDeps: LinkRefreshDeps = {
25
+ readRegistry: async (companionPath) => {
26
+ try {
27
+ return (await readCompanionRegistry(companionPath)).repos;
28
+ } catch (error) {
29
+ // No registry file means no linked repositories.
30
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
31
+ throw error;
32
+ }
33
+ },
34
+ isWrappedForClaude: async (repoPath) => (await readWrapState(repoPath)).runtimes.has("claude"),
35
+ registeredCompanionPaths: () => new GlobalConfigStore().list(),
36
+ reconcileSkillLinks: reconcileWorkingRepoClaudeSkillLinks,
37
+ reconcileCommandLinks: reconcileWorkingRepoClaudeCommandLinks,
38
+ };
39
+
40
+ /**
41
+ * Re-links the companion's Claude skills and commands into every linked Claude-wrapped
42
+ * Working Repository. Links only; runtime documents wait for the wrap pass. Failures
43
+ * are reported per repository, never thrown.
44
+ */
45
+ export async function refreshWorkingClaudeLinks(
46
+ companionPath: string,
47
+ config: FrameworkConfig,
48
+ deps: LinkRefreshDeps = linkRefreshDeps,
49
+ ): Promise<LinkRefreshResult[]> {
50
+ const enabled = config.allowedAgents?.includes("claude") ?? false;
51
+ const results: LinkRefreshResult[] = [];
52
+ let registered: string[] | undefined;
53
+ let repositories: LinkedRepository[];
54
+ try {
55
+ repositories = await deps.readRegistry(companionPath);
56
+ } catch (error) {
57
+ return [{ repository: null, outcome: "failed", error: errorMessage(error) }];
58
+ }
59
+
60
+ for (const repository of repositories) {
61
+ try {
62
+ if (!(await deps.isWrappedForClaude(repository.path))) continue;
63
+ registered ??= await deps.registeredCompanionPaths();
64
+ const skills = await deps.reconcileSkillLinks(
65
+ repository.path,
66
+ companionPath,
67
+ enabled,
68
+ registered,
69
+ );
70
+ const commands = await deps.reconcileCommandLinks(
71
+ repository.path,
72
+ companionPath,
73
+ enabled,
74
+ registered,
75
+ );
76
+ const written = skills === "written" || commands === "written";
77
+ results.push({ repository, outcome: written ? "written" : "current" });
78
+ } catch (error) {
79
+ results.push({
80
+ repository,
81
+ outcome: "failed",
82
+ error: errorMessage(error),
83
+ });
84
+ }
85
+ }
86
+ return results;
87
+ }
@@ -1,5 +1,6 @@
1
1
  import { getActiveDistribution } from "../../distribution";
2
2
  import { FRAMEWORK_NAME } from "../../framework";
3
+ import { launchPassesClaudePluginFolders } from "../../runtime/claude-delivery-plan";
3
4
  import { recordCompanionSyncUnfinished } from "../../runtime/companion-git-state";
4
5
  import { withCompanionLock, type CompanionLockOptions } from "../../runtime/companion-lock";
5
6
  import {
@@ -7,15 +8,11 @@ import {
7
8
  syncCompanionUnattended,
8
9
  type UnattendedSyncOutcome,
9
10
  } from "../../runtime/companion-sync";
10
- import { readWrapStateSync, type WrapState } from "../../runtime/wrap-state";
11
+ import { managedSessionKind, type SessionKind } from "../../runtime/session-kind";
12
+ import type { WrapState } from "../../runtime/wrap-state";
11
13
  import { syncCompanionFiles } from "../../tools/setup";
12
- import { renderAudienceLayer, type RenderedAudienceLayer } from "../../tools/setup/audience-layer";
13
- import {
14
- readAudienceSelection,
15
- resolveAudience,
16
- } from "../../tools/setup/dynamic-plugins/audiences";
17
- import { buildSetupInstallationPlan } from "../../tools/setup/engine";
18
14
  import type { CapabilityPlugin, LaunchPreflightContext } from "../../tools/setup/plugin";
15
+ import type { ClaudePluginFolders } from "../../tools/setup/providers/claude-plugin-folders";
19
16
  import { attendedLockOptions, syncCompanionGit } from "./companion-git-sync";
20
17
  import { readCompanionConfig } from "./config-store";
21
18
  import { entryFailureMessage, firstFailure, type ProjectionChangedPaths } from "./projection-types";
@@ -40,8 +37,7 @@ export type PrepareStepName =
40
37
  | "file-sync"
41
38
  | "launch-projection"
42
39
  | "working-target"
43
- | "preflight"
44
- | "audience-layer";
40
+ | "preflight";
45
41
 
46
42
  export interface PrepareStep {
47
43
  step: PrepareStepName;
@@ -68,8 +64,8 @@ export interface PrepareResult {
68
64
  failure?: unknown;
69
65
  /** The wrap state the `working-target` step's pass left; a launch reads no manifest itself. */
70
66
  wrapState?: WrapState;
71
- /** The active audience's layer, rendered for an attended prepare only. */
72
- audienceLayer?: RenderedAudienceLayer;
67
+ /** The Claude plugin folders the `file-sync` step gathered; a launch gathers none itself. */
68
+ claudePluginDirectories?: string[];
73
69
  }
74
70
 
75
71
  export interface PrepareSessionInput {
@@ -98,10 +94,9 @@ const SYNC_LOCK_TIMEOUT_MS = 5 * 60_000;
98
94
  */
99
95
  async function runCapabilityPreflight(context: LaunchPreflightContext): Promise<void> {
100
96
  const { config, providerId } = context;
101
- /** Base only: an audience plugin's `forProvider` hooks never run. */
102
97
  const preflights = getActiveDistribution()
103
- .registry.baseRegistrations()
104
- .flatMap(({ plugin }) => {
98
+ .registry.getAll()
99
+ .flatMap((plugin) => {
105
100
  if (plugin.kind !== "capability" || !plugin.isEnabled(config)) return [];
106
101
  const capability = plugin as CapabilityPlugin;
107
102
  const preflight = capability.forProvider?.[providerId]?.preflight;
@@ -133,7 +128,8 @@ export const sessionPrepareDeps = {
133
128
  syncCompanionGit,
134
129
  syncCompanionUnattended,
135
130
  recordCompanionSyncUnfinished,
136
- syncCompanionFiles,
131
+ syncCompanionFiles: async (...args: Parameters<typeof syncCompanionFiles>) =>
132
+ (await syncCompanionFiles(...args)).claudePluginFolders,
137
133
  projectLaunch: (input: Parameters<typeof project>[1]) => project("launch", input),
138
134
  /**
139
135
  * The Projection Root a Claude launch guarantees for itself. Companion
@@ -147,11 +143,6 @@ export const sessionPrepareDeps = {
147
143
  withCompanionLock,
148
144
  /** As the launch decides it: Git may prompt only with a terminal on both ends. */
149
145
  isInteractiveTerminal: () => !!(process.stdin.isTTY && process.stdout.isTTY),
150
- /** The launch's `MATE_AUDIENCE`; read by an attended prepare only. */
151
- audienceSelection: () => readAudienceSelection(process.env),
152
- /** The audience a checkout is wrapped for, from its manifest; `null` for base. */
153
- wrappedAudience: (repoPath: string) => readWrapStateSync(repoPath).audience,
154
- renderAudienceLayer,
155
146
  };
156
147
 
157
148
  function message(error: unknown): string {
@@ -164,7 +155,7 @@ class StepLog {
164
155
  readonly warnings: string[] = [];
165
156
  failure: unknown;
166
157
  wrapState: WrapState | undefined;
167
- audienceLayer: RenderedAudienceLayer | undefined;
158
+ claudePluginDirectories: string[] | undefined;
168
159
  stopped = false;
169
160
  companionChanged = false;
170
161
 
@@ -192,10 +183,18 @@ async function runFileSync(
192
183
  ): Promise<void> {
193
184
  const deps = sessionPrepareDeps;
194
185
  try {
195
- if (input.repository) {
196
- await deps.syncCompanionFiles(input.companionPath, config, input.repository.path);
197
- } else {
198
- await deps.syncCompanionFiles(input.companionPath, config);
186
+ const folders = input.repository
187
+ ? await deps.syncCompanionFiles(input.companionPath, config, input.repository.path)
188
+ : await deps.syncCompanionFiles(input.companionPath, config);
189
+ run.claudePluginDirectories = [...folders.launch];
190
+ /**
191
+ * The Claude plan speaks for Claude sessions only; OpenCode loads no plugin
192
+ * folder. With a Working Repository the wrap pass reports publication;
193
+ * without one no wrap pass runs, so this step does.
194
+ */
195
+ if (reportsClaudePluginFolders(input)) {
196
+ run.warnings.push(...folders.loadWarnings);
197
+ if (!input.repository) run.warnings.push(...publicationWarnings(folders));
199
198
  }
200
199
  run.push({ step: "file-sync", outcome: "ok" });
201
200
  } catch (error) {
@@ -215,6 +214,24 @@ function wrapDetail(wrapped: {
215
214
  return parts.length > 0 ? `updated (${parts.join("; ")})` : "updated";
216
215
  }
217
216
 
217
+ /** A managed Claude launch; plain sessions' prepares and OpenCode report no plugin-folder problem. */
218
+ function reportsClaudePluginFolders(input: PrepareSessionInput): boolean {
219
+ return input.runtime === "claude" && launchPassesClaudePluginFolders(sessionKindOf(input));
220
+ }
221
+
222
+ /** What a publication left undone, one sentence per folder. */
223
+ function publicationWarnings(folders: ClaudePluginFolders | undefined): string[] {
224
+ const publication = folders?.publication;
225
+ if (!publication) return [];
226
+ return [...publication.unpublished.map((entry) => entry.warning), ...publication.warnings];
227
+ }
228
+
229
+ /** Attended is a managed launch, with or without a Working Repository; the rest serve plain sessions. */
230
+ function sessionKindOf(input: PrepareSessionInput): SessionKind {
231
+ if (input.mode !== "attended") return "unmanaged";
232
+ return managedSessionKind(Boolean(input.repository));
233
+ }
234
+
218
235
  function runtimesOf(input: PrepareSessionInput): PrepareRuntime[] {
219
236
  return input.mode !== "attended" && input.runtimes?.length
220
237
  ? [...input.runtimes]
@@ -241,6 +258,9 @@ async function reconcileWrap(
241
258
  return;
242
259
  }
243
260
  run.warnings.push(...wrapped.warnings);
261
+ if (reportsClaudePluginFolders(input)) {
262
+ run.warnings.push(...publicationWarnings(wrapped.claudePluginFolders));
263
+ }
244
264
  run.wrapState = wrapped.wrapState;
245
265
  run.push({
246
266
  step: "working-target",
@@ -324,6 +344,7 @@ async function runLocal(
324
344
  config,
325
345
  repository: input.repository,
326
346
  providerId: runtime,
347
+ sessionKind: sessionKindOf(input),
327
348
  }),
328
349
  ),
329
350
  );
@@ -337,71 +358,12 @@ async function runLocal(
337
358
  });
338
359
  }
339
360
 
340
- /**
341
- * The active audience's launch layer, after the base prepare. Only a managed
342
- * launch reaches here: a bare session's prepare never renders one, whatever
343
- * `MATE_AUDIENCE` its environment carries. An audience the companion does not
344
- * declare renders nothing; hydration already warned about it.
345
- */
346
- async function renderLayer(
347
- input: PrepareSessionInput,
348
- config: FrameworkConfig,
349
- run: StepLog,
350
- ): Promise<void> {
351
- const deps = sessionPrepareDeps;
352
- /** Adopted from the wrap: the wrap delivers it, so the launch renders no layer of its own. */
353
- const wrapped = input.repository ? deps.wrappedAudience(input.repository.path) : null;
354
- if (wrapped) {
355
- run.push({ step: "audience-layer", outcome: "ok", detail: `${wrapped}: from the wrap` });
356
- return;
357
- }
358
- const audience = resolveAudience(config, deps.audienceSelection());
359
- if (!audience.ok || audience.active === null) return;
360
- try {
361
- const registry = getActiveDistribution().registry;
362
- const layer = await deps.renderAudienceLayer({
363
- companionPath: input.companionPath,
364
- config,
365
- audience: audience.active,
366
- activeProviders: buildSetupInstallationPlan(config, registry.baseRegistrations())
367
- .activeProviders,
368
- registrations: registry.audienceRegistrations(audience.active),
369
- });
370
- run.audienceLayer = layer;
371
- run.push({
372
- step: "audience-layer",
373
- outcome: "ok",
374
- detail: `${layer.audience}: ${layer.rendered ? "rendered" : "current"}`,
375
- });
376
- } catch (error) {
377
- run.fail("audience-layer", error);
378
- }
379
- }
380
-
381
- /**
382
- * A checkout wrapped for one audience refuses a launch for another before
383
- * anything runs: the wrap's on-disk content would reach that session.
384
- */
385
- function audienceConflict(input: PrepareSessionInput, config: FrameworkConfig): string | null {
386
- if (!input.repository) return null;
387
- const wrapped = sessionPrepareDeps.wrappedAudience(input.repository.path);
388
- if (!wrapped) return null;
389
- const selected = resolveAudience(config, sessionPrepareDeps.audienceSelection());
390
- if (!selected.ok || selected.active === null || selected.active === wrapped) return null;
391
- return `${FRAMEWORK_NAME}: ${input.repository.id} is wrapped for audience "${wrapped}", but MATE_AUDIENCE selects "${selected.active}". Run \`${FRAMEWORK_NAME} wrap --audience ${selected.active}\` in ${input.repository.path}, or launch from another worktree wrapped for "${selected.active}".`;
392
- }
393
-
394
361
  async function runAttended(
395
362
  input: PrepareSessionInput,
396
363
  config: FrameworkConfig,
397
364
  run: StepLog,
398
365
  ): Promise<void> {
399
366
  const deps = sessionPrepareDeps;
400
- const conflict = audienceConflict(input, config);
401
- if (conflict) {
402
- run.fail("audience-layer", new LaunchPreflightError(conflict));
403
- return;
404
- }
405
367
  if (input.skipGit) {
406
368
  run.push({ step: "git-sync", outcome: "skipped", detail: "--no-git" });
407
369
  } else if (config.git !== "auto") {
@@ -422,8 +384,6 @@ async function runAttended(
422
384
  }
423
385
  }
424
386
  await runLocal(input, config, run);
425
- if (run.stopped) return;
426
- await renderLayer(input, config, run);
427
387
  }
428
388
 
429
389
  /** The sync's own config read: only a fast-forward can have changed it. */
@@ -478,7 +438,9 @@ export async function prepareSession(input: PrepareSessionInput): Promise<Prepar
478
438
  ...(run.companionChanged ? { companionChanged: true as const } : {}),
479
439
  ...(run.failure === undefined ? {} : { failure: run.failure }),
480
440
  ...(run.wrapState ? { wrapState: run.wrapState } : {}),
481
- ...(run.audienceLayer ? { audienceLayer: run.audienceLayer } : {}),
441
+ ...(run.claudePluginDirectories
442
+ ? { claudePluginDirectories: run.claudePluginDirectories }
443
+ : {}),
482
444
  });
483
445
 
484
446
  const locked = await deps.withCompanionLock(input.companionPath, lockOptions(input), async () => {
@@ -1,23 +1,8 @@
1
- import type {
2
- CapabilityConfig,
3
- CliToolConfig,
4
- FrameworkConfig,
5
- GitModeProfile,
6
- OpenSpecSchemaProfile,
7
- } from "./types";
1
+ import type { CapabilityConfig, CliToolConfig, FrameworkConfig, GitModeProfile } from "./types";
8
2
 
9
- export type OpenSpecSchemaSelection = OpenSpecSchemaProfile | "default";
10
3
  export type GitModeSelection = GitModeProfile | "default";
11
- const DEFAULT_OPENSPEC_SCHEMA_SELECTION: OpenSpecSchemaSelection = "default";
12
4
  export const DEFAULT_GIT_MODE_SELECTION: GitModeSelection = "default";
13
5
 
14
- export interface OpenSpecSchemaCompatibility {
15
- id: OpenSpecSchemaSelection;
16
- label: string;
17
- description: string;
18
- defaultSelected: boolean;
19
- }
20
-
21
6
  export interface GitModeCompatibility {
22
7
  id: GitModeSelection;
23
8
  label: string;
@@ -25,21 +10,6 @@ export interface GitModeCompatibility {
25
10
  defaultSelected: boolean;
26
11
  }
27
12
 
28
- const BUILTIN_OPENSPEC_SCHEMA_COMPATIBILITIES: OpenSpecSchemaCompatibility[] = [
29
- {
30
- id: "default",
31
- label: "Default (vanilla OpenSpec)",
32
- description: "Use OpenSpec's built-in spec-driven workflow.",
33
- defaultSelected: true,
34
- },
35
- {
36
- id: "mate-v1",
37
- label: "Mate v1",
38
- description: "Use Mate's schema with proposal, design, spec, and task artifacts.",
39
- defaultSelected: false,
40
- },
41
- ];
42
-
43
13
  const BUILTIN_GIT_MODE_COMPATIBILITIES: GitModeCompatibility[] = [
44
14
  {
45
15
  id: "default",
@@ -59,33 +29,9 @@ export interface SetupSelections {
59
29
  allowedAgents: string[];
60
30
  packageManagers: string[];
61
31
  capabilities: CapabilityConfig[];
62
- selectedOpenSpecSchema: OpenSpecSchemaSelection;
63
32
  selectedGitMode: GitModeSelection;
64
33
  }
65
34
 
66
- export function getOpenSpecSchemaSelection(
67
- capabilities: CapabilityConfig[],
68
- ): OpenSpecSchemaSelection {
69
- const openspec = capabilities.find((capability) => capability.name === "openspec");
70
- return openspec?.schemaProfile ?? DEFAULT_OPENSPEC_SCHEMA_SELECTION;
71
- }
72
-
73
- export function applyOpenSpecSchemaSelection(
74
- capabilities: CapabilityConfig[],
75
- schemaSelection: OpenSpecSchemaSelection | undefined,
76
- ): CapabilityConfig[] {
77
- if (schemaSelection === undefined) return capabilities;
78
-
79
- return capabilities.map((capability) => {
80
- if (capability.name !== "openspec") return capability;
81
- if (schemaSelection === "default") {
82
- const { schemaProfile: _schemaProfile, ...rest } = capability;
83
- return rest;
84
- }
85
- return { ...capability, schemaProfile: schemaSelection };
86
- });
87
- }
88
-
89
35
  interface SetupCompatibilityBase {
90
36
  id: string;
91
37
  label: string;
@@ -222,24 +168,6 @@ export function listSetupPackageManagerCompatibilities(): SetupPackageManagerCom
222
168
  );
223
169
  }
224
170
 
225
- export function listSetupOpenSpecSchemaCompatibilities(
226
- activeSchemaProfile?: OpenSpecSchemaSelection,
227
- ): OpenSpecSchemaCompatibility[] {
228
- const compatibilities = [...BUILTIN_OPENSPEC_SCHEMA_COMPATIBILITIES];
229
- if (
230
- activeSchemaProfile !== undefined &&
231
- !BUILTIN_OPENSPEC_SCHEMA_COMPATIBILITIES.some((entry) => entry.id === activeSchemaProfile)
232
- ) {
233
- compatibilities.push({
234
- id: activeSchemaProfile,
235
- label: "Custom",
236
- description: `Keep the project's current custom schema profile ("${activeSchemaProfile}"). Selecting Default or Mate v1 replaces it.`,
237
- defaultSelected: false,
238
- });
239
- }
240
- return compatibilities;
241
- }
242
-
243
171
  export function listSetupGitModeCompatibilities(): GitModeCompatibility[] {
244
172
  return [...BUILTIN_GIT_MODE_COMPATIBILITIES];
245
173
  }
@@ -272,7 +200,6 @@ export function getDefaultSetupSelections(): SetupSelections {
272
200
  allowedAgents,
273
201
  packageManagers,
274
202
  capabilities,
275
- selectedOpenSpecSchema: DEFAULT_OPENSPEC_SCHEMA_SELECTION,
276
203
  selectedGitMode: DEFAULT_GIT_MODE_SELECTION,
277
204
  };
278
205
  }
@@ -284,11 +211,27 @@ export function getSetupSelectionsFromConfig(config: FrameworkConfig): SetupSele
284
211
  ...new Set([...getLockedPackageManagers(), ...(config.packageManagers ?? [])]),
285
212
  ],
286
213
  capabilities: dedupeCapabilities(config.capabilities ?? []),
287
- selectedOpenSpecSchema: getOpenSpecSchemaSelection(config.capabilities ?? []),
288
214
  selectedGitMode: config.git ?? DEFAULT_GIT_MODE_SELECTION,
289
215
  };
290
216
  }
291
217
 
218
+ /**
219
+ * Re-selected capabilities keep their saved settings (such as openspec's
220
+ * `protectedBranches`); pickers and flags only name the capabilities.
221
+ */
222
+ export function keepCapabilitySettings(
223
+ selected: CapabilityConfig[],
224
+ saved: CapabilityConfig[],
225
+ ): CapabilityConfig[] {
226
+ const byName = new Map(saved.map((capability) => [capability.name, capability]));
227
+ return selected.map((capability) => {
228
+ // `schemaProfile` is a removed setting; a re-save drops it.
229
+ const { schemaProfile: _removed, ...settings } = (byName.get(capability.name) ??
230
+ {}) as CapabilityConfig & { schemaProfile?: unknown };
231
+ return { ...capability, ...settings };
232
+ });
233
+ }
234
+
292
235
  export function dedupeCapabilities(capabilities: CapabilityConfig[]): CapabilityConfig[] {
293
236
  const seen = new Set<string>();
294
237
  const result: CapabilityConfig[] = [];
@@ -58,12 +58,12 @@ export interface CliToolConfig {
58
58
  redirectCwd: "companionRoot" | false;
59
59
  }
60
60
 
61
- export type OpenSpecSchemaProfile = "mate-v1" | (string & {});
62
61
  export type GitModeProfile = "auto";
63
62
 
64
63
  export interface CapabilityConfig {
65
64
  name: string;
66
- schemaProfile?: OpenSpecSchemaProfile;
65
+ /** openspec only: branch glob patterns (`release/*`) treated as protected besides the default branch. */
66
+ protectedBranches?: string[];
67
67
  }
68
68
 
69
69
  export type FrameworkType = "working" | "companion" | "hub";