aiwcli 0.11.1 → 0.12.1
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/dist/commands/clear.d.ts +8 -0
- package/dist/commands/clear.js +86 -0
- package/dist/lib/bmad-installer.d.ts +2 -27
- package/dist/lib/bmad-installer.js +3 -43
- package/dist/lib/claude-settings-types.d.ts +2 -1
- package/dist/lib/env-compat.d.ts +0 -8
- package/dist/lib/env-compat.js +0 -12
- package/dist/lib/git/index.d.ts +0 -1
- package/dist/lib/gitignore-manager.d.ts +0 -2
- package/dist/lib/gitignore-manager.js +1 -1
- package/dist/lib/hooks-merger.d.ts +1 -15
- package/dist/lib/hooks-merger.js +1 -1
- package/dist/lib/index.d.ts +3 -7
- package/dist/lib/index.js +3 -11
- package/dist/lib/output.d.ts +2 -1
- package/dist/lib/settings-hierarchy.d.ts +1 -13
- package/dist/lib/settings-hierarchy.js +1 -1
- package/dist/lib/template-installer.d.ts +5 -9
- package/dist/lib/template-installer.js +3 -13
- package/dist/lib/template-linter.d.ts +3 -10
- package/dist/lib/template-linter.js +2 -2
- package/dist/lib/template-resolver.d.ts +6 -0
- package/dist/lib/template-resolver.js +10 -0
- package/dist/lib/template-settings-reconstructor.d.ts +1 -1
- package/dist/lib/template-settings-reconstructor.js +17 -24
- package/dist/lib/terminal.d.ts +3 -14
- package/dist/lib/terminal.js +0 -4
- package/dist/lib/version.d.ts +2 -11
- package/dist/lib/version.js +3 -3
- package/dist/lib/windsurf-hooks-merger.d.ts +1 -15
- package/dist/lib/windsurf-hooks-merger.js +1 -1
- package/dist/templates/_shared/.codex/workflows/handoff.md +1 -1
- package/dist/templates/_shared/.windsurf/workflows/handoff.md +1 -1
- package/dist/templates/_shared/hooks-ts/session_end.ts +75 -4
- package/dist/templates/_shared/hooks-ts/session_start.ts +11 -13
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +6 -8
- package/dist/templates/_shared/lib-ts/CLAUDE.md +56 -7
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +176 -29
- package/dist/templates/_shared/lib-ts/base/logger.ts +1 -1
- package/dist/templates/_shared/lib-ts/base/state-io.ts +11 -2
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +181 -165
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +14 -13
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +3 -2
- package/dist/templates/_shared/lib-ts/package.json +1 -2
- package/dist/templates/_shared/lib-ts/templates/plan-context.ts +27 -34
- package/dist/templates/_shared/lib-ts/types.ts +17 -2
- package/dist/templates/_shared/scripts/resume_handoff.ts +4 -4
- package/dist/templates/_shared/scripts/save_handoff.ts +7 -7
- package/dist/templates/_shared/scripts/status_line.ts +104 -71
- package/dist/templates/_shared/workflows/handoff.md +1 -1
- package/dist/templates/cc-native/.claude/settings.json +182 -175
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +23 -1
- package/dist/templates/cc-native/_cc-native/agents/plan-questions/PLAN-QUESTIONER.md +70 -0
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +6 -1
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +142 -111
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +54 -0
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +52 -0
- package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +53 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +19 -19
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +6 -5
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +114 -83
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +107 -10
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +6 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +0 -4
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +40 -219
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-enhancement.ts +41 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-questions.ts +102 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +26 -227
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/base/base-agent.ts +217 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +4 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/claude-agent.ts +65 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/codex-agent.ts +185 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/gemini-agent.ts +39 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/orchestrator-claude-agent.ts +195 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/schemas.ts +201 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +2 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +17 -16
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +13 -108
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +3 -3
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +2 -14
- package/oclif.manifest.json +1 -1
- package/package.json +1 -2
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +0 -119
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +0 -130
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +0 -107
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-EVOLUTION.md → plan-review/ARCH-EVOLUTION.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-PATTERNS.md → plan-review/ARCH-PATTERNS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-STRUCTURE.md → plan-review/ARCH-STRUCTURE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ASSUMPTION-TRACER.md → plan-review/ASSUMPTION-TRACER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{CLARITY-AUDITOR.md → plan-review/CLARITY-AUDITOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-FEASIBILITY.md → plan-review/COMPLETENESS-FEASIBILITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-GAPS.md → plan-review/COMPLETENESS-GAPS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-ORDERING.md → plan-review/COMPLETENESS-ORDERING.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{CONSTRAINT-VALIDATOR.md → plan-review/CONSTRAINT-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DESIGN-ADR-VALIDATOR.md → plan-review/DESIGN-ADR-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DESIGN-SCALE-MATCHER.md → plan-review/DESIGN-SCALE-MATCHER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DEVILS-ADVOCATE.md → plan-review/DEVILS-ADVOCATE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DOCUMENTATION-PHILOSOPHY.md → plan-review/DOCUMENTATION-PHILOSOPHY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{HANDOFF-READINESS.md → plan-review/HANDOFF-READINESS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{HIDDEN-COMPLEXITY.md → plan-review/HIDDEN-COMPLEXITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{INCREMENTAL-DELIVERY.md → plan-review/INCREMENTAL-DELIVERY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-DEPENDENCY.md → plan-review/RISK-DEPENDENCY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-FMEA.md → plan-review/RISK-FMEA.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-PREMORTEM.md → plan-review/RISK-PREMORTEM.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-REVERSIBILITY.md → plan-review/RISK-REVERSIBILITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SCOPE-BOUNDARY.md → plan-review/SCOPE-BOUNDARY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SIMPLICITY-GUARDIAN.md → plan-review/SIMPLICITY-GUARDIAN.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SKEPTIC.md → plan-review/SKEPTIC.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-BEHAVIOR-AUDITOR.md → plan-review/TESTDRIVEN-BEHAVIOR-AUDITOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-CHARACTERIZATION.md → plan-review/TESTDRIVEN-CHARACTERIZATION.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-FIRST-VALIDATOR.md → plan-review/TESTDRIVEN-FIRST-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-PYRAMID-ANALYZER.md → plan-review/TESTDRIVEN-PYRAMID-ANALYZER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TRADEOFF-COSTS.md → plan-review/TRADEOFF-COSTS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TRADEOFF-STAKEHOLDERS.md → plan-review/TRADEOFF-STAKEHOLDERS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{VERIFY-COVERAGE.md → plan-review/VERIFY-COVERAGE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{VERIFY-STRENGTH.md → plan-review/VERIFY-STRENGTH.md} +0 -0
|
@@ -1,165 +1,181 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subprocess environment utilities.
|
|
3
|
-
* See SPEC.md §5.10
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { execSync, execFile } from "node:child_process";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Check if this is an internal subprocess call.
|
|
10
|
-
* All hooks should check this and return early to prevent recursion.
|
|
11
|
-
*/
|
|
12
|
-
export function isInternalCall(): boolean {
|
|
13
|
-
return process.env.AIWCLI_INTERNAL_CALL === "true";
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Get environment for internal subprocess calls.
|
|
18
|
-
* Returns a copy of process.env with AIWCLI_INTERNAL_CALL=true
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
*
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Subprocess environment utilities.
|
|
3
|
+
* See SPEC.md §5.10
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { execSync, execFile } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if this is an internal subprocess call.
|
|
10
|
+
* All hooks should check this and return early to prevent recursion.
|
|
11
|
+
*/
|
|
12
|
+
export function isInternalCall(): boolean {
|
|
13
|
+
return process.env.AIWCLI_INTERNAL_CALL === "true";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get environment for internal subprocess calls.
|
|
18
|
+
* Returns a copy of process.env with AIWCLI_INTERNAL_CALL=true and
|
|
19
|
+
* Claude Code nesting-detection env vars removed so subprocess
|
|
20
|
+
* claude instances can run without being blocked.
|
|
21
|
+
*/
|
|
22
|
+
export function getInternalSubprocessEnv(): Record<string, string | undefined> {
|
|
23
|
+
const env = {
|
|
24
|
+
...process.env,
|
|
25
|
+
AIWCLI_INTERNAL_CALL: "true",
|
|
26
|
+
};
|
|
27
|
+
// Explicitly delete vars that block subprocess calls (set to undefined does not work)
|
|
28
|
+
delete env.CLAUDECODE;
|
|
29
|
+
delete env.CLAUDE_CODE_ENTRYPOINT;
|
|
30
|
+
return env;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Find an executable on the system PATH.
|
|
34
|
+
* Uses `where` on Windows, `which` on Unix.
|
|
35
|
+
* On Windows, prefers .cmd/.exe over extensionless shims since
|
|
36
|
+
* execFileSync cannot spawn extensionless shell scripts.
|
|
37
|
+
* Returns the first match or null if not found.
|
|
38
|
+
*/
|
|
39
|
+
export function findExecutable(name: string): string | null {
|
|
40
|
+
try {
|
|
41
|
+
const cmd = process.platform === "win32" ? `where ${name}` : `which ${name}`;
|
|
42
|
+
const lines = execSync(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], shell: true })
|
|
43
|
+
.trim()
|
|
44
|
+
.split(/\r?\n/)
|
|
45
|
+
.map((l) => l.trim())
|
|
46
|
+
.filter(Boolean);
|
|
47
|
+
|
|
48
|
+
if (lines.length === 0) return null;
|
|
49
|
+
|
|
50
|
+
// On Windows, `where` may return an extensionless shim first (e.g. npm creates
|
|
51
|
+
// both `claude` and `claude.cmd`). execFileSync can't spawn the extensionless
|
|
52
|
+
// one, so prefer .cmd or .exe.
|
|
53
|
+
if (process.platform === "win32") {
|
|
54
|
+
const preferred = lines.find((l) => /\.(cmd|exe)$/i.test(l));
|
|
55
|
+
return preferred ?? lines[0] ?? null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return lines[0] ?? null;
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Type guard for Node.js child_process exec errors.
|
|
66
|
+
* ExecSync throws objects with these extra properties on non-zero exit or timeout.
|
|
67
|
+
*/
|
|
68
|
+
export interface ExecSyncError {
|
|
69
|
+
killed: boolean;
|
|
70
|
+
signal: string | null;
|
|
71
|
+
stdout: Buffer | string;
|
|
72
|
+
stderr: Buffer | string;
|
|
73
|
+
status: number | null;
|
|
74
|
+
message: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Check if an unknown error is an ExecSync error with process info. */
|
|
78
|
+
export function isExecSyncError(e: unknown): e is ExecSyncError {
|
|
79
|
+
return (
|
|
80
|
+
typeof e === "object" &&
|
|
81
|
+
e !== null &&
|
|
82
|
+
"killed" in e &&
|
|
83
|
+
"signal" in e
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Quote a string for use as a cmd.exe argument when shell: true.
|
|
89
|
+
* Wraps in double quotes and escapes inner double quotes as "".
|
|
90
|
+
* On non-Windows platforms, returns the string unchanged (execFile
|
|
91
|
+
* handles quoting automatically without shell).
|
|
92
|
+
*/
|
|
93
|
+
export function shellQuoteWin(arg: string): string {
|
|
94
|
+
if (process.platform !== "win32") return arg;
|
|
95
|
+
return '"' + arg.replaceAll('"', '""') + '"';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// Async Subprocess Execution
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Result from an async subprocess execution.
|
|
104
|
+
* Never throws — callers inspect fields to determine outcome.
|
|
105
|
+
*/
|
|
106
|
+
export interface ExecResult {
|
|
107
|
+
stdout: string;
|
|
108
|
+
stderr: string;
|
|
109
|
+
exitCode: number;
|
|
110
|
+
killed: boolean;
|
|
111
|
+
signal: string | null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Options for execFileAsync. */
|
|
115
|
+
export interface ExecAsyncOptions {
|
|
116
|
+
/** Data piped to the child's stdin. */
|
|
117
|
+
input?: string;
|
|
118
|
+
/** Timeout in milliseconds (not seconds). */
|
|
119
|
+
timeout?: number;
|
|
120
|
+
/** Environment variables for the child process. */
|
|
121
|
+
env?: Record<string, string | undefined>;
|
|
122
|
+
/** Maximum bytes on stdout/stderr. Default: 10 MB. */
|
|
123
|
+
maxBuffer?: number;
|
|
124
|
+
/** Use shell for execution. Required on Windows for .cmd files. */
|
|
125
|
+
shell?: boolean;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Async subprocess execution that does NOT block the event loop.
|
|
130
|
+
* Drop-in replacement for execFileSync in Promise-based parallel patterns.
|
|
131
|
+
*
|
|
132
|
+
* Returns ExecResult on both success and non-zero exit.
|
|
133
|
+
* On timeout: result.killed = true, result.signal = "SIGTERM".
|
|
134
|
+
* On spawn failure: result.exitCode = -1, result.stderr contains error.
|
|
135
|
+
*/
|
|
136
|
+
export function execFileAsync(
|
|
137
|
+
file: string,
|
|
138
|
+
args: string[],
|
|
139
|
+
options?: ExecAsyncOptions,
|
|
140
|
+
): Promise<ExecResult> {
|
|
141
|
+
return new Promise((resolve) => {
|
|
142
|
+
const child = execFile(
|
|
143
|
+
file,
|
|
144
|
+
args,
|
|
145
|
+
{
|
|
146
|
+
encoding: "utf-8",
|
|
147
|
+
timeout: options?.timeout ?? 0,
|
|
148
|
+
env: options?.env as NodeJS.ProcessEnv,
|
|
149
|
+
maxBuffer: options?.maxBuffer ?? 10 * 1024 * 1024,
|
|
150
|
+
shell: options?.shell,
|
|
151
|
+
},
|
|
152
|
+
(error, stdout, stderr) => {
|
|
153
|
+
if (error) {
|
|
154
|
+
// execFile callback error includes process exit info
|
|
155
|
+
const errObj = error as unknown as Record<string, unknown>;
|
|
156
|
+
resolve({
|
|
157
|
+
stdout: String(stdout ?? ""),
|
|
158
|
+
stderr: String(stderr ?? ""),
|
|
159
|
+
exitCode: typeof errObj.code === "number" ? errObj.code : (error as any).status ?? 1,
|
|
160
|
+
killed: Boolean(errObj.killed),
|
|
161
|
+
signal: typeof errObj.signal === "string" ? errObj.signal : null,
|
|
162
|
+
});
|
|
163
|
+
} else {
|
|
164
|
+
resolve({
|
|
165
|
+
stdout: String(stdout ?? ""),
|
|
166
|
+
stderr: String(stderr ?? ""),
|
|
167
|
+
exitCode: 0,
|
|
168
|
+
killed: false,
|
|
169
|
+
signal: null,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// Pipe input to stdin if provided
|
|
176
|
+
if (options?.input != null && child.stdin) {
|
|
177
|
+
child.stdin.write(options.input);
|
|
178
|
+
child.stdin.end();
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
* - extractPlanPathFromResult: parse plan path from ExitPlanMode output
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import * as fs from "node:fs";
|
|
12
|
-
import * as path from "node:path";
|
|
13
11
|
import * as crypto from "node:crypto";
|
|
14
|
-
import
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
|
|
15
15
|
import { atomicWrite } from "../base/atomic-write.js";
|
|
16
|
+
import { getContextDir, getContextPlansDir, sanitizeTitle } from "../base/constants.js";
|
|
16
17
|
import { logDebug, logInfo, logWarn, logError } from "../base/logger.js";
|
|
17
18
|
import { generateSlug } from "../base/utils.js";
|
|
18
19
|
import type { ContextState } from "../types.js";
|
|
@@ -43,8 +44,8 @@ export function archivePlan(
|
|
|
43
44
|
let content: string;
|
|
44
45
|
try {
|
|
45
46
|
content = fs.readFileSync(planPath, "utf-8");
|
|
46
|
-
} catch (
|
|
47
|
-
logError("plan_manager", `Failed to read plan: ${
|
|
47
|
+
} catch (error_: any) {
|
|
48
|
+
logError("plan_manager", `Failed to read plan: ${error_}`);
|
|
48
49
|
return [null, null, null];
|
|
49
50
|
}
|
|
50
51
|
|
|
@@ -148,8 +149,8 @@ export function findLatestPlan(
|
|
|
148
149
|
if (state?.plan_path && fs.existsSync(state.plan_path)) {
|
|
149
150
|
return state.plan_path;
|
|
150
151
|
}
|
|
151
|
-
} catch (
|
|
152
|
-
logWarn("plan_manager", `Failed to check state.json plan_path: ${
|
|
152
|
+
} catch (error: any) {
|
|
153
|
+
logWarn("plan_manager", `Failed to check state.json plan_path: ${error}`);
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
// 2. Fall back to most recent .md in plans/ dir
|
|
@@ -182,7 +183,7 @@ export function findLatestPlan(
|
|
|
182
183
|
* See SPEC.md §9.4
|
|
183
184
|
*/
|
|
184
185
|
export function generatePlanId(): string {
|
|
185
|
-
return crypto.randomUUID().
|
|
186
|
+
return crypto.randomUUID().replaceAll('-', "").slice(0, 8);
|
|
186
187
|
}
|
|
187
188
|
|
|
188
189
|
/**
|
|
@@ -191,8 +192,8 @@ export function generatePlanId(): string {
|
|
|
191
192
|
* See SPEC.md §9.5
|
|
192
193
|
*/
|
|
193
194
|
export function normalizePlanContent(text: string): string {
|
|
194
|
-
let result = text.
|
|
195
|
-
result = result.
|
|
195
|
+
let result = text.replaceAll(/<[^>]+>/g, "");
|
|
196
|
+
result = result.replaceAll(/\s+/g, " ").trim();
|
|
196
197
|
return result;
|
|
197
198
|
}
|
|
198
199
|
|
|
@@ -249,8 +250,8 @@ export function findPlanPathInTranscript(transcriptPath: string): string | null
|
|
|
249
250
|
let lines: string[];
|
|
250
251
|
try {
|
|
251
252
|
lines = fs.readFileSync(transcriptPath, "utf-8").split(/\r?\n/);
|
|
252
|
-
} catch (
|
|
253
|
-
logWarn("plan_manager", `Failed to read transcript: ${
|
|
253
|
+
} catch (error: any) {
|
|
254
|
+
logWarn("plan_manager", `Failed to read transcript: ${error}`);
|
|
254
255
|
return null;
|
|
255
256
|
}
|
|
256
257
|
|
|
@@ -282,7 +283,7 @@ export function findPlanPathInTranscript(transcriptPath: string): string | null
|
|
|
282
283
|
if (!filePath) continue;
|
|
283
284
|
|
|
284
285
|
// Check if path contains .claude/plans/ as consecutive parts
|
|
285
|
-
const parts = filePath.
|
|
286
|
+
const parts = filePath.replaceAll('\\', "/").split("/");
|
|
286
287
|
for (let j = 0; j < parts.length - 1; j++) {
|
|
287
288
|
if (parts[j] === ".claude" && parts[j + 1] === "plans") {
|
|
288
289
|
logInfo("plan_manager", `Extracted plan path from transcript: ${filePath}`);
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import * as fs from "node:fs";
|
|
10
|
-
import * as path from "node:path";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
|
|
11
12
|
import { getContextHandoffsDir } from "../base/constants.js";
|
|
12
13
|
import { getContext } from "../context/context-store.js";
|
|
13
14
|
import type { HandoffSections } from "../types.js";
|
|
@@ -29,7 +30,7 @@ export function findLatestHandoff(contextId: string, projectRoot?: string): stri
|
|
|
29
30
|
.sort();
|
|
30
31
|
|
|
31
32
|
if (entries.length === 0) return null;
|
|
32
|
-
return path.join(handoffsDir, entries
|
|
33
|
+
return path.join(handoffsDir, entries.at(-1)!);
|
|
33
34
|
} catch {
|
|
34
35
|
return null;
|
|
35
36
|
}
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
"test:unit": "mocha '__tests__/base/**/*.test.ts' '__tests__/templates/**/*.test.ts'",
|
|
8
8
|
"test:contract": "mocha '__tests__/context/**/*.test.ts' '__tests__/handoff/**/*.test.ts'",
|
|
9
9
|
"test:integration": "mocha '__tests__/integration/**/*.test.ts'",
|
|
10
|
-
"test:parity": "mocha '__tests__/integration/python-parity.test.ts'"
|
|
11
|
-
"fixtures": "python __tests__/fixtures/generate_fixtures.py"
|
|
10
|
+
"test:parity": "mocha '__tests__/integration/python-parity.test.ts'"
|
|
12
11
|
},
|
|
13
12
|
"devDependencies": {
|
|
14
13
|
"mocha": "^10.0.0",
|
|
@@ -1,65 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Plan
|
|
3
|
-
*
|
|
2
|
+
* Plan evaluation guidance template.
|
|
3
|
+
* Injected as context to guide the Plan agent during plan creation.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export function getEvaluationContextReminder(): string {
|
|
7
|
-
return `##
|
|
7
|
+
return `## Write This Plan for a Different Agent
|
|
8
8
|
|
|
9
|
-
The agent executing this plan has
|
|
9
|
+
The agent executing this plan has zero context from this conversation — no chat history, no memory of files explored or decisions made.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Write as if you are that agent. What would you need?
|
|
12
12
|
|
|
13
|
-
###
|
|
13
|
+
### Structure
|
|
14
14
|
|
|
15
15
|
\`\`\`
|
|
16
|
-
# Plan:
|
|
16
|
+
# Plan: [descriptive title]
|
|
17
17
|
|
|
18
18
|
## Background
|
|
19
|
-
Why this change is needed (2-3 sentences)
|
|
19
|
+
Why this change is needed (2-3 sentences of motivation)
|
|
20
20
|
|
|
21
21
|
## Task
|
|
22
|
-
What exactly to build
|
|
22
|
+
What exactly to build or change
|
|
23
23
|
|
|
24
24
|
## Files
|
|
25
25
|
**Modify:**
|
|
26
|
-
- \`exact/path/to/file.
|
|
26
|
+
- \`exact/path/to/file.ext\` — What changes and why
|
|
27
27
|
|
|
28
28
|
**Reference:**
|
|
29
|
-
- \`exact/path/to/reference.
|
|
29
|
+
- \`exact/path/to/reference.ext\` — Why relevant (e.g., "pattern to follow at lines 12-30")
|
|
30
30
|
|
|
31
31
|
## Steps
|
|
32
|
-
|
|
32
|
+
Numbered steps with specific details. For each step, consider whether any of the skills available in your system-reminder messages would help the implementation agent — if so, reference the skill inline at the point of use.
|
|
33
|
+
|
|
34
|
+
1. [Specific action with function names, patterns, or code snippets]
|
|
33
35
|
2. [Enough detail for someone who never saw this conversation]
|
|
34
36
|
|
|
35
37
|
## Constraints
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
## Documentation
|
|
39
|
-
Decisions not written down are lost when this session ends. Update the nearest CLAUDE.md and MEMORY.md so the next session inherits what you learned.
|
|
40
|
-
|
|
41
|
-
**CLAUDE.md** (nearest to changed code — cascades to subdirectories):
|
|
42
|
-
- \`exact/path/to/CLAUDE.md\` — What to document
|
|
43
|
-
|
|
44
|
-
**What to write:**
|
|
45
|
-
- Architectural choices and why alternatives were rejected
|
|
46
|
-
- Non-obvious constraints (what breaks if this changes)
|
|
47
|
-
- Workarounds with context on the underlying issue
|
|
48
|
-
- Patterns that prevent future mistakes
|
|
38
|
+
Technical requirements, preferences, or limitations discovered during planning
|
|
49
39
|
|
|
50
|
-
|
|
40
|
+
## Verification
|
|
41
|
+
Binary-testable checks the implementation agent runs to confirm success. Reference relevant skills inline where they aid verification.
|
|
51
42
|
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
## Decisions Worth Preserving
|
|
44
|
+
Decisions made during this session that would be lost without documentation. Focus on:
|
|
45
|
+
- What was chosen and why the alternatives were rejected
|
|
46
|
+
- Constraints that aren't obvious from the code itself
|
|
47
|
+
- Patterns discovered that prevent future mistakes
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
**Omit entries for:** Routine changes with no decisions (rename, formatting, dependency bump).
|
|
57
|
-
When in doubt, write it — a lean entry is better than a lost decision.
|
|
49
|
+
The implementation agent should document these so the next session inherits what this session learned.
|
|
58
50
|
\`\`\`
|
|
59
51
|
|
|
60
52
|
### Self-Check
|
|
61
|
-
- [ ] Could I execute this
|
|
62
|
-
- [ ] Are file paths exact (not "the auth file")?
|
|
53
|
+
- [ ] Could I execute this plan having never seen this conversation?
|
|
54
|
+
- [ ] Are all file paths exact (not "the auth file")?
|
|
63
55
|
- [ ] Are implementation details specific (not "use the approach we discussed")?
|
|
64
|
-
- [ ]
|
|
56
|
+
- [ ] Are relevant skills referenced where they add value?
|
|
57
|
+
- [ ] Are key decisions captured so they survive this session?`;
|
|
65
58
|
}
|
|
@@ -108,13 +108,28 @@ export interface HookInput {
|
|
|
108
108
|
transcript_path?: string;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
// §1.7
|
|
111
|
+
// §1.7 — Three hook output patterns (see hook-utils.ts for emit functions)
|
|
112
112
|
export interface HookOutput {
|
|
113
|
+
// Pattern 1: hookSpecificOutput (PreToolUse, PostToolUse, UserPromptSubmit, etc.)
|
|
113
114
|
hookSpecificOutput?: {
|
|
114
115
|
additionalContext?: string;
|
|
115
116
|
hookEventName?: string;
|
|
116
|
-
permissionDecision?: "allow" | "deny";
|
|
117
|
+
permissionDecision?: "allow" | "deny" | "ask";
|
|
117
118
|
permissionDecisionReason?: string;
|
|
119
|
+
updatedInput?: Record<string, unknown>;
|
|
120
|
+
};
|
|
121
|
+
// Pattern 2: Top-level decision (UserPromptSubmit, Stop, SubagentStop)
|
|
122
|
+
decision?: "block";
|
|
123
|
+
reason?: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// §1.7b — PermissionRequest output (structurally different from HookOutput)
|
|
127
|
+
export interface PermissionRequestOutput {
|
|
128
|
+
decision: {
|
|
129
|
+
behavior: "allow" | "deny";
|
|
130
|
+
message?: string;
|
|
131
|
+
updatedInput?: Record<string, unknown>;
|
|
132
|
+
updatedPermissions?: Record<string, unknown>;
|
|
118
133
|
};
|
|
119
134
|
}
|
|
120
135
|
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
import * as fs from "node:fs";
|
|
16
16
|
import * as path from "node:path";
|
|
17
17
|
|
|
18
|
+
import { getProjectRoot } from "../lib-ts/base/constants.js";
|
|
19
|
+
import { getGitStatusShort } from "../lib-ts/base/git-state.js";
|
|
20
|
+
import { eprint } from "../lib-ts/base/utils.js";
|
|
21
|
+
import { findActiveContextId } from "../lib-ts/context/context-store.js";
|
|
18
22
|
import {
|
|
19
23
|
findLatestHandoff,
|
|
20
24
|
readHandoffSections,
|
|
21
25
|
getHandoffTimestamp,
|
|
22
26
|
getHandoffPlanReference,
|
|
23
27
|
} from "../lib-ts/handoff/handoff-reader.js";
|
|
24
|
-
import { getProjectRoot } from "../lib-ts/base/constants.js";
|
|
25
|
-
import { findActiveContextId } from "../lib-ts/context/context-store.js";
|
|
26
|
-
import { getGitStatusShort } from "../lib-ts/base/git-state.js";
|
|
27
|
-
import { eprint } from "../lib-ts/base/utils.js";
|
|
28
28
|
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
// Helpers
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
import * as fs from "node:fs";
|
|
23
23
|
import * as path from "node:path";
|
|
24
24
|
|
|
25
|
-
import { getContext, saveState } from "../lib-ts/context/context-store.js";
|
|
26
|
-
import { getHandoffFolderPath, getProjectRoot } from "../lib-ts/base/constants.js";
|
|
27
25
|
import { atomicWrite } from "../lib-ts/base/atomic-write.js";
|
|
28
|
-
import {
|
|
26
|
+
import { getHandoffFolderPath, getProjectRoot } from "../lib-ts/base/constants.js";
|
|
29
27
|
import { getGitStatusShort } from "../lib-ts/base/git-state.js";
|
|
28
|
+
import { logInfo, logWarn, logError } from "../lib-ts/base/logger.js";
|
|
30
29
|
import { eprint } from "../lib-ts/base/utils.js";
|
|
30
|
+
import { getContext, saveState } from "../lib-ts/context/context-store.js";
|
|
31
31
|
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
33
|
// Parsing helpers
|
|
@@ -250,8 +250,8 @@ function main(): void {
|
|
|
250
250
|
} else {
|
|
251
251
|
logWarn("save_handoff", `Failed to copy plan: ${error}`);
|
|
252
252
|
}
|
|
253
|
-
} catch (
|
|
254
|
-
logWarn("save_handoff", `Failed to read plan: ${
|
|
253
|
+
} catch (error) {
|
|
254
|
+
logWarn("save_handoff", `Failed to read plan: ${error}`);
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
257
|
|
|
@@ -339,8 +339,8 @@ function main(): void {
|
|
|
339
339
|
} else {
|
|
340
340
|
logWarn("save_handoff", `Could not load context state for ${contextId}`);
|
|
341
341
|
}
|
|
342
|
-
} catch (
|
|
343
|
-
logWarn("save_handoff", `Handoff saved but auto-resume won't work (context update failed): ${
|
|
342
|
+
} catch (error) {
|
|
343
|
+
logWarn("save_handoff", `Handoff saved but auto-resume won't work (context update failed): ${error}`);
|
|
344
344
|
}
|
|
345
345
|
|
|
346
346
|
// Output success message
|