@uniqbit/mate-core 0.15.5 → 0.16.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/.claude-plugin/plugin.json +2 -2
- package/claude-plugin/hooks/hooks.json +3 -6
- package/claude-plugin/hooks/session-guidance.mjs +8 -0
- package/claude-plugin/hooks/ts-loader.mjs +19 -0
- package/package.json +6 -4
- package/src/cli/commands/artifact/artifact.ts +19 -3
- package/src/cli/commands/artifact/finish/command.ts +183 -57
- package/src/cli/commands/artifact/finish/engine.ts +80 -91
- package/src/cli/commands/artifact/finish/finisher.ts +26 -33
- package/src/cli/commands/artifact/finish/git.ts +34 -23
- package/src/cli/commands/artifact/finish/index.ts +9 -3
- package/src/cli/commands/artifact/finish/openspec.ts +225 -97
- package/src/cli/commands/artifact/pending/command.ts +175 -0
- package/src/cli/commands/artifact/pending/discovery.ts +249 -0
- package/src/cli/commands/artifact/pending/index.ts +17 -0
- package/src/cli/commands/cap/index-cmd.ts +9 -1
- package/src/cli/commands/cap/index.ts +2 -6
- package/src/cli/commands/companion/companion.ts +5 -1
- package/src/cli/commands/companion/link.ts +2 -2
- package/src/cli/commands/companion/sync.ts +92 -0
- package/src/cli/commands/doctor.ts +0 -3
- package/src/cli/commands/launch/shared.ts +23 -5
- package/src/cli/commands/report/collector.ts +72 -78
- package/src/cli/commands/report/contract.ts +40 -1
- package/src/cli/commands/report/highlight.ts +27 -0
- package/src/cli/commands/report/index.ts +11 -18
- package/src/cli/commands/report/renderer.ts +199 -2
- package/src/cli/commands/report/types.ts +26 -1
- package/src/cli/commands/shared/companion-selection.ts +107 -10
- package/src/cli/commands/studio/areas.ts +68 -0
- package/src/cli/commands/studio/index.ts +69 -0
- package/src/cli/commands/studio/inventory.ts +55 -0
- package/src/cli/commands/studio/mate-inventory.ts +43 -0
- package/src/cli/commands/studio/openspec-cli.ts +198 -0
- package/src/cli/commands/studio/payload.ts +184 -0
- package/src/cli/commands/studio/routes.ts +2 -0
- package/src/cli/commands/studio/selection.ts +61 -0
- package/src/cli/commands/studio/server.ts +201 -0
- package/src/cli/commands/studio/snapshot.ts +63 -0
- package/src/cli/commands/studio/topology.ts +199 -0
- package/src/cli/commands/studio/views/client.ts +197 -0
- package/src/cli/commands/studio/views/companion-picker.tsx +91 -0
- package/src/cli/commands/studio/views/companion-selector.tsx +90 -0
- package/src/cli/commands/studio/views/dashboard/changes.tsx +107 -0
- package/src/cli/commands/studio/views/dashboard/index.tsx +19 -0
- package/src/cli/commands/studio/views/document.tsx +256 -0
- package/src/cli/commands/studio/views/error.tsx +20 -0
- package/src/cli/commands/studio/views/model.ts +34 -0
- package/src/cli/commands/studio/views/pairings.tsx +38 -0
- package/src/cli/commands/studio/views/skills/index.tsx +87 -0
- package/src/cli/commands/studio/views/specs/index.tsx +101 -0
- package/src/cli/commands/studio/views/styles.ts +389 -0
- package/src/cli/commands/studio/views/warnings.tsx +21 -0
- package/src/cli/commands/studio/views/workflow/index.tsx +21 -0
- package/src/cli/commands/studio/views/workflow/steps.ts +329 -0
- package/src/cli/commands/studio/views/workflow/transcript.tsx +190 -0
- package/src/cli/commands/unwrap.ts +70 -0
- package/src/cli/commands/wrap.ts +164 -0
- package/src/cli/main.ts +68 -19
- package/src/cli/parse-flags.ts +36 -11
- package/src/cli/usage.ts +12 -3
- package/src/framework.ts +1 -7
- package/src/hooks/session-banner.ts +64 -11
- package/src/hooks/session-guidance.ts +40 -0
- package/src/hooks/validate-artifact-path.ts +108 -35
- package/src/lib/fs-utils.ts +9 -0
- package/src/lib/install.ts +33 -0
- package/src/lib/orchestrator/adapters/base.ts +14 -125
- package/src/lib/orchestrator/adapters/claude.ts +0 -11
- package/src/lib/orchestrator/adapters/opencode.ts +2 -32
- package/src/lib/orchestrator/companion-git-sync.ts +94 -84
- package/src/lib/orchestrator/config-store.ts +2 -21
- package/src/lib/orchestrator/editor.ts +12 -22
- package/src/lib/orchestrator/framework-context.ts +17 -6
- package/src/lib/orchestrator/global-config-store.ts +1 -1
- package/src/lib/orchestrator/launcher.ts +97 -7
- package/src/lib/orchestrator/opencode-guidance.ts +4 -56
- package/src/lib/orchestrator/projection-claude-entry.ts +198 -0
- package/src/lib/orchestrator/projection-claude-skills.ts +120 -0
- package/src/lib/orchestrator/projection-companion-link.ts +62 -0
- package/src/lib/orchestrator/projection-entries.ts +377 -0
- package/src/lib/orchestrator/projection-record.ts +56 -0
- package/src/lib/orchestrator/projection-runtime-documents.ts +424 -0
- package/src/lib/orchestrator/projection-types.ts +169 -0
- package/src/lib/orchestrator/repo-local-registry.ts +37 -133
- package/src/lib/orchestrator/repo-local-store.ts +96 -0
- package/src/lib/orchestrator/setup-compatibilities.ts +1 -9
- package/src/lib/orchestrator/types.ts +1 -0
- package/src/lib/orchestrator/working-repo-projection.ts +366 -0
- package/src/lib/orchestrator/workspace-inventory.ts +1 -1
- package/src/lib/package-paths.ts +11 -1
- package/src/opencode/companion-hooks.ts +89 -245
- package/src/opencode/companion-policy.ts +35 -10
- package/src/opencode/index.ts +1 -0
- package/src/opencode/projected-guidance.ts +56 -0
- package/src/opencode/tui.tsx +13 -4
- package/src/playbooks/companion-guidance.ts +32 -116
- package/src/plugins.ts +0 -1
- package/src/runtime/companion-git-state.ts +156 -0
- package/src/runtime/companion-git.ts +203 -0
- package/src/runtime/companion-guidance.ts +222 -0
- package/src/runtime/companion-sync.ts +298 -0
- package/src/runtime/env-names.ts +30 -0
- package/src/runtime/env.ts +67 -35
- package/src/runtime/framework.ts +10 -0
- package/src/runtime/freshness.ts +58 -0
- package/src/runtime/index.ts +104 -0
- package/src/runtime/install.ts +30 -0
- package/src/runtime/policy.ts +66 -0
- package/src/runtime/projected-guidance.ts +45 -0
- package/src/runtime/projection.ts +224 -0
- package/src/runtime/repo-local.ts +64 -0
- package/src/templates/capabilities/openspec-cap/mate-minimal/schema.yaml +56 -0
- package/src/templates/capabilities/openspec-cap/mate-minimal/templates/spec.md +48 -0
- package/src/templates/capabilities/openspec-cap/mate-minimal/templates/tasks.md +22 -0
- package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +31 -90
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/design.md +3 -0
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/explore-brief.md +6 -6
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/tasks.md +3 -0
- package/src/templates/capabilities/openspec-cap/openspec-conventions.yaml +30 -0
- package/src/templates/capabilities/react-doctor/claude/hooks/react-doctor.sh +2 -2
- package/src/templates/mate-skills/agents/mate-artifact-publish/SKILL.md +185 -0
- package/src/templates/mate-skills/agents/mate-artifact-publish/references/openspec.md +227 -0
- package/src/templates/mate-skills/agents/mate-domain-modeling/SKILL.md +68 -0
- package/src/templates/mate-skills/agents/mate-domain-modeling/references/ADR-FORMAT.md +9 -0
- package/src/templates/mate-skills/agents/mate-domain-modeling/references/CONTEXT-FORMAT.md +18 -0
- package/src/templates/mate-skills/agents/mate-grill-me/SKILL.md +14 -0
- package/src/templates/mate-skills/agents/mate-grill-with-docs/SKILL.md +29 -0
- package/src/templates/mate-skills/agents/mate-grilling/SKILL.md +49 -0
- package/src/templates/mate-skills/agents/mate-interview-me/SKILL.md +147 -0
- package/src/templates/{capabilities/openspec-cap/mate-skills → mate-skills}/agents/mate-openspec-backfill/SKILL.md +4 -2
- package/src/templates/mate-skills/agents/mate-show-me/SKILL.md +139 -0
- package/src/templates/mate-skills/agents/mate-simplify-code/SKILL.md +503 -0
- package/src/templates/mate-skills/claude/mate-artifact-publish/SKILL.md +185 -0
- package/src/templates/mate-skills/claude/mate-artifact-publish/references/openspec.md +227 -0
- package/src/templates/mate-skills/claude/mate-domain-modeling/SKILL.md +68 -0
- package/src/templates/mate-skills/claude/mate-domain-modeling/references/ADR-FORMAT.md +9 -0
- package/src/templates/mate-skills/claude/mate-domain-modeling/references/CONTEXT-FORMAT.md +18 -0
- package/src/templates/mate-skills/claude/mate-grill-me/SKILL.md +14 -0
- package/src/templates/mate-skills/claude/mate-grill-with-docs/SKILL.md +29 -0
- package/src/templates/mate-skills/claude/mate-grilling/SKILL.md +49 -0
- package/src/templates/mate-skills/claude/mate-interview-me/SKILL.md +147 -0
- package/src/templates/mate-skills/claude/mate-openspec-backfill/SKILL.md +67 -0
- package/src/templates/mate-skills/claude/mate-show-me/SKILL.md +139 -0
- package/src/templates/mate-skills/claude/mate-simplify-code/SKILL.md +503 -0
- package/src/templates/report-assets/README.md +32 -0
- package/src/templates/report-assets/mermaid.LICENSE +21 -0
- package/src/templates/report-assets/mermaid.min.js +4376 -0
- package/src/templates/root/TEMPLATE_CLAUDE.md +1 -9
- package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +1476 -197
- package/src/tools/setup/capabilities/graphify.ts +16 -7
- package/src/tools/setup/capabilities/openspec.ts +63 -56
- package/src/tools/setup/capabilities/tokensave.ts +47 -0
- package/src/tools/setup/engine.ts +34 -6
- package/src/tools/setup/mate.ts +42 -13
- package/src/tools/setup/plugin.ts +9 -0
- package/src/tools/setup/plugins/guidance.ts +11 -1
- package/src/tools/setup/providers/claude-format.ts +49 -4
- package/src/tools/setup/providers/claude-plugin-hooks.ts +117 -0
- package/src/tools/setup/providers/claude.ts +55 -220
- package/src/tools/setup/providers/opencode.ts +41 -14
- package/src/tools/setup/runtime-documents.ts +174 -0
- package/src/tools/setup/surface-target.ts +50 -0
- package/src/tools/setup/working-repo-cleanup.ts +33 -26
- package/src/tools/setup/working-repo-local-state.ts +21 -1
- package/src/tools/setup.ts +25 -3
- package/wrappers/bin/graphify +57 -8
- package/wrappers/bin/openspec +50 -3
- package/claude-plugin/hooks/artifact-finish-nudge.mjs +0 -8
- package/src/cli/commands/cap/headroom.ts +0 -52
- package/src/cli/commands/workspace/list.ts +0 -25
- package/src/cli/commands/workspace/materialize.ts +0 -46
- package/src/cli/commands/workspace/workspace.ts +0 -22
- package/src/hooks/artifact-finish-nudge.ts +0 -244
- package/src/lib/orchestrator/headroom/proxy.ts +0 -116
- package/src/lib/orchestrator/workspace-materialize.ts +0 -80
- package/src/templates/capabilities/openspec-cap/mate-skills/agents/mate-artifact-finish/SKILL.md +0 -51
- package/src/templates/capabilities/openspec-cap/mate-skills/agents/mate-artifact-finish/references/openspec.md +0 -134
- package/src/templates/capabilities/openspec-cap/mate-skills/claude/mate-artifact-finish/SKILL.md +0 -58
- package/src/templates/capabilities/openspec-cap/mate-skills/claude/mate-artifact-finish/references/openspec.md +0 -139
- package/src/tools/setup/capabilities/headroom.ts +0 -57
- /package/src/cli/commands/{workspace → companion}/open.ts +0 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { CHANGE_PLACEHOLDER } from "../../selection";
|
|
2
|
+
|
|
3
|
+
export type WorkflowStepKind = "skill" | "review" | "completion" | "optional";
|
|
4
|
+
export type WorkflowStepBadge = "optional" | "skill" | "openspec" | "human-in-loop";
|
|
5
|
+
export type WorkflowProfile = "mate-v1" | "mate-minimal";
|
|
6
|
+
|
|
7
|
+
export interface WorkflowAlternative {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
prompt: string;
|
|
11
|
+
copyPrompt: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface WorkflowStep {
|
|
15
|
+
kind: WorkflowStepKind;
|
|
16
|
+
id: string;
|
|
17
|
+
/** The action the stage performs. */
|
|
18
|
+
what: string;
|
|
19
|
+
/** The reason the stage is in the sequence at this position. */
|
|
20
|
+
why: string;
|
|
21
|
+
title: string;
|
|
22
|
+
badges: WorkflowStepBadge[];
|
|
23
|
+
sessionBreakBefore?: boolean;
|
|
24
|
+
prompt: string;
|
|
25
|
+
copyPrompt: string;
|
|
26
|
+
alternatives?: WorkflowAlternative[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface WorkflowBranch {
|
|
30
|
+
profile: WorkflowProfile;
|
|
31
|
+
label: string;
|
|
32
|
+
description: string;
|
|
33
|
+
steps: WorkflowStep[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface WorkflowPlan {
|
|
37
|
+
start: WorkflowStep;
|
|
38
|
+
branches: WorkflowBranch[];
|
|
39
|
+
shared: WorkflowStep[];
|
|
40
|
+
finish: WorkflowStep;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const PRE_EXPLORE_SKILLS = ["mate-interview-me", "mate-grill-me"] as const;
|
|
44
|
+
|
|
45
|
+
/** The optional post-apply stretch, in presented order, each keyed to the skill that drives it. */
|
|
46
|
+
const SHARED_OPTIONAL_SKILLS = {
|
|
47
|
+
"show-me": "mate-show-me",
|
|
48
|
+
simplify: "mate-simplify-code",
|
|
49
|
+
} as const;
|
|
50
|
+
|
|
51
|
+
type SharedOptionalId = keyof typeof SHARED_OPTIONAL_SKILLS;
|
|
52
|
+
|
|
53
|
+
function stepPrompt(
|
|
54
|
+
step: Omit<WorkflowStep, "prompt" | "copyPrompt">,
|
|
55
|
+
profile?: WorkflowProfile,
|
|
56
|
+
includeChange = true,
|
|
57
|
+
): string {
|
|
58
|
+
const changeNote = includeChange ? ` for ${CHANGE_PLACEHOLDER}` : " for";
|
|
59
|
+
const schemaNote = profile && profile !== "mate-v1" ? ` (use schema: ${profile})` : "";
|
|
60
|
+
|
|
61
|
+
switch (step.id) {
|
|
62
|
+
case "pre-explore":
|
|
63
|
+
return `/mate-interview-me or /mate-grill-me${changeNote}`;
|
|
64
|
+
case "explore":
|
|
65
|
+
return `/openspec-explore${schemaNote}${changeNote}`;
|
|
66
|
+
case "proposal":
|
|
67
|
+
case "propose":
|
|
68
|
+
return `/openspec-propose${schemaNote}${changeNote}`;
|
|
69
|
+
case "specs":
|
|
70
|
+
case "design":
|
|
71
|
+
case "tasks":
|
|
72
|
+
return `Review ${includeChange ? CHANGE_PLACEHOLDER : ""}/`;
|
|
73
|
+
case "apply":
|
|
74
|
+
return `/openspec-apply-change${schemaNote}${changeNote}`;
|
|
75
|
+
case "show-me":
|
|
76
|
+
return `/mate-show-me${changeNote}`;
|
|
77
|
+
case "simplify":
|
|
78
|
+
return `/mate-simplify-code${changeNote}`;
|
|
79
|
+
case "archive":
|
|
80
|
+
return `/openspec-archive-change${changeNote}`;
|
|
81
|
+
case "finish":
|
|
82
|
+
return `/mate-artifact-publish${changeNote}`;
|
|
83
|
+
default:
|
|
84
|
+
return `Use ${step.title}${schemaNote}${changeNote}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function skillStep(
|
|
89
|
+
id: string,
|
|
90
|
+
title: string,
|
|
91
|
+
what: string,
|
|
92
|
+
why: string,
|
|
93
|
+
profile?: WorkflowProfile,
|
|
94
|
+
): WorkflowStep {
|
|
95
|
+
const step = {
|
|
96
|
+
kind: "skill" as const,
|
|
97
|
+
id,
|
|
98
|
+
title,
|
|
99
|
+
what,
|
|
100
|
+
why,
|
|
101
|
+
badges: ["skill", "openspec"] as WorkflowStepBadge[],
|
|
102
|
+
};
|
|
103
|
+
return {
|
|
104
|
+
...step,
|
|
105
|
+
prompt: stepPrompt(step, profile),
|
|
106
|
+
copyPrompt: stepPrompt(step, profile, false),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function reviewStep(
|
|
111
|
+
id: string,
|
|
112
|
+
title: string,
|
|
113
|
+
what: string,
|
|
114
|
+
why: string,
|
|
115
|
+
profile?: WorkflowProfile,
|
|
116
|
+
): WorkflowStep {
|
|
117
|
+
const step = {
|
|
118
|
+
kind: "review" as const,
|
|
119
|
+
id,
|
|
120
|
+
title,
|
|
121
|
+
what,
|
|
122
|
+
why,
|
|
123
|
+
badges: ["human-in-loop", "openspec"] as WorkflowStepBadge[],
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
...step,
|
|
127
|
+
prompt: stepPrompt(step, profile),
|
|
128
|
+
copyPrompt: stepPrompt(step, profile, false),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function optionalStep(
|
|
133
|
+
id: "pre-explore" | SharedOptionalId,
|
|
134
|
+
title: string,
|
|
135
|
+
what: string,
|
|
136
|
+
why: string,
|
|
137
|
+
alternatives: WorkflowAlternative[] = [],
|
|
138
|
+
): WorkflowStep {
|
|
139
|
+
const step = {
|
|
140
|
+
kind: "optional" as const,
|
|
141
|
+
id,
|
|
142
|
+
title,
|
|
143
|
+
what,
|
|
144
|
+
why,
|
|
145
|
+
badges: ["optional", "skill"] as WorkflowStepBadge[],
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
...step,
|
|
149
|
+
prompt: stepPrompt(step),
|
|
150
|
+
copyPrompt: stepPrompt(step, undefined, false),
|
|
151
|
+
...(id === "pre-explore" || alternatives.length > 0 ? { alternatives } : {}),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function withSessionBreak(step: WorkflowStep): WorkflowStep {
|
|
156
|
+
return { ...step, sessionBreakBefore: true };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function workflowPlan(
|
|
160
|
+
availableSkills: readonly string[] = [
|
|
161
|
+
...PRE_EXPLORE_SKILLS,
|
|
162
|
+
...Object.values(SHARED_OPTIONAL_SKILLS),
|
|
163
|
+
],
|
|
164
|
+
): WorkflowPlan {
|
|
165
|
+
const availableSkillSet = new Set(availableSkills);
|
|
166
|
+
const preExploreAlternatives: WorkflowAlternative[] = [];
|
|
167
|
+
for (const name of PRE_EXPLORE_SKILLS) {
|
|
168
|
+
if (!availableSkillSet.has(name)) continue;
|
|
169
|
+
preExploreAlternatives.push({
|
|
170
|
+
name,
|
|
171
|
+
description:
|
|
172
|
+
name === "mate-interview-me"
|
|
173
|
+
? "Difference: guided, one-question-at-a-time clarification. Best when: the request is ambiguous and intent, constraints, or scope still need confirmation."
|
|
174
|
+
: "Difference: adversarial, round-based review of the full design-tree frontier. Best when: the direction is mostly known but hidden assumptions, risks, and downstream impacts need surfacing.",
|
|
175
|
+
prompt: `/${name} for ${CHANGE_PLACEHOLDER}`,
|
|
176
|
+
copyPrompt: `/${name} for`,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
const start = optionalStep(
|
|
180
|
+
"pre-explore",
|
|
181
|
+
"pre-explore",
|
|
182
|
+
"Clarifies intent with confidence and explicit confirmation, or stress-tests the full design frontier before the schema path turns it into artifacts.",
|
|
183
|
+
"One conversational choice settles uncertainty early without invoking documentation or implementation work.",
|
|
184
|
+
preExploreAlternatives,
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const branches: WorkflowBranch[] = [
|
|
188
|
+
{
|
|
189
|
+
profile: "mate-v1",
|
|
190
|
+
label: "mate-v1",
|
|
191
|
+
description: "Full planning path for changes that need exploration and technical design.",
|
|
192
|
+
steps: [
|
|
193
|
+
skillStep(
|
|
194
|
+
"explore",
|
|
195
|
+
"explore",
|
|
196
|
+
"Resolves repository evidence, unknowns, options, and direction.",
|
|
197
|
+
"It separates uncertainty from the proposal's committed motivation and scope.",
|
|
198
|
+
"mate-v1",
|
|
199
|
+
),
|
|
200
|
+
skillStep(
|
|
201
|
+
"proposal",
|
|
202
|
+
"proposal",
|
|
203
|
+
"Commits why the change is needed and what is in scope.",
|
|
204
|
+
"A written scope gives specs, design, and tasks one decision to build from.",
|
|
205
|
+
"mate-v1",
|
|
206
|
+
),
|
|
207
|
+
reviewStep(
|
|
208
|
+
"specs",
|
|
209
|
+
"specs",
|
|
210
|
+
"Defines the observable behavior the system must provide.",
|
|
211
|
+
"The delta is the contract that will later be merged into the canonical spec.",
|
|
212
|
+
"mate-v1",
|
|
213
|
+
),
|
|
214
|
+
reviewStep(
|
|
215
|
+
"design",
|
|
216
|
+
"design",
|
|
217
|
+
"Records the technical decisions for implementing the change.",
|
|
218
|
+
"Separating how from what exposes risks before implementation starts.",
|
|
219
|
+
"mate-v1",
|
|
220
|
+
),
|
|
221
|
+
reviewStep(
|
|
222
|
+
"tasks",
|
|
223
|
+
"tasks",
|
|
224
|
+
"Orders the implementation and verification work.",
|
|
225
|
+
"Small, ordered tasks turn the agreed artifacts into a trackable build path.",
|
|
226
|
+
"mate-v1",
|
|
227
|
+
),
|
|
228
|
+
withSessionBreak(
|
|
229
|
+
skillStep(
|
|
230
|
+
"apply",
|
|
231
|
+
"apply",
|
|
232
|
+
"Implements the change by working through the generated tasks.",
|
|
233
|
+
"Applying only approved tasks keeps code changes inside the agreed scope.",
|
|
234
|
+
"mate-v1",
|
|
235
|
+
),
|
|
236
|
+
),
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
profile: "mate-minimal",
|
|
241
|
+
label: "mate-minimal",
|
|
242
|
+
description:
|
|
243
|
+
"Short path that creates only specs and tasks for one-Area, low-risk changes; switch to mate-v1 when uncertain.",
|
|
244
|
+
steps: [
|
|
245
|
+
skillStep(
|
|
246
|
+
"propose",
|
|
247
|
+
"propose",
|
|
248
|
+
"Creates the concise, parser-compatible behavior change.",
|
|
249
|
+
"OpenSpec generates the minimal specs and tasks artifacts from the agreed direction.",
|
|
250
|
+
"mate-minimal",
|
|
251
|
+
),
|
|
252
|
+
reviewStep(
|
|
253
|
+
"specs",
|
|
254
|
+
"specs",
|
|
255
|
+
"Defines the behavior change as a concise, parser-compatible user story.",
|
|
256
|
+
"Reviewing the generated delta confirms the small change before tasks are applied.",
|
|
257
|
+
"mate-minimal",
|
|
258
|
+
),
|
|
259
|
+
reviewStep(
|
|
260
|
+
"tasks",
|
|
261
|
+
"tasks",
|
|
262
|
+
"Orders the implementation and verification work.",
|
|
263
|
+
"The checklist keeps a deliberately small change concrete and reviewable.",
|
|
264
|
+
"mate-minimal",
|
|
265
|
+
),
|
|
266
|
+
withSessionBreak(
|
|
267
|
+
skillStep(
|
|
268
|
+
"apply",
|
|
269
|
+
"apply",
|
|
270
|
+
"Implements the change by working through the generated tasks.",
|
|
271
|
+
"Applying only the short approved checklist keeps the low-risk boundary intact.",
|
|
272
|
+
"mate-minimal",
|
|
273
|
+
),
|
|
274
|
+
),
|
|
275
|
+
],
|
|
276
|
+
},
|
|
277
|
+
];
|
|
278
|
+
|
|
279
|
+
const showMe = optionalStep(
|
|
280
|
+
"show-me",
|
|
281
|
+
"mate show me",
|
|
282
|
+
"Shows what the applied change did as a rendered diagram and a colorized diff, delivered as a Mate report.",
|
|
283
|
+
"Seeing the resulting structure and the actual diff is what tells the reviewer whether cleanup is warranted.",
|
|
284
|
+
);
|
|
285
|
+
const simplify = optionalStep(
|
|
286
|
+
"simplify",
|
|
287
|
+
"mate simplify code",
|
|
288
|
+
"Looks for smaller, clearer code once the tests pass, preserving behavior and rerunning the relevant tests.",
|
|
289
|
+
"Cleanup lands before the specs are archived, so shipped code and recorded specs stay aligned.",
|
|
290
|
+
);
|
|
291
|
+
/** Peers, not prerequisites: an unreported skill drops its step and the rest keep their order. */
|
|
292
|
+
const optionalReview: WorkflowStep[] = [];
|
|
293
|
+
for (const step of [showMe, simplify]) {
|
|
294
|
+
if (!availableSkillSet.has(SHARED_OPTIONAL_SKILLS[step.id as SharedOptionalId])) continue;
|
|
295
|
+
optionalReview.push(optionalReview.length === 0 ? withSessionBreak(step) : step);
|
|
296
|
+
}
|
|
297
|
+
const archive = skillStep(
|
|
298
|
+
"archive",
|
|
299
|
+
"openspec archive change",
|
|
300
|
+
"Archives the verified change and its approved artifacts, applying its delta specs to the canonical specs.",
|
|
301
|
+
"Archiving validates the change, requires it to be complete, and is the precondition publishing refuses without.",
|
|
302
|
+
);
|
|
303
|
+
const finishStep = {
|
|
304
|
+
kind: "completion" as const,
|
|
305
|
+
id: "finish",
|
|
306
|
+
title: "mate artifact publish",
|
|
307
|
+
what: "Presents the already-archived changes and the drifted canonical specs as two selectable tables, then runs `mate artifact publish` for exactly what the developer picks — announcing that each selection is committed, tagged, and pushed to the companion Git repository before the first one runs.",
|
|
308
|
+
why: "It is the only sanctioned completion; a hand-committed publication leaves the anchor untagged and unrevertable, and a drifted spec shipped under an old archive's tag misfiles today's edit under a change that did not produce it.",
|
|
309
|
+
badges: ["skill"] as WorkflowStepBadge[],
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
start,
|
|
314
|
+
branches,
|
|
315
|
+
shared: [...optionalReview, archive],
|
|
316
|
+
finish: {
|
|
317
|
+
...finishStep,
|
|
318
|
+
prompt: stepPrompt(finishStep),
|
|
319
|
+
copyPrompt: stepPrompt(finishStep, undefined, false),
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function workflowSteps(profile: WorkflowProfile = "mate-v1"): WorkflowStep[] {
|
|
325
|
+
const plan = workflowPlan();
|
|
326
|
+
const branch = plan.branches.find((entry) => entry.profile === profile);
|
|
327
|
+
if (!branch) return [plan.start, ...plan.shared, plan.finish];
|
|
328
|
+
return [plan.start, ...branch.steps, ...plan.shared, plan.finish];
|
|
329
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { FRAMEWORK_NAME } from "../../framework";
|
|
2
|
+
import { findRepoLocalLinkedRepository } from "../../lib/orchestrator/repo-local-registry";
|
|
3
|
+
import { unwrapWorkingRuntimeDocuments } from "../../lib/orchestrator/working-repo-projection";
|
|
4
|
+
|
|
5
|
+
export interface UnwrapCommandDependencies {
|
|
6
|
+
findRepoLocalLinkedRepository?: typeof findRepoLocalLinkedRepository;
|
|
7
|
+
unwrapWorkingRuntimeDocuments?: typeof unwrapWorkingRuntimeDocuments;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const unwrapCommandDeps: Required<UnwrapCommandDependencies> = {
|
|
11
|
+
findRepoLocalLinkedRepository,
|
|
12
|
+
unwrapWorkingRuntimeDocuments,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Withdraws the runtime documents `mate wrap` placed and nothing else: the
|
|
17
|
+
* Projection Root, the Repository Link and the companion link all stay, so a
|
|
18
|
+
* Managed Session resolves exactly as it did before the wrap. That is what
|
|
19
|
+
* separates this from `mate working cleanup`, which removes Mate's whole local
|
|
20
|
+
* integration including the link.
|
|
21
|
+
*
|
|
22
|
+
* Takes no companion. The manifest at the Projection Root records what was
|
|
23
|
+
* placed, so unwrapping needs no answer to which companion placed it — which
|
|
24
|
+
* also means it still works when the companion is gone.
|
|
25
|
+
*/
|
|
26
|
+
export async function runUnwrapCommand(
|
|
27
|
+
argv: string[] = [],
|
|
28
|
+
cwd: string = process.cwd(),
|
|
29
|
+
deps: UnwrapCommandDependencies = {},
|
|
30
|
+
): Promise<void> {
|
|
31
|
+
const findRepository =
|
|
32
|
+
deps.findRepoLocalLinkedRepository ?? unwrapCommandDeps.findRepoLocalLinkedRepository;
|
|
33
|
+
const unwrap =
|
|
34
|
+
deps.unwrapWorkingRuntimeDocuments ?? unwrapCommandDeps.unwrapWorkingRuntimeDocuments;
|
|
35
|
+
|
|
36
|
+
if (argv.length > 0) {
|
|
37
|
+
console.error(`${FRAMEWORK_NAME}: usage: ${FRAMEWORK_NAME} unwrap`);
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const repository = await findRepository(cwd);
|
|
43
|
+
if (!repository) {
|
|
44
|
+
console.error(
|
|
45
|
+
`${FRAMEWORK_NAME}: this directory is not a working repository linked to a companion.`,
|
|
46
|
+
);
|
|
47
|
+
process.exitCode = 1;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const result = await unwrap(repository.path);
|
|
52
|
+
if (result.kind === "failed") {
|
|
53
|
+
console.error(
|
|
54
|
+
`${FRAMEWORK_NAME}: failed to withdraw ${result.document} for ${repository.id}: ${result.error.message}`,
|
|
55
|
+
);
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (result.kind === "absent") {
|
|
61
|
+
console.log(`Not wrapped: ${repository.id}`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(`Unwrapped ${repository.id}`);
|
|
66
|
+
for (const document of result.documents) {
|
|
67
|
+
console.log(` Withdrawn: ${document}`);
|
|
68
|
+
}
|
|
69
|
+
console.log(`\`${FRAMEWORK_NAME} claude\` and \`${FRAMEWORK_NAME} opencode\` work here again.`);
|
|
70
|
+
}
|