@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,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
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Stdin and stdout of a Claude hook shim. */
|
|
2
|
+
|
|
3
|
+
export type HookEnv = Record<string, string | undefined>;
|
|
4
|
+
|
|
5
|
+
/** One run of a hook shim: its environment, directory, shim arguments and stdin payload. */
|
|
6
|
+
export interface HookCall {
|
|
7
|
+
env: HookEnv;
|
|
8
|
+
cwd: string;
|
|
9
|
+
argv: readonly string[];
|
|
10
|
+
input: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface HookOutcome {
|
|
14
|
+
exitCode: number;
|
|
15
|
+
stdout: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** A hook's JSON output line, or nothing. Always exit zero: a hook must never block the session. */
|
|
19
|
+
export function hookOutcome(output: object | null): HookOutcome {
|
|
20
|
+
return { exitCode: 0, stdout: output ? `${JSON.stringify(output)}\n` : "" };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function readStdin(): Promise<string> {
|
|
24
|
+
if (process.stdin.isTTY) return "";
|
|
25
|
+
const chunks: Buffer[] = [];
|
|
26
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
27
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The hook payload on stdin; input that is not JSON reads as `null`, no event. */
|
|
31
|
+
async function readHookInput(): Promise<unknown> {
|
|
32
|
+
const raw = await readStdin();
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(raw) as unknown;
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A hook shim's whole run: read the payload, build the outcome, write it.
|
|
42
|
+
* `onError` turns a failure in either step into an outcome, so the shim still exits zero.
|
|
43
|
+
*/
|
|
44
|
+
export async function runHook(
|
|
45
|
+
build: (input: unknown) => HookOutcome | Promise<HookOutcome>,
|
|
46
|
+
onError: (error: unknown) => HookOutcome,
|
|
47
|
+
): Promise<number> {
|
|
48
|
+
let outcome: HookOutcome;
|
|
49
|
+
try {
|
|
50
|
+
outcome = await build(await readHookInput());
|
|
51
|
+
} catch (error) {
|
|
52
|
+
outcome = onError(error);
|
|
53
|
+
}
|
|
54
|
+
writeHookStdout(outcome.stdout);
|
|
55
|
+
return outcome.exitCode;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Swallows stdout errors: a parent that stopped reading would otherwise end the write in an uncaught `EPIPE`. */
|
|
59
|
+
function writeHookStdout(stdout: string): void {
|
|
60
|
+
if (!stdout) return;
|
|
61
|
+
process.stdout.on("error", () => {});
|
|
62
|
+
process.stdout.write(stdout);
|
|
63
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Show the active Mate companion paths in Claude Code, and repair the
|
|
3
|
+
* companion's Git state before the session reads it.
|
|
4
|
+
*/
|
|
5
|
+
import { errorMessage } from "../runtime/error-message";
|
|
3
6
|
import { unmanagedClaudeDelivery } from "../runtime/claude-delivery-plan";
|
|
4
7
|
import {
|
|
5
8
|
syncCompanionUnattended,
|
|
@@ -7,24 +10,11 @@ import {
|
|
|
7
10
|
type UnattendedSyncOptions,
|
|
8
11
|
} from "../runtime/companion-sync";
|
|
9
12
|
import { hasLaunchEnvironment, resolveCompanionRuntime } from "../runtime/env";
|
|
13
|
+
import { FRAMEWORK_NAME } from "../runtime/framework";
|
|
10
14
|
import { projectionFreshness, projectionStalenessLines } from "../runtime/freshness";
|
|
11
15
|
import { mateVersion } from "../runtime/install";
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
export interface BannerOutcome {
|
|
15
|
-
exitCode: number;
|
|
16
|
-
stdout: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Marks the copy a wrap inlines into the Working Repository's settings. A
|
|
21
|
-
* managed launch loads the plugin's own copy *and* that document, and both
|
|
22
|
-
* registrations resolve to the same command against the same environment, so
|
|
23
|
-
* nothing inside the process tells them apart. The flag is what keeps the
|
|
24
|
-
* banner from printing twice in exactly the session that is configured
|
|
25
|
-
* correctly — the projected copy defers, the launch's own copy prints.
|
|
26
|
-
*/
|
|
27
|
-
export const PROJECTED_BANNER_FLAG = "--projected";
|
|
16
|
+
import { PROJECTED_FLAG } from "../runtime/projected-flag";
|
|
17
|
+
import { hookOutcome, runHook, type HookEnv, type HookOutcome } from "./hook-io";
|
|
28
18
|
|
|
29
19
|
/**
|
|
30
20
|
* Resolves in-process from either source — no subprocess in either branch.
|
|
@@ -36,13 +26,11 @@ export async function buildBanner(
|
|
|
36
26
|
cwd: string = process.cwd(),
|
|
37
27
|
argv: readonly string[] = [],
|
|
38
28
|
syncOptions: UnattendedSyncOptions = {},
|
|
39
|
-
): Promise<
|
|
40
|
-
if (argv.includes(
|
|
41
|
-
return { exitCode: 0, stdout: "" };
|
|
42
|
-
}
|
|
29
|
+
): Promise<HookOutcome> {
|
|
30
|
+
if (argv.includes(PROJECTED_FLAG) && hasLaunchEnvironment(env)) return hookOutcome(null);
|
|
43
31
|
|
|
44
32
|
const { context, projection } = resolveCompanionRuntime(env, cwd);
|
|
45
|
-
if (!context.repositoryPath || !context.companionPath) return
|
|
33
|
+
if (!context.repositoryPath || !context.companionPath) return hookOutcome(null);
|
|
46
34
|
|
|
47
35
|
/**
|
|
48
36
|
* Repaired before the session reads the companion, so its first reads are
|
|
@@ -78,12 +66,16 @@ export async function buildBanner(
|
|
|
78
66
|
/** `systemMessage` only: the model must never be told to run the command. */
|
|
79
67
|
for (const note of gitNotes) lines.push(` ${note}`);
|
|
80
68
|
|
|
81
|
-
return
|
|
69
|
+
return hookOutcome({ systemMessage: lines.join("\n") });
|
|
82
70
|
}
|
|
83
71
|
|
|
84
|
-
|
|
85
|
-
export
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
/** Plugin-shim entry; exits zero even when the banner fails. */
|
|
73
|
+
export function run(argv: readonly string[] = process.argv.slice(2)): Promise<number> {
|
|
74
|
+
return runHook(
|
|
75
|
+
() => buildBanner(process.env, process.cwd(), argv),
|
|
76
|
+
(error) =>
|
|
77
|
+
hookOutcome({
|
|
78
|
+
systemMessage: `${FRAMEWORK_NAME}: session banner failed: ${errorMessage(error)}`,
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
89
81
|
}
|
|
@@ -1,27 +1,6 @@
|
|
|
1
1
|
// Deliver the companion guidance to a Claude session Mate did not launch.
|
|
2
|
-
import { currentAudienceLayer, readLayerGuidanceSync } from "../runtime/audience-layer";
|
|
3
|
-
import { resolveProjection } from "../runtime/projection";
|
|
4
2
|
import { buildProjectedGuidance } from "../runtime/projected-guidance";
|
|
5
|
-
import {
|
|
6
|
-
import type { HookEnv } from "./validate-artifact-path";
|
|
7
|
-
|
|
8
|
-
export interface GuidanceOutcome {
|
|
9
|
-
exitCode: number;
|
|
10
|
-
stdout: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* The guidance of the audience this checkout is wrapped for, from its layer.
|
|
15
|
-
* The wrap is the only source: an inherited `MATE_AUDIENCE` selects nothing.
|
|
16
|
-
*/
|
|
17
|
-
function wrappedAudienceGuidance(cwd: string): string {
|
|
18
|
-
const projection = resolveProjection(cwd);
|
|
19
|
-
if (!projection) return "";
|
|
20
|
-
const audience = readWrapStateSync(projection.repoRoot).audience;
|
|
21
|
-
if (!audience) return "";
|
|
22
|
-
const layer = currentAudienceLayer(projection.companionPath, audience);
|
|
23
|
-
return layer ? readLayerGuidanceSync(layer, "CLAUDE.md") : "";
|
|
24
|
-
}
|
|
3
|
+
import { hookOutcome, type HookEnv, type HookOutcome } from "./hook-io";
|
|
25
4
|
|
|
26
5
|
/**
|
|
27
6
|
* `SessionStart.additionalContext` is the file-based form of the managed
|
|
@@ -33,21 +12,16 @@ function wrappedAudienceGuidance(cwd: string): string {
|
|
|
33
12
|
* Emits nothing rather than a note when no companion resolves: an unwrapped
|
|
34
13
|
* repository must read exactly as it did before the hook existed.
|
|
35
14
|
*/
|
|
36
|
-
export function buildSessionGuidance(env: HookEnv, cwd: string = process.cwd()):
|
|
37
|
-
const
|
|
38
|
-
if (!
|
|
39
|
-
const layer = wrappedAudienceGuidance(cwd);
|
|
40
|
-
const guidance = layer ? `${companionGuidance}\n\n${layer}` : companionGuidance;
|
|
15
|
+
export function buildSessionGuidance(env: HookEnv, cwd: string = process.cwd()): HookOutcome {
|
|
16
|
+
const guidance = buildProjectedGuidance(env, cwd);
|
|
17
|
+
if (!guidance) return hookOutcome(null);
|
|
41
18
|
|
|
42
|
-
return {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
},
|
|
49
|
-
})}\n`,
|
|
50
|
-
};
|
|
19
|
+
return hookOutcome({
|
|
20
|
+
hookSpecificOutput: {
|
|
21
|
+
hookEventName: "SessionStart",
|
|
22
|
+
additionalContext: guidance,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
51
25
|
}
|
|
52
26
|
|
|
53
27
|
// Plugin-shim entry.
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/** Prepares a plain Claude session in a Claude-wrapped repository as `mate claude` would. */
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
|
|
4
|
+
import { errorMessage } from "../runtime/error-message";
|
|
4
5
|
import { unmanagedClaudeDelivery } from "../runtime/claude-delivery-plan";
|
|
5
6
|
import { hasLaunchEnvironment } from "../runtime/env";
|
|
7
|
+
import { FRAMEWORK_NAME } from "../runtime/framework";
|
|
8
|
+
import { PROJECTED_FLAG } from "../runtime/projected-flag";
|
|
6
9
|
import { resolveProjection } from "../runtime/projection";
|
|
7
10
|
import { runtimeDirectoryPath, runtimeMateBin } from "../runtime/runtime-directory";
|
|
8
11
|
import {
|
|
@@ -12,12 +15,8 @@ import {
|
|
|
12
15
|
type PrepareRun,
|
|
13
16
|
type PrepareRunner,
|
|
14
17
|
} from "../runtime/session-prepare-runner";
|
|
15
|
-
import type
|
|
16
|
-
|
|
17
|
-
export interface SessionPrepareHookOutcome {
|
|
18
|
-
exitCode: number;
|
|
19
|
-
stdout: string;
|
|
20
|
-
}
|
|
18
|
+
import { instructionHookPayload, type InstructionHookDeps } from "./companion-instructions";
|
|
19
|
+
import { hookOutcome, runHook, type HookCall, type HookEnv, type HookOutcome } from "./hook-io";
|
|
21
20
|
|
|
22
21
|
export interface SessionPrepareHookDeps {
|
|
23
22
|
/** The runtime-directory launcher; `null` until a `mate` run wrote it. */
|
|
@@ -25,6 +24,8 @@ export interface SessionPrepareHookDeps {
|
|
|
25
24
|
runner: (mateBin: string, repoPath: string) => PrepareRunner;
|
|
26
25
|
startSync: (mateBin: string, repoPath: string) => void;
|
|
27
26
|
timeoutMs: number;
|
|
27
|
+
/** The main thread's instruction read, after the prepare. */
|
|
28
|
+
instructions?: InstructionHookDeps;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export const sessionPrepareHookDeps: SessionPrepareHookDeps = {
|
|
@@ -37,12 +38,9 @@ export const sessionPrepareHookDeps: SessionPrepareHookDeps = {
|
|
|
37
38
|
timeoutMs: PREPARE_TIMEOUT_MS,
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
const INERT: SessionPrepareHookOutcome = { exitCode: 0, stdout: "" };
|
|
41
|
-
|
|
42
41
|
/** `systemMessage` only: the model must never be told to repair the companion. */
|
|
43
|
-
function operatorMessage(lines: string[]):
|
|
44
|
-
|
|
45
|
-
return { exitCode: 0, stdout: `${JSON.stringify({ systemMessage: lines.join("\n") })}\n` };
|
|
42
|
+
function operatorMessage(lines: string[]): HookOutcome {
|
|
43
|
+
return hookOutcome(lines.length > 0 ? { systemMessage: lines.join("\n") } : null);
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
async function runLocalPhase(
|
|
@@ -54,10 +52,18 @@ async function runLocalPhase(
|
|
|
54
52
|
try {
|
|
55
53
|
return await pending;
|
|
56
54
|
} catch (error) {
|
|
57
|
-
return { ok: false, notes: [
|
|
55
|
+
return { ok: false, notes: [errorMessage(error)] };
|
|
58
56
|
}
|
|
59
57
|
}
|
|
60
58
|
|
|
59
|
+
/** The local phase's notes, and the Git sync it leaves for the caller to start. */
|
|
60
|
+
interface Prepared {
|
|
61
|
+
notes: string[];
|
|
62
|
+
startSync: () => void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const NO_SYNC = (): void => {};
|
|
66
|
+
|
|
61
67
|
/**
|
|
62
68
|
* Inert under a launch environment or where the plan gives it nothing to own.
|
|
63
69
|
* Awaits the local phase within a bound, then starts Git sync detached. Always
|
|
@@ -67,35 +73,116 @@ export async function runSessionPrepareHook(
|
|
|
67
73
|
env: HookEnv,
|
|
68
74
|
cwd: string = process.cwd(),
|
|
69
75
|
deps: SessionPrepareHookDeps = sessionPrepareHookDeps,
|
|
70
|
-
): Promise<
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
): Promise<HookOutcome> {
|
|
77
|
+
const prepared = await guardedPrepare(env, cwd, deps);
|
|
78
|
+
return operatorMessage([...prepared.notes, ...startSync(prepared)]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The folder the prepare runs in, and the main thread's instructions are read from. */
|
|
82
|
+
function prepareDirectory(env: HookEnv, cwd: string): string {
|
|
83
|
+
return env.CLAUDE_PROJECT_DIR || cwd;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function prepareFailure(error: unknown): string {
|
|
87
|
+
return `${FRAMEWORK_NAME}: session prepare failed: ${errorMessage(error)}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** A prepare that throws becomes a note, so the instructions after it still go out. */
|
|
91
|
+
async function guardedPrepare(
|
|
92
|
+
env: HookEnv,
|
|
93
|
+
cwd: string,
|
|
94
|
+
deps: SessionPrepareHookDeps,
|
|
95
|
+
): Promise<Prepared> {
|
|
96
|
+
try {
|
|
97
|
+
return await prepare(env, cwd, deps);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return { notes: [prepareFailure(error)], startSync: NO_SYNC };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** A sync that fails to start becomes a note. */
|
|
104
|
+
function startSync(prepared: Prepared): string[] {
|
|
105
|
+
try {
|
|
106
|
+
prepared.startSync();
|
|
107
|
+
return [];
|
|
108
|
+
} catch (error) {
|
|
109
|
+
return [prepareFailure(error)];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function prepare(env: HookEnv, cwd: string, deps: SessionPrepareHookDeps): Promise<Prepared> {
|
|
114
|
+
const nothing = { notes: [], startSync: NO_SYNC };
|
|
115
|
+
if (hasLaunchEnvironment(env)) return nothing;
|
|
116
|
+
const projectDir = prepareDirectory(env, cwd);
|
|
73
117
|
const projection = resolveProjection(projectDir);
|
|
74
|
-
if (!projection) return
|
|
118
|
+
if (!projection) return nothing;
|
|
75
119
|
const plan = unmanagedClaudeDelivery(projection.repoRoot);
|
|
76
120
|
const reconciles = plan.reconcile === "prepare-hook";
|
|
77
121
|
const syncs = plan.gitSync === "prepare-hook";
|
|
78
|
-
if (!reconciles && !syncs) return
|
|
122
|
+
if (!reconciles && !syncs) return nothing;
|
|
79
123
|
|
|
80
124
|
const mateBin = deps.mateBin();
|
|
81
125
|
if (!mateBin) {
|
|
82
|
-
return
|
|
83
|
-
|
|
84
|
-
|
|
126
|
+
return {
|
|
127
|
+
notes: [
|
|
128
|
+
`${FRAMEWORK_NAME}: session prepare skipped: ${runtimeDirectoryPath()} is missing; run \`${FRAMEWORK_NAME}\` once to repair it.`,
|
|
129
|
+
],
|
|
130
|
+
startSync: NO_SYNC,
|
|
131
|
+
};
|
|
85
132
|
}
|
|
86
133
|
|
|
87
134
|
const local = reconciles
|
|
88
135
|
? await runLocalPhase(deps, mateBin, projectDir)
|
|
89
136
|
: { ok: true, notes: [] };
|
|
90
|
-
if (syncs) deps.startSync(mateBin, projectDir);
|
|
91
|
-
|
|
92
137
|
const verdict = local.ok ? "warning" : "failed";
|
|
93
|
-
return
|
|
138
|
+
return {
|
|
139
|
+
notes: local.notes.map((note) => `${FRAMEWORK_NAME}: session prepare ${verdict}: ${note}`),
|
|
140
|
+
startSync: syncs ? () => deps.startSync(mateBin, projectDir) : NO_SYNC,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Legacy rule: wraps before `--projected` registered this copy bare, and only
|
|
146
|
+
* this hook re-wraps them. So a bare copy counts as the wrap's unless
|
|
147
|
+
* `CLAUDE_PLUGIN_ROOT`, set for plugin hooks only, marks it as the plugin's.
|
|
148
|
+
*/
|
|
149
|
+
function isWrapCopy({ argv, env }: HookCall): boolean {
|
|
150
|
+
return argv.includes(PROJECTED_FLAG) || !env.CLAUDE_PLUGIN_ROOT;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The prepare, the main thread's Companion Instructions, then the Git sync, in
|
|
155
|
+
* one hook: Claude Code runs an event's hooks in parallel, and the prepare and
|
|
156
|
+
* a fast-forward both rewrite the files the instructions are read from. Only
|
|
157
|
+
* the wrap's copy runs any. Input that is not a JSON object is no event.
|
|
158
|
+
*/
|
|
159
|
+
export async function runSessionStartHook(
|
|
160
|
+
call: HookCall,
|
|
161
|
+
deps: SessionPrepareHookDeps = sessionPrepareHookDeps,
|
|
162
|
+
): Promise<HookOutcome> {
|
|
163
|
+
const { env, cwd, input } = call;
|
|
164
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return hookOutcome(null);
|
|
165
|
+
if (!isWrapCopy(call)) return hookOutcome(null);
|
|
166
|
+
const prepared = await guardedPrepare(env, cwd, deps);
|
|
167
|
+
const { hookSpecificOutput, systemMessage } =
|
|
168
|
+
instructionHookPayload(
|
|
169
|
+
{ env, cwd, input, projected: true, directory: prepareDirectory(env, cwd) },
|
|
170
|
+
deps.instructions,
|
|
171
|
+
) ?? {};
|
|
172
|
+
const notes = [...prepared.notes, ...startSync(prepared)];
|
|
173
|
+
const messages = systemMessage ? [...notes, systemMessage] : notes;
|
|
174
|
+
if (!hookSpecificOutput) return operatorMessage(messages);
|
|
175
|
+
return hookOutcome({
|
|
176
|
+
hookSpecificOutput,
|
|
177
|
+
...(messages.length > 0 ? { systemMessage: messages.join("\n") } : {}),
|
|
178
|
+
});
|
|
94
179
|
}
|
|
95
180
|
|
|
96
|
-
/** Plugin-shim entry. */
|
|
97
|
-
export
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
181
|
+
/** Plugin-shim entry; exits zero even when reading its input fails. */
|
|
182
|
+
export function run(argv: readonly string[] = process.argv.slice(2)): Promise<number> {
|
|
183
|
+
return runHook(
|
|
184
|
+
(input) => runSessionStartHook({ env: process.env, cwd: process.cwd(), argv, input }),
|
|
185
|
+
(error) =>
|
|
186
|
+
operatorMessage([`${FRAMEWORK_NAME}: session start hook failed: ${errorMessage(error)}`]),
|
|
187
|
+
);
|
|
101
188
|
}
|