@uniqbit/mate-core 0.18.4 → 1.0.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude-plugin/hooks/companion-instructions.mjs +9 -0
- package/claude-plugin/hooks/hooks.json +12 -2
- package/opencode-plugin/src/add-dir.ts +76 -10
- package/opencode-plugin/src/companion.ts +1 -8
- package/opencode-plugin/src/server.ts +2 -15
- 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 +200 -129
- 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 +257 -78
- package/src/cli/commands/install.ts +0 -8
- 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/terminal-launch.ts +7 -16
- 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 +9 -42
- 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 +5 -2
- 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 +10 -36
- 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 +60 -162
- package/src/lib/orchestrator/adapters/opencode.ts +9 -24
- package/src/lib/orchestrator/config-store.ts +2 -70
- 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 -190
- package/src/lib/orchestrator/projection-entries.ts +45 -15
- package/src/lib/orchestrator/projection-runtime-documents.ts +15 -6
- package/src/lib/orchestrator/projection-types.ts +3 -2
- package/src/lib/orchestrator/refresh-working-claude-links.ts +87 -0
- package/src/lib/orchestrator/session-prepare.ts +49 -87
- 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 -88
- 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/guidance.ts +0 -17
- 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 +16 -15
- package/src/runtime/projected-flag.ts +7 -0
- package/src/runtime/projected-guidance.ts +1 -0
- package/src/runtime/runtime-document-manifest.ts +62 -28
- 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 -6
- 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/agents/{mate-artifact-publish → 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/claude/{mate-artifact-publish → 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 +273 -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/audiences.ts +7 -39
- package/src/tools/setup/dynamic-plugins/declarations.ts +1 -1
- package/src/tools/setup/dynamic-plugins/hydrate.ts +4 -7
- package/src/tools/setup/dynamic-plugins/install.ts +24 -28
- package/src/tools/setup/engine.ts +93 -148
- package/src/tools/setup/managed-output.ts +51 -0
- package/src/tools/setup/mate.ts +86 -11
- package/src/tools/setup/plugin.ts +37 -17
- package/src/tools/setup/plugins/gitignore.ts +1 -7
- 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 +285 -34
- package/src/tools/setup/providers/opencode.ts +180 -11
- package/src/tools/setup/registry.ts +2 -24
- package/src/tools/setup/surface-target.ts +4 -13
- package/src/tools/setup/utils.ts +14 -4
- package/src/tools/setup.ts +131 -18
- package/wrappers/bin/graphify +52 -4
- package/wrappers/bin/openspec +33 -9
- package/wrappers/lib/graphify-gate.mjs +8 -0
- package/opencode-plugin/src/audience-layer.ts +0 -91
- package/src/cli/commands/studio/views/workflow/transcript.tsx +0 -190
- package/src/runtime/audience-layer.ts +0 -84
- package/src/runtime/opencode-audience-layer.ts +0 -176
- package/src/tools/setup/audience-isolation.fixture.ts +0 -325
- package/src/tools/setup/audience-layer.ts +0 -207
- package/src/tools/setup/audience-registrations.ts +0 -52
- package/src/tools/setup/contribution-ledger.ts +0 -207
- package/src/tools/setup/isolation-diagnostics.ts +0 -139
- package/src/tools/setup/runtime-documents.ts +0 -291
- package/src/tools/setup/upgrade-sweep.ts +0 -153
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Reads the operator's `claude` arguments as tokens, for where Mate's appended prompt goes. */
|
|
2
|
+
|
|
3
|
+
export const APPEND_PROMPT_FLAG = "--append-system-prompt";
|
|
4
|
+
|
|
5
|
+
function flagArgs(args: readonly string[]): readonly string[] {
|
|
6
|
+
const terminator = args.indexOf("--");
|
|
7
|
+
return terminator === -1 ? args : args.slice(0, terminator);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The operator's own `--append-system-prompt` values, lifted out of `args`:
|
|
12
|
+
* Claude keeps only the last such flag, so each would replace Mate's Agent
|
|
13
|
+
* Guidance instead of following it. A trailing flag without a value
|
|
14
|
+
* stays, for Claude to reject. `boundary` is where a lifted flag stood before
|
|
15
|
+
* an argument that is no flag: a variadic option such as `--add-dir` before
|
|
16
|
+
* it would otherwise read that argument as its own value, so Mate's flag goes
|
|
17
|
+
* there. Only the first such place is kept: Claude reads one prompt.
|
|
18
|
+
*/
|
|
19
|
+
export function liftAppendedPrompts(args: readonly string[]): {
|
|
20
|
+
args: string[];
|
|
21
|
+
prompts: string[];
|
|
22
|
+
boundary?: number;
|
|
23
|
+
} {
|
|
24
|
+
const flags = flagArgs(args);
|
|
25
|
+
const kept: string[] = [];
|
|
26
|
+
const prompts: string[] = [];
|
|
27
|
+
const liftedAt: number[] = [];
|
|
28
|
+
for (let index = 0; index < flags.length; index += 1) {
|
|
29
|
+
const arg = flags[index]!;
|
|
30
|
+
if (arg === APPEND_PROMPT_FLAG && index + 1 < flags.length) {
|
|
31
|
+
prompts.push(flags[index + 1]!);
|
|
32
|
+
liftedAt.push(kept.length);
|
|
33
|
+
index += 1;
|
|
34
|
+
} else if (arg.startsWith(`${APPEND_PROMPT_FLAG}=`)) {
|
|
35
|
+
prompts.push(arg.slice(APPEND_PROMPT_FLAG.length + 1));
|
|
36
|
+
liftedAt.push(kept.length);
|
|
37
|
+
} else {
|
|
38
|
+
kept.push(arg);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const passthrough = [...kept, ...args.slice(flags.length)];
|
|
42
|
+
const boundary = liftedAt.find(
|
|
43
|
+
(at) => at > 0 && at < passthrough.length && !passthrough[at]!.startsWith("-"),
|
|
44
|
+
);
|
|
45
|
+
return {
|
|
46
|
+
args: passthrough,
|
|
47
|
+
prompts,
|
|
48
|
+
...(boundary === undefined ? {} : { boundary }),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -5,126 +5,75 @@ import path from "node:path";
|
|
|
5
5
|
import { FRAMEWORK_NAME } from "../../../framework";
|
|
6
6
|
import { buildCompanionGuidance } from "../../../playbooks/companion-guidance";
|
|
7
7
|
import {
|
|
8
|
+
launchPluginFolders,
|
|
8
9
|
planClaudeDelivery,
|
|
9
10
|
type ClaudeDeliveryPlan,
|
|
10
|
-
type SessionKind,
|
|
11
11
|
} from "../../../runtime/claude-delivery-plan";
|
|
12
|
-
import {
|
|
13
|
-
audienceLayerDir,
|
|
14
|
-
currentAudienceLayer,
|
|
15
|
-
readLayerGuidanceSync,
|
|
16
|
-
} from "../../../runtime/audience-layer";
|
|
17
12
|
import { hookGroupKey, type ClaudeHookGroup } from "../../../runtime/claude-hook-group";
|
|
13
|
+
import { resolveSecretPolicy } from "../../../runtime/policy";
|
|
14
|
+
import { managedSessionKind } from "../../../runtime/session-kind";
|
|
18
15
|
import { NOT_WRAPPED } from "../../../runtime/wrap-state";
|
|
19
16
|
import { writeAtomic } from "../../../runtime/write-atomic";
|
|
20
17
|
import {
|
|
21
18
|
getCompanionClaudeMcpConfigPath,
|
|
22
19
|
getCompanionClaudeSettingsPath,
|
|
23
20
|
} from "../../../tools/setup/providers/claude";
|
|
21
|
+
import { definesUserClaudeAttribution } from "../../../tools/setup/providers/claude-attribution";
|
|
24
22
|
import type { ClaudeSettings } from "../../../tools/setup/providers/claude-format";
|
|
25
|
-
import { getContextModePackageRoot, validateContextModePackage } from "../../context-mode-package";
|
|
26
23
|
import { getClaudePluginRoot, validateClaudePluginAssets } from "../../package-paths";
|
|
27
|
-
import type { RenderedAudienceLayer } from "../../../tools/setup/audience-layer";
|
|
28
24
|
import type { PrepareResult } from "../session-prepare";
|
|
29
25
|
import { LaunchPreflightError } from "../types";
|
|
30
26
|
import { type AdapterContext, LaunchAdapter } from "./base";
|
|
31
|
-
|
|
32
|
-
/** The active audience's layer, attached to this launch only. */
|
|
33
|
-
interface ClaudeLayerDelivery {
|
|
34
|
-
audience: string;
|
|
35
|
-
root: string;
|
|
36
|
-
/** The layer's MCP configuration, when the layer holds one. */
|
|
37
|
-
mcpConfigPath?: string;
|
|
38
|
-
}
|
|
27
|
+
import { APPEND_PROMPT_FLAG, liftAppendedPrompts } from "./claude-args";
|
|
39
28
|
|
|
40
29
|
/** The paths are present only where the plan names that channel and the file exists. */
|
|
41
30
|
interface ClaudeDelivery {
|
|
42
31
|
plan: ClaudeDeliveryPlan;
|
|
43
32
|
settingsPath?: string;
|
|
44
33
|
mcpConfigPath?: string;
|
|
45
|
-
layer?: ClaudeLayerDelivery;
|
|
46
|
-
/**
|
|
47
|
-
* The guidance of the audience the checkout is wrapped for. The wrap carries
|
|
48
|
-
* the rest of that audience; its guidance hook stays silent in a managed
|
|
49
|
-
* session, so the launch's own system prompt carries this.
|
|
50
|
-
*/
|
|
51
|
-
wrappedGuidance?: string;
|
|
52
34
|
}
|
|
53
35
|
|
|
54
36
|
/** A Claude launch's context: the delivery its plan chose, and the files it chose or wrote. */
|
|
55
37
|
interface ClaudeLaunchContext extends AdapterContext {
|
|
56
38
|
delivery: ClaudeDelivery;
|
|
39
|
+
/** The Capability plugin folders this launch's own prepare gathered, in order. */
|
|
40
|
+
pluginDirectories: readonly string[];
|
|
57
41
|
}
|
|
58
42
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Where a launch writes its derived `--settings`. Per Working Repository and
|
|
65
|
-
* audience, so concurrent launches never race; a companion-scoped launch
|
|
66
|
-
* derives one only for an audience. Without an audience it is git-ignored
|
|
67
|
-
* under the companion. With one it carries that audience's hooks and
|
|
68
|
-
* permission entries, so it lives beside the audience's layer, outside the
|
|
69
|
-
* companion, where no other audience's session can read it.
|
|
70
|
-
*/
|
|
71
|
-
export function launchSettingsPath(
|
|
72
|
-
companionPath: string,
|
|
73
|
-
repositoryId: string | null,
|
|
74
|
-
audience?: string,
|
|
75
|
-
): string {
|
|
76
|
-
const scope = repositoryId === null ? "companion-scope" : `settings.${safeName(repositoryId)}`;
|
|
77
|
-
if (audience) {
|
|
78
|
-
return path.join(audienceLayerDir(companionPath, audience), `launch-${scope}.json`);
|
|
79
|
-
}
|
|
80
|
-
return path.join(companionPath, ".claude", "state", `launch-${scope}.json`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function readSettings(settingsPath: string): Promise<ClaudeSettings> {
|
|
84
|
-
if (!existsSync(settingsPath)) return {};
|
|
85
|
-
return JSON.parse(await fs.readFile(settingsPath, "utf8")) as ClaudeSettings;
|
|
43
|
+
/** Where a launch with a Working Repository writes its derived `--settings`; git-ignored. */
|
|
44
|
+
export function launchSettingsPath(companionPath: string, repositoryId: string): string {
|
|
45
|
+
const safeId = repositoryId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
46
|
+
return path.join(companionPath, ".claude", "state", `launch-settings.${safeId}.json`);
|
|
86
47
|
}
|
|
87
48
|
|
|
88
49
|
/**
|
|
89
|
-
* Companion settings minus `exclude`, so each wrap-carried group runs once
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
50
|
+
* Companion settings minus `exclude`, so each wrap-carried group runs once. A
|
|
51
|
+
* file, not inline JSON (platform arg limits); per repository, so concurrent
|
|
52
|
+
* launches never race. `--settings` outranks every file scope, so `attribution`
|
|
53
|
+
* is dropped when the user or the repository sets its own, even one added
|
|
54
|
+
* after the companion's last sync.
|
|
93
55
|
*/
|
|
94
56
|
async function writeLaunchSettings(
|
|
95
57
|
companionSettingsPath: string,
|
|
96
58
|
target: string,
|
|
97
59
|
exclude: readonly ClaudeHookGroup[],
|
|
98
|
-
|
|
60
|
+
repoRoot: string,
|
|
99
61
|
): Promise<void> {
|
|
100
|
-
const settings = await
|
|
101
|
-
const layer = layerSettingsPath ? await readSettings(layerSettingsPath) : {};
|
|
62
|
+
const settings = JSON.parse(await fs.readFile(companionSettingsPath, "utf8")) as ClaudeSettings;
|
|
102
63
|
const excluded = new Set(exclude.map(hookGroupKey));
|
|
103
64
|
const hooks: Record<string, ClaudeHookGroup[]> = {};
|
|
104
65
|
for (const [event, groups] of Object.entries(settings.hooks ?? {})) {
|
|
105
66
|
const kept = (groups ?? []).filter((group) => !excluded.has(hookGroupKey(group)));
|
|
106
67
|
if (kept.length > 0) hooks[event] = kept;
|
|
107
68
|
}
|
|
108
|
-
for (const [event, groups] of Object.entries(layer.hooks ?? {})) {
|
|
109
|
-
hooks[event] = [...(hooks[event] ?? []), ...(groups ?? [])];
|
|
110
|
-
}
|
|
111
69
|
const derived: ClaudeSettings = { ...settings, hooks };
|
|
112
70
|
if (Object.keys(hooks).length === 0) delete derived.hooks;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const allow = [...new Set([...(settings.permissions?.allow ?? []), ...layerAllow])];
|
|
116
|
-
derived.permissions = { ...settings.permissions, allow };
|
|
71
|
+
if (definesUserClaudeAttribution({ repoRoot })) {
|
|
72
|
+
delete derived.attribution;
|
|
117
73
|
}
|
|
118
74
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
/** Beside the audience's layer: as private as the layer itself. */
|
|
122
|
-
await fs.mkdir(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
123
|
-
await writeAtomic(target, text, 0o600);
|
|
124
|
-
} else {
|
|
125
|
-
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
126
|
-
await writeAtomic(target, text);
|
|
127
|
-
}
|
|
75
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
76
|
+
await writeAtomic(target, `${JSON.stringify(derived, null, 2)}\n`);
|
|
128
77
|
}
|
|
129
78
|
|
|
130
79
|
/** The `--settings` file the plan's hook owner calls for, writing it when derived. */
|
|
@@ -132,38 +81,21 @@ async function settingsPathFor(
|
|
|
132
81
|
plan: ClaudeDeliveryPlan,
|
|
133
82
|
companionPath: string,
|
|
134
83
|
repository: AdapterContext["repository"],
|
|
135
|
-
layer: RenderedAudienceLayer | undefined,
|
|
136
84
|
): Promise<string | undefined> {
|
|
137
85
|
const companionSettingsPath = getCompanionClaudeSettingsPath(companionPath);
|
|
138
|
-
if (plan.hooks.owner === "none") return undefined;
|
|
139
|
-
if (
|
|
140
|
-
if (plan.hooks.owner === "settings" && !layer) return companionSettingsPath;
|
|
86
|
+
if (plan.hooks.owner === "none" || !existsSync(companionSettingsPath)) return undefined;
|
|
87
|
+
if (plan.hooks.owner === "settings") return companionSettingsPath;
|
|
141
88
|
/** Owner `wrap` is planned only for `managed-repo`, which has a repository. */
|
|
142
|
-
const settingsPath = launchSettingsPath(
|
|
143
|
-
companionPath,
|
|
144
|
-
plan.hooks.owner === "wrap" ? repository!.id : null,
|
|
145
|
-
layer?.audience,
|
|
146
|
-
);
|
|
89
|
+
const settingsPath = launchSettingsPath(companionPath, repository!.id);
|
|
147
90
|
await writeLaunchSettings(
|
|
148
91
|
companionSettingsPath,
|
|
149
92
|
settingsPath,
|
|
150
93
|
plan.hooks.launchSettingsExclude,
|
|
151
|
-
|
|
94
|
+
repository!.path,
|
|
152
95
|
);
|
|
153
96
|
return settingsPath;
|
|
154
97
|
}
|
|
155
98
|
|
|
156
|
-
/** The layer as this launch attaches it: the directory, and its MCP configuration when present. */
|
|
157
|
-
function layerDelivery(layer: RenderedAudienceLayer | undefined): ClaudeLayerDelivery | undefined {
|
|
158
|
-
if (!layer) return undefined;
|
|
159
|
-
const mcpConfigPath = getCompanionClaudeMcpConfigPath(layer.root);
|
|
160
|
-
return {
|
|
161
|
-
audience: layer.audience,
|
|
162
|
-
root: layer.root,
|
|
163
|
-
...(existsSync(mcpConfigPath) ? { mcpConfigPath } : {}),
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
99
|
export class ClaudeAdapter extends LaunchAdapter<ClaudeLaunchContext> {
|
|
168
100
|
readonly toolName = "claude";
|
|
169
101
|
readonly interactive = true;
|
|
@@ -175,7 +107,7 @@ export class ClaudeAdapter extends LaunchAdapter<ClaudeLaunchContext> {
|
|
|
175
107
|
*/
|
|
176
108
|
async planLaunch(context: AdapterContext, prepared: PrepareResult): Promise<ClaudeLaunchContext> {
|
|
177
109
|
const { repository, companionPath } = context;
|
|
178
|
-
const kind
|
|
110
|
+
const kind = managedSessionKind(Boolean(repository));
|
|
179
111
|
const wrap = prepared.wrapState ?? NOT_WRAPPED;
|
|
180
112
|
if (repository && !wrap.runtimes.has("claude")) {
|
|
181
113
|
throw new LaunchPreflightError(
|
|
@@ -184,24 +116,13 @@ export class ClaudeAdapter extends LaunchAdapter<ClaudeLaunchContext> {
|
|
|
184
116
|
}
|
|
185
117
|
const plan = planClaudeDelivery(kind, wrap);
|
|
186
118
|
const mcpConfigPath = getCompanionClaudeMcpConfigPath(companionPath);
|
|
187
|
-
const settingsPath = await settingsPathFor(
|
|
188
|
-
plan,
|
|
189
|
-
companionPath,
|
|
190
|
-
repository,
|
|
191
|
-
prepared.audienceLayer,
|
|
192
|
-
);
|
|
193
|
-
const layer = layerDelivery(prepared.audienceLayer);
|
|
194
|
-
const wrappedLayer =
|
|
195
|
-
!layer && wrap.audience ? currentAudienceLayer(companionPath, wrap.audience) : null;
|
|
196
|
-
const wrappedGuidance = wrappedLayer ? readLayerGuidanceSync(wrappedLayer, "CLAUDE.md") : "";
|
|
119
|
+
const settingsPath = await settingsPathFor(plan, companionPath, repository);
|
|
197
120
|
const delivery: ClaudeDelivery = {
|
|
198
121
|
plan,
|
|
199
122
|
...(plan.mcp === "mcp-config" && existsSync(mcpConfigPath) ? { mcpConfigPath } : {}),
|
|
200
123
|
...(settingsPath ? { settingsPath } : {}),
|
|
201
|
-
...(layer ? { layer } : {}),
|
|
202
|
-
...(wrappedGuidance ? { wrappedGuidance } : {}),
|
|
203
124
|
};
|
|
204
|
-
return { ...context, delivery };
|
|
125
|
+
return { ...context, delivery, pluginDirectories: prepared.claudePluginDirectories ?? [] };
|
|
205
126
|
}
|
|
206
127
|
|
|
207
128
|
/** A failed Claude wrap pass names the entry that failed. */
|
|
@@ -216,7 +137,7 @@ export class ClaudeAdapter extends LaunchAdapter<ClaudeLaunchContext> {
|
|
|
216
137
|
return super.launchFailure(prepared);
|
|
217
138
|
}
|
|
218
139
|
|
|
219
|
-
async validateLaunch(
|
|
140
|
+
async validateLaunch(_context: ClaudeLaunchContext): Promise<void> {
|
|
220
141
|
/** The bundled plugin carries the artifact-path guard; never launch without it. */
|
|
221
142
|
try {
|
|
222
143
|
validateClaudePluginAssets();
|
|
@@ -226,71 +147,48 @@ export class ClaudeAdapter extends LaunchAdapter<ClaudeLaunchContext> {
|
|
|
226
147
|
{ cause: error },
|
|
227
148
|
);
|
|
228
149
|
}
|
|
229
|
-
|
|
230
|
-
if (context.capabilities.some((capability) => capability.name === "context-mode")) {
|
|
231
|
-
try {
|
|
232
|
-
await validateContextModePackage(context.companionPath);
|
|
233
|
-
} catch (error) {
|
|
234
|
-
throw new Error(
|
|
235
|
-
`Claude context-mode plugin is unavailable: ${(error as Error).message} Repair it with \`mate companion setup\`.`,
|
|
236
|
-
{ cause: error },
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
150
|
}
|
|
241
151
|
|
|
242
152
|
/**
|
|
243
|
-
* Plan in, flags out; decides nothing. `--append-system-prompt
|
|
244
|
-
*
|
|
153
|
+
* Plan in, flags out; decides nothing. `--append-system-prompt` is
|
|
154
|
+
* unconditional: no settings document can carry it. It holds the Agent
|
|
155
|
+
* Guidance alone; the Companion Instructions come from the wrap's session hook.
|
|
156
|
+
* `--plugin-dir` passes the folders `launchPluginFolders` names, from the
|
|
157
|
+
* running install's bundled plugin.
|
|
245
158
|
*/
|
|
246
159
|
buildArgs(context: ClaudeLaunchContext, args: string[]): string[] {
|
|
247
|
-
const { plan, settingsPath, mcpConfigPath
|
|
248
|
-
const companionGuidance = buildCompanionGuidance(context);
|
|
249
|
-
const systemPrompt = wrappedGuidance
|
|
250
|
-
? `${companionGuidance}\n\n${wrappedGuidance}`
|
|
251
|
-
: companionGuidance;
|
|
160
|
+
const { plan, settingsPath, mcpConfigPath } = context.delivery;
|
|
252
161
|
/** Layered on the user's own sources so global skills and plugins still load. */
|
|
253
162
|
const settingsArgs =
|
|
254
163
|
plan.hooks.owner !== "none" && settingsPath
|
|
255
164
|
? ["--setting-sources", "user,project,local", "--settings", settingsPath]
|
|
256
165
|
: [];
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
166
|
+
const addDirArgs = plan.dirAccess === "add-dir" ? ["--add-dir", context.companionPath] : [];
|
|
167
|
+
const pluginDirArgs = launchPluginFolders(
|
|
168
|
+
plan,
|
|
169
|
+
context.pluginDirectories,
|
|
170
|
+
getClaudePluginRoot(),
|
|
171
|
+
).flatMap((dir) => ["--plugin-dir", dir]);
|
|
172
|
+
const mcpConfigArgs =
|
|
173
|
+
plan.mcp === "mcp-config" && mcpConfigPath ? ["--mcp-config", mcpConfigPath] : [];
|
|
174
|
+
|
|
175
|
+
const guidance = buildCompanionGuidance({
|
|
176
|
+
...context,
|
|
177
|
+
secretGuardrails: resolveSecretPolicy(context.companionPath).guardrails,
|
|
178
|
+
});
|
|
179
|
+
const { args: passthrough, prompts, boundary } = liftAppendedPrompts(args);
|
|
180
|
+
const promptArgs = [APPEND_PROMPT_FLAG, [guidance, ...prompts].filter(Boolean).join("\n\n")];
|
|
181
|
+
const launchArgs = [...settingsArgs, ...mcpConfigArgs, ...pluginDirArgs];
|
|
182
|
+
|
|
183
|
+
if (boundary === undefined) {
|
|
184
|
+
return [...addDirArgs, ...promptArgs, ...launchArgs, ...passthrough];
|
|
185
|
+
}
|
|
277
186
|
return [
|
|
278
187
|
...addDirArgs,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
...
|
|
282
|
-
...
|
|
283
|
-
/** The bundled plugin from the running install, beside context-mode's. */
|
|
284
|
-
"--plugin-dir",
|
|
285
|
-
getClaudePluginRoot(),
|
|
286
|
-
...contextModeArgs,
|
|
287
|
-
...args,
|
|
188
|
+
...launchArgs,
|
|
189
|
+
...passthrough.slice(0, boundary),
|
|
190
|
+
...promptArgs,
|
|
191
|
+
...passthrough.slice(boundary),
|
|
288
192
|
];
|
|
289
193
|
}
|
|
290
|
-
|
|
291
|
-
extendEnvironment(_context: ClaudeLaunchContext): NodeJS.ProcessEnv {
|
|
292
|
-
return {
|
|
293
|
-
CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD: "1",
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
194
|
}
|
|
@@ -6,6 +6,7 @@ const path = await import("node:path");
|
|
|
6
6
|
|
|
7
7
|
import { MATE_ENV } from "../../../runtime/env";
|
|
8
8
|
import { validateGuidanceData, type MateGuidanceFile } from "../../../runtime/guidance";
|
|
9
|
+
import { resolveSecretPolicy } from "../../../runtime/policy";
|
|
9
10
|
|
|
10
11
|
import { validateOpenCodePluginAssets } from "../../package-paths";
|
|
11
12
|
import { readOpenCodeConfig } from "../../../tools/setup/providers/opencode-format";
|
|
@@ -56,12 +57,7 @@ function readInvocation(launchWorkingDirectory: string, args: string[]): OpenCod
|
|
|
56
57
|
return { command, rest, hostsPrivateServer: Boolean(hostsServer) && !choosesRemoteServer };
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
interface OpenCodeLaunchContext extends AdapterContext {
|
|
61
|
-
audienceLayer?: string;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export class OpenCodeAdapter extends LaunchAdapter<OpenCodeLaunchContext> {
|
|
60
|
+
export class OpenCodeAdapter extends LaunchAdapter {
|
|
65
61
|
readonly toolName = "opencode";
|
|
66
62
|
readonly interactive = true;
|
|
67
63
|
private readonly requiredRuntimeAssets = [path.join(".opencode", "opencode.json")] as const;
|
|
@@ -84,15 +80,6 @@ export class OpenCodeAdapter extends LaunchAdapter<OpenCodeLaunchContext> {
|
|
|
84
80
|
);
|
|
85
81
|
}
|
|
86
82
|
|
|
87
|
-
async planLaunch(
|
|
88
|
-
context: AdapterContext,
|
|
89
|
-
prepared: PrepareResult,
|
|
90
|
-
): Promise<OpenCodeLaunchContext> {
|
|
91
|
-
return prepared.audienceLayer
|
|
92
|
-
? { ...context, audienceLayer: prepared.audienceLayer.root }
|
|
93
|
-
: context;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
83
|
/** A prepared repository reports its failure as one summary, as it always has. */
|
|
97
84
|
launchFailure(prepared: PrepareResult): unknown {
|
|
98
85
|
return prepared.repositoryId
|
|
@@ -116,29 +103,27 @@ export class OpenCodeAdapter extends LaunchAdapter<OpenCodeLaunchContext> {
|
|
|
116
103
|
* overlay carried now lives in the wrapped `.opencode/opencode.json`. An
|
|
117
104
|
* inherited `OPENCODE_CONFIG_DIR` or `OPENCODE_CONFIG_CONTENT` passes through.
|
|
118
105
|
*/
|
|
119
|
-
extendEnvironment(context:
|
|
106
|
+
extendEnvironment(context: AdapterContext, args: string[] = []): NodeJS.ProcessEnv {
|
|
120
107
|
const { hostsPrivateServer } = readInvocation(context.launchWorkingDirectory, args);
|
|
121
108
|
return {
|
|
122
109
|
// Built fresh per launch from the live capability config; always set so
|
|
123
110
|
// a stale value inherited from an outer Mate session can never leak in.
|
|
124
111
|
// Masked unless the launch hosts its own server: a payload that reached
|
|
125
|
-
// the shared service would apply this session's guidance
|
|
126
|
-
// audience layer — to every other.
|
|
112
|
+
// the shared service would apply this session's guidance to every other.
|
|
127
113
|
[MATE_ENV.guidanceJson]: hostsPrivateServer
|
|
128
114
|
? JSON.stringify(this.buildGuidance(context))
|
|
129
115
|
: "",
|
|
130
116
|
};
|
|
131
117
|
}
|
|
132
118
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const guidance = buildOpenCodeGuidance(context.capabilities, {
|
|
119
|
+
private buildGuidance(context: AdapterContext): MateGuidanceFile {
|
|
120
|
+
return buildOpenCodeGuidance(context.capabilities, {
|
|
136
121
|
companionScoped: !context.repository,
|
|
122
|
+
secretGuardrails: resolveSecretPolicy(context.companionPath).guardrails,
|
|
137
123
|
});
|
|
138
|
-
return context.audienceLayer ? { ...guidance, audienceLayer: context.audienceLayer } : guidance;
|
|
139
124
|
}
|
|
140
125
|
|
|
141
|
-
async validateLaunch(context:
|
|
126
|
+
async validateLaunch(context: AdapterContext): Promise<void> {
|
|
142
127
|
this.validateOpenCodeVersion();
|
|
143
128
|
|
|
144
129
|
const missingAssets = await Promise.all(
|
|
@@ -202,7 +187,7 @@ export class OpenCodeAdapter extends LaunchAdapter<OpenCodeLaunchContext> {
|
|
|
202
187
|
|
|
203
188
|
// Self-check the guidance payload this launch is about to inject so a
|
|
204
189
|
// broken playbook template fails preflight instead of inside OpenCode.
|
|
205
|
-
private validateGuidance(context:
|
|
190
|
+
private validateGuidance(context: AdapterContext): string[] {
|
|
206
191
|
const built = this.buildGuidance(context);
|
|
207
192
|
const { guidance, errors } = validateGuidanceData(built);
|
|
208
193
|
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { normalizeBaseAudience } from "../../tools/setup/dynamic-plugins/audiences";
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
BASE_AUDIENCE,
|
|
7
|
-
BASE_AUDIENCE_KEYS,
|
|
8
|
-
normalizeBaseAudience,
|
|
9
|
-
} from "../../tools/setup/dynamic-plugins/audiences";
|
|
10
|
-
|
|
11
|
-
import { getActiveDistribution } from "../../distribution";
|
|
12
5
|
import { FRAMEWORK_NAME } from "../../framework";
|
|
13
6
|
import {
|
|
14
7
|
getDefaultSetupSelections,
|
|
@@ -300,69 +293,12 @@ function validateAudiences(config: FrameworkConfig): void {
|
|
|
300
293
|
}
|
|
301
294
|
}
|
|
302
295
|
|
|
303
|
-
/**
|
|
304
|
-
* The release that introduces the `audiences.base` shape. An older Mate would
|
|
305
|
-
* read a saved file as an empty base plus an audience called `base`, so every
|
|
306
|
-
* save in the new shape raises the companion's `engines` floor to it — capped
|
|
307
|
-
* at the running release, so a pre-release build never locks itself out.
|
|
308
|
-
*/
|
|
309
|
-
export const BASE_AUDIENCE_MIN_VERSION = "0.19.0";
|
|
310
|
-
|
|
311
|
-
/** The floor version a save writes: the introducing release, or the running one when older. */
|
|
312
|
-
export function baseAudienceFloorVersion(
|
|
313
|
-
runningVersion: string = getActiveDistribution().config.version,
|
|
314
|
-
): string {
|
|
315
|
-
const running = semver.coerce(runningVersion)?.version;
|
|
316
|
-
return running && semver.lt(running, BASE_AUDIENCE_MIN_VERSION)
|
|
317
|
-
? running
|
|
318
|
-
: BASE_AUDIENCE_MIN_VERSION;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/** Only companions carry audiences; a hub or a working repository keeps the root shape. */
|
|
322
|
-
function usesAudiences(config: FrameworkConfig): boolean {
|
|
323
|
-
return config.type === undefined || config.type === "companion";
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/** The floor that admits the base audience shape, keeping a higher one. */
|
|
327
|
-
function raisedEngineFloor(range: string | undefined): string {
|
|
328
|
-
const version = baseAudienceFloorVersion();
|
|
329
|
-
const floor = `>=${version}`;
|
|
330
|
-
if (!range || semver.validRange(range) === null) return range ?? floor;
|
|
331
|
-
const minimum = semver.minVersion(range);
|
|
332
|
-
return minimum && semver.gte(minimum, version) ? range : floor;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* The file shape of a companion config: the base set's audience-scoped keys
|
|
337
|
-
* under `audiences.base`, first among the audiences. The in-memory config
|
|
338
|
-
* keeps them at the root, so its readers never see the difference.
|
|
339
|
-
*/
|
|
340
|
-
function toFileShape(config: FrameworkConfig): FrameworkConfig {
|
|
341
|
-
if (!usesAudiences(config)) return config;
|
|
342
|
-
const base: Record<string, unknown> = {};
|
|
343
|
-
const rest: Record<string, unknown> = { ...config };
|
|
344
|
-
for (const key of BASE_AUDIENCE_KEYS) {
|
|
345
|
-
if (rest[key] === undefined) continue;
|
|
346
|
-
base[key] = rest[key];
|
|
347
|
-
delete rest[key];
|
|
348
|
-
}
|
|
349
|
-
if (Object.keys(base).length === 0) return config;
|
|
350
|
-
return {
|
|
351
|
-
...(rest as unknown as FrameworkConfig),
|
|
352
|
-
audiences: { [BASE_AUDIENCE]: base, ...config.audiences } as FrameworkConfig["audiences"],
|
|
353
|
-
engines: {
|
|
354
|
-
...config.engines,
|
|
355
|
-
[FRAMEWORK_NAME]: raisedEngineFloor(config.engines?.[FRAMEWORK_NAME]),
|
|
356
|
-
},
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
296
|
export class ConfigStore extends YamlFileStore<FrameworkConfig> {
|
|
361
297
|
constructor(configPath = process.env.MATE_CONFIG ?? defaultConfigPath()) {
|
|
362
298
|
super(path.resolve(configPath));
|
|
363
299
|
}
|
|
364
300
|
|
|
365
|
-
/** Either file shape loads as the base set at the root; the
|
|
301
|
+
/** Either file shape loads as the base set at the root; the next save writes the root shape. */
|
|
366
302
|
override async load(): Promise<FrameworkConfig> {
|
|
367
303
|
const merged = mergeWithDefaults(
|
|
368
304
|
migrateProfilesToAllowedAgents(normalizeBaseAudience(await super.load())),
|
|
@@ -373,10 +309,6 @@ export class ConfigStore extends YamlFileStore<FrameworkConfig> {
|
|
|
373
309
|
return merged;
|
|
374
310
|
}
|
|
375
311
|
|
|
376
|
-
override async save(config: FrameworkConfig): Promise<void> {
|
|
377
|
-
await super.save(toFileShape(config));
|
|
378
|
-
}
|
|
379
|
-
|
|
380
312
|
protected async onMissing(): Promise<FrameworkConfig> {
|
|
381
313
|
const config = defaultConfig();
|
|
382
314
|
await this.save(config);
|
|
@@ -74,28 +74,28 @@ export const editorWorkspacePath = workspaceDocumentPath;
|
|
|
74
74
|
|
|
75
75
|
interface WorkspaceDocument {
|
|
76
76
|
workspacePath: string;
|
|
77
|
-
folders: [
|
|
77
|
+
folders: string[];
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Writes the generated `.mate/workspace.code-workspace` file (working
|
|
82
|
-
* repository first, companion
|
|
83
|
-
* order. Called by editor-launching `injectEditorFolder`,
|
|
84
|
-
* `mate companion open`.
|
|
82
|
+
* repository first, then each companion in the given order) and returns its
|
|
83
|
+
* path and folder order. Called by editor-launching `injectEditorFolder`,
|
|
84
|
+
* which backs `mate companion open`.
|
|
85
85
|
*/
|
|
86
86
|
async function writeWorkspaceDocument(
|
|
87
|
-
|
|
87
|
+
companionPaths: string[],
|
|
88
88
|
repoPath: string,
|
|
89
89
|
): Promise<WorkspaceDocument> {
|
|
90
|
-
await project("workspace", { repoPath,
|
|
90
|
+
await project("workspace", { repoPath, workspaceCompanionPaths: companionPaths });
|
|
91
91
|
return {
|
|
92
92
|
workspacePath: workspaceDocumentPath(repoPath),
|
|
93
|
-
folders: [
|
|
93
|
+
folders: [repoPath, ...companionPaths].map((folder) => path.resolve(folder)),
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
export async function injectEditorFolder(
|
|
98
|
-
|
|
98
|
+
companionPaths: string[],
|
|
99
99
|
repoPath: string,
|
|
100
100
|
cli = "code",
|
|
101
101
|
spawnProcess: SpawnLike = spawn,
|
|
@@ -113,7 +113,7 @@ export async function injectEditorFolder(
|
|
|
113
113
|
|
|
114
114
|
const { binary } = resolved;
|
|
115
115
|
|
|
116
|
-
const { folders: workspaceFolders } = await writeWorkspaceDocument(
|
|
116
|
+
const { folders: workspaceFolders } = await writeWorkspaceDocument(companionPaths, repoPath);
|
|
117
117
|
|
|
118
118
|
const child = spawnProcess(binary, ["--add", ...workspaceFolders], {
|
|
119
119
|
stdio: "ignore",
|