@uniqbit/mate-core 0.18.5 → 1.0.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude-plugin/hooks/companion-instructions.mjs +9 -0
- package/claude-plugin/hooks/hooks.json +12 -2
- package/package.json +10 -2
- package/src/brand/art.generated.ts +69 -0
- package/src/brand/index.ts +12 -0
- package/src/brand/palette.ts +19 -0
- package/src/brand/svg.generated.ts +8 -0
- package/src/brand/terminal.ts +83 -0
- package/src/brand/types.ts +33 -0
- package/src/cli/commands/artifact/artifact.ts +8 -0
- package/src/cli/commands/artifact/delivery/archive.ts +117 -0
- package/src/cli/commands/artifact/delivery/branch-flags.ts +55 -0
- package/src/cli/commands/artifact/delivery/environment.ts +104 -0
- package/src/cli/commands/artifact/delivery/link.ts +154 -0
- package/src/cli/commands/artifact/delivery/output.ts +58 -0
- package/src/cli/commands/artifact/finish/command.ts +33 -3
- package/src/cli/commands/artifact/finish/engine.ts +47 -3
- package/src/cli/commands/artifact/finish/finisher.ts +10 -0
- package/src/cli/commands/artifact/finish/openspec.ts +177 -138
- package/src/cli/commands/artifact/finish/secret-scan.ts +43 -0
- package/src/cli/commands/artifact/pending/command.ts +31 -3
- package/src/cli/commands/artifact/pending/discovery.ts +21 -57
- package/src/cli/commands/cap/graphify.ts +52 -3
- package/src/cli/commands/cap/index-cmd.ts +8 -1
- package/src/cli/commands/cap/openspec.ts +13 -25
- package/src/cli/commands/companion/open.ts +56 -39
- package/src/cli/commands/companion/update.ts +75 -42
- package/src/cli/commands/doctor.ts +176 -3
- package/src/cli/commands/plugin/install.ts +9 -4
- package/src/cli/commands/plugin/plugin.ts +3 -2
- package/src/cli/commands/report/adapter.ts +11 -2
- package/src/cli/commands/report/contract.ts +21 -0
- package/src/cli/commands/report/highlight.ts +34 -4
- package/src/cli/commands/report/markdown.ts +110 -0
- package/src/cli/commands/report/palette.ts +118 -0
- package/src/cli/commands/report/renderer.ts +456 -153
- package/src/cli/commands/report/types.ts +16 -0
- package/src/cli/commands/setup.ts +7 -15
- package/src/cli/commands/studio/client-asset.ts +16 -0
- package/src/cli/commands/studio/openspec-cli.ts +10 -58
- package/src/cli/commands/studio/routes.ts +2 -0
- package/src/cli/commands/studio/selection.ts +19 -1
- package/src/cli/commands/studio/server.ts +42 -1
- package/src/cli/commands/studio/views/client.ts +6 -616
- package/src/cli/commands/studio/views/dashboard/changes.tsx +5 -10
- package/src/cli/commands/studio/views/document.tsx +137 -69
- package/src/cli/commands/studio/views/markdown.tsx +250 -0
- package/src/cli/commands/studio/views/palette.ts +134 -0
- package/src/cli/commands/studio/views/reports/index.tsx +2 -7
- package/src/cli/commands/studio/views/skills/index.tsx +43 -62
- package/src/cli/commands/studio/views/specs/index.tsx +7 -15
- package/src/cli/commands/studio/views/studio-client/actions.ts +51 -0
- package/src/cli/commands/studio/views/studio-client/companion.ts +13 -0
- package/src/cli/commands/studio/views/studio-client/index.ts +32 -0
- package/src/cli/commands/studio/views/studio-client/keys.ts +3 -0
- package/src/cli/commands/studio/views/studio-client/main.ts +3 -0
- package/src/cli/commands/studio/views/studio-client/page.ts +146 -0
- package/src/cli/commands/studio/views/studio-client/panels.ts +57 -0
- package/src/cli/commands/studio/views/studio-client/reports.ts +122 -0
- package/src/cli/commands/studio/views/studio-client/theme.ts +63 -0
- package/src/cli/commands/studio/views/studio-client/vault-editor.ts +166 -0
- package/src/cli/commands/studio/views/studio-client/vault-pane.ts +202 -0
- package/src/cli/commands/studio/views/studio-client/vault-tree.ts +258 -0
- package/src/cli/commands/studio/views/studio-client.generated.ts +2 -0
- package/src/cli/commands/studio/views/styles.ts +217 -206
- package/src/cli/commands/studio/views/vault/state.ts +3 -5
- package/src/cli/commands/studio/views/workflow/delivery-path.tsx +370 -0
- package/src/cli/commands/studio/views/workflow/graph.ts +220 -0
- package/src/cli/commands/studio/views/workflow/index.tsx +2 -3
- package/src/cli/commands/studio/views/workflow/steps.ts +228 -287
- package/src/cli/commands/wrap.ts +5 -13
- package/src/cli/main.ts +4 -1
- package/src/cli/parse-flags.ts +25 -7
- package/src/cli/setup-selector.tsx +38 -162
- package/src/cli/usage.ts +4 -0
- package/src/hooks/companion-instructions.ts +190 -0
- package/src/hooks/hook-io.ts +63 -0
- package/src/hooks/session-banner.ts +21 -29
- package/src/hooks/session-guidance.ts +9 -17
- package/src/hooks/session-prepare.ts +115 -28
- package/src/hooks/validate-artifact-path.ts +82 -24
- package/src/index.ts +4 -0
- package/src/lib/change-layout.ts +154 -0
- package/src/lib/components/wizard-header.tsx +3 -1
- package/src/lib/context-mode-package.ts +24 -11
- package/src/lib/delivery/archive-change.ts +154 -0
- package/src/lib/delivery/branches.ts +108 -0
- package/src/lib/delivery/environment.ts +29 -0
- package/src/lib/delivery/forge.ts +279 -0
- package/src/lib/delivery/overlap.ts +202 -0
- package/src/lib/delivery/record-delivery.ts +196 -0
- package/src/lib/delivery/record.ts +343 -0
- package/src/lib/delivery/refresh.ts +174 -0
- package/src/lib/delivery/repository-access.ts +97 -0
- package/src/lib/delivery/stages.ts +111 -0
- package/src/lib/delivery/token.ts +58 -0
- package/src/lib/frontmatter.ts +18 -0
- package/src/lib/opencode-plugin-package.ts +5 -5
- package/src/lib/openspec-cli.ts +116 -0
- package/src/lib/openspec-scopes.ts +86 -0
- package/src/lib/orchestrator/adapters/claude-args.ts +50 -0
- package/src/lib/orchestrator/adapters/claude.ts +48 -40
- package/src/lib/orchestrator/adapters/opencode.ts +2 -0
- package/src/lib/orchestrator/editor.ts +9 -9
- package/src/lib/orchestrator/openspec-store.ts +14 -54
- package/src/lib/orchestrator/projection-claude-commands.ts +36 -0
- package/src/lib/orchestrator/projection-claude-links.ts +159 -0
- package/src/lib/orchestrator/projection-claude-skills.ts +19 -137
- package/src/lib/orchestrator/projection-entries.ts +45 -14
- package/src/lib/orchestrator/projection-runtime-documents.ts +15 -6
- package/src/lib/orchestrator/projection-types.ts +3 -0
- package/src/lib/orchestrator/refresh-working-claude-links.ts +87 -0
- package/src/lib/orchestrator/session-prepare.ts +45 -5
- package/src/lib/orchestrator/setup-compatibilities.ts +18 -75
- package/src/lib/orchestrator/types.ts +2 -2
- package/src/lib/orchestrator/wrap-pass.ts +22 -1
- package/src/lib/package-paths.ts +8 -0
- package/src/opencode/companion-hooks.ts +64 -5
- package/src/opencode/companion-policy.ts +37 -18
- package/src/opencode/projected-guidance.ts +4 -1
- package/src/opencode/session-prepare.ts +2 -1
- package/src/opencode/tui.tsx +4 -2
- package/src/runtime/artifact-refusal.ts +63 -0
- package/src/runtime/claude-delivery-plan.ts +89 -7
- package/src/runtime/commit-attribution-guard.ts +181 -0
- package/src/runtime/companion-guidance.ts +15 -3
- package/src/runtime/companion-instructions.ts +340 -0
- package/src/runtime/error-message.ts +4 -0
- package/src/runtime/index.ts +5 -0
- package/src/runtime/managed-block.ts +117 -0
- package/src/runtime/markdown-scan.ts +266 -0
- package/src/runtime/policy.ts +15 -1
- package/src/runtime/projected-flag.ts +7 -0
- package/src/runtime/projected-guidance.ts +1 -0
- package/src/runtime/runtime-document-manifest.ts +62 -11
- package/src/runtime/secret-guard.ts +148 -0
- package/src/runtime/secret-patterns.ts +164 -0
- package/src/runtime/secret-scan.ts +90 -0
- package/src/runtime/session-kind.ts +13 -0
- package/src/runtime/shell-heredoc.ts +81 -0
- package/src/runtime/wrap-state.ts +36 -2
- package/src/runtime/write-atomic.ts +64 -0
- package/src/templates/capabilities/openspec-cap/mate-minimal/schema.yaml +8 -6
- package/src/templates/capabilities/openspec-cap/mate-minimal/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +7 -9
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/openspec-conventions.yaml +10 -12
- package/src/templates/mate-skills/THIRD-PARTY-NOTICES.md +40 -0
- package/src/templates/mate-skills/agents/mate-apply-change/SKILL.md +76 -0
- package/src/templates/mate-skills/agents/mate-apply-change/references/openspec-apply-change.md +198 -0
- package/src/templates/mate-skills/agents/mate-archive-change/SKILL.md +59 -0
- package/src/templates/mate-skills/{claude/mate-openspec-backfill → agents/mate-backfill-spec}/SKILL.md +4 -4
- package/src/templates/mate-skills/agents/mate-clarify-intent/SKILL.md +95 -0
- package/src/templates/mate-skills/agents/mate-explore-change/SKILL.md +50 -0
- package/src/templates/mate-skills/agents/mate-explore-change/references/openspec-explore.md +348 -0
- package/src/templates/mate-skills/agents/mate-grill-me/SKILL.md +3 -3
- package/src/templates/mate-skills/agents/mate-interview-me/SKILL.md +5 -5
- package/src/templates/mate-skills/agents/mate-propose-change/SKILL.md +103 -0
- package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-propose.md +167 -0
- package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-update-change.md +101 -0
- package/src/templates/mate-skills/{claude/mate-artifact-publish → agents/mate-publish-artifact}/SKILL.md +91 -23
- package/src/templates/mate-skills/agents/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
- package/src/templates/mate-skills/agents/mate-show-me/SKILL.md +74 -16
- package/src/templates/mate-skills/agents/mate-simplify-code/SKILL.md +74 -68
- package/src/templates/mate-skills/claude/mate-apply-change/SKILL.md +76 -0
- package/src/templates/mate-skills/claude/mate-apply-change/references/openspec-apply-change.md +198 -0
- package/src/templates/mate-skills/claude/mate-archive-change/SKILL.md +59 -0
- package/src/templates/mate-skills/{agents/mate-openspec-backfill → claude/mate-backfill-spec}/SKILL.md +4 -4
- package/src/templates/mate-skills/claude/mate-clarify-intent/SKILL.md +95 -0
- package/src/templates/mate-skills/claude/mate-explore-change/SKILL.md +50 -0
- package/src/templates/mate-skills/claude/mate-explore-change/references/openspec-explore.md +348 -0
- package/src/templates/mate-skills/claude/mate-grill-me/SKILL.md +3 -3
- package/src/templates/mate-skills/claude/mate-interview-me/SKILL.md +5 -5
- package/src/templates/mate-skills/claude/mate-propose-change/SKILL.md +103 -0
- package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-propose.md +167 -0
- package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-update-change.md +101 -0
- package/src/templates/mate-skills/{agents/mate-artifact-publish → claude/mate-publish-artifact}/SKILL.md +91 -23
- package/src/templates/mate-skills/claude/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
- package/src/templates/mate-skills/claude/mate-show-me/SKILL.md +74 -16
- package/src/templates/mate-skills/claude/mate-simplify-code/SKILL.md +74 -68
- package/src/templates/mate-skills/openspec-references.ts +140 -0
- package/src/templates/root/TEMPLATE_AGENTS.md +2 -2
- package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +3727 -320
- package/src/tools/setup/capabilities/context-mode.ts +25 -5
- package/src/tools/setup/capabilities/graphify-gate.ts +31 -0
- package/src/tools/setup/capabilities/graphify-secrets.ts +255 -0
- package/src/tools/setup/capabilities/graphify.ts +12 -0
- package/src/tools/setup/capabilities/openspec.ts +151 -270
- package/src/tools/setup/capabilities/tokensave.ts +36 -3
- package/src/tools/setup/context-services.ts +9 -17
- package/src/tools/setup/dynamic-plugins/install.ts +24 -28
- package/src/tools/setup/engine.ts +97 -21
- package/src/tools/setup/managed-output.ts +51 -0
- package/src/tools/setup/mate.ts +86 -11
- package/src/tools/setup/plugin.ts +35 -0
- package/src/tools/setup/plugins/guidance.ts +7 -4
- package/src/tools/setup/providers/agent-file-sections.ts +4 -2
- package/src/tools/setup/providers/claude-attribution.ts +65 -0
- package/src/tools/setup/providers/claude-plugin-folders.ts +625 -0
- package/src/tools/setup/providers/claude-plugin-hooks.ts +39 -19
- package/src/tools/setup/providers/claude.ts +284 -8
- package/src/tools/setup/providers/opencode.ts +180 -1
- package/src/tools/setup/utils.ts +14 -4
- package/src/tools/setup.ts +126 -10
- package/wrappers/bin/graphify +52 -4
- package/wrappers/bin/openspec +33 -9
- package/wrappers/lib/graphify-gate.mjs +8 -0
- package/src/cli/commands/studio/views/workflow/transcript.tsx +0 -190
- package/src/tools/setup/runtime-documents.ts +0 -291
package/src/cli/parse-flags.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type GitModeSelection,
|
|
3
|
-
type OpenSpecSchemaSelection,
|
|
4
3
|
listSetupCapabilityCompatibilities,
|
|
5
4
|
} from "../lib/orchestrator/setup-compatibilities";
|
|
6
5
|
import type { CapabilityConfig } from "../lib/orchestrator/types";
|
|
@@ -63,6 +62,31 @@ export function parseFlags(
|
|
|
63
62
|
return flags;
|
|
64
63
|
}
|
|
65
64
|
|
|
65
|
+
/** Tokens that are neither flags nor flag values, read with the rules of {@link parseFlags}. */
|
|
66
|
+
export function positionalArgs(
|
|
67
|
+
argv: string[],
|
|
68
|
+
booleanFlags: BooleanFlagSet = NO_BOOLEAN_FLAGS,
|
|
69
|
+
): string[] {
|
|
70
|
+
const names: string[] = [];
|
|
71
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
72
|
+
const token = argv[index];
|
|
73
|
+
if (!token.startsWith("--")) names.push(token);
|
|
74
|
+
else if (token.includes("=") || booleanFlags.has(token.slice(2))) continue;
|
|
75
|
+
else if (argv[index + 1] && !argv[index + 1].startsWith("--")) index += 1;
|
|
76
|
+
}
|
|
77
|
+
return names;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** A flag's value when it was given one, so a bare or repeated flag reads as absent. */
|
|
81
|
+
export function stringFlag(value: CliFlags[string] | undefined): string | undefined {
|
|
82
|
+
return typeof value === "string" ? value : undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The first of `names` given bare or more than once, which {@link stringFlag} would read as absent. */
|
|
86
|
+
export function malformedValueFlag(flags: CliFlags, names: readonly string[]): string | undefined {
|
|
87
|
+
return names.find((name) => flags[name] !== undefined && typeof flags[name] !== "string");
|
|
88
|
+
}
|
|
89
|
+
|
|
66
90
|
export function parseAllowedAgents(flags: CliFlags): string[] | undefined {
|
|
67
91
|
const allowedAgents = flags["allowed-agent"];
|
|
68
92
|
return Array.isArray(allowedAgents)
|
|
@@ -95,12 +119,6 @@ export function parseCapabilities(flags: CliFlags): CapabilityConfig[] | undefin
|
|
|
95
119
|
return names.map((name) => index.get(name) ?? { name });
|
|
96
120
|
}
|
|
97
121
|
|
|
98
|
-
export function parseOpenSpecSchema(flags: CliFlags): OpenSpecSchemaSelection | undefined {
|
|
99
|
-
const schema = flags["openspec-schema"];
|
|
100
|
-
if (schema === "mate-v1" || schema === "default") return schema;
|
|
101
|
-
return undefined;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
122
|
export function parseGitMode(flags: CliFlags): GitModeSelection | undefined {
|
|
105
123
|
const gitMode = flags["git-mode"];
|
|
106
124
|
if (gitMode === "auto" || gitMode === "default") return gitMode;
|
|
@@ -9,14 +9,10 @@ import { getRequiredPluginIds, withRequiredSelections } from "../tools/setup/pol
|
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
type GitModeSelection,
|
|
12
|
-
type OpenSpecSchemaSelection,
|
|
13
12
|
type SetupSelections,
|
|
14
|
-
applyOpenSpecSchemaSelection,
|
|
15
13
|
DEFAULT_GIT_MODE_SELECTION,
|
|
16
|
-
getOpenSpecSchemaSelection,
|
|
17
14
|
listSetupCapabilityCompatibilities,
|
|
18
15
|
listSetupGitModeCompatibilities,
|
|
19
|
-
listSetupOpenSpecSchemaCompatibilities,
|
|
20
16
|
listSetupPackageManagerCompatibilities,
|
|
21
17
|
listSetupProviderCompatibilities,
|
|
22
18
|
} from "../lib/orchestrator/setup-compatibilities";
|
|
@@ -28,16 +24,14 @@ interface SetupSelectorOptions {
|
|
|
28
24
|
stderr?: NodeJS.WriteStream;
|
|
29
25
|
}
|
|
30
26
|
|
|
31
|
-
export type WizardStep = "providers" | "capabilities" | "
|
|
27
|
+
export type WizardStep = "providers" | "capabilities" | "gitMode";
|
|
32
28
|
|
|
33
29
|
interface SetupSelectorProps {
|
|
34
30
|
activeIndex: number;
|
|
35
31
|
currentStep: number;
|
|
36
|
-
steps: WizardStep[];
|
|
37
32
|
selectedProviders: Set<string>;
|
|
38
33
|
selectedPackageManagers: Set<string>;
|
|
39
34
|
selectedCapabilities: Set<string>;
|
|
40
|
-
selectedOpenSpecSchema: OpenSpecSchemaSelection;
|
|
41
35
|
selectedGitMode: GitModeSelection;
|
|
42
36
|
onMounted?: () => void;
|
|
43
37
|
}
|
|
@@ -47,7 +41,7 @@ export const setupSelectorDeps = {
|
|
|
47
41
|
};
|
|
48
42
|
|
|
49
43
|
export interface SetupSelectorItem {
|
|
50
|
-
kind: "provider" | "packageManager" | "capability" | "
|
|
44
|
+
kind: "provider" | "packageManager" | "capability" | "gitMode";
|
|
51
45
|
id: string;
|
|
52
46
|
label: string;
|
|
53
47
|
description: string;
|
|
@@ -62,48 +56,17 @@ export interface SetupSelectorState {
|
|
|
62
56
|
selectedProviders: Set<string>;
|
|
63
57
|
selectedPackageManagers: Set<string>;
|
|
64
58
|
selectedCapabilities: Set<string>;
|
|
65
|
-
selectedOpenSpecSchema: OpenSpecSchemaSelection;
|
|
66
59
|
selectedGitMode: GitModeSelection;
|
|
67
60
|
}
|
|
68
61
|
|
|
69
|
-
const
|
|
70
|
-
"providers",
|
|
71
|
-
"capabilities",
|
|
72
|
-
"gitMode",
|
|
73
|
-
];
|
|
62
|
+
export const WIZARD_STEPS: readonly WizardStep[] = ["providers", "capabilities", "gitMode"];
|
|
74
63
|
|
|
75
64
|
const STEP_DISPLAY_NAMES: Record<WizardStep, string> = {
|
|
76
65
|
providers: "Agents",
|
|
77
66
|
capabilities: "Capabilities",
|
|
78
|
-
openspecSchema: "OpenSpec Schema Profile",
|
|
79
67
|
gitMode: "Git Auto Mode",
|
|
80
68
|
};
|
|
81
69
|
|
|
82
|
-
export const OPENSPEC_SCHEMA_SKIP_MESSAGE = "OpenSpec not selected - skipping";
|
|
83
|
-
|
|
84
|
-
function hasOpenSpecSelected(selectedCapabilities: Set<string>): boolean {
|
|
85
|
-
return selectedCapabilities.has("openspec");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function getOpenSpecSchemaStepMessage(
|
|
89
|
-
step: WizardStep,
|
|
90
|
-
selectedCapabilities: Set<string>,
|
|
91
|
-
): string | null {
|
|
92
|
-
return step === "openspecSchema" && !hasOpenSpecSelected(selectedCapabilities)
|
|
93
|
-
? OPENSPEC_SCHEMA_SKIP_MESSAGE
|
|
94
|
-
: null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function buildWizardSteps(options: {
|
|
98
|
-
selectedCapabilities: Set<string>;
|
|
99
|
-
lockOpenSpecSchemaStep?: boolean;
|
|
100
|
-
}): WizardStep[] {
|
|
101
|
-
if (!hasOpenSpecSelected(options.selectedCapabilities) && !options.lockOpenSpecSchemaStep) {
|
|
102
|
-
return [...BASE_WIZARD_STEPS];
|
|
103
|
-
}
|
|
104
|
-
return ["providers", "capabilities", "openspecSchema", "gitMode"];
|
|
105
|
-
}
|
|
106
|
-
|
|
107
70
|
export function parseSelectorInput(
|
|
108
71
|
input: string,
|
|
109
72
|
): "up" | "down" | "toggle" | "submit" | "cancel" | "back" | null {
|
|
@@ -147,9 +110,7 @@ function markRequiredItems(items: SetupSelectorItem[]): SetupSelectorItem[] {
|
|
|
147
110
|
);
|
|
148
111
|
}
|
|
149
112
|
|
|
150
|
-
export function getSelectableItems(
|
|
151
|
-
activeOpenSpecSchema?: OpenSpecSchemaSelection,
|
|
152
|
-
): SetupSelectorItem[] {
|
|
113
|
+
export function getSelectableItems(): SetupSelectorItem[] {
|
|
153
114
|
const baseItems: SetupSelectorItem[] = [
|
|
154
115
|
...listSetupProviderCompatibilities().map((entry) => ({
|
|
155
116
|
kind: "provider" as const,
|
|
@@ -177,12 +138,6 @@ export function getSelectableItems(
|
|
|
177
138
|
return markRequiredItems([
|
|
178
139
|
...baseItems,
|
|
179
140
|
...getDistributionSelectorItems(knownIds),
|
|
180
|
-
...listSetupOpenSpecSchemaCompatibilities(activeOpenSpecSchema).map((entry) => ({
|
|
181
|
-
kind: "openspecSchema" as const,
|
|
182
|
-
id: entry.id,
|
|
183
|
-
label: entry.label,
|
|
184
|
-
description: entry.description,
|
|
185
|
-
})),
|
|
186
141
|
...listSetupGitModeCompatibilities().map((entry) => ({
|
|
187
142
|
kind: "gitMode" as const,
|
|
188
143
|
id: entry.id,
|
|
@@ -192,17 +147,13 @@ export function getSelectableItems(
|
|
|
192
147
|
]);
|
|
193
148
|
}
|
|
194
149
|
|
|
195
|
-
export function getItemsForStep(
|
|
196
|
-
step: WizardStep,
|
|
197
|
-
activeOpenSpecSchema?: OpenSpecSchemaSelection,
|
|
198
|
-
): SetupSelectorItem[] {
|
|
150
|
+
export function getItemsForStep(step: WizardStep): SetupSelectorItem[] {
|
|
199
151
|
const kindMap: Record<WizardStep, SetupSelectorItem["kind"]> = {
|
|
200
152
|
providers: "provider",
|
|
201
153
|
capabilities: "capability",
|
|
202
|
-
openspecSchema: "openspecSchema",
|
|
203
154
|
gitMode: "gitMode",
|
|
204
155
|
};
|
|
205
|
-
return getSelectableItems(
|
|
156
|
+
return getSelectableItems().filter((item) => item.kind === kindMap[step]);
|
|
206
157
|
}
|
|
207
158
|
|
|
208
159
|
export function getDisabledCapabilityIds(selectedPackageManagers: Set<string>): Set<string> {
|
|
@@ -260,7 +211,6 @@ export function toggleSelectableItem(
|
|
|
260
211
|
selectedProviders,
|
|
261
212
|
selectedPackageManagers,
|
|
262
213
|
selectedCapabilities,
|
|
263
|
-
selectedOpenSpecSchema: state.selectedOpenSpecSchema,
|
|
264
214
|
selectedGitMode: state.selectedGitMode,
|
|
265
215
|
};
|
|
266
216
|
}
|
|
@@ -269,40 +219,30 @@ export function selectRadioItem(
|
|
|
269
219
|
state: SetupSelectorState,
|
|
270
220
|
current: SetupSelectorItem,
|
|
271
221
|
): SetupSelectorState {
|
|
272
|
-
if (current.kind !== "
|
|
222
|
+
if (current.kind !== "gitMode") return state;
|
|
273
223
|
return {
|
|
274
224
|
selectedProviders: new Set(state.selectedProviders),
|
|
275
225
|
selectedPackageManagers: new Set(state.selectedPackageManagers),
|
|
276
226
|
selectedCapabilities: new Set(state.selectedCapabilities),
|
|
277
|
-
|
|
278
|
-
current.kind === "openspecSchema"
|
|
279
|
-
? (current.id as OpenSpecSchemaSelection)
|
|
280
|
-
: state.selectedOpenSpecSchema,
|
|
281
|
-
selectedGitMode:
|
|
282
|
-
current.kind === "gitMode" ? (current.id as GitModeSelection) : state.selectedGitMode,
|
|
227
|
+
selectedGitMode: current.id as GitModeSelection,
|
|
283
228
|
};
|
|
284
229
|
}
|
|
285
230
|
|
|
286
231
|
function getInitialActiveIndexForStep(
|
|
287
232
|
step: WizardStep,
|
|
288
|
-
selectedOpenSpecSchema: OpenSpecSchemaSelection,
|
|
289
233
|
selectedGitMode: GitModeSelection = DEFAULT_GIT_MODE_SELECTION,
|
|
290
234
|
): number {
|
|
291
|
-
if (step !== "
|
|
292
|
-
const
|
|
293
|
-
const selected = step === "gitMode" ? selectedGitMode : selectedOpenSpecSchema;
|
|
294
|
-
const index = items.findIndex((item) => item.id === selected);
|
|
235
|
+
if (step !== "gitMode") return 0;
|
|
236
|
+
const index = getItemsForStep(step).findIndex((item) => item.id === selectedGitMode);
|
|
295
237
|
return index >= 0 ? index : 0;
|
|
296
238
|
}
|
|
297
239
|
|
|
298
240
|
function SetupSelectorApp({
|
|
299
241
|
activeIndex,
|
|
300
242
|
currentStep,
|
|
301
|
-
steps,
|
|
302
243
|
selectedProviders,
|
|
303
244
|
selectedPackageManagers,
|
|
304
245
|
selectedCapabilities,
|
|
305
|
-
selectedOpenSpecSchema,
|
|
306
246
|
selectedGitMode,
|
|
307
247
|
onMounted,
|
|
308
248
|
}: SetupSelectorProps) {
|
|
@@ -310,26 +250,18 @@ function SetupSelectorApp({
|
|
|
310
250
|
onMounted?.();
|
|
311
251
|
}, [onMounted]);
|
|
312
252
|
|
|
313
|
-
const totalSteps =
|
|
314
|
-
const stepKey =
|
|
253
|
+
const totalSteps = WIZARD_STEPS.length;
|
|
254
|
+
const stepKey = WIZARD_STEPS[currentStep];
|
|
315
255
|
const stepName = STEP_DISPLAY_NAMES[stepKey];
|
|
316
256
|
const disabledCapabilityIds = getDisabledCapabilityIds(selectedPackageManagers);
|
|
317
|
-
const
|
|
318
|
-
const
|
|
319
|
-
const showOpenSpecSkipMessage = skipMessage !== null;
|
|
320
|
-
const navigableItems = showOpenSpecSkipMessage
|
|
321
|
-
? []
|
|
322
|
-
: getItemsForStep(stepKey, selectedOpenSpecSchema);
|
|
323
|
-
const isRadioStep =
|
|
324
|
-
(stepKey === "openspecSchema" || stepKey === "gitMode") && !showOpenSpecSkipMessage;
|
|
257
|
+
const navigableItems = getItemsForStep(stepKey);
|
|
258
|
+
const isRadioStep = stepKey === "gitMode";
|
|
325
259
|
const isLastStep = currentStep === totalSteps - 1;
|
|
326
260
|
const enterHint = isLastStep ? "enter confirm" : "enter next";
|
|
327
261
|
const backHint = currentStep > 0 ? " · ← back" : "";
|
|
328
|
-
const footerHint =
|
|
329
|
-
?
|
|
330
|
-
:
|
|
331
|
-
? `↑↓ navigate · enter select · ${enterHint}${backHint} · q cancel`
|
|
332
|
-
: `↑↓ navigate · space toggle · ${enterHint}${backHint} · q cancel`;
|
|
262
|
+
const footerHint = isRadioStep
|
|
263
|
+
? `↑↓ navigate · enter select · ${enterHint}${backHint} · q cancel`
|
|
264
|
+
: `↑↓ navigate · space toggle · ${enterHint}${backHint} · q cancel`;
|
|
333
265
|
|
|
334
266
|
const menuItems: SelectMenuItem[] = navigableItems.flatMap((item): SelectMenuItem[] => {
|
|
335
267
|
if (item.kind === "provider") {
|
|
@@ -356,16 +288,6 @@ function SetupSelectorApp({
|
|
|
356
288
|
},
|
|
357
289
|
];
|
|
358
290
|
}
|
|
359
|
-
if (item.kind === "openspecSchema") {
|
|
360
|
-
return [
|
|
361
|
-
{
|
|
362
|
-
key: `openspecSchema:${item.id}`,
|
|
363
|
-
label: item.label,
|
|
364
|
-
description: item.description,
|
|
365
|
-
indicator: renderIndicator(selectedOpenSpecSchema === item.id, { mode: "radio" }),
|
|
366
|
-
},
|
|
367
|
-
];
|
|
368
|
-
}
|
|
369
291
|
if (item.kind === "gitMode") {
|
|
370
292
|
return [
|
|
371
293
|
{
|
|
@@ -384,15 +306,11 @@ function SetupSelectorApp({
|
|
|
384
306
|
<WizardHeader title={stepName} step={currentStep} totalSteps={totalSteps} />
|
|
385
307
|
{currentStep === 0 && <Text dimColor>Always installing: bun (node) · uv (python)</Text>}
|
|
386
308
|
<Box marginTop={1} flexDirection="column">
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
activeIndex={activeIndex}
|
|
393
|
-
mode={isRadioStep ? "radio" : "multi"}
|
|
394
|
-
/>
|
|
395
|
-
)}
|
|
309
|
+
<SelectMenu
|
|
310
|
+
items={menuItems}
|
|
311
|
+
activeIndex={activeIndex}
|
|
312
|
+
mode={isRadioStep ? "radio" : "multi"}
|
|
313
|
+
/>
|
|
396
314
|
</Box>
|
|
397
315
|
<WizardFooter hint={footerHint} />
|
|
398
316
|
</Box>
|
|
@@ -426,11 +344,7 @@ export async function selectSetupCompatibilities(
|
|
|
426
344
|
let selectedProviders = new Set(initialSelections.allowedAgents);
|
|
427
345
|
let selectedPackageManagers = new Set(initialSelections.packageManagers);
|
|
428
346
|
let selectedCapabilities = new Set(initialSelections.capabilities.map((entry) => entry.name));
|
|
429
|
-
let selectedOpenSpecSchema =
|
|
430
|
-
initialSelections.selectedOpenSpecSchema ??
|
|
431
|
-
getOpenSpecSchemaSelection(initialSelections.capabilities);
|
|
432
347
|
let selectedGitMode = initialSelections.selectedGitMode;
|
|
433
|
-
let openSpecSchemaStepLocked = hasOpenSpecSelected(selectedCapabilities);
|
|
434
348
|
let rendered: ReturnType<typeof inkRender> | undefined;
|
|
435
349
|
|
|
436
350
|
const cleanup = () => {
|
|
@@ -449,31 +363,17 @@ export async function selectSetupCompatibilities(
|
|
|
449
363
|
resolve(value);
|
|
450
364
|
};
|
|
451
365
|
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
selectedCapabilities,
|
|
455
|
-
lockOpenSpecSchemaStep: openSpecSchemaStepLocked,
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
const getCurrentStep = () => getSteps()[currentStep];
|
|
459
|
-
|
|
460
|
-
const getCurrentItems = () => {
|
|
461
|
-
const step = getCurrentStep();
|
|
462
|
-
return step === "openspecSchema" && !hasOpenSpecSelected(selectedCapabilities)
|
|
463
|
-
? []
|
|
464
|
-
: getItemsForStep(step, selectedOpenSpecSchema);
|
|
465
|
-
};
|
|
366
|
+
const getCurrentStep = () => WIZARD_STEPS[currentStep];
|
|
367
|
+
const getCurrentItems = () => getItemsForStep(getCurrentStep());
|
|
466
368
|
|
|
467
369
|
const rerender = () => {
|
|
468
370
|
rendered?.rerender(
|
|
469
371
|
<SetupSelectorApp
|
|
470
372
|
activeIndex={activeIndex}
|
|
471
373
|
currentStep={currentStep}
|
|
472
|
-
steps={getSteps()}
|
|
473
374
|
selectedProviders={selectedProviders}
|
|
474
375
|
selectedPackageManagers={selectedPackageManagers}
|
|
475
376
|
selectedCapabilities={selectedCapabilities}
|
|
476
|
-
selectedOpenSpecSchema={selectedOpenSpecSchema}
|
|
477
377
|
selectedGitMode={selectedGitMode}
|
|
478
378
|
/>,
|
|
479
379
|
);
|
|
@@ -510,14 +410,13 @@ export async function selectSetupCompatibilities(
|
|
|
510
410
|
if (action === "toggle") {
|
|
511
411
|
const stepItems = getCurrentItems();
|
|
512
412
|
const current = stepItems[activeIndex];
|
|
513
|
-
if (!current
|
|
413
|
+
if (!current) return;
|
|
514
414
|
|
|
515
415
|
const next = toggleSelectableItem(
|
|
516
416
|
{
|
|
517
417
|
selectedProviders,
|
|
518
418
|
selectedPackageManagers,
|
|
519
419
|
selectedCapabilities,
|
|
520
|
-
selectedOpenSpecSchema,
|
|
521
420
|
selectedGitMode,
|
|
522
421
|
},
|
|
523
422
|
current,
|
|
@@ -525,7 +424,6 @@ export async function selectSetupCompatibilities(
|
|
|
525
424
|
selectedProviders = next.selectedProviders;
|
|
526
425
|
selectedPackageManagers = next.selectedPackageManagers;
|
|
527
426
|
selectedCapabilities = next.selectedCapabilities;
|
|
528
|
-
selectedOpenSpecSchema = next.selectedOpenSpecSchema;
|
|
529
427
|
selectedGitMode = next.selectedGitMode;
|
|
530
428
|
activeIndex = Math.max(0, Math.min(activeIndex, getCurrentItems().length - 1));
|
|
531
429
|
rerender();
|
|
@@ -534,20 +432,14 @@ export async function selectSetupCompatibilities(
|
|
|
534
432
|
|
|
535
433
|
if (action === "submit") {
|
|
536
434
|
const stepKey = getCurrentStep();
|
|
537
|
-
if (
|
|
538
|
-
(stepKey === "openspecSchema" && hasOpenSpecSelected(selectedCapabilities)) ||
|
|
539
|
-
stepKey === "gitMode"
|
|
540
|
-
) {
|
|
435
|
+
if (stepKey === "gitMode") {
|
|
541
436
|
const current = getCurrentItems()[activeIndex];
|
|
542
|
-
|
|
543
|
-
stepKey === "gitMode" ? selectedGitMode : selectedOpenSpecSchema;
|
|
544
|
-
if (current && current.id !== selectedRadioValue) {
|
|
437
|
+
if (current && current.id !== selectedGitMode) {
|
|
545
438
|
const next = selectRadioItem(
|
|
546
439
|
{
|
|
547
440
|
selectedProviders,
|
|
548
441
|
selectedPackageManagers,
|
|
549
442
|
selectedCapabilities,
|
|
550
|
-
selectedOpenSpecSchema,
|
|
551
443
|
selectedGitMode,
|
|
552
444
|
},
|
|
553
445
|
current,
|
|
@@ -555,25 +447,15 @@ export async function selectSetupCompatibilities(
|
|
|
555
447
|
selectedProviders = next.selectedProviders;
|
|
556
448
|
selectedPackageManagers = next.selectedPackageManagers;
|
|
557
449
|
selectedCapabilities = next.selectedCapabilities;
|
|
558
|
-
selectedOpenSpecSchema = next.selectedOpenSpecSchema;
|
|
559
450
|
selectedGitMode = next.selectedGitMode;
|
|
560
451
|
rerender();
|
|
561
452
|
return;
|
|
562
453
|
}
|
|
563
454
|
}
|
|
564
455
|
|
|
565
|
-
if (
|
|
566
|
-
openSpecSchemaStepLocked = true;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
const steps = getSteps();
|
|
570
|
-
if (currentStep < steps.length - 1) {
|
|
456
|
+
if (currentStep < WIZARD_STEPS.length - 1) {
|
|
571
457
|
currentStep++;
|
|
572
|
-
activeIndex = getInitialActiveIndexForStep(
|
|
573
|
-
steps[currentStep],
|
|
574
|
-
selectedOpenSpecSchema,
|
|
575
|
-
selectedGitMode,
|
|
576
|
-
);
|
|
458
|
+
activeIndex = getInitialActiveIndexForStep(WIZARD_STEPS[currentStep], selectedGitMode);
|
|
577
459
|
rerender();
|
|
578
460
|
return;
|
|
579
461
|
}
|
|
@@ -583,19 +465,15 @@ export async function selectSetupCompatibilities(
|
|
|
583
465
|
doResolve({
|
|
584
466
|
allowedAgents: [...selectedProviders],
|
|
585
467
|
packageManagers: [...selectedPackageManagers],
|
|
586
|
-
capabilities:
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
],
|
|
596
|
-
selectedOpenSpecSchema,
|
|
597
|
-
),
|
|
598
|
-
selectedOpenSpecSchema,
|
|
468
|
+
capabilities: [
|
|
469
|
+
...listSetupCapabilityCompatibilities()
|
|
470
|
+
.filter((entry) => selectedCapabilities.has(entry.capability.name))
|
|
471
|
+
.map((entry) => entry.capability),
|
|
472
|
+
// Distribution-registered capabilities without a builtin entry.
|
|
473
|
+
...[...selectedCapabilities]
|
|
474
|
+
.filter((id) => !builtinCapabilityIds.has(id))
|
|
475
|
+
.map((id) => ({ name: id })),
|
|
476
|
+
],
|
|
599
477
|
selectedGitMode,
|
|
600
478
|
});
|
|
601
479
|
rendered?.unmount();
|
|
@@ -624,11 +502,9 @@ export async function selectSetupCompatibilities(
|
|
|
624
502
|
<SetupSelectorApp
|
|
625
503
|
activeIndex={activeIndex}
|
|
626
504
|
currentStep={currentStep}
|
|
627
|
-
steps={getSteps()}
|
|
628
505
|
selectedProviders={selectedProviders}
|
|
629
506
|
selectedPackageManagers={selectedPackageManagers}
|
|
630
507
|
selectedCapabilities={selectedCapabilities}
|
|
631
|
-
selectedOpenSpecSchema={selectedOpenSpecSchema}
|
|
632
508
|
selectedGitMode={selectedGitMode}
|
|
633
509
|
onMounted={onMounted}
|
|
634
510
|
/>,
|
package/src/cli/usage.ts
CHANGED
|
@@ -30,6 +30,10 @@ export function usage(): string {
|
|
|
30
30
|
` ${n} artifact publish <anchor|change-name> [--type openspec] [--no-push] [--json]`,
|
|
31
31
|
" target: a dated archive anchor, or a change name matching exactly one archive",
|
|
32
32
|
` ${n} artifact pending [--json]`,
|
|
33
|
+
` ${n} artifact link <change>... [--repository <org/repo>] [--branch <b> [--direct]]`,
|
|
34
|
+
" [--not-applicable <reason>] [--pr <url>] [--refresh]",
|
|
35
|
+
" [--confirm-merged|--confirm-open|--confirm-dropped] [--json]",
|
|
36
|
+
` ${n} artifact archive <change> [--repository <org/repo>] [--branch <b> [--direct]|--not-applicable <reason>|--skip-branch] [--json]`,
|
|
33
37
|
` ${n} doctor`,
|
|
34
38
|
` ${n} report [--days N] [--input FILE|-] [--json]`,
|
|
35
39
|
` ${n} studio [--writable] [--terminal] [--detach-timeout <minutes>]`,
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/** Delivers the Companion Instructions to a Claude main thread or subagent. */
|
|
2
|
+
import { errorMessage } from "../runtime/error-message";
|
|
3
|
+
import { planClaudeInstructions } from "../runtime/claude-delivery-plan";
|
|
4
|
+
import {
|
|
5
|
+
COMPANION_INSTRUCTIONS_BUDGET,
|
|
6
|
+
buildCompanionInstructions,
|
|
7
|
+
companionInstructionsUnavailableLine,
|
|
8
|
+
type CompanionInstructionsInput,
|
|
9
|
+
type InstructionReader,
|
|
10
|
+
} from "../runtime/companion-instructions";
|
|
11
|
+
import { MATE_ENV, hasLaunchEnvironment } from "../runtime/env";
|
|
12
|
+
import { FRAMEWORK_NAME } from "../runtime/framework";
|
|
13
|
+
import { PROJECTED_FLAG } from "../runtime/projected-flag";
|
|
14
|
+
import { resolveProjection } from "../runtime/projection";
|
|
15
|
+
import { managedSessionKind, type SessionKind } from "../runtime/session-kind";
|
|
16
|
+
import { hookOutcome, runHook, type HookCall, type HookEnv } from "./hook-io";
|
|
17
|
+
|
|
18
|
+
export interface InstructionHookPayload {
|
|
19
|
+
hookSpecificOutput?: { hookEventName: InstructionEvent; additionalContext: string };
|
|
20
|
+
systemMessage?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface InstructionHookInput {
|
|
24
|
+
hook_event_name?: string;
|
|
25
|
+
agent_type?: string;
|
|
26
|
+
cwd?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A hook run, with the shim arguments already read. */
|
|
30
|
+
export interface InstructionHookCall extends Omit<HookCall, "argv"> {
|
|
31
|
+
/** The copy a wrap carries, marked `--projected`. */
|
|
32
|
+
projected: boolean;
|
|
33
|
+
/** Where the projection resolves; the event's `cwd` when unset. */
|
|
34
|
+
directory?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface InstructionHookDeps {
|
|
38
|
+
read?: InstructionReader;
|
|
39
|
+
resolveProjection?: typeof resolveProjection;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Agents never given a copy, since Claude Code's matchers cannot exclude them:
|
|
44
|
+
* Explore and Plan skip project memory by design, and a fork inherits its
|
|
45
|
+
* parent's context, which already holds the instructions.
|
|
46
|
+
*/
|
|
47
|
+
const SKIPPED_AGENTS = new Set(["Explore", "Plan", "fork"]);
|
|
48
|
+
|
|
49
|
+
type InstructionEvent = "SessionStart" | "SubagentStart";
|
|
50
|
+
|
|
51
|
+
const INPUT_FIELDS = ["hook_event_name", "agent_type", "cwd"] as const;
|
|
52
|
+
|
|
53
|
+
function normalizeInput(raw: unknown): InstructionHookInput {
|
|
54
|
+
const input: InstructionHookInput = {};
|
|
55
|
+
if (!raw || typeof raw !== "object") return input;
|
|
56
|
+
for (const field of INPUT_FIELDS) {
|
|
57
|
+
const value = (raw as Record<string, unknown>)[field];
|
|
58
|
+
if (typeof value === "string") input[field] = value;
|
|
59
|
+
}
|
|
60
|
+
return input;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function sessionKind(env: HookEnv): SessionKind {
|
|
64
|
+
if (!hasLaunchEnvironment(env)) return "unmanaged";
|
|
65
|
+
return managedSessionKind(Boolean(env[MATE_ENV.repositoryPath]));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A copy runs only because the bundled plugin or a Claude wrap installed it, so
|
|
70
|
+
* a session with no launch environment is planned as Claude-wrapped without
|
|
71
|
+
* reading the Checkout's manifest: a linked worktree runs the main checkout's
|
|
72
|
+
* wrapped settings and has none of its own.
|
|
73
|
+
*/
|
|
74
|
+
function ownsEvent(event: InstructionEvent, kind: SessionKind, projected: boolean): boolean {
|
|
75
|
+
const owners = planClaudeInstructions(kind, true);
|
|
76
|
+
if (event === "SessionStart") return owners.main === "wrap-session-hook" && projected;
|
|
77
|
+
if (owners.subagents === "plugin-subagent-hook") return !projected;
|
|
78
|
+
if (owners.subagents === "wrap-subagent-hook") return projected;
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** A launch sets every path; a partial one is not completed from a projection, which may name another repository. */
|
|
83
|
+
function launchReaderInput(env: HookEnv): CompanionInstructionsInput | null {
|
|
84
|
+
const companionPath = env[MATE_ENV.companionPath];
|
|
85
|
+
const id = env[MATE_ENV.repositoryId];
|
|
86
|
+
const repoPath = env[MATE_ENV.repositoryPath];
|
|
87
|
+
const wrapperBinPath = env[MATE_ENV.wrapperBinPath];
|
|
88
|
+
if (!companionPath || !id || !repoPath || !wrapperBinPath) return null;
|
|
89
|
+
return { companionPath, repository: { id, path: repoPath }, wrapperBinPath };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** The environment always wins: a managed session never reads the projection. */
|
|
93
|
+
function readerInput(
|
|
94
|
+
env: HookEnv,
|
|
95
|
+
kind: SessionKind,
|
|
96
|
+
cwd: string,
|
|
97
|
+
resolve: typeof resolveProjection,
|
|
98
|
+
): CompanionInstructionsInput | null {
|
|
99
|
+
if (kind !== "unmanaged") return launchReaderInput(env);
|
|
100
|
+
const projection = resolve(cwd);
|
|
101
|
+
if (!projection) return null;
|
|
102
|
+
return {
|
|
103
|
+
companionPath: projection.companionPath,
|
|
104
|
+
/** A linked worktree on a projection copied from the main checkout names itself, not that checkout. */
|
|
105
|
+
repository: { id: projection.repositoryId, path: projection.repoRoot },
|
|
106
|
+
wrapperBinPath: projection.wrapperBinPath,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function oversizeMessage(source: string): string {
|
|
111
|
+
const marker = FRAMEWORK_NAME.toUpperCase();
|
|
112
|
+
const budget = COMPANION_INSTRUCTIONS_BUDGET.toLocaleString("en-US");
|
|
113
|
+
return `${FRAMEWORK_NAME}: the ${marker}:COMPANION block in ${source} leaves no room within the ${budget}-character budget, so the instructions exceed it; Claude Code may deliver them as a file preview. Shorten the block.`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function evaluate(
|
|
117
|
+
{ env, cwd, projected, directory }: InstructionHookCall,
|
|
118
|
+
input: InstructionHookInput,
|
|
119
|
+
deps: InstructionHookDeps,
|
|
120
|
+
): InstructionHookPayload | null {
|
|
121
|
+
const resolve = deps.resolveProjection ?? resolveProjection;
|
|
122
|
+
const read = deps.read ?? buildCompanionInstructions;
|
|
123
|
+
const event = input.hook_event_name;
|
|
124
|
+
if (event !== "SessionStart" && event !== "SubagentStart") return null;
|
|
125
|
+
if (event === "SubagentStart" && SKIPPED_AGENTS.has(input.agent_type ?? "")) return null;
|
|
126
|
+
|
|
127
|
+
const kind = sessionKind(env);
|
|
128
|
+
if (!ownsEvent(event, kind, projected)) return null;
|
|
129
|
+
|
|
130
|
+
const reader = readerInput(env, kind, directory || input.cwd || cwd, resolve);
|
|
131
|
+
if (!reader) return null;
|
|
132
|
+
|
|
133
|
+
const result = read(reader);
|
|
134
|
+
if (result.kind === "unavailable") {
|
|
135
|
+
const line = companionInstructionsUnavailableLine(result);
|
|
136
|
+
return instructionOutput(event, line, line);
|
|
137
|
+
}
|
|
138
|
+
const warning = result.oversized ? oversizeMessage(result.source) : undefined;
|
|
139
|
+
return instructionOutput(event, result.text, warning);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function instructionOutput(
|
|
143
|
+
event: InstructionEvent,
|
|
144
|
+
additionalContext: string,
|
|
145
|
+
systemMessage?: string,
|
|
146
|
+
): InstructionHookPayload {
|
|
147
|
+
return {
|
|
148
|
+
hookSpecificOutput: { hookEventName: event, additionalContext },
|
|
149
|
+
...(systemMessage ? { systemMessage } : {}),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function failure(error: unknown): InstructionHookPayload {
|
|
154
|
+
return {
|
|
155
|
+
systemMessage: `${FRAMEWORK_NAME}: companion instructions hook failed: ${errorMessage(error)}`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Its own hook output, apart from the Agent Guidance's: Claude Code caps each
|
|
161
|
+
* output at 10,000 characters. A missing `AGENTS.md` is stated as a fact, never
|
|
162
|
+
* as an instruction to stop: a SessionStart hook cannot block, and a pause
|
|
163
|
+
* would stall a `-p` or SDK session.
|
|
164
|
+
*/
|
|
165
|
+
export function instructionHookPayload(
|
|
166
|
+
call: InstructionHookCall,
|
|
167
|
+
deps: InstructionHookDeps = {},
|
|
168
|
+
): InstructionHookPayload | null {
|
|
169
|
+
try {
|
|
170
|
+
return evaluate(call, normalizeInput(call.input), deps);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
return failure(error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Plugin-shim entry; exits zero even when reading its input fails. */
|
|
177
|
+
export function run(argv: readonly string[] = process.argv.slice(2)): Promise<number> {
|
|
178
|
+
return runHook(
|
|
179
|
+
(input) =>
|
|
180
|
+
hookOutcome(
|
|
181
|
+
instructionHookPayload({
|
|
182
|
+
env: process.env,
|
|
183
|
+
cwd: process.cwd(),
|
|
184
|
+
projected: argv.includes(PROJECTED_FLAG),
|
|
185
|
+
input,
|
|
186
|
+
}),
|
|
187
|
+
),
|
|
188
|
+
(error) => hookOutcome(failure(error)),
|
|
189
|
+
);
|
|
190
|
+
}
|