@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,377 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { version } from "../../../package.json";
|
|
5
|
+
import {
|
|
6
|
+
PROJECTION_YAML_FILE,
|
|
7
|
+
computeProjectionStamp,
|
|
8
|
+
readProjectionFile,
|
|
9
|
+
writeProjectionPair,
|
|
10
|
+
} from "../../runtime/projection";
|
|
11
|
+
import {
|
|
12
|
+
repoLocalDirName,
|
|
13
|
+
repoLocalDirPath,
|
|
14
|
+
repoLocalRegistryPath,
|
|
15
|
+
} from "../../runtime/repo-local";
|
|
16
|
+
import { TOKENSAVE_WORKING_REPO_EXCLUDE_ENTRIES } from "../../tools/setup/capabilities/tokensave-shared";
|
|
17
|
+
import {
|
|
18
|
+
ensureWorkingRepoLocalExcludes,
|
|
19
|
+
managedWorkingRepoExcludesPresent,
|
|
20
|
+
reconcileWorkingRepoCapabilityExcludes,
|
|
21
|
+
removeWorkingRepoLocalExcludes,
|
|
22
|
+
workingRepoCapabilityExcludesPresent,
|
|
23
|
+
} from "../../tools/setup/working-repo-local-state";
|
|
24
|
+
import { companionLinkPresent, linkCompanion, unlinkCompanion } from "./projection-companion-link";
|
|
25
|
+
import {
|
|
26
|
+
reconcileWorkingRepoClaudeSkillLinks,
|
|
27
|
+
removeWorkingRepoClaudeSkillLinks,
|
|
28
|
+
} from "./projection-claude-skills";
|
|
29
|
+
import { buildProjection } from "./projection-record";
|
|
30
|
+
import {
|
|
31
|
+
CLAUDE_WORKING_SETTINGS_PATH,
|
|
32
|
+
LEGACY_TOKENSAVE_CLAUDE_MD_PATH,
|
|
33
|
+
legacyTokensaveClaudeMdPresent,
|
|
34
|
+
removeWorkingRepoClaudeSettings,
|
|
35
|
+
stripTokensaveClaudeMdAppend,
|
|
36
|
+
syncWorkingRepoClaudeAdditionalDirectories,
|
|
37
|
+
workingRepoClaudeSettingsPresent,
|
|
38
|
+
} from "./projection-claude-entry";
|
|
39
|
+
import {
|
|
40
|
+
CLAUDE_LOCAL_CONFIG_DOCUMENT,
|
|
41
|
+
CLAUDE_MCP_DOCUMENT,
|
|
42
|
+
CLAUDE_SETTINGS_DOCUMENT,
|
|
43
|
+
OPENCODE_CONFIG_DOCUMENT,
|
|
44
|
+
placeRuntimeDocument,
|
|
45
|
+
removeRuntimeDocument,
|
|
46
|
+
runtimeDocumentPresent,
|
|
47
|
+
} from "./projection-runtime-documents";
|
|
48
|
+
import {
|
|
49
|
+
upsertRepoLocalCompanionPointer,
|
|
50
|
+
upsertRepoLocalLinkedRepository,
|
|
51
|
+
writeRepoLocalFrameworkConfig,
|
|
52
|
+
} from "./repo-local-store";
|
|
53
|
+
import type { ProjectionEntry, ProjectionInput } from "./projection-types";
|
|
54
|
+
|
|
55
|
+
export const WORKSPACE_DOCUMENT_FILE = "workspace.code-workspace";
|
|
56
|
+
|
|
57
|
+
/** The shared editor workspace document, inside the Projection Root. */
|
|
58
|
+
export function workspaceDocumentPath(repoPath: string): string {
|
|
59
|
+
return path.join(repoLocalDirPath(repoPath), WORKSPACE_DOCUMENT_FILE);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function pathExists(candidatePath: string): Promise<boolean> {
|
|
63
|
+
try {
|
|
64
|
+
await fs.access(candidatePath);
|
|
65
|
+
return true;
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function writeProjectionPairEntry(input: ProjectionInput): Promise<"written" | "current"> {
|
|
72
|
+
const { companionPath, repository } = input;
|
|
73
|
+
const resolvedCompanionPath = path.resolve(companionPath!);
|
|
74
|
+
const registryContent = await fs
|
|
75
|
+
.readFile(repoLocalRegistryPath(input.repoPath), "utf8")
|
|
76
|
+
.catch(() => "");
|
|
77
|
+
const stamp = computeProjectionStamp({ version, registryContent });
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The stamp cannot see a re-pin: neither of its inputs — mate version,
|
|
81
|
+
* repo-local registry — changes when a differently-linked companion is
|
|
82
|
+
* resolved, so the projected companion is compared alongside it.
|
|
83
|
+
*/
|
|
84
|
+
const existing = readProjectionFile(input.repoPath);
|
|
85
|
+
if (
|
|
86
|
+
existing?.stamp === stamp &&
|
|
87
|
+
path.resolve(existing.projection.companionPath) === resolvedCompanionPath
|
|
88
|
+
) {
|
|
89
|
+
return "current";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
writeProjectionPair(input.repoPath, {
|
|
93
|
+
stamp,
|
|
94
|
+
projection: buildProjection(resolvedCompanionPath, repository!),
|
|
95
|
+
});
|
|
96
|
+
return "written";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function tokensaveEnabled(input: ProjectionInput): boolean {
|
|
100
|
+
return (input.config?.capabilities ?? []).some((capability) => capability.name === "tokensave");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Built on first use rather than at module evaluation: the catalogue reaches
|
|
105
|
+
* modules that reach back for `project`, so nothing here may be needed before
|
|
106
|
+
* its own module finishes loading.
|
|
107
|
+
*/
|
|
108
|
+
function buildEntries(): readonly ProjectionEntry[] {
|
|
109
|
+
const localDir = repoLocalDirName();
|
|
110
|
+
|
|
111
|
+
return [
|
|
112
|
+
/**
|
|
113
|
+
* First, always. Every scope below writes a path this block covers, so a
|
|
114
|
+
* reader that races a write sees an excluded file rather than a tracked one.
|
|
115
|
+
*/
|
|
116
|
+
{
|
|
117
|
+
id: "git-excludes",
|
|
118
|
+
kind: "merged",
|
|
119
|
+
path: path.join(".git", "info", "exclude"),
|
|
120
|
+
scopes: ["link", "session", "launch", "wrap"],
|
|
121
|
+
write: async (input) =>
|
|
122
|
+
(await ensureWorkingRepoLocalExcludes(input.repoPath)) ? "written" : "current",
|
|
123
|
+
removal: {
|
|
124
|
+
by: "self",
|
|
125
|
+
remove: async (input) =>
|
|
126
|
+
(await removeWorkingRepoLocalExcludes(input.repoPath)) ? "removed" : "absent",
|
|
127
|
+
},
|
|
128
|
+
present: managedWorkingRepoExcludesPresent,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: "projection-root",
|
|
132
|
+
kind: "owned",
|
|
133
|
+
path: localDir,
|
|
134
|
+
scopes: ["link", "session", "workspace", "wrap"],
|
|
135
|
+
write: async (input) => {
|
|
136
|
+
const root = repoLocalDirPath(input.repoPath);
|
|
137
|
+
if (await pathExists(root)) return "current";
|
|
138
|
+
await fs.mkdir(root, { recursive: true });
|
|
139
|
+
return "written";
|
|
140
|
+
},
|
|
141
|
+
removal: {
|
|
142
|
+
by: "self",
|
|
143
|
+
remove: async (input) => {
|
|
144
|
+
const root = repoLocalDirPath(input.repoPath);
|
|
145
|
+
if (!(await pathExists(root))) return "absent";
|
|
146
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
147
|
+
return "removed";
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
/**
|
|
152
|
+
* Directly after the root that holds it, so every scope that writes the
|
|
153
|
+
* root also exposes the companion through it.
|
|
154
|
+
*/
|
|
155
|
+
{
|
|
156
|
+
id: "companion-link",
|
|
157
|
+
kind: "owned",
|
|
158
|
+
path: path.join(localDir, "companion"),
|
|
159
|
+
scopes: ["link", "session", "wrap"],
|
|
160
|
+
write: async (input) =>
|
|
161
|
+
input.companionPath ? linkCompanion(input.repoPath, input.companionPath) : "skipped",
|
|
162
|
+
degradable: true,
|
|
163
|
+
removal: {
|
|
164
|
+
by: "self",
|
|
165
|
+
remove: (input) => unlinkCompanion(input.repoPath),
|
|
166
|
+
},
|
|
167
|
+
present: companionLinkPresent,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: "repo-local-framework",
|
|
171
|
+
kind: "owned",
|
|
172
|
+
path: path.join(localDir, "config", "framework.yaml"),
|
|
173
|
+
scopes: ["link"],
|
|
174
|
+
write: async (input) =>
|
|
175
|
+
(await writeRepoLocalFrameworkConfig(input.repoPath)) ? "written" : "current",
|
|
176
|
+
removal: { by: "entry", entry: "projection-root" },
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: "repo-local-registry",
|
|
180
|
+
kind: "owned",
|
|
181
|
+
path: path.join(localDir, "config", "registry.yaml"),
|
|
182
|
+
scopes: ["link"],
|
|
183
|
+
write: async (input) => {
|
|
184
|
+
if (!input.companionPath || !input.repository || !input.source) return "skipped";
|
|
185
|
+
await upsertRepoLocalLinkedRepository(input.repoPath, input.repository);
|
|
186
|
+
await upsertRepoLocalCompanionPointer(
|
|
187
|
+
input.repoPath,
|
|
188
|
+
input.companionPath,
|
|
189
|
+
input.repository.id,
|
|
190
|
+
input.source,
|
|
191
|
+
);
|
|
192
|
+
return "written";
|
|
193
|
+
},
|
|
194
|
+
removal: { by: "entry", entry: "projection-root" },
|
|
195
|
+
},
|
|
196
|
+
/** One entry, two files: the pair is written as a unit or not at all. */
|
|
197
|
+
{
|
|
198
|
+
id: "projection-pair",
|
|
199
|
+
kind: "owned",
|
|
200
|
+
path: path.join(localDir, PROJECTION_YAML_FILE),
|
|
201
|
+
scopes: ["session"],
|
|
202
|
+
write: async (input) =>
|
|
203
|
+
input.companionPath && input.repository ? writeProjectionPairEntry(input) : "skipped",
|
|
204
|
+
removal: { by: "entry", entry: "projection-root" },
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
id: "claude-skill-links",
|
|
208
|
+
kind: "merged",
|
|
209
|
+
path: path.join(".claude", "skills"),
|
|
210
|
+
scopes: ["wrap"],
|
|
211
|
+
removeOnUnwrap: true,
|
|
212
|
+
write: async (input) => {
|
|
213
|
+
if (!input.config) return "skipped";
|
|
214
|
+
const registeredCompanionPaths = input.globalConfigStore
|
|
215
|
+
? await input.globalConfigStore.list()
|
|
216
|
+
: [];
|
|
217
|
+
return reconcileWorkingRepoClaudeSkillLinks(
|
|
218
|
+
input.repoPath,
|
|
219
|
+
input.companionPath ?? null,
|
|
220
|
+
input.config.allowedAgents?.includes("claude") ?? false,
|
|
221
|
+
registeredCompanionPaths,
|
|
222
|
+
);
|
|
223
|
+
},
|
|
224
|
+
removal: {
|
|
225
|
+
by: "self",
|
|
226
|
+
remove: (input) =>
|
|
227
|
+
removeWorkingRepoClaudeSkillLinks(input.repoPath, input.registeredCompanionPaths ?? []),
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
/**
|
|
231
|
+
* Deliberately outside the managed block, so a Capability's exclusion
|
|
232
|
+
* survives a core-block rewrite — and therefore survives `unproject` too,
|
|
233
|
+
* because the Capability's local data does.
|
|
234
|
+
*/
|
|
235
|
+
{
|
|
236
|
+
id: "capability-excludes",
|
|
237
|
+
kind: "merged",
|
|
238
|
+
path: path.join(".git", "info", "exclude"),
|
|
239
|
+
scopes: ["launch"],
|
|
240
|
+
write: async (input) => {
|
|
241
|
+
if (!input.config) return "skipped";
|
|
242
|
+
return (await reconcileWorkingRepoCapabilityExcludes(
|
|
243
|
+
input.repoPath,
|
|
244
|
+
tokensaveEnabled(input) ? TOKENSAVE_WORKING_REPO_EXCLUDE_ENTRIES : [],
|
|
245
|
+
TOKENSAVE_WORKING_REPO_EXCLUDE_ENTRIES,
|
|
246
|
+
))
|
|
247
|
+
? "written"
|
|
248
|
+
: "current";
|
|
249
|
+
},
|
|
250
|
+
removal: {
|
|
251
|
+
by: "retained",
|
|
252
|
+
because: "the Capability's local data is still in the Working Repository",
|
|
253
|
+
},
|
|
254
|
+
present: (repoPath) =>
|
|
255
|
+
workingRepoCapabilityExcludesPresent(repoPath, TOKENSAVE_WORKING_REPO_EXCLUDE_ENTRIES),
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
id: "claude-working-settings",
|
|
259
|
+
kind: "merged",
|
|
260
|
+
path: CLAUDE_WORKING_SETTINGS_PATH,
|
|
261
|
+
scopes: ["launch", "wrap"],
|
|
262
|
+
write: async (input) =>
|
|
263
|
+
input.companionPath
|
|
264
|
+
? syncWorkingRepoClaudeAdditionalDirectories(
|
|
265
|
+
input.repoPath,
|
|
266
|
+
input.companionPath,
|
|
267
|
+
input.globalConfigStore,
|
|
268
|
+
)
|
|
269
|
+
: "skipped",
|
|
270
|
+
removal: {
|
|
271
|
+
by: "self",
|
|
272
|
+
remove: (input) =>
|
|
273
|
+
removeWorkingRepoClaudeSettings(input.repoPath, input.registeredCompanionPaths ?? []),
|
|
274
|
+
},
|
|
275
|
+
present: workingRepoClaudeSettingsPresent,
|
|
276
|
+
},
|
|
277
|
+
/**
|
|
278
|
+
* Removal-only: Mate never writes this block, an external installer does.
|
|
279
|
+
* Its `write` is the same strip, so a launch heals a repository the
|
|
280
|
+
* installer touched without waiting for a cleanup.
|
|
281
|
+
*/
|
|
282
|
+
{
|
|
283
|
+
id: "legacy-tokensave-claude-md",
|
|
284
|
+
kind: "merged",
|
|
285
|
+
path: LEGACY_TOKENSAVE_CLAUDE_MD_PATH,
|
|
286
|
+
scopes: ["launch"],
|
|
287
|
+
write: async (input) =>
|
|
288
|
+
(await stripTokensaveClaudeMdAppend(input.repoPath)) === "removed" ? "written" : "current",
|
|
289
|
+
removal: { by: "self", remove: (input) => stripTokensaveClaudeMdAppend(input.repoPath) },
|
|
290
|
+
present: legacyTokensaveClaudeMdPresent,
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
id: "workspace-document",
|
|
294
|
+
kind: "owned",
|
|
295
|
+
path: path.join(localDir, WORKSPACE_DOCUMENT_FILE),
|
|
296
|
+
scopes: ["workspace"],
|
|
297
|
+
write: async (input) => {
|
|
298
|
+
if (!input.companionPath) return "skipped";
|
|
299
|
+
const target = workspaceDocumentPath(input.repoPath);
|
|
300
|
+
const folders = [path.resolve(input.repoPath), path.resolve(input.companionPath)];
|
|
301
|
+
const next = `${JSON.stringify({ folders: folders.map((candidate) => ({ path: candidate })) }, null, 2)}\n`;
|
|
302
|
+
if ((await fs.readFile(target, "utf8").catch(() => null)) === next) return "current";
|
|
303
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
304
|
+
await fs.writeFile(target, next, "utf8");
|
|
305
|
+
return "written";
|
|
306
|
+
},
|
|
307
|
+
removal: { by: "entry", entry: "projection-root" },
|
|
308
|
+
},
|
|
309
|
+
/**
|
|
310
|
+
* Last: the documents an Agent Runtime discovers on its own. One declaration
|
|
311
|
+
* per destination, all three placing a value some Runtime Surface rendered —
|
|
312
|
+
* no format knowledge reaches this catalogue.
|
|
313
|
+
*
|
|
314
|
+
* A launch renders them again, because every value here is pinned to the
|
|
315
|
+
* mate that wrote it — the OpenCode plugin package's version, the plugin
|
|
316
|
+
* root the Claude hook commands name — and a repository wrapped once would
|
|
317
|
+
* otherwise keep the pins of whichever release wrapped it. Re-rendering is
|
|
318
|
+
* reconciliation, not an append: `placeRuntimeDocument` withdraws what the
|
|
319
|
+
* previous pass recorded before applying this one's, and a pass that
|
|
320
|
+
* supplied no render at all claims nothing and leaves the record standing.
|
|
321
|
+
*
|
|
322
|
+
* The `launch` scope declares them so `mate working cleanup` reaches them
|
|
323
|
+
* and so a launch answers for a destination it finds recorded, but a launch
|
|
324
|
+
* renders nothing for them: a Managed Session is refused in a wrapped
|
|
325
|
+
* repository, so the only pass that ever places one is `mate wrap`.
|
|
326
|
+
*/
|
|
327
|
+
/**
|
|
328
|
+
* Removal-only, like the legacy CLAUDE.md append: releases before local
|
|
329
|
+
* scope projected the companion's MCP servers here, and a server left in
|
|
330
|
+
* `.mcp.json` would sit "pending approval" forever beside the live local
|
|
331
|
+
* one. Its `write` is the same strip, so a re-wrap heals a repository an
|
|
332
|
+
* older mate wrapped without waiting for a cleanup.
|
|
333
|
+
*/
|
|
334
|
+
{
|
|
335
|
+
id: "mcp-runtime-document",
|
|
336
|
+
kind: "merged",
|
|
337
|
+
path: CLAUDE_MCP_DOCUMENT,
|
|
338
|
+
scopes: ["launch", "wrap"],
|
|
339
|
+
write: async (input) =>
|
|
340
|
+
(await removeRuntimeDocument(input.repoPath, CLAUDE_MCP_DOCUMENT)) === "removed"
|
|
341
|
+
? "written"
|
|
342
|
+
: "current",
|
|
343
|
+
removal: {
|
|
344
|
+
by: "self",
|
|
345
|
+
remove: (input) => removeRuntimeDocument(input.repoPath, CLAUDE_MCP_DOCUMENT),
|
|
346
|
+
},
|
|
347
|
+
present: (repoPath) => runtimeDocumentPresent(repoPath, CLAUDE_MCP_DOCUMENT),
|
|
348
|
+
},
|
|
349
|
+
...(
|
|
350
|
+
[
|
|
351
|
+
["claude-runtime-document", CLAUDE_SETTINGS_DOCUMENT],
|
|
352
|
+
["claude-local-mcp-document", CLAUDE_LOCAL_CONFIG_DOCUMENT],
|
|
353
|
+
["opencode-runtime-document", OPENCODE_CONFIG_DOCUMENT],
|
|
354
|
+
] as const
|
|
355
|
+
).map(([id, documentPath]) => ({
|
|
356
|
+
id,
|
|
357
|
+
kind: "merged" as const,
|
|
358
|
+
path: documentPath.split("/").join(path.sep),
|
|
359
|
+
scopes: ["launch", "wrap"] as const,
|
|
360
|
+
write: (input: ProjectionInput) =>
|
|
361
|
+
placeRuntimeDocument(input.repoPath, documentPath, input.runtimeDocuments),
|
|
362
|
+
removal: {
|
|
363
|
+
by: "self" as const,
|
|
364
|
+
remove: (input: { repoPath: string }) =>
|
|
365
|
+
removeRuntimeDocument(input.repoPath, documentPath),
|
|
366
|
+
},
|
|
367
|
+
present: (repoPath: string) => runtimeDocumentPresent(repoPath, documentPath),
|
|
368
|
+
})),
|
|
369
|
+
];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
let entries: readonly ProjectionEntry[] | null = null;
|
|
373
|
+
|
|
374
|
+
export function projectionEntries(): readonly ProjectionEntry[] {
|
|
375
|
+
entries ??= buildEntries();
|
|
376
|
+
return entries;
|
|
377
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import { version } from "../../../package.json";
|
|
4
|
+
import {
|
|
5
|
+
PROJECTION_ENV_NAMES,
|
|
6
|
+
PROJECTION_FIELDS,
|
|
7
|
+
type MateProjection,
|
|
8
|
+
} from "../../runtime/projection";
|
|
9
|
+
import {
|
|
10
|
+
GRAPHIFY_OUTPUT_SUBDIR,
|
|
11
|
+
GRAPHIFY_STORE_SEGMENT,
|
|
12
|
+
} from "../../tools/setup/capabilities/graphify";
|
|
13
|
+
import { getReactDoctorBinPath, getWrapperBinPath } from "../package-paths";
|
|
14
|
+
import type { LinkedRepository } from "./types";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The one derivation of the projected paths. Both the Projection Root and a
|
|
18
|
+
* managed launch materialize this record, so neither can hold its own copy of a
|
|
19
|
+
* layout the other computes.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Both bin paths and `GRAPHIFY_OUT` are unconditional: a path exists whether or
|
|
24
|
+
* not its Capability is enabled, and enablement is a predicate, read live from
|
|
25
|
+
* the companion rather than cached here.
|
|
26
|
+
*/
|
|
27
|
+
export function buildProjection(
|
|
28
|
+
companionPath: string,
|
|
29
|
+
repository: LinkedRepository,
|
|
30
|
+
): MateProjection {
|
|
31
|
+
return {
|
|
32
|
+
version,
|
|
33
|
+
companionPath,
|
|
34
|
+
repositoryPath: repository.path,
|
|
35
|
+
repositoryId: repository.id,
|
|
36
|
+
wrapperBinPath: getWrapperBinPath(),
|
|
37
|
+
reactDoctorBinPath: getReactDoctorBinPath(),
|
|
38
|
+
graphifyOut: path.join(
|
|
39
|
+
companionPath,
|
|
40
|
+
GRAPHIFY_STORE_SEGMENT,
|
|
41
|
+
repository.id,
|
|
42
|
+
GRAPHIFY_OUTPUT_SUBDIR,
|
|
43
|
+
),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The record spelled as environment variables. `graphifyy` resolves its output
|
|
49
|
+
* directory from `GRAPHIFY_OUT` at import time, so a launch that omitted it
|
|
50
|
+
* would leak `graphify-out/` into the Working Repository.
|
|
51
|
+
*/
|
|
52
|
+
export function projectionEnvironment(projection: MateProjection): NodeJS.ProcessEnv {
|
|
53
|
+
return Object.fromEntries(
|
|
54
|
+
PROJECTION_FIELDS.map((field) => [PROJECTION_ENV_NAMES[field], projection[field]]),
|
|
55
|
+
);
|
|
56
|
+
}
|