@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.
- package/claude-plugin/hooks/companion-instructions.mjs +9 -0
- package/claude-plugin/hooks/hooks.json +12 -2
- package/package.json +10 -2
- package/src/brand/art.generated.ts +69 -0
- package/src/brand/index.ts +12 -0
- package/src/brand/palette.ts +19 -0
- package/src/brand/svg.generated.ts +8 -0
- package/src/brand/terminal.ts +83 -0
- package/src/brand/types.ts +33 -0
- package/src/cli/commands/artifact/artifact.ts +8 -0
- package/src/cli/commands/artifact/delivery/archive.ts +117 -0
- package/src/cli/commands/artifact/delivery/branch-flags.ts +55 -0
- package/src/cli/commands/artifact/delivery/environment.ts +104 -0
- package/src/cli/commands/artifact/delivery/link.ts +154 -0
- package/src/cli/commands/artifact/delivery/output.ts +58 -0
- package/src/cli/commands/artifact/finish/command.ts +33 -3
- package/src/cli/commands/artifact/finish/engine.ts +47 -3
- package/src/cli/commands/artifact/finish/finisher.ts +10 -0
- package/src/cli/commands/artifact/finish/openspec.ts +177 -138
- package/src/cli/commands/artifact/finish/secret-scan.ts +43 -0
- package/src/cli/commands/artifact/pending/command.ts +31 -3
- package/src/cli/commands/artifact/pending/discovery.ts +21 -57
- package/src/cli/commands/cap/graphify.ts +52 -3
- package/src/cli/commands/cap/index-cmd.ts +8 -1
- package/src/cli/commands/cap/openspec.ts +13 -25
- package/src/cli/commands/companion/open.ts +56 -39
- package/src/cli/commands/companion/update.ts +75 -42
- package/src/cli/commands/doctor.ts +176 -3
- package/src/cli/commands/plugin/install.ts +9 -4
- package/src/cli/commands/plugin/plugin.ts +3 -2
- package/src/cli/commands/report/adapter.ts +11 -2
- package/src/cli/commands/report/contract.ts +21 -0
- package/src/cli/commands/report/highlight.ts +34 -4
- package/src/cli/commands/report/markdown.ts +110 -0
- package/src/cli/commands/report/palette.ts +118 -0
- package/src/cli/commands/report/renderer.ts +456 -153
- package/src/cli/commands/report/types.ts +16 -0
- package/src/cli/commands/setup.ts +7 -15
- package/src/cli/commands/studio/client-asset.ts +16 -0
- package/src/cli/commands/studio/openspec-cli.ts +10 -58
- package/src/cli/commands/studio/routes.ts +2 -0
- package/src/cli/commands/studio/selection.ts +19 -1
- package/src/cli/commands/studio/server.ts +42 -1
- package/src/cli/commands/studio/views/client.ts +6 -616
- package/src/cli/commands/studio/views/dashboard/changes.tsx +5 -10
- package/src/cli/commands/studio/views/document.tsx +137 -69
- package/src/cli/commands/studio/views/markdown.tsx +250 -0
- package/src/cli/commands/studio/views/palette.ts +134 -0
- package/src/cli/commands/studio/views/reports/index.tsx +2 -7
- package/src/cli/commands/studio/views/skills/index.tsx +43 -62
- package/src/cli/commands/studio/views/specs/index.tsx +7 -15
- package/src/cli/commands/studio/views/studio-client/actions.ts +51 -0
- package/src/cli/commands/studio/views/studio-client/companion.ts +13 -0
- package/src/cli/commands/studio/views/studio-client/index.ts +32 -0
- package/src/cli/commands/studio/views/studio-client/keys.ts +3 -0
- package/src/cli/commands/studio/views/studio-client/main.ts +3 -0
- package/src/cli/commands/studio/views/studio-client/page.ts +146 -0
- package/src/cli/commands/studio/views/studio-client/panels.ts +57 -0
- package/src/cli/commands/studio/views/studio-client/reports.ts +122 -0
- package/src/cli/commands/studio/views/studio-client/theme.ts +63 -0
- package/src/cli/commands/studio/views/studio-client/vault-editor.ts +166 -0
- package/src/cli/commands/studio/views/studio-client/vault-pane.ts +202 -0
- package/src/cli/commands/studio/views/studio-client/vault-tree.ts +258 -0
- package/src/cli/commands/studio/views/studio-client.generated.ts +2 -0
- package/src/cli/commands/studio/views/styles.ts +217 -206
- package/src/cli/commands/studio/views/vault/state.ts +3 -5
- package/src/cli/commands/studio/views/workflow/delivery-path.tsx +370 -0
- package/src/cli/commands/studio/views/workflow/graph.ts +220 -0
- package/src/cli/commands/studio/views/workflow/index.tsx +2 -3
- package/src/cli/commands/studio/views/workflow/steps.ts +228 -287
- package/src/cli/commands/wrap.ts +5 -13
- package/src/cli/main.ts +4 -1
- package/src/cli/parse-flags.ts +25 -7
- package/src/cli/setup-selector.tsx +38 -162
- package/src/cli/usage.ts +4 -0
- package/src/hooks/companion-instructions.ts +190 -0
- package/src/hooks/hook-io.ts +63 -0
- package/src/hooks/session-banner.ts +21 -29
- package/src/hooks/session-guidance.ts +9 -17
- package/src/hooks/session-prepare.ts +115 -28
- package/src/hooks/validate-artifact-path.ts +82 -24
- package/src/index.ts +4 -0
- package/src/lib/change-layout.ts +154 -0
- package/src/lib/components/wizard-header.tsx +3 -1
- package/src/lib/context-mode-package.ts +24 -11
- package/src/lib/delivery/archive-change.ts +154 -0
- package/src/lib/delivery/branches.ts +108 -0
- package/src/lib/delivery/environment.ts +29 -0
- package/src/lib/delivery/forge.ts +279 -0
- package/src/lib/delivery/overlap.ts +202 -0
- package/src/lib/delivery/record-delivery.ts +196 -0
- package/src/lib/delivery/record.ts +343 -0
- package/src/lib/delivery/refresh.ts +174 -0
- package/src/lib/delivery/repository-access.ts +97 -0
- package/src/lib/delivery/stages.ts +111 -0
- package/src/lib/delivery/token.ts +58 -0
- package/src/lib/frontmatter.ts +18 -0
- package/src/lib/opencode-plugin-package.ts +5 -5
- package/src/lib/openspec-cli.ts +116 -0
- package/src/lib/openspec-scopes.ts +86 -0
- package/src/lib/orchestrator/adapters/claude-args.ts +50 -0
- package/src/lib/orchestrator/adapters/claude.ts +48 -40
- package/src/lib/orchestrator/adapters/opencode.ts +2 -0
- package/src/lib/orchestrator/editor.ts +9 -9
- package/src/lib/orchestrator/openspec-store.ts +14 -54
- package/src/lib/orchestrator/projection-claude-commands.ts +36 -0
- package/src/lib/orchestrator/projection-claude-links.ts +159 -0
- package/src/lib/orchestrator/projection-claude-skills.ts +19 -137
- package/src/lib/orchestrator/projection-entries.ts +45 -14
- package/src/lib/orchestrator/projection-runtime-documents.ts +15 -6
- package/src/lib/orchestrator/projection-types.ts +3 -0
- package/src/lib/orchestrator/refresh-working-claude-links.ts +87 -0
- package/src/lib/orchestrator/session-prepare.ts +45 -5
- package/src/lib/orchestrator/setup-compatibilities.ts +18 -75
- package/src/lib/orchestrator/types.ts +2 -2
- package/src/lib/orchestrator/wrap-pass.ts +22 -1
- package/src/lib/package-paths.ts +8 -0
- package/src/opencode/companion-hooks.ts +64 -5
- package/src/opencode/companion-policy.ts +37 -18
- package/src/opencode/projected-guidance.ts +4 -1
- package/src/opencode/session-prepare.ts +2 -1
- package/src/opencode/tui.tsx +4 -2
- package/src/runtime/artifact-refusal.ts +63 -0
- package/src/runtime/claude-delivery-plan.ts +89 -7
- package/src/runtime/commit-attribution-guard.ts +181 -0
- package/src/runtime/companion-guidance.ts +15 -3
- package/src/runtime/companion-instructions.ts +340 -0
- package/src/runtime/error-message.ts +4 -0
- package/src/runtime/index.ts +5 -0
- package/src/runtime/managed-block.ts +117 -0
- package/src/runtime/markdown-scan.ts +266 -0
- package/src/runtime/policy.ts +15 -1
- package/src/runtime/projected-flag.ts +7 -0
- package/src/runtime/projected-guidance.ts +1 -0
- package/src/runtime/runtime-document-manifest.ts +62 -11
- package/src/runtime/secret-guard.ts +148 -0
- package/src/runtime/secret-patterns.ts +164 -0
- package/src/runtime/secret-scan.ts +90 -0
- package/src/runtime/session-kind.ts +13 -0
- package/src/runtime/shell-heredoc.ts +81 -0
- package/src/runtime/wrap-state.ts +36 -2
- package/src/runtime/write-atomic.ts +64 -0
- package/src/templates/capabilities/openspec-cap/mate-minimal/schema.yaml +8 -6
- package/src/templates/capabilities/openspec-cap/mate-minimal/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +7 -9
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/openspec-conventions.yaml +10 -12
- package/src/templates/mate-skills/THIRD-PARTY-NOTICES.md +40 -0
- package/src/templates/mate-skills/agents/mate-apply-change/SKILL.md +76 -0
- package/src/templates/mate-skills/agents/mate-apply-change/references/openspec-apply-change.md +198 -0
- package/src/templates/mate-skills/agents/mate-archive-change/SKILL.md +59 -0
- package/src/templates/mate-skills/{claude/mate-openspec-backfill → agents/mate-backfill-spec}/SKILL.md +4 -4
- package/src/templates/mate-skills/agents/mate-clarify-intent/SKILL.md +95 -0
- package/src/templates/mate-skills/agents/mate-explore-change/SKILL.md +50 -0
- package/src/templates/mate-skills/agents/mate-explore-change/references/openspec-explore.md +348 -0
- package/src/templates/mate-skills/agents/mate-grill-me/SKILL.md +3 -3
- package/src/templates/mate-skills/agents/mate-interview-me/SKILL.md +5 -5
- package/src/templates/mate-skills/agents/mate-propose-change/SKILL.md +103 -0
- package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-propose.md +167 -0
- package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-update-change.md +101 -0
- package/src/templates/mate-skills/{claude/mate-artifact-publish → agents/mate-publish-artifact}/SKILL.md +91 -23
- package/src/templates/mate-skills/agents/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
- package/src/templates/mate-skills/agents/mate-show-me/SKILL.md +74 -16
- package/src/templates/mate-skills/agents/mate-simplify-code/SKILL.md +74 -68
- package/src/templates/mate-skills/claude/mate-apply-change/SKILL.md +76 -0
- package/src/templates/mate-skills/claude/mate-apply-change/references/openspec-apply-change.md +198 -0
- package/src/templates/mate-skills/claude/mate-archive-change/SKILL.md +59 -0
- package/src/templates/mate-skills/{agents/mate-openspec-backfill → claude/mate-backfill-spec}/SKILL.md +4 -4
- package/src/templates/mate-skills/claude/mate-clarify-intent/SKILL.md +95 -0
- package/src/templates/mate-skills/claude/mate-explore-change/SKILL.md +50 -0
- package/src/templates/mate-skills/claude/mate-explore-change/references/openspec-explore.md +348 -0
- package/src/templates/mate-skills/claude/mate-grill-me/SKILL.md +3 -3
- package/src/templates/mate-skills/claude/mate-interview-me/SKILL.md +5 -5
- package/src/templates/mate-skills/claude/mate-propose-change/SKILL.md +103 -0
- package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-propose.md +167 -0
- package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-update-change.md +101 -0
- package/src/templates/mate-skills/{agents/mate-artifact-publish → claude/mate-publish-artifact}/SKILL.md +91 -23
- package/src/templates/mate-skills/claude/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
- package/src/templates/mate-skills/claude/mate-show-me/SKILL.md +74 -16
- package/src/templates/mate-skills/claude/mate-simplify-code/SKILL.md +74 -68
- package/src/templates/mate-skills/openspec-references.ts +140 -0
- package/src/templates/root/TEMPLATE_AGENTS.md +2 -2
- package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +3727 -320
- package/src/tools/setup/capabilities/context-mode.ts +25 -5
- package/src/tools/setup/capabilities/graphify-gate.ts +31 -0
- package/src/tools/setup/capabilities/graphify-secrets.ts +255 -0
- package/src/tools/setup/capabilities/graphify.ts +12 -0
- package/src/tools/setup/capabilities/openspec.ts +151 -270
- package/src/tools/setup/capabilities/tokensave.ts +36 -3
- package/src/tools/setup/context-services.ts +9 -17
- package/src/tools/setup/dynamic-plugins/install.ts +24 -28
- package/src/tools/setup/engine.ts +97 -21
- package/src/tools/setup/managed-output.ts +51 -0
- package/src/tools/setup/mate.ts +86 -11
- package/src/tools/setup/plugin.ts +35 -0
- package/src/tools/setup/plugins/guidance.ts +7 -4
- package/src/tools/setup/providers/agent-file-sections.ts +4 -2
- package/src/tools/setup/providers/claude-attribution.ts +65 -0
- package/src/tools/setup/providers/claude-plugin-folders.ts +625 -0
- package/src/tools/setup/providers/claude-plugin-hooks.ts +39 -19
- package/src/tools/setup/providers/claude.ts +284 -8
- package/src/tools/setup/providers/opencode.ts +180 -1
- package/src/tools/setup/utils.ts +14 -4
- package/src/tools/setup.ts +126 -10
- package/wrappers/bin/graphify +52 -4
- package/wrappers/bin/openspec +33 -9
- package/wrappers/lib/graphify-gate.mjs +8 -0
- package/src/cli/commands/studio/views/workflow/transcript.tsx +0 -190
- package/src/tools/setup/runtime-documents.ts +0 -291
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource hono/jsx */
|
|
2
|
-
|
|
3
|
-
import type { StudioCompanionPayload } from "../../payload";
|
|
4
|
-
import {
|
|
5
|
-
workflowPlan,
|
|
6
|
-
type WorkflowBranch,
|
|
7
|
-
type WorkflowStep,
|
|
8
|
-
type WorkflowStepBadge,
|
|
9
|
-
} from "./steps";
|
|
10
|
-
|
|
11
|
-
type WorkflowPlan = ReturnType<typeof workflowPlan>;
|
|
12
|
-
|
|
13
|
-
export function WorkflowTranscript({ payload }: { payload: StudioCompanionPayload }) {
|
|
14
|
-
return <CommandTranscript plan={workflowPlan(payload.skills)} />;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function CommandTranscript({ plan }: { plan: WorkflowPlan }) {
|
|
18
|
-
return (
|
|
19
|
-
<section className="workflow-console" aria-label="Workflow execution map">
|
|
20
|
-
<header className="workflow-console-header">
|
|
21
|
-
<div className="workflow-console-header-title">
|
|
22
|
-
<span>Workflow map</span>
|
|
23
|
-
<strong>Companion delivery path</strong>
|
|
24
|
-
</div>
|
|
25
|
-
<span>Read-only guide</span>
|
|
26
|
-
</header>
|
|
27
|
-
<div className="workflow-console-body">
|
|
28
|
-
<ConsoleLine step={plan.start} marker="01" />
|
|
29
|
-
<div className="workflow-console-context">
|
|
30
|
-
<span>precondition</span>
|
|
31
|
-
<div>
|
|
32
|
-
<p>The desired change, its scope, and the risk involved are clear.</p>
|
|
33
|
-
<small>If any of that is unclear, pre-explore above resolves it first.</small>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
<div className="workflow-console-schema" data-workflow-schema-root>
|
|
37
|
-
<div
|
|
38
|
-
className="workflow-console-tabs"
|
|
39
|
-
data-workflow-schema-switch
|
|
40
|
-
role="tablist"
|
|
41
|
-
aria-label="Planning depth"
|
|
42
|
-
>
|
|
43
|
-
{plan.branches.map((branch, index) => (
|
|
44
|
-
<button
|
|
45
|
-
key={branch.profile}
|
|
46
|
-
type="button"
|
|
47
|
-
role="tab"
|
|
48
|
-
data-workflow-schema-profile={branch.profile}
|
|
49
|
-
aria-selected={index === 0}
|
|
50
|
-
>
|
|
51
|
-
<strong>{branch.label}</strong>
|
|
52
|
-
<small>{index === 0 ? "full planning" : "minimal planning"}</small>
|
|
53
|
-
</button>
|
|
54
|
-
))}
|
|
55
|
-
</div>
|
|
56
|
-
<div className="workflow-console-panels">
|
|
57
|
-
{plan.branches.map((branch, branchIndex) => (
|
|
58
|
-
<section
|
|
59
|
-
key={branch.profile}
|
|
60
|
-
data-workflow-schema-panel={branch.profile}
|
|
61
|
-
data-active={branchIndex === 0 ? "true" : "false"}
|
|
62
|
-
role="tabpanel"
|
|
63
|
-
>
|
|
64
|
-
<div className="workflow-console-branch-rule">
|
|
65
|
-
<p className="workflow-console-branch-rule-description">
|
|
66
|
-
{planningRule(branch.profile)}
|
|
67
|
-
</p>
|
|
68
|
-
</div>
|
|
69
|
-
{branch.steps.map((step, index) => (
|
|
70
|
-
<div key={step.id}>
|
|
71
|
-
{step.sessionBreakBefore ? <WorkflowSessionBreak /> : null}
|
|
72
|
-
<ConsoleLine
|
|
73
|
-
step={step}
|
|
74
|
-
marker={`${branchIndex === 0 ? "F" : "M"}${String(index + 1).padStart(2, "0")}`}
|
|
75
|
-
/>
|
|
76
|
-
</div>
|
|
77
|
-
))}
|
|
78
|
-
</section>
|
|
79
|
-
))}
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
<div className="workflow-console-divider">paths rejoin</div>
|
|
83
|
-
{plan.shared.map((step) => (
|
|
84
|
-
<div key={step.id}>
|
|
85
|
-
{step.sessionBreakBefore ? <WorkflowSessionBreak /> : null}
|
|
86
|
-
<ConsoleLine step={step} marker="+" />
|
|
87
|
-
</div>
|
|
88
|
-
))}
|
|
89
|
-
<div className="workflow-console-divider">feature is on production</div>
|
|
90
|
-
<ConsoleLine step={plan.finish} marker="END" />
|
|
91
|
-
</div>
|
|
92
|
-
</section>
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function planningRule(profile: WorkflowBranch["profile"]): string {
|
|
97
|
-
return profile === "mate-v1"
|
|
98
|
-
? "Use when intent is clear, but the solution, repository evidence, or risk still needs exploration."
|
|
99
|
-
: "Use only when intent and scope are clear, the change is low-risk, and it stays within one Area.";
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function ConsoleLine({ step, marker }: { step: WorkflowStep; marker: string }) {
|
|
103
|
-
const hasAlternatives = Boolean(step.alternatives?.length);
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<article className="workflow-console-line">
|
|
107
|
-
<span className="workflow-console-gutter">{marker}</span>
|
|
108
|
-
<div className="workflow-console-line-body">
|
|
109
|
-
<div className="workflow-console-step-title">
|
|
110
|
-
<strong>{step.title}</strong>
|
|
111
|
-
<WorkflowBadges badges={step.badges} />
|
|
112
|
-
</div>
|
|
113
|
-
{!hasAlternatives ? (
|
|
114
|
-
step.kind === "review" ? (
|
|
115
|
-
<div className="workflow-console-command workflow-console-review">
|
|
116
|
-
<span className="workflow-console-command-label">Review</span>
|
|
117
|
-
<code>{step.prompt}</code>
|
|
118
|
-
</div>
|
|
119
|
-
) : (
|
|
120
|
-
<div className="workflow-console-command">
|
|
121
|
-
<span className="workflow-console-command-label">Run</span>
|
|
122
|
-
<code>{step.prompt}</code>
|
|
123
|
-
<WorkflowCopy text={step.copyPrompt} label={`${step.title} prompt`} />
|
|
124
|
-
</div>
|
|
125
|
-
)
|
|
126
|
-
) : null}
|
|
127
|
-
{hasAlternatives ? <WorkflowOptions step={step} /> : null}
|
|
128
|
-
<div className="workflow-console-explanation">
|
|
129
|
-
<div className="workflow-console-detail">
|
|
130
|
-
<span>What</span>
|
|
131
|
-
<p>{step.what}</p>
|
|
132
|
-
</div>
|
|
133
|
-
<div className="workflow-console-detail">
|
|
134
|
-
<span>Why</span>
|
|
135
|
-
<small>{step.why}</small>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
{!hasAlternatives ? <WorkflowOptions step={step} /> : null}
|
|
139
|
-
</div>
|
|
140
|
-
</article>
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function WorkflowOptions({ step }: { step: WorkflowStep }) {
|
|
145
|
-
if (!step.alternatives?.length) return null;
|
|
146
|
-
return (
|
|
147
|
-
<div className="workflow-option-list">
|
|
148
|
-
{step.alternatives.map((alternative) => (
|
|
149
|
-
<div key={alternative.name} className="workflow-option">
|
|
150
|
-
<div className="workflow-console-command">
|
|
151
|
-
<span className="workflow-console-command-label">Option</span>
|
|
152
|
-
<code>{alternative.prompt}</code>
|
|
153
|
-
<WorkflowCopy text={alternative.copyPrompt} label={`${alternative.name} prompt`} />
|
|
154
|
-
</div>
|
|
155
|
-
<small>{alternative.description}</small>
|
|
156
|
-
</div>
|
|
157
|
-
))}
|
|
158
|
-
</div>
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function WorkflowCopy({ text, label }: { text: string; label: string }) {
|
|
163
|
-
return (
|
|
164
|
-
<button type="button" className="runway-step-copy" data-copy={text} data-copy-label={label}>
|
|
165
|
-
Copy
|
|
166
|
-
</button>
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function WorkflowSessionBreak() {
|
|
171
|
-
return <div className="workflow-session-break">New session / clean chat</div>;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function WorkflowBadges({ badges }: { badges: WorkflowStepBadge[] }) {
|
|
175
|
-
return (
|
|
176
|
-
<span className="workflow-badges">
|
|
177
|
-
{badges.map((badge) => (
|
|
178
|
-
<em key={badge} className={`runway-step-badge runway-step-badge-${badge}`}>
|
|
179
|
-
{badgeLabel(badge)}
|
|
180
|
-
</em>
|
|
181
|
-
))}
|
|
182
|
-
</span>
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function badgeLabel(badge: WorkflowStepBadge): string {
|
|
187
|
-
if (badge === "human-in-loop") return "Review";
|
|
188
|
-
if (badge === "openspec") return "OpenSpec";
|
|
189
|
-
return badge;
|
|
190
|
-
}
|
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
|
|
3
|
-
import { runtimeOpenCodePluginRoot } from "../../runtime/runtime-directory";
|
|
4
|
-
import {
|
|
5
|
-
CLAUDE_LOCAL_CONFIG_DOCUMENT,
|
|
6
|
-
CLAUDE_SETTINGS_DOCUMENT,
|
|
7
|
-
OPENCODE_CONFIG_DOCUMENT,
|
|
8
|
-
OPENCODE_MCP_SERVERS_AT,
|
|
9
|
-
type ManagedRegion,
|
|
10
|
-
type RenderedRuntimeDocument,
|
|
11
|
-
} from "../../runtime/runtime-document-manifest";
|
|
12
|
-
import type { CapabilityContributionInput, SetupContext } from "./plugin";
|
|
13
|
-
import {
|
|
14
|
-
mateBannerHookGroups,
|
|
15
|
-
mateGuardHookGroups,
|
|
16
|
-
mateGuidanceHookGroups,
|
|
17
|
-
mateSessionPrepareHookGroups,
|
|
18
|
-
} from "./providers/claude-plugin-hooks";
|
|
19
|
-
import {
|
|
20
|
-
renderManagedClaudeHookGroups,
|
|
21
|
-
renderManagedClaudeMcpServers,
|
|
22
|
-
renderManagedClaudePermissionEntries,
|
|
23
|
-
} from "./providers/claude";
|
|
24
|
-
import {
|
|
25
|
-
renderCompanionExternalDirectoryPermissions,
|
|
26
|
-
renderManagedOpenCodeMcpServers,
|
|
27
|
-
} from "./providers/opencode";
|
|
28
|
-
import { isRecord, readOpenCodeConfig } from "./providers/opencode-format";
|
|
29
|
-
import { projectionRepoRoot } from "./surface-target";
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* The working target's documents, rendered as values. Each is a document the
|
|
33
|
-
* runtime finds by walking up from the current directory, and every path inside
|
|
34
|
-
* one still names the Companion Repository — only where the document is placed
|
|
35
|
-
* differs from the companion target.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
export { CLAUDE_LOCAL_CONFIG_DOCUMENT };
|
|
39
|
-
|
|
40
|
-
function nonEmpty(region: ManagedRegion): boolean {
|
|
41
|
-
if (region.kind === "list") return region.values.length > 0;
|
|
42
|
-
if (region.kind === "map") return Object.keys(region.entries).length > 0;
|
|
43
|
-
return true;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function document(documentPath: string, regions: ManagedRegion[]): RenderedRuntimeDocument[] {
|
|
47
|
-
const kept = regions.filter(nonEmpty);
|
|
48
|
-
return kept.length > 0 ? [{ path: documentPath, regions: kept }] : [];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* The Mate plugin root is what makes an Unmanaged OpenCode session load the
|
|
53
|
-
* companion guidance at all: OpenCode has no config channel for a generated
|
|
54
|
-
* system prompt, so the plugin is the only injection point. It is named through
|
|
55
|
-
* the runtime directory, so a reinstall does not strand a wrapped repository.
|
|
56
|
-
* The managed launch carries no overlay of its own, so this document is the one
|
|
57
|
-
* place every OpenCode session in the repository loads it from.
|
|
58
|
-
*/
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Loaded by a managed session beside the launch's plugin. Mate hooks here run
|
|
62
|
-
* twice there, inertly: guard is input-pure, guidance/prepare yield under a
|
|
63
|
-
* launch env, banner is `--projected`. Capability groups run once: the launch
|
|
64
|
-
* omits them from `--settings`. A launch passes no `--add-dir`; it relies on
|
|
65
|
-
* `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD` for companion `CLAUDE.md`.
|
|
66
|
-
*/
|
|
67
|
-
function claudeSettingsRegions(
|
|
68
|
-
ctx: SetupContext,
|
|
69
|
-
inputs: CapabilityContributionInput[],
|
|
70
|
-
): ManagedRegion[] {
|
|
71
|
-
const capabilityHooks = renderManagedClaudeHookGroups(inputs);
|
|
72
|
-
const mateHooks: Record<string, unknown[]> = {
|
|
73
|
-
PreToolUse: mateGuardHookGroups(),
|
|
74
|
-
SessionStart: [
|
|
75
|
-
...mateBannerHookGroups(),
|
|
76
|
-
...mateGuidanceHookGroups(),
|
|
77
|
-
...mateSessionPrepareHookGroups(),
|
|
78
|
-
],
|
|
79
|
-
};
|
|
80
|
-
const events = [...new Set([...Object.keys(mateHooks), ...Object.keys(capabilityHooks)])];
|
|
81
|
-
return [
|
|
82
|
-
...events.map((event): ManagedRegion => ({
|
|
83
|
-
at: ["hooks", event],
|
|
84
|
-
kind: "list",
|
|
85
|
-
values: [...(mateHooks[event] ?? []), ...(capabilityHooks[event] ?? [])],
|
|
86
|
-
})),
|
|
87
|
-
/**
|
|
88
|
-
* `stripPrefix` heals a repository an older Mate wrapped. That version wrote
|
|
89
|
-
* the whole launch environment here, and Claude Code applies `env` to the
|
|
90
|
-
* session, so every hook in a bare session inherited `MATE_*` and read the
|
|
91
|
-
* repository as a Managed Session — silently, since looking managed is
|
|
92
|
-
* exactly what makes the guidance hook emit nothing. Guidance must be the
|
|
93
|
-
* projection's to give in an Unmanaged Session, so no `MATE_*` key may
|
|
94
|
-
* survive here.
|
|
95
|
-
*/
|
|
96
|
-
{
|
|
97
|
-
at: ["env"],
|
|
98
|
-
kind: "map",
|
|
99
|
-
entries: { CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD: "1" },
|
|
100
|
-
stripPrefix: "MATE_",
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
at: ["permissions", "allow"],
|
|
104
|
-
kind: "list",
|
|
105
|
-
values: renderManagedClaudePermissionEntries(ctx.companionPath, inputs),
|
|
106
|
-
},
|
|
107
|
-
];
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function renderDocumentsForTarget(
|
|
111
|
-
ctx: SetupContext,
|
|
112
|
-
inputsByRuntime: Map<string, CapabilityContributionInput[]>,
|
|
113
|
-
): RenderedRuntimeDocument[] {
|
|
114
|
-
const repoRoot = projectionRepoRoot(ctx);
|
|
115
|
-
|
|
116
|
-
const documents: RenderedRuntimeDocument[] = [];
|
|
117
|
-
if (ctx.activeProviders.includes("claude")) {
|
|
118
|
-
const inputs = inputsByRuntime.get("claude") ?? [];
|
|
119
|
-
documents.push(...document(CLAUDE_SETTINGS_DOCUMENT, claudeSettingsRegions(ctx, inputs)));
|
|
120
|
-
/**
|
|
121
|
-
* Local scope, not `.mcp.json`: a project-scoped server is inert until a
|
|
122
|
-
* human approves it in a session, which an Unmanaged Session is exactly the
|
|
123
|
-
* case that cannot rely on. The repo path is the key Claude Code files a
|
|
124
|
-
* project under, resolved so a symlinked working directory still matches.
|
|
125
|
-
*
|
|
126
|
-
* Confirmed against a real `~/.claude.json`: the keys there are the
|
|
127
|
-
* directories sessions started in, not repository roots — sibling
|
|
128
|
-
* directories of one checkout each hold their own entry, as do temporary
|
|
129
|
-
* directories. So this reaches exactly one session: the one started at the
|
|
130
|
-
* repository root. A session started in a subdirectory of a wrapped
|
|
131
|
-
* repository has its own key, finds no `mcpServers` under it, and sees none
|
|
132
|
-
* of these servers — the Unmanaged Session case this document exists for,
|
|
133
|
-
* missed. That limit is deliberate. The directories a session might start
|
|
134
|
-
* in are unbounded, and every one of them would be a fresh entry in the
|
|
135
|
-
* user's global configuration that nothing later reclaims; keying the
|
|
136
|
-
* repository root only keeps the projection's footprint there to a single
|
|
137
|
-
* key. A subdirectory session still gets the rest of the projection — the
|
|
138
|
-
* documents inside the repository are found by the runtime's own upward
|
|
139
|
-
* walk — it loses only the MCP servers.
|
|
140
|
-
*/
|
|
141
|
-
documents.push(
|
|
142
|
-
...document(CLAUDE_LOCAL_CONFIG_DOCUMENT, [
|
|
143
|
-
{
|
|
144
|
-
at: ["projects", repoRoot, "mcpServers"],
|
|
145
|
-
kind: "map",
|
|
146
|
-
entries: renderManagedClaudeMcpServers(inputs),
|
|
147
|
-
},
|
|
148
|
-
]),
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
if (ctx.activeProviders.includes("opencode")) {
|
|
152
|
-
const inputs = inputsByRuntime.get("opencode") ?? [];
|
|
153
|
-
documents.push(
|
|
154
|
-
...document(OPENCODE_CONFIG_DOCUMENT, [
|
|
155
|
-
{
|
|
156
|
-
at: ["plugins"],
|
|
157
|
-
kind: "list",
|
|
158
|
-
values: [runtimeOpenCodePluginRoot()],
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
at: [...OPENCODE_MCP_SERVERS_AT],
|
|
162
|
-
kind: "map",
|
|
163
|
-
entries: renderManagedOpenCodeMcpServers(inputs),
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
at: ["permissions"],
|
|
167
|
-
kind: "list",
|
|
168
|
-
values: renderCompanionExternalDirectoryPermissions(ctx.companionPath),
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
at: ["skills"],
|
|
172
|
-
kind: "list",
|
|
173
|
-
values: [path.join(ctx.companionPath, ".agents", "skills")],
|
|
174
|
-
},
|
|
175
|
-
{ at: ["references"], kind: "map", entries: { mate: ctx.companionPath } },
|
|
176
|
-
]),
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
return documents;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/** Keys Mate renders its own regions for; the companion's values join those regions. */
|
|
183
|
-
const MATE_OPENCODE_KEYS = new Set(["plugins", "permissions", "skills", "mcp", "references"]);
|
|
184
|
-
|
|
185
|
-
function relativePathValues(value: unknown, at: string[]): string[] {
|
|
186
|
-
if (typeof value === "string") {
|
|
187
|
-
return value.startsWith("./") || value.startsWith("../") ? [at.join(".")] : [];
|
|
188
|
-
}
|
|
189
|
-
if (Array.isArray(value)) {
|
|
190
|
-
return value.flatMap((entry, index) => relativePathValues(entry, [...at, String(index)]));
|
|
191
|
-
}
|
|
192
|
-
if (isRecord(value)) {
|
|
193
|
-
return Object.entries(value).flatMap(([key, entry]) => relativePathValues(entry, [...at, key]));
|
|
194
|
-
}
|
|
195
|
-
return [];
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/** Replaces the region at `at` with what `merge` builds from it, or appends one. */
|
|
199
|
-
function upsertRegion(
|
|
200
|
-
regions: ManagedRegion[],
|
|
201
|
-
at: string[],
|
|
202
|
-
merge: (found: ManagedRegion | undefined) => ManagedRegion,
|
|
203
|
-
): void {
|
|
204
|
-
const key = JSON.stringify(at);
|
|
205
|
-
const index = regions.findIndex((region) => JSON.stringify(region.at) === key);
|
|
206
|
-
if (index === -1) regions.push(merge(undefined));
|
|
207
|
-
else regions[index] = merge(regions[index]);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/** Mate's own values stay; companion values it does not already list go first. */
|
|
211
|
-
function mergeListRegion(regions: ManagedRegion[], at: string[], extra: unknown[]): void {
|
|
212
|
-
upsertRegion(regions, at, (found) => {
|
|
213
|
-
const own = found?.kind === "list" ? found.values : [];
|
|
214
|
-
const seen = new Set(own.map((value) => JSON.stringify(value)));
|
|
215
|
-
const values = [...extra.filter((value) => !seen.has(JSON.stringify(value))), ...own];
|
|
216
|
-
return { at, kind: "list", values };
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/** Mate's own entries win over companion entries of the same name. */
|
|
221
|
-
function mergeMapRegion(
|
|
222
|
-
regions: ManagedRegion[],
|
|
223
|
-
at: string[],
|
|
224
|
-
extra: Record<string, unknown>,
|
|
225
|
-
): void {
|
|
226
|
-
upsertRegion(regions, at, (found) => {
|
|
227
|
-
const own = found?.kind === "map" ? found.entries : {};
|
|
228
|
-
return { at, kind: "map", entries: { ...extra, ...own } };
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/** Relative reference paths resolved against the companion's `.opencode/`. */
|
|
233
|
-
function resolveReferences(
|
|
234
|
-
references: Record<string, unknown>,
|
|
235
|
-
companionDir: string,
|
|
236
|
-
): Record<string, unknown> {
|
|
237
|
-
return Object.fromEntries(
|
|
238
|
-
Object.entries(references).map(([name, reference]) => [
|
|
239
|
-
name,
|
|
240
|
-
typeof reference === "string" && !path.isAbsolute(reference)
|
|
241
|
-
? path.resolve(companionDir, reference)
|
|
242
|
-
: reference,
|
|
243
|
-
]),
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Folds the companion's `.opencode/opencode.json` into the wrapped document.
|
|
249
|
-
* Mate's regions beat companion values; other top-level keys yield to the
|
|
250
|
-
* repository's. `references` resolve against the companion's `.opencode/`;
|
|
251
|
-
* other relative paths are reported, not rewritten.
|
|
252
|
-
*/
|
|
253
|
-
export async function mergeCompanionOpenCodeConfig(
|
|
254
|
-
companionPath: string,
|
|
255
|
-
documents: RenderedRuntimeDocument[],
|
|
256
|
-
): Promise<{ documents: RenderedRuntimeDocument[]; warnings: string[] }> {
|
|
257
|
-
const index = documents.findIndex((document) => document.path === OPENCODE_CONFIG_DOCUMENT);
|
|
258
|
-
const companionConfigPath = path.join(companionPath, ".opencode", "opencode.json");
|
|
259
|
-
const companion = index === -1 ? null : await readOpenCodeConfig(companionConfigPath);
|
|
260
|
-
if (!companion?.present) return { documents, warnings: [] };
|
|
261
|
-
|
|
262
|
-
const regions = [...documents[index]!.regions] as ManagedRegion[];
|
|
263
|
-
const warnings: string[] = [];
|
|
264
|
-
|
|
265
|
-
const companionDir = path.dirname(companionConfigPath);
|
|
266
|
-
for (const [key, value] of Object.entries(companion.config)) {
|
|
267
|
-
if (key === "$schema") continue;
|
|
268
|
-
if (key === "references" && isRecord(value)) {
|
|
269
|
-
mergeMapRegion(regions, ["references"], resolveReferences(value, companionDir));
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
for (const at of relativePathValues(value, [key])) {
|
|
273
|
-
warnings.push(
|
|
274
|
-
`${companionConfigPath}: ${at} is a relative path, which resolves against the working repository once merged into ${OPENCODE_CONFIG_DOCUMENT}; use an absolute path.`,
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
if (MATE_OPENCODE_KEYS.has(key)) {
|
|
278
|
-
if (key === "mcp" && isRecord(value) && isRecord(value.servers)) {
|
|
279
|
-
mergeMapRegion(regions, [...OPENCODE_MCP_SERVERS_AT], value.servers);
|
|
280
|
-
} else if (Array.isArray(value)) {
|
|
281
|
-
mergeListRegion(regions, [key], value);
|
|
282
|
-
}
|
|
283
|
-
continue;
|
|
284
|
-
}
|
|
285
|
-
regions.push({ at: [key], kind: "value", value, yields: true });
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const next = [...documents];
|
|
289
|
-
next[index] = { path: OPENCODE_CONFIG_DOCUMENT, regions };
|
|
290
|
-
return { documents: next, warnings };
|
|
291
|
-
}
|