@xdevops/issue-auto-finish 1.0.97 → 1.0.98
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/{PtyRunner-XMWDMH3L.js → PtyRunner-AVP7C4HC.js} +2 -2
- package/dist/ai-runner/AIRunner.d.ts +2 -0
- package/dist/ai-runner/AIRunner.d.ts.map +1 -1
- package/dist/ai-runner/PlanFileResolver.d.ts +20 -2
- package/dist/ai-runner/PlanFileResolver.d.ts.map +1 -1
- package/dist/ai-runner/PtyRunner.d.ts.map +1 -1
- package/dist/{ai-runner-S2ATTGWX.js → ai-runner-GPHHQKUT.js} +2 -2
- package/dist/{analyze-DAVYPBHK.js → analyze-SYJXCCU7.js} +2 -2
- package/dist/{braindump-A4R3A4QT.js → braindump-QIUTH777.js} +2 -2
- package/dist/{chunk-BPVRMZU4.js → chunk-APROB5LF.js} +9 -9
- package/dist/{chunk-SNSEW7DS.js → chunk-CKYGI2V2.js} +1 -1
- package/dist/{chunk-OPWP73PW.js → chunk-DUQUGPMI.js} +3 -2
- package/dist/chunk-DUQUGPMI.js.map +1 -0
- package/dist/{chunk-2XACBKPB.js → chunk-URL4HZ66.js} +2 -2
- package/dist/{chunk-HD6V7KPE.js → chunk-XOOKCEAK.js} +112 -37
- package/dist/chunk-XOOKCEAK.js.map +1 -0
- package/dist/cli.js +5 -5
- package/dist/index.js +4 -4
- package/dist/{init-OD7CLRWK.js → init-FDXIJNXF.js} +2 -2
- package/dist/lib.js +2 -2
- package/dist/phases/BasePhase.d.ts.map +1 -1
- package/dist/{restart-JVVOYC6C.js → restart-BRO6NIID.js} +2 -2
- package/dist/run.js +4 -4
- package/dist/{start-INU24RRG.js → start-ZIJDXV56.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-HD6V7KPE.js.map +0 -1
- package/dist/chunk-OPWP73PW.js.map +0 -1
- /package/dist/{PtyRunner-XMWDMH3L.js.map → PtyRunner-AVP7C4HC.js.map} +0 -0
- /package/dist/{ai-runner-S2ATTGWX.js.map → ai-runner-GPHHQKUT.js.map} +0 -0
- /package/dist/{analyze-DAVYPBHK.js.map → analyze-SYJXCCU7.js.map} +0 -0
- /package/dist/{braindump-A4R3A4QT.js.map → braindump-QIUTH777.js.map} +0 -0
- /package/dist/{chunk-BPVRMZU4.js.map → chunk-APROB5LF.js.map} +0 -0
- /package/dist/{chunk-SNSEW7DS.js.map → chunk-CKYGI2V2.js.map} +0 -0
- /package/dist/{chunk-2XACBKPB.js.map → chunk-URL4HZ66.js.map} +0 -0
- /package/dist/{init-OD7CLRWK.js.map → init-FDXIJNXF.js.map} +0 -0
- /package/dist/{restart-JVVOYC6C.js.map → restart-BRO6NIID.js.map} +0 -0
- /package/dist/{start-INU24RRG.js.map → start-ZIJDXV56.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -89,11 +89,11 @@ var defaults = getCliDefaults();
|
|
|
89
89
|
var program = new Command();
|
|
90
90
|
program.name("issue-auto-finish").description("Issue Auto-Finish: AI-powered issue resolution daemon").version(pkg.version, "-v, --version");
|
|
91
91
|
program.command("init").description("Launch the interactive web setup wizard").option("-p, --port <port>", "Port for the setup wizard", "3456").option("-c, --config <path>", "Config file path to generate").action(async (opts) => {
|
|
92
|
-
const { initCommand } = await import("./init-
|
|
92
|
+
const { initCommand } = await import("./init-FDXIJNXF.js");
|
|
93
93
|
await initCommand({ port: parseInt(opts.port, 10), config: opts.config });
|
|
94
94
|
});
|
|
95
95
|
program.command("start").description("Start the issue-auto-finish daemon service").option("-c, --config <path>", "Path to .env config file").option("-d, --daemon", "Run as a background daemon").action(async (opts) => {
|
|
96
|
-
const { startCommand } = await import("./start-
|
|
96
|
+
const { startCommand } = await import("./start-ZIJDXV56.js");
|
|
97
97
|
await startCommand({ config: opts.config, daemon: opts.daemon });
|
|
98
98
|
});
|
|
99
99
|
program.command("stop").description("Stop the running service").option("-p, --port <port>", "Web UI port", defaults.port).option("-H, --host <host>", "Service host", defaults.host).option("-f, --force", "Force kill via SIGKILL").action(async (opts) => {
|
|
@@ -105,7 +105,7 @@ program.command("stop").description("Stop the running service").option("-p, --po
|
|
|
105
105
|
});
|
|
106
106
|
});
|
|
107
107
|
program.command("restart").description("Restart the service (stop + start)").option("-c, --config <path>", "Path to .env config file").option("-p, --port <port>", "Web UI port", defaults.port).option("-H, --host <host>", "Service host", defaults.host).option("-d, --daemon", "Restart as a background daemon").action(async (opts) => {
|
|
108
|
-
const { restartCommand } = await import("./restart-
|
|
108
|
+
const { restartCommand } = await import("./restart-BRO6NIID.js");
|
|
109
109
|
await restartCommand({
|
|
110
110
|
config: opts.config,
|
|
111
111
|
host: opts.host,
|
|
@@ -118,7 +118,7 @@ program.command("doctor").description("Check environment dependencies").action(a
|
|
|
118
118
|
await doctorCommand();
|
|
119
119
|
});
|
|
120
120
|
program.command("analyze").description("Analyze target repository and generate knowledge.json").option("-d, --dir <path>", "Project directory to analyze (defaults to PROJECT_WORK_DIR)").option("-o, --output <path>", "Output path for knowledge.json").option("-f, --force", "Overwrite existing knowledge.json").action(async (opts) => {
|
|
121
|
-
const { analyzeCommand } = await import("./analyze-
|
|
121
|
+
const { analyzeCommand } = await import("./analyze-SYJXCCU7.js");
|
|
122
122
|
await analyzeCommand({ dir: opts.dir, output: opts.output, force: opts.force });
|
|
123
123
|
});
|
|
124
124
|
program.command("status").description("Show service status").option("-p, --port <port>", "Web UI port to query", defaults.port).option("-H, --host <host>", "Service host to query", defaults.host).option("--json", "Output raw JSON").action(async (opts) => {
|
|
@@ -251,7 +251,7 @@ program.command("issues").description("List tracked issues").option("-p, --port
|
|
|
251
251
|
});
|
|
252
252
|
});
|
|
253
253
|
program.command("braindump").description("Batch braindump: split ideas into tasks and execute in parallel").option("-f, --file <path>", "Read input from file").option("-t, --target <branch>", "Target branch for merging").option("-c, --concurrency <n>", "Max concurrent tasks").option("--runner <mode>", "Default AI runner mode (claude-internal, cursor-agent, codebuddy)").option("--auto", "Skip confirmation and execute immediately").option("-p, --port <port>", "Service port (proxy to running daemon)", defaults.port).option("-H, --host <host>", "Service host", defaults.host).action(async (opts) => {
|
|
254
|
-
const { braindumpCommand } = await import("./braindump-
|
|
254
|
+
const { braindumpCommand } = await import("./braindump-QIUTH777.js");
|
|
255
255
|
await braindumpCommand({
|
|
256
256
|
file: opts.file,
|
|
257
257
|
target: opts.target,
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
main
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-APROB5LF.js";
|
|
4
4
|
import "./chunk-5M5SB6ZA.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-URL4HZ66.js";
|
|
6
6
|
import "./chunk-Z3OBKODV.js";
|
|
7
7
|
import "./chunk-EU4XFZ2T.js";
|
|
8
8
|
import "./chunk-KC5S66OZ.js";
|
|
9
9
|
import "./chunk-SEO57UYI.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-DUQUGPMI.js";
|
|
11
11
|
import "./chunk-GPZX4DSY.js";
|
|
12
12
|
import "./chunk-MSL7ROVK.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-XOOKCEAK.js";
|
|
14
14
|
import "./chunk-R32Q3RGK.js";
|
|
15
15
|
import "./chunk-ACVOOHAR.js";
|
|
16
16
|
import "./chunk-B7TVVODN.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSetupRouter
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-URL4HZ66.js";
|
|
4
4
|
import "./chunk-Z3OBKODV.js";
|
|
5
5
|
import "./chunk-SEO57UYI.js";
|
|
6
6
|
import "./chunk-ACVOOHAR.js";
|
|
@@ -88,4 +88,4 @@ async function initCommand(options) {
|
|
|
88
88
|
export {
|
|
89
89
|
initCommand
|
|
90
90
|
};
|
|
91
|
-
//# sourceMappingURL=init-
|
|
91
|
+
//# sourceMappingURL=init-FDXIJNXF.js.map
|
package/dist/lib.js
CHANGED
|
@@ -13,14 +13,14 @@ import {
|
|
|
13
13
|
registerPhase,
|
|
14
14
|
registerPipeline,
|
|
15
15
|
resolvePipelineMode
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-DUQUGPMI.js";
|
|
17
17
|
import {
|
|
18
18
|
AsyncMutex,
|
|
19
19
|
GitOperations,
|
|
20
20
|
eventBus
|
|
21
21
|
} from "./chunk-GPZX4DSY.js";
|
|
22
22
|
import "./chunk-MSL7ROVK.js";
|
|
23
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-XOOKCEAK.js";
|
|
24
24
|
import "./chunk-R32Q3RGK.js";
|
|
25
25
|
import "./chunk-ACVOOHAR.js";
|
|
26
26
|
import "./chunk-B7TVVODN.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BasePhase.d.ts","sourceRoot":"","sources":["../../src/phases/BasePhase.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"BasePhase.d.ts","sourceRoot":"","sources":["../../src/phases/BasePhase.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-WIEUIU6L.js";
|
|
4
4
|
import {
|
|
5
5
|
startCommand
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-CKYGI2V2.js";
|
|
7
7
|
import {
|
|
8
8
|
dim,
|
|
9
9
|
yellow
|
|
@@ -45,4 +45,4 @@ async function restartCommand(options) {
|
|
|
45
45
|
export {
|
|
46
46
|
restartCommand
|
|
47
47
|
};
|
|
48
|
-
//# sourceMappingURL=restart-
|
|
48
|
+
//# sourceMappingURL=restart-BRO6NIID.js.map
|
package/dist/run.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
main
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-APROB5LF.js";
|
|
4
4
|
import "./chunk-5M5SB6ZA.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-URL4HZ66.js";
|
|
6
6
|
import "./chunk-Z3OBKODV.js";
|
|
7
7
|
import "./chunk-EU4XFZ2T.js";
|
|
8
8
|
import "./chunk-KC5S66OZ.js";
|
|
9
9
|
import "./chunk-SEO57UYI.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-DUQUGPMI.js";
|
|
11
11
|
import "./chunk-GPZX4DSY.js";
|
|
12
12
|
import "./chunk-MSL7ROVK.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-XOOKCEAK.js";
|
|
14
14
|
import "./chunk-R32Q3RGK.js";
|
|
15
15
|
import "./chunk-ACVOOHAR.js";
|
|
16
16
|
import "./chunk-B7TVVODN.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startCommand
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CKYGI2V2.js";
|
|
4
4
|
import "./chunk-B7XUZJOK.js";
|
|
5
5
|
import "./chunk-SEO57UYI.js";
|
|
6
6
|
import "./chunk-FJTZKAJA.js";
|
|
@@ -13,4 +13,4 @@ import "./chunk-GF2RRYHB.js";
|
|
|
13
13
|
export {
|
|
14
14
|
startCommand
|
|
15
15
|
};
|
|
16
|
-
//# sourceMappingURL=start-
|
|
16
|
+
//# sourceMappingURL=start-ZIJDXV56.js.map
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ai-runner/PtyRunner.ts","../src/ai-runner/PlanFileResolver.ts","../src/ai-runner/DialogClassifier.ts","../src/hooks/HookEventWatcher.ts","../src/hooks/HookInjector.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { AIRunner, RunOptions, RunResult, StreamEvent, DialogOption } from './AIRunner.js';\nimport type { PtyProfile } from './AIRunnerRegistry.js';\nimport { getPtyProfile, getRegistryEntry } from './AIRunnerRegistry.js';\nimport { getRunnerCapabilities } from './AIRunnerRegistry.js';\nimport { resolveModelForRunner } from './ModelMapping.js';\nimport { PlanFileResolver } from './PlanFileResolver.js';\nimport { DialogClassifier } from './DialogClassifier.js';\nimport type { TerminalManager } from '../terminal/TerminalManager.js';\nimport { HookEventWatcher } from '../hooks/HookEventWatcher.js';\nimport { HookInjector } from '../hooks/HookInjector.js';\nimport { isShuttingDown } from '../shutdown/ShutdownSignal.js';\nimport { logger as rootLogger } from '../logger.js';\n\nconst logger = rootLogger.child('PtyRunner');\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n// eslint-disable-next-line no-control-regex\nconst ANSI_RE = /\\x1b\\[[?><=]*[0-9;]*[a-zA-Z~]|\\x1b\\][^\\x07]*\\x07|\\x1b\\(B/g;\n\nexport function stripAnsi(str: string): string {\n return str.replace(ANSI_RE, '');\n}\n\n/** Best-effort extraction of issue IID from a worktree path like `.../issue-123/...` */\nfunction extractIidFromPath(workDir: string): number | undefined {\n const match = workDir.match(/issue-(\\d+)/);\n return match ? parseInt(match[1], 10) : undefined;\n}\n\n/** Claude Code / Codebuddy shows a prompt when idle and waiting for user input.\n * - Claude Code: ❯ (U+276F) — unique character, safe to match broadly\n * - Codebuddy: > — appears between separator lines, above ⏵⏵ status bar\n */\nexport function isIdlePrompt(stripped: string): boolean {\n const lines = stripped.split('\\n').filter((l) => l.trim());\n if (lines.length === 0) return false;\n const last = lines[lines.length - 1].trim();\n // 1. Prompt character on the last non-empty line\n if (/^[❯>$%]\\s*$/.test(last) || /[❯>]\\s*$/.test(last)) return true;\n // 2. Claude Code TUI: ❯ may appear mid-line due to cursor-positioned rendering\n // (status bar appended below, cursor coordinate artifacts like \"42\" after ❯).\n // Three-step heuristic: standalone ❯ + not followed by a sentence = idle.\n if (lines.some((l) => {\n const t = l.trim();\n // ❯ must be standalone (preceded by whitespace or at start of string)\n if (!/(?:^|\\s)❯/.test(t)) return false;\n // ❯ at line end → definitely idle\n if (/❯\\s*$/.test(t)) return true;\n // ❯ followed by 3+ letter word → input echo / queued message, not idle\n return !/❯\\s+[A-Za-z]{3,}/.test(t);\n })) return true;\n // 3. Codebuddy TUI: > prompt appears above the ⏵⏵ status bar, so it's NOT\n // the last line. Use ⏵ as a Codebuddy-specific guard to distinguish from\n // random > in markdown/agent output.\n if (lines.some((l) => /^>/.test(l.trim())) && /⏵/.test(stripped)) return true;\n // 4. cursor-agent TUI: → placeholder in input box + footer with / commands,\n // and NO \"ctrl+c to stop\" (which indicates generation in progress).\n if (\n /→/.test(stripped) &&\n /\\/\\s*commands/.test(stripped) &&\n !/ctrl\\+c\\s*to\\s*stop/i.test(stripped)\n ) {\n return true;\n }\n return false;\n}\n\n// ---- Known TUI noise patterns ------------------------------------------------\n\nconst SPINNER_CHARS = '✶✻✽✢✾◆❖●◐◑◒◓○⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠂⠒⠐⠈⠠⠤·*…⋯⬡⬢';\nconst SPINNER_RE = new RegExp(`^[${SPINNER_CHARS}\\\\s]+$`);\nconst SPINNER_FRAGMENT_RE = new RegExp(`^[${SPINNER_CHARS}][a-zA-Z]{0,3}$`);\nconst STATUS_BAR_RE = /bypass permissions|shift\\+tab|ctrl\\+[a-z].*(?:to |edit)|to interrupt|to cycle|⏵.*(?:bypass|permission)/;\nconst THINKING_RE = /^[^\\w]*\\w[\\w-]*…\\s*$/;\nconst SEPARATOR_RE = /^[─━═-]{10,}$/;\nconst EFFORT_RE = /^[◐◑◒◓]\\s+(?:low|medium|high)\\s+·\\s+\\/effort$/;\n// Claude Code status bar: spinner + elapsed time + token count, e.g. \"✶80s · ↓ 14.2k tokens)\"\nconst CLAUDE_STATUS_BAR_RE = new RegExp(`^[${SPINNER_CHARS}]\\\\s*\\\\d+[smh]`);\n// Cursor positioning artifacts: bare 1-3 digit numbers left after ANSI stripping\nconst CURSOR_ARTIFACT_RE = /^\\d{1,3}$/;\n\n// ---- cursor-agent TUI noise patterns ----------------------------------------\nconst CURSOR_GENERATING_RE = /^[⬡⬢]\\s*Generating/;\nconst CURSOR_FOOTER_RE = /^\\/\\s*commands\\s*·\\s*@\\s*files\\s*·\\s*!\\s*shell$/;\nconst CURSOR_STATUS_RE = /^▶︎?\\s*Auto-run/;\nconst CURSOR_MODEL_RE = /^(?:Opus|Claude|GPT|Gemini|当前使用的模型)\\s*/;\nconst BOX_DRAWING_RE = /^[┌┐└┘│─┬┴├┤┼╭╮╰╯═║╔╗╚╝╠╣╦╩╬\\s]*$/;\n// Claude Code queued message indicator — NOT an idle prompt\nconst QUEUED_MSG_RE = /Press\\s*up\\s*to\\s*edit\\s*queued\\s*messages/;\n\n// ---- Claude Code stop hook / session-end patterns ---------------------------\n// After completing a task, Claude Code runs a \"stop hook\" and then displays a\n// summary + banner. These outputs must be classified as noise to prevent them\n// from resetting `lastOutputTime` and blocking the idle debounce.\n\n// Stop hook thinking line: \"✽Fiddle-faddling… (running stop hook · 9m39s · ↓ 6.8k tokens)\"\n// Extends THINKING_RE to allow a trailing parenthetical (stats / hook info).\nconst STOP_HOOK_RE = /^[^\\w]*\\w[\\w-]*…\\s*\\(.*\\)\\s*$/;\n\n// Session summary: \"✻Worked for 9m39s\" / \"✻Cogitated for 3m37s\" / \"✻工作了 5m12s\"\n// Claude Code rotates verbs (Worked, Cogitated, Pondered, Reasoned …).\n// Match any `<spinner><Word> for <duration>` or Chinese `工作了`.\n// The `m` flag lets `^` match at the start of each line inside multi-line PTY chunks.\nconst WORKED_SUMMARY_RE = new RegExp(`^[${SPINNER_CHARS}]\\\\s*(?:\\\\w+\\\\s+for|工作了)\\\\s+\\\\d+[smh]`, 'm');\n\n// Claude Code banner after stop hook — uses Block Element characters (▐▛▜▌▝▘█)\n// that are distinct from traditional Box Drawing characters (┌┐└┘│─).\n// Pure block-element lines (logo art) and lines starting with block elements\n// followed by product info (e.g. \"▐▛███▜▌ Claude Code v2.1.78\").\nconst CLAUDE_BANNER_RE = /^[▐▛▜▌▝▘█\\s]+$/;\nconst CLAUDE_BANNER_INFO_RE = /^[▐▛▜▌▝▘█]+\\s+(?:Claude|Opus|Gemini|GPT|Sonnet|Haiku)/;\n\n/** Workspace trust dialog — TUI strips spaces, so use \\s* between words.\n * Claude Code: \"trust this folder\" / \"I trust this\"\n * Codebuddy: \"Do you trust the files in this folder?\"\n */\nexport const TRUST_DIALOG_RE = /trust\\s*(?:the\\s*files\\s*in\\s*)?this\\s*(?:folder|workspace)|I\\s*trust\\s*this/i;\n\n/** Permission / confirmation dialog — Claude Code asks \"Do you want to proceed?\"\n * when running potentially dangerous commands in plan mode.\n * The dialog shows numbered options with \"Yes\" pre-selected (❯ 1. Yes).\n * Auto-confirm by sending Enter to select the highlighted option. */\nexport const PERMISSION_DIALOG_RE =\n /Do\\s*you\\s*want\\s*to\\s*proceed\\s*\\?|(?:❯|>)\\s*\\d+\\.\\s*Yes\\b|Allow\\s+(?:this|the)\\s+(?:action|command)\\s*\\?/i;\n\n/** Plan confirmation dialog — Claude Code shows this when the plan is finalized\n * and ready to execute. This is the definitive signal that plan mode is done.\n * Matches: \"Claude has written up a plan and is ready to execute\"\n * or \"Yes, and bypass permissions\" (unique to this dialog). */\nexport const PLAN_CONFIRM_RE =\n /written\\s*up\\s*a\\s*plan|ready\\s*to\\s*execute.*(?:Would|proceed)|Yes,?\\s*and\\s*bypass\\s*permissions/i;\n\n// ---- Interactive dialog detection (brainstorming interview, etc.) ----------\n\nconst NUMBERED_OPTION_RE = /(?:❯|>)?\\s*(\\d+)\\.\\s*(.+)/;\n\n/** Navigation hint shown at the bottom of Claude Code interactive selection menus.\n * Presence of this line is a strong positive signal for an interactive dialog. */\nexport const INTERACTIVE_NAV_HINT_RE =\n /Enter\\s*to\\s*select|↑.*↓.*navigate|Esc\\s*to\\s*cancel/i;\n\n/** Claude Code checkbox header (e.g. \"☐ 操作确认\") — a strong TUI dialog signal. */\nconst CHECKBOX_HEADER_RE = /☐/;\n\n/** ❯ prefix on an option line — Claude Code highlights the selected option with ❯. */\nconst HIGHLIGHTED_OPTION_RE = /❯\\s*\\d+\\./;\n\nexport type DialogConfidence = 'high' | 'low';\n\n/**\n * Parse a TUI frame containing a numbered-choice dialog. Returns the question\n * text and option list, or null if the frame doesn't contain a valid dialog.\n *\n * When a navigation hint (\"Enter to select · ↑/↓ to navigate\") is present,\n * the minimum option threshold is lowered to 1 (ANSI stripping may corrupt\n * some option lines). Otherwise, at least 2 numbered options are required.\n */\nexport function parseInteractiveDialog(stripped: string): { question: string; options: DialogOption[] } | null {\n const lines = stripped.split(/[\\r\\n]+/).map(l => l.trim()).filter(Boolean);\n const options: DialogOption[] = [];\n const questionParts: string[] = [];\n let optionsStarted = false;\n\n for (const line of lines) {\n if (isTuiNoise(line)) continue;\n if (INTERACTIVE_NAV_HINT_RE.test(line)) continue;\n const m = NUMBERED_OPTION_RE.exec(line);\n if (m) {\n const label = m[2].trim();\n // Guard against decimal numbers (e.g. \"3.14\") — label must be >= 2 chars\n // and not purely numeric.\n if (label.length >= 2 && !/^\\d+$/.test(label)) {\n // Handle merged lines: ANSI cursor-position stripping can merge the\n // question text and the first option onto a single line. Extract\n // the prefix before the match as question text.\n if (m.index > 0 && !optionsStarted) {\n const prefix = line.slice(0, m.index)\n .replace(/[─━═╌☐]+/g, ' ')\n .trim();\n if (prefix.length > 0) questionParts.push(prefix);\n }\n optionsStarted = true;\n options.push({ index: parseInt(m[1], 10), label });\n }\n } else if (!optionsStarted) {\n if (!/^[❯>$%]\\s*$/.test(line) && !/^[─━═╌]{4,}$/.test(line)) {\n questionParts.push(line);\n }\n }\n }\n\n const minRequired = INTERACTIVE_NAV_HINT_RE.test(stripped) ? 1 : 2;\n if (options.length < minRequired) return null;\n return { question: questionParts.join(' ').trim(), options };\n}\n\n/**\n * Assess the confidence that a frame is a genuine interactive dialog vs. an\n * agent's numbered markdown list. High-confidence signals include TUI-specific\n * chrome that only appears in real dialogs (nav hint, ❯ highlight, ☐ header).\n */\nexport function getDialogConfidence(stripped: string): DialogConfidence {\n if (INTERACTIVE_NAV_HINT_RE.test(stripped)) return 'high';\n if (HIGHLIGHTED_OPTION_RE.test(stripped)) return 'high';\n if (CHECKBOX_HEADER_RE.test(stripped)) return 'high';\n return 'low';\n}\n\n/**\n * Returns true when the frame contains an interactive numbered-choice dialog\n * that is NOT already handled by PLAN_CONFIRM_RE or PERMISSION_DIALOG_RE.\n *\n * Two detection paths:\n * 1. Standard: parseInteractiveDialog finds >= 2 numbered options.\n * 2. Nav-hint fallback: the frame contains a navigation hint line\n * (\"Enter to select · ↑/↓ to navigate\") AND at least 1 numbered option.\n * This handles cases where ANSI stripping corrupts some option lines.\n */\nexport function isInteractiveDialog(stripped: string): boolean {\n if (PLAN_CONFIRM_RE.test(stripped)) return false;\n if (PERMISSION_DIALOG_RE.test(stripped)) return false;\n if (parseInteractiveDialog(stripped) !== null) return true;\n // Fallback: nav hint + at least 1 numbered option\n if (INTERACTIVE_NAV_HINT_RE.test(stripped)) {\n const lines = stripped.split(/[\\r\\n]+/).map(l => l.trim()).filter(Boolean);\n return lines.some(l => !isTuiNoise(l) && NUMBERED_OPTION_RE.test(l));\n }\n return false;\n}\n\n/**\n * Check if a PTY data frame contains active work content alongside the idle\n * prompt. Claude Code TUI co-renders the ❯ prompt with tool results, file\n * listings, and thinking indicators in a single data frame. When both are\n * present, the frame is a \"mixed frame\" — the agent is still active.\n *\n * Returns true when the frame has meaningful non-noise, non-prompt content.\n */\nexport function containsActiveWork(stripped: string): boolean {\n const lines = stripped.split(/[\\r\\n]+/).filter(l => l.trim());\n let inTipBlock = false;\n\n return lines.some(line => {\n const t = line.trim();\n if (t.length === 0) return false;\n\n if (/Tip:/i.test(t)) {\n inTipBlock = true;\n return false;\n }\n if (inTipBlock) return false;\n\n if (/ctrl\\+o to expand/i.test(t)) return false;\n if (/^⎿/.test(t)) return false;\n if (/^[❯>$%]\\s*$/.test(t) || /[❯>]\\s*$/.test(t)) return false;\n // Table data rows (e.g. │pnpmlint│✅通过│) — completed report output, not active work\n if (/^[│║┃]/.test(t) && /[│║┃]$/.test(t)) return false;\n if (isTuiNoise(t)) return false;\n return true;\n });\n}\n\n/** True when the line is Claude Code TUI chrome (spinners, status bar, etc.) */\nexport function isTuiNoise(line: string): boolean {\n const t = line.replace(/\\r/g, '').trim();\n if (t.length === 0) return true;\n if (SPINNER_RE.test(t)) return true;\n if (t.length <= 4 && SPINNER_FRAGMENT_RE.test(t)) return true;\n if (STATUS_BAR_RE.test(t)) return true;\n if (THINKING_RE.test(t)) return true;\n if (SEPARATOR_RE.test(t)) return true;\n if (EFFORT_RE.test(t)) return true;\n if (CLAUDE_STATUS_BAR_RE.test(t)) return true;\n if (QUEUED_MSG_RE.test(t)) return true;\n if (CURSOR_ARTIFACT_RE.test(t)) return true;\n // cursor-agent patterns\n if (CURSOR_GENERATING_RE.test(t)) return true;\n if (CURSOR_FOOTER_RE.test(t)) return true;\n if (CURSOR_STATUS_RE.test(t)) return true;\n if (CURSOR_MODEL_RE.test(t)) return true;\n if (BOX_DRAWING_RE.test(t) && t.length > 1) return true;\n // Claude Code stop hook / session-end patterns\n if (STOP_HOOK_RE.test(t)) return true;\n if (WORKED_SUMMARY_RE.test(t)) return true;\n if (CLAUDE_BANNER_RE.test(t) && t.length > 1) return true;\n if (CLAUDE_BANNER_INFO_RE.test(t)) return true;\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Timer pause/resume for user-input waiting\n// ---------------------------------------------------------------------------\n\n/**\n * Manages timer pause/resume when an interactive dialog is forwarded to the\n * user. Tracks cumulative wall-clock consumption so the remaining budget is\n * correctly restored after the user responds.\n */\nexport class InputWaitController {\n private _waiting = false;\n private wallClockUsedMs = 0;\n private wallClockSegmentStart: number;\n\n constructor(private readonly totalBudgetMs: number) {\n this.wallClockSegmentStart = Date.now();\n }\n\n get waiting(): boolean { return this._waiting; }\n\n pause(): void {\n if (this._waiting) return;\n this._waiting = true;\n this.wallClockUsedMs += Date.now() - this.wallClockSegmentStart;\n }\n\n resume(): void {\n if (!this._waiting) return;\n this._waiting = false;\n this.wallClockSegmentStart = Date.now();\n }\n\n get remainingMs(): number {\n return Math.max(this.totalBudgetMs - this.wallClockUsedMs, 60_000);\n }\n\n get usedMs(): number {\n return this.wallClockUsedMs;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Completion detection result\n// ---------------------------------------------------------------------------\n\ninterface CompletionResult {\n output: string;\n timedOut: boolean;\n timeoutType?: 'wall-clock' | 'idle';\n /** Whether the agent was actively producing output when timeout fired */\n wasActiveAtTimeout?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Session tracking\n// ---------------------------------------------------------------------------\n\ninterface PtySessionInfo {\n sessionId: string;\n agentMode: string;\n /** Current interactive mode (e.g. 'bypass', 'plan', 'accept-edits'). Tracked\n * so that mode-cycle keys are only sent when the mode actually needs to change. */\n currentMode: string;\n /** The mode this session was launched with via CLI args (undefined = agent default) */\n startedWithMode?: string;\n}\n\n// ---------------------------------------------------------------------------\n// PtyRunner\n// ---------------------------------------------------------------------------\n\n/**\n * AIRunner implementation that drives persistent interactive AI agent\n * terminal sessions (PTY) per issue.\n *\n * Supports multiple agent types (claude-internal, codebuddy, cursor-agent).\n * Each phase can use a different agent; sessions are reused when the agent\n * doesn't change between consecutive phases, and recreated when it does.\n */\nexport class PtyRunner implements AIRunner {\n /** workDir → active session info (sessionId + current agent type) */\n private sessions = new Map<string, PtySessionInfo>();\n\n /** Sessions that were forcefully killed (via killByWorkDir/killAll). Checked by\n * detectCompletion's onExit handler to report failure instead of success. */\n private killedSessions = new Set<string>();\n\n constructor(\n private readonly nvmNodeVersion: string,\n private readonly terminalManager: TerminalManager,\n private readonly defaultAgentMode: string,\n private readonly phaseAgentMap: Record<string, string>,\n private readonly globalModel?: string,\n private readonly idleDetectMs: number = 30_000,\n ) {}\n\n // ---- AIRunner interface ---------------------------------------------------\n\n async run(options: RunOptions): Promise<RunResult> {\n if (isShuttingDown()) {\n logger.warn('PtyRunner skipped — service is shutting down');\n return { success: false, output: 'Service shutting down', exitCode: null };\n }\n\n const { prompt, workDir, timeoutMs, onStreamEvent, phaseName } = options;\n const agentMode = this.resolveAgentForPhase(phaseName);\n const startMode = options.mode; // 'plan' | undefined currently; extensible for future modes\n const continueSession = options.continueSession ?? false;\n\n logger.info('PtyRunner.run()', { workDir, timeoutMs, phaseName, agentMode, continueSession });\n\n // 1. Ensure persistent PTY session (creates or reuses based on agent match)\n const { sessionId, isNew } = this.ensureSession(workDir, agentMode, startMode);\n\n // 2. Wait for Claude Code to be ready before sending commands.\n // New sessions need time to initialize (banner, config loading, etc.).\n if (isNew) {\n logger.info('Waiting for AI agent prompt (new session)', { sessionId, phaseName });\n // Custom agents (e.g. claude-internal) may show banners/notices before\n // entering interactive mode. Allow up to 5 min for first-time startup.\n await this.waitForPrompt(sessionId, 300_000);\n } else if (continueSession) {\n // Continue-session mode: the agent may still be working from a previous\n // timeout. Use a longer wait (30s) to detect if it has settled to idle.\n logger.info('Waiting for AI agent prompt (continue-session)', { sessionId, phaseName });\n await this.waitForPrompt(sessionId, 30_000);\n } else {\n // Reused session may have been interrupted (Ctrl+C) by retryFromPhase.\n // Brief wait for the agent to settle back to idle prompt.\n logger.info('Waiting for AI agent prompt (reused session)', { sessionId, phaseName });\n await this.waitForPrompt(sessionId, 10_000);\n }\n\n // 2.5 Native plan mode: two-phase execution\n // Phase 1: switch to plan mode → execute plan prompt (no artifact gate)\n // Phase 2: switch to bypass → commit plan to disk file (with artifact gate)\n if (startMode === 'plan' && this.shouldUseNativePlan(agentMode)) {\n return this.runNativePlanMode(sessionId, isNew, options, agentMode, workDir);\n }\n\n // Continue-session path: skip /clear and new prompt, just wait for the\n // agent to finish whatever it was doing before the timeout.\n if (continueSession && !isNew) {\n logger.info('Continue-session mode: attaching detectCompletion (no /clear)', { sessionId });\n const result = await this.detectCompletion(sessionId, options, onStreamEvent);\n logger.info('PtyRunner continue-session completed', {\n workDir, agentMode, phaseName,\n timedOut: result.timedOut,\n outputLength: result.output.length,\n });\n return this.buildRunResult(result, sessionId);\n }\n\n // 2.5 Toggle interactive mode if needed (Shift+Tab fallback for reused sessions)\n await this.ensurePlanMode(sessionId, agentMode, startMode === 'plan', workDir);\n\n // 3. Send /clear to reset context.\n // /clear only resets conversation history — the ❯ prompt stays on screen\n // and is NOT re-emitted by the PTY (TUI sends delta updates only).\n // A brief delay is sufficient for the command to process.\n await this.writeCommand(sessionId, '/clear', agentMode);\n await new Promise(resolve => setTimeout(resolve, 2_000));\n\n // 4. Write prompt to file\n const promptFile = this.writePromptFile(workDir, prompt);\n\n // 5. Send instruction to read the prompt file\n const instruction = `Please read and follow all instructions in ${promptFile}`;\n await this.writeCommand(sessionId, instruction, agentMode);\n\n // 6. Wait for completion\n const result = await this.detectCompletion(sessionId, options, onStreamEvent);\n\n logger.info('PtyRunner phase completed', {\n workDir,\n agentMode,\n phaseName,\n timedOut: result.timedOut,\n outputLength: result.output.length,\n });\n\n return this.buildRunResult(result, sessionId);\n }\n\n killAll(): void {\n for (const [, info] of this.sessions) {\n this.killedSessions.add(info.sessionId);\n this.terminalManager.destroy(info.sessionId);\n }\n this.sessions.clear();\n logger.info('PtyRunner: all managed sessions destroyed');\n }\n\n killByWorkDir(targetWorkDir: string): number {\n const info = this.sessions.get(targetWorkDir);\n if (!info) return 0;\n this.killedSessions.add(info.sessionId);\n this.terminalManager.destroy(info.sessionId);\n this.sessions.delete(targetWorkDir);\n return 1;\n }\n\n interruptByWorkDir(targetWorkDir: string): boolean {\n const info = this.sessions.get(targetWorkDir);\n if (!info) return false;\n\n const session = this.terminalManager.get(info.sessionId);\n if (!session) {\n // Session died externally — clean up tracking\n this.sessions.delete(targetWorkDir);\n return false;\n }\n\n // Send Ctrl+C to interrupt any running task without destroying the session\n this.terminalManager.write(info.sessionId, '\\x03');\n logger.info('Interrupted PTY session for retry', {\n workDir: targetWorkDir,\n sessionId: info.sessionId,\n });\n return true;\n }\n\n // ---- Agent resolution ----------------------------------------------------\n\n /** Get the Enter key sequence for the given agent mode */\n private getEnterKey(agentMode: string): string {\n const profile = getPtyProfile(agentMode);\n return profile?.enterKey ?? '\\r';\n }\n\n /**\n * Write a command to the PTY and press Enter.\n *\n * Some agents (codebuddy) use input coalescing in their TUI: when text and\n * Enter arrive in the same PTY write, the Enter is treated as a newline\n * character (paste) instead of triggering message submission. For these\n * agents (PtyProfile.separateEnter=true), text and Enter are sent in\n * separate writes with a 150ms gap so the Enter key is recognized as\n * a standalone key press.\n */\n private async writeCommand(sessionId: string, text: string, agentMode: string): Promise<void> {\n const enterKey = this.getEnterKey(agentMode);\n const profile = getPtyProfile(agentMode);\n\n if (profile?.separateEnter) {\n this.terminalManager.write(sessionId, text);\n await new Promise(resolve => setTimeout(resolve, 150));\n this.terminalManager.write(sessionId, enterKey);\n } else {\n this.terminalManager.write(sessionId, text + enterKey);\n }\n }\n\n /** Resolve agent mode for a given phase (fallback to default) */\n private resolveAgentForPhase(phaseName?: string): string {\n if (phaseName && this.phaseAgentMap[phaseName]) {\n return this.phaseAgentMap[phaseName];\n }\n return this.defaultAgentMode;\n }\n\n /** Look up PtyProfile from registry + resolve agent-specific binary and model */\n private resolveProfileAndModel(agentMode: string): {\n profile: PtyProfile;\n model: string | undefined;\n binary: string;\n } {\n const profile = getPtyProfile(agentMode);\n if (!profile) {\n throw new Error(\n `Agent \"${agentMode}\" has no PtyProfile — not supported in PTY mode. ` +\n `Compatible agents: claude-internal, codebuddy, cursor-agent`,\n );\n }\n const entry = getRegistryEntry(agentMode);\n const binary = (entry && process.env[entry.binaryEnvKey]) || entry?.defaultBinary || agentMode;\n const model = this.globalModel\n ? resolveModelForRunner(agentMode, this.globalModel)\n : undefined;\n return { profile, model, binary };\n }\n\n // ---- Session management ---------------------------------------------------\n\n private ensureSession(workDir: string, agentMode: string, startMode?: string): { sessionId: string; isNew: boolean } {\n const existing = this.sessions.get(workDir);\n\n // Agent switched → destroy old session\n if (existing && existing.agentMode !== agentMode) {\n logger.info('Agent switched, destroying old PTY session', {\n workDir,\n oldAgent: existing.agentMode,\n newAgent: agentMode,\n sessionId: existing.sessionId,\n });\n this.terminalManager.destroy(existing.sessionId);\n this.sessions.delete(workDir);\n }\n\n // Same agent → reuse if alive\n if (existing && existing.agentMode === agentMode) {\n if (this.terminalManager.get(existing.sessionId)) {\n logger.info('Reusing existing PTY session (same agent)', {\n workDir,\n agentMode,\n sessionId: existing.sessionId,\n });\n return { sessionId: existing.sessionId, isNew: false };\n }\n // Session died externally, clean up tracking\n this.sessions.delete(workDir);\n }\n\n // Check for orphaned sessions (e.g. from previous run or WebSocket)\n // Cannot safely reuse — we don't know what agent spawned it\n const orphan = this.terminalManager.findByWorkDir(workDir);\n if (orphan) {\n logger.info('Destroying orphaned PTY session on workDir', {\n workDir,\n sessionId: orphan.id,\n });\n this.terminalManager.destroy(orphan.id);\n }\n\n // Create new managed session with the resolved agent's binary and args\n const { profile, model, binary } = this.resolveProfileAndModel(agentMode);\n const args = profile.buildPtyArgs({ model, startMode });\n const issueIid = extractIidFromPath(workDir);\n\n const info = this.terminalManager.create({\n workDir,\n issueIid,\n command: binary,\n args,\n managed: true,\n });\n\n this.sessions.set(workDir, {\n sessionId: info.id,\n agentMode,\n // Always set initial mode to defaultModeName — CLI args always use bypass\n // (startMode is applied at runtime via Shift+Tab in ensurePlanMode).\n currentMode: profile.defaultModeName ?? 'bypass',\n startedWithMode: startMode,\n });\n logger.info('Created new PTY session', {\n workDir,\n agentMode,\n binary,\n args,\n sessionId: info.id,\n pid: info.pid,\n issueIid,\n });\n return { sessionId: info.id, isNew: true };\n }\n\n // ---- Prompt delivery ------------------------------------------------------\n\n /**\n * Wait for the AI agent to show its idle prompt (❯ or >), indicating\n * it is ready to accept input. Used before sending /clear and instructions\n * to prevent commands from arriving before the agent is initialized.\n */\n private waitForPrompt(sessionId: string, timeoutMs: number = 60_000): Promise<void> {\n return new Promise<void>((resolve) => {\n let promptSeen = false;\n let trustDialogHandled = false;\n let stabilityTimer: ReturnType<typeof setTimeout> | undefined;\n // After a prompt is first detected, wait this long with no new output\n // to confirm the agent is truly idle (not still initialising — e.g.\n // MCP auth, IDE extension install can arrive after the ❯ prompt renders).\n const STABILITY_MS = 3_000;\n\n // Secondary: TUI status bar (e.g. \"bypass permissions on (shift+tab to cycle)\")\n // is a strong signal that the input area is fully rendered. The ❯ prompt\n // exists in the same data frame but isIdlePrompt may fail on full-screen\n // renders where ❯ is embedded among other TUI content.\n const TUI_READY_RE = /bypass\\s*permissions|shift\\+?\\s*tab\\s*to\\s*cycle/i;\n let tuiReady = false;\n\n // Fallback: Claude Code TUI may render the ❯ prompt via cursor-positioning\n // sequences that don't produce a discrete onData event. When the banner has\n // been shown and the PTY goes silent, treat the agent as ready.\n let bannerSeen = false;\n let silenceTimer: ReturnType<typeof setTimeout> | undefined;\n const SILENCE_READY_MS = 8_000;\n\n const timer = setTimeout(() => {\n if (stabilityTimer) clearTimeout(stabilityTimer);\n if (silenceTimer) clearTimeout(silenceTimer);\n subscription.dispose();\n logger.warn('Timed out waiting for AI agent prompt', { sessionId, timeoutMs });\n // Resolve instead of reject — best-effort, let the run proceed\n resolve();\n }, timeoutMs);\n\n const done = (reason: string) => {\n clearTimeout(timer);\n if (stabilityTimer) clearTimeout(stabilityTimer);\n if (silenceTimer) clearTimeout(silenceTimer);\n subscription.dispose();\n logger.info('AI agent prompt detected', { sessionId, reason });\n resolve();\n };\n\n const resetSilenceTimer = () => {\n if (!bannerSeen) return;\n if (silenceTimer) clearTimeout(silenceTimer);\n silenceTimer = setTimeout(() => {\n if (promptSeen) return; // Primary detection already succeeded\n logger.info('Banner shown and PTY silent — treating agent as ready', { sessionId });\n done('silence-after-banner');\n }, SILENCE_READY_MS);\n };\n\n const subscription = this.terminalManager.onData(sessionId, (data: string) => {\n const stripped = stripAnsi(data);\n\n // Classify TUI noise (consistent with detectCompletion)\n const nonEmptyLines = stripped.split('\\n').filter((l) => l.trim());\n const isNoise = nonEmptyLines.length === 0 || nonEmptyLines.every((l) => isTuiNoise(l));\n\n // Detect Claude Code banner to enable silence-based readiness fallback.\n if (!bannerSeen && /Claude\\s*Code/i.test(stripped)) {\n bannerSeen = true;\n }\n\n // Reset silence timer on every data event (noise or not) — we want to\n // detect true silence (no PTY output at all), not filtered silence.\n resetSilenceTimer();\n\n // Auto-confirm workspace trust dialog in new worktree directories.\n // TUI cursor positioning removes spaces after ANSI stripping, so use\n // \\s* between words to match both \"trust this folder\" and \"trustthisfolder\".\n if (!trustDialogHandled && TRUST_DIALOG_RE.test(stripped)) {\n trustDialogHandled = true;\n logger.info('Trust dialog detected, auto-confirming', { sessionId });\n setTimeout(() => this.terminalManager.write(sessionId, '\\r'), 500);\n }\n\n // Auto-approve permission/confirmation dialogs that appear during\n // startup or between phases (e.g. leftover prompt from a previous task).\n if (PERMISSION_DIALOG_RE.test(stripped)) {\n logger.info('Permission dialog detected in waitForPrompt, auto-confirming', { sessionId });\n setTimeout(() => this.terminalManager.write(sessionId, '\\r'), 500);\n }\n\n if (isIdlePrompt(stripped)) {\n promptSeen = true;\n }\n\n if (!tuiReady && TUI_READY_RE.test(stripped)) {\n tuiReady = true;\n }\n\n // Once the agent is considered ready (idle prompt detected OR TUI status\n // bar rendered), (re)start a stability timer.\n // CRITICAL: TUI noise (spinners, status bar ~100ms updates) must NOT\n // reset the timer — otherwise it can never complete. Only substantive\n // output (e.g. MCP auth dialog, extension install notice) resets it.\n if (promptSeen || tuiReady) {\n if (stabilityTimer && isNoise) {\n // Already waiting — noise-only frame, let the timer continue\n } else {\n if (stabilityTimer) clearTimeout(stabilityTimer);\n const reason = promptSeen ? 'idle-prompt-stable' : 'status-bar-ready';\n stabilityTimer = setTimeout(() => done(reason), STABILITY_MS);\n }\n }\n });\n });\n }\n\n // ---- Interactive mode switching --------------------------------------------\n\n /**\n * Switch the PTY session to (or away from) plan mode by pressing the mode\n * cycle key (Shift+Tab) until the target mode is detected in the output.\n *\n * This is a no-op when the agent has no modeCycleKey configured, or the\n * session is already in the desired mode.\n */\n private async ensurePlanMode(\n sessionId: string,\n agentMode: string,\n wantPlan: boolean,\n workDir: string,\n ): Promise<void> {\n const profile = getPtyProfile(agentMode);\n if (!profile?.modeCycleKey || !profile.detectMode || !profile.planModeName) return;\n\n const session = this.sessions.get(workDir);\n if (!session) return;\n\n const targetMode = wantPlan ? profile.planModeName : (profile.defaultModeName ?? 'bypass');\n if (session.currentMode === targetMode) {\n logger.info('PTY already in target mode', { sessionId, targetMode });\n return;\n }\n\n const MAX_ATTEMPTS = 5;\n for (let i = 0; i < MAX_ATTEMPTS; i++) {\n // Start collecting BEFORE sending the key — the mode indicator is emitted\n // within milliseconds of the key press and would be missed if we waited.\n const outputPromise = this.collectRecentOutput(sessionId, 3_000);\n this.terminalManager.write(sessionId, profile.modeCycleKey);\n const recentOutput = await outputPromise;\n\n const detected = profile.detectMode(recentOutput);\n\n if (detected) {\n session.currentMode = detected;\n if (detected === targetMode) {\n logger.info('PTY mode switched', {\n sessionId, agentMode, targetMode, attempts: i + 1,\n });\n return;\n }\n }\n }\n\n logger.warn('Failed to switch PTY mode after max attempts', {\n sessionId, agentMode, targetMode, current: session.currentMode,\n });\n }\n\n /**\n * Briefly subscribe to PTY output and collect all data emitted during a\n * window. Used by ensurePlanMode to read the mode indicator after pressing\n * the cycle key.\n */\n private collectRecentOutput(sessionId: string, durationMs: number): Promise<string> {\n return new Promise(resolve => {\n const chunks: string[] = [];\n const subscription = this.terminalManager.onData(sessionId, (data: string) => {\n chunks.push(stripAnsi(data));\n });\n setTimeout(() => {\n subscription.dispose();\n resolve(chunks.join(''));\n }, durationMs);\n });\n }\n\n // ---- Native plan mode (two-phase execution) --------------------------------\n\n /**\n * Whether the agent supports the two-phase native plan strategy:\n * bypass start → Shift+Tab to plan → execute → Shift+Tab to bypass → commit file.\n *\n * Conditions: runner does NOT natively handle plan mode on its own (nativePlanMode=false)\n * AND the PTY profile supports interactive mode switching (modeCycleKey + planModeName).\n */\n private shouldUseNativePlan(agentMode: string): boolean {\n const caps = getRunnerCapabilities(agentMode);\n const profile = getPtyProfile(agentMode);\n return !caps?.nativePlanMode && !!profile?.modeCycleKey && !!profile?.planModeName;\n }\n\n /**\n * Two-phase plan execution:\n * Phase 1 — Switch to plan mode, send plan prompt, wait for idle (no artifact gate).\n * Phase 2 — Deterministic copy: resolve plan file from CLI-native storage\n * (~/.claude/plans/) and copy it to the expected artifact path.\n *\n * Phase 2 was previously prompt-driven (\"ask agent to write file\"), which was\n * unreliable because /clear wiped the agent's context. Now it uses PlanFileResolver\n * to find the plan file by snapshot diffing + content validation.\n */\n private async runNativePlanMode(\n sessionId: string,\n isNew: boolean,\n options: RunOptions,\n agentMode: string,\n workDir: string,\n ): Promise<RunResult> {\n const continueSession = options.continueSession && !isNew;\n\n // ── Phase 2 prep: snapshot plan directory before Phase 1 starts ──\n const resolver = PlanFileResolver.forRunner(agentMode);\n resolver.takeBeforeSnapshot();\n\n const issueIid = extractIidFromPath(workDir);\n const contentHint = issueIid\n ? PlanFileResolver.buildContentHint(issueIid)\n : undefined;\n\n // ── Phase 1: native plan mode ──\n if (continueSession) {\n logger.info('Native plan mode: continue-session (no /clear, no prompt)', { sessionId });\n } else {\n logger.info('Native plan mode: switching to plan', { sessionId, agentMode });\n await this.ensurePlanMode(sessionId, agentMode, true, workDir);\n\n if (!isNew) {\n await this.writeCommand(sessionId, '/clear', agentMode);\n await new Promise(resolve => setTimeout(resolve, 2_000));\n }\n\n const promptFile = this.writePromptFile(workDir, options.prompt);\n await this.writeCommand(\n sessionId,\n `Please read and follow all instructions in ${promptFile}`,\n agentMode,\n );\n }\n\n // Primary signal: plan confirmation dialog (\"Claude has written up a plan\n // and is ready to execute\"). Secondary: artifact file check as debounce fallback.\n const planArtifactCheck = () => resolver.hasNewOrModifiedFiles();\n\n const planResult = await this.detectCompletion(sessionId, {\n ...options,\n completionSignal: PLAN_CONFIRM_RE,\n artifactCheck: planArtifactCheck,\n }, options.onStreamEvent, continueSession);\n\n if (planResult.timedOut) {\n logger.warn('Native plan mode: plan phase timed out', {\n sessionId, wasActive: planResult.wasActiveAtTimeout,\n });\n return this.buildRunResult(planResult, sessionId);\n }\n\n // ── Phase 2: deterministic plan file copy ──\n logger.info('Native plan mode: resolving plan file from CLI storage', { sessionId });\n\n const resolved = resolver.resolve(contentHint);\n\n if (!resolved) {\n logger.error('Native plan mode: no plan file found in CLI storage', { sessionId, workDir });\n return {\n success: false,\n output: planResult.output,\n errorMessage: 'Plan 阶段完成但未在 CLI 计划目录中找到计划文件',\n sessionId,\n exitCode: null,\n };\n }\n\n // Copy resolved plan file to all expected artifact paths\n const artifactPaths = options.artifactPaths ?? [];\n if (artifactPaths.length > 0) {\n for (const targetPath of artifactPaths) {\n const targetDir = path.dirname(targetPath);\n if (!fs.existsSync(targetDir)) {\n fs.mkdirSync(targetDir, { recursive: true });\n }\n fs.writeFileSync(targetPath, resolved.content, 'utf-8');\n logger.info('Plan file copied to artifact path', {\n source: path.basename(resolved.sourcePath),\n target: targetPath,\n size: resolved.content.length,\n });\n }\n } else {\n logger.warn('Native plan mode: no artifactPaths specified, plan file not copied', {\n sessionId,\n resolvedFile: resolved.sourcePath,\n });\n }\n\n logger.info('Native plan mode completed (deterministic copy)', {\n sessionId,\n planSource: path.basename(resolved.sourcePath),\n artifactsCopied: artifactPaths.length,\n });\n\n return this.buildRunResult(planResult, sessionId);\n }\n\n /** Map detectCompletion result to RunResult. */\n private buildRunResult(\n detectResult: CompletionResult,\n sessionId: string,\n ): RunResult {\n return {\n success: !detectResult.timedOut,\n output: detectResult.output,\n errorMessage: detectResult.timedOut\n ? (detectResult.timeoutType === 'idle' ? 'AI 长时间无响应,已超时终止' : '执行超时')\n : undefined,\n sessionId,\n exitCode: detectResult.timedOut ? null : 0,\n timeoutType: detectResult.timeoutType,\n wasActiveAtTimeout: detectResult.wasActiveAtTimeout,\n };\n }\n\n private writePromptFile(workDir: string, prompt: string): string {\n const dir = path.join(workDir, '.claude-plan');\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n const gitignorePath = path.join(dir, '.gitignore');\n const entry = '.phase-prompt.md';\n if (!fs.existsSync(gitignorePath) || !fs.readFileSync(gitignorePath, 'utf-8').includes(entry)) {\n fs.appendFileSync(gitignorePath, `${entry}\\n`, 'utf-8');\n }\n\n const relPath = '.claude-plan/.phase-prompt.md';\n const absPath = path.join(workDir, relPath);\n fs.writeFileSync(absPath, prompt, 'utf-8');\n return relPath;\n }\n\n // ---- Completion detection -------------------------------------------------\n\n private detectCompletion(\n sessionId: string,\n options: RunOptions,\n onStreamEvent?: (event: StreamEvent) => void,\n skipEchoDetection?: boolean,\n ): Promise<CompletionResult> {\n return new Promise<CompletionResult>((resolve) => {\n const outputLines: string[] = [];\n let lastOutputTime = Date.now();\n let hasSubstantiveOutput = false;\n let echoConsumed = skipEchoDetection ?? false;\n let resolved = false;\n let debounceTimer: ReturnType<typeof setTimeout> | undefined;\n const DIALOG_BUFFER_MAX = 40;\n const dialogBuffer: string[] = [];\n let dialogHandled = false;\n // Low-confidence dialog quiesce: wait for silence before forwarding.\n const DIALOG_QUIESCE_MS = 2_500;\n let dialogQuiesceTimer: ReturnType<typeof setTimeout> | undefined;\n let pendingDialogParsed: { question: string; options: DialogOption[] } | null = null;\n // Primary completion signal: isIdlePrompt() detects Claude Code's ❯ prompt.\n // The idle check below is a safety-net for truly stuck sessions — uses the\n // AI-level idle timeout (default 20 min) instead of the short PTY_IDLE_DETECT_MS.\n const idleTimeoutMs = options.idleTimeoutMs ?? 600_000;\n const timeoutMs = options.timeoutMs;\n\n // Timer pause/resume for user-input waiting: when an interactive dialog is\n // forwarded to the frontend, we pause both wall-clock and idle timers so\n // that waiting for human response is not counted as a timeout.\n const inputWait = new InputWaitController(timeoutMs);\n\n const pauseTimersForInput = () => {\n if (inputWait.waiting) return;\n inputWait.pause();\n clearTimeout(wallTimer);\n logger.info('Timers paused — waiting for user input', {\n sessionId, wallClockUsedMs: inputWait.usedMs,\n });\n };\n\n const resumeTimersAfterInput = () => {\n if (!inputWait.waiting) return;\n inputWait.resume();\n lastOutputTime = Date.now();\n wallTimer = scheduleWallTimer(inputWait.remainingMs);\n logger.info('Timers resumed after user input', {\n sessionId, remainingMs: inputWait.remainingMs, wallClockUsedMs: inputWait.usedMs,\n });\n };\n\n // Progressive wall-clock timeout: if the agent is still actively producing\n // output when the wall-clock timer fires, extend the deadline instead of\n // immediately failing. Prevents the timeout→retry→/clear→restart death loop.\n const GRACE_WINDOW_MS = options.timeoutGraceMs ?? 60_000;\n const EXTENSION_MS = options.timeoutExtensionMs ?? 600_000;\n const MAX_EXTENSIONS = options.timeoutMaxExtensions ?? 3;\n let extensions = 0;\n\n const finish = (result: CompletionResult) => {\n if (resolved) return;\n resolved = true;\n cleanup();\n resolve(result);\n };\n\n // Wall-clock timeout with progressive extension\n const scheduleWallTimer = (delayMs: number): ReturnType<typeof setTimeout> => {\n return setTimeout(() => {\n if (inputWait.waiting) return;\n const recentMs = Date.now() - lastOutputTime;\n const isActive = hasSubstantiveOutput && recentMs < GRACE_WINDOW_MS;\n if (isActive && extensions < MAX_EXTENSIONS) {\n extensions++;\n logger.info('Wall-clock timeout extended (agent still active)', {\n sessionId, extensions, maxExtensions: MAX_EXTENSIONS,\n lastOutputAgoMs: recentMs,\n });\n wallTimer = scheduleWallTimer(EXTENSION_MS);\n return;\n }\n finish({\n output: outputLines.join(''),\n timedOut: true,\n timeoutType: 'wall-clock',\n wasActiveAtTimeout: isActive,\n });\n }, delayMs);\n };\n let wallTimer = scheduleWallTimer(timeoutMs);\n\n // Idle safety-net: if no output for a long time, treat as idle timeout (failure).\n // The primary \"task done\" signal is isIdlePrompt() below, not this check.\n const idleCheck = setInterval(() => {\n if (inputWait.waiting) return;\n if (!hasSubstantiveOutput) return;\n if (Date.now() - lastOutputTime >= idleTimeoutMs) {\n finish({\n output: outputLines.join(''),\n timedOut: true,\n timeoutType: 'idle',\n wasActiveAtTimeout: false,\n });\n }\n }, 5_000);\n\n // Hook-based dialog detection (PreToolUse → AskUserQuestion)\n const dialogClassifier = new DialogClassifier();\n let hookWatcher: HookEventWatcher | undefined;\n let hookSub: { dispose(): void } | undefined;\n\n const hookInjector = new HookInjector();\n const eventsFilePath = hookInjector.getEventsFilePath(options.workDir);\n if (fs.existsSync(eventsFilePath)) {\n hookWatcher = new HookEventWatcher(eventsFilePath);\n hookSub = hookWatcher.onEvent((event) => {\n dialogClassifier.ingestHookEvent(event);\n\n if (event.event === 'ask_user_question' && options.onInputRequired && !dialogHandled && !resolved) {\n const askData = dialogClassifier.consumePendingAskUser();\n if (askData) {\n dialogHandled = true;\n dialogBuffer.length = 0;\n if (dialogQuiesceTimer) { clearTimeout(dialogQuiesceTimer); dialogQuiesceTimer = undefined; }\n pauseTimersForInput();\n logger.info('AskUserQuestion detected via hook (high confidence, zero delay)', {\n sessionId, question: askData.question.slice(0, 80),\n optionCount: askData.options.length,\n });\n options.onInputRequired({\n type: 'interactive-dialog',\n content: askData.question,\n options: askData.options,\n }).then((response) => {\n resumeTimersAfterInput();\n dialogHandled = false;\n if (!resolved && response) {\n this.terminalManager.write(sessionId, response + '\\r');\n }\n }).catch((err) => {\n logger.warn('onInputRequired callback failed (hook-based)', {\n error: (err as Error).message,\n });\n resumeTimersAfterInput();\n dialogHandled = false;\n });\n }\n }\n });\n hookWatcher.start();\n }\n\n // Subscribe to PTY output\n const subscription = this.terminalManager.onData(sessionId, (data: string) => {\n if (resolved) return;\n\n const stripped = stripAnsi(data);\n\n // Classify TUI noise (spinners, status bar, separators, etc.)\n const nonEmptyLines = stripped.split('\\n').filter((l) => l.trim());\n const isNoise = nonEmptyLines.length === 0 || nonEmptyLines.every((l) => isTuiNoise(l));\n const isIdle = isIdlePrompt(stripped);\n\n // Detect \"mixed frames\": the TUI co-renders ❯ with active work content\n // (tool results, file listings, thinking indicators) in a single data\n // chunk. These are NOT truly idle — the agent is still working.\n const isMixedFrame = isIdle && containsActiveWork(stripped);\n\n // Update last-activity timestamp for real content.\n // - Pure noise (spinners) → no update (would prevent debounce)\n // - Pure idle (❯ only) → no update (debounce needs to expire)\n // - Mixed frame (❯ + work content) → DO update (agent is still active)\n // - Normal content → update\n const hasRealContent = !isNoise && stripped.trim().length > 0;\n if (hasRealContent && (!isIdle || isMixedFrame)) {\n lastOutputTime = Date.now();\n // Cancel low-confidence dialog quiesce — new output means the agent\n // is still working and the detected numbered list was not a real dialog.\n if (dialogQuiesceTimer) {\n clearTimeout(dialogQuiesceTimer);\n dialogQuiesceTimer = undefined;\n pendingDialogParsed = null;\n logger.info('Dialog quiesce cancelled — new substantive output arrived', { sessionId });\n }\n }\n\n // Skip the echo of our instruction\n if (!echoConsumed && stripped.includes('.phase-prompt.md')) {\n echoConsumed = true;\n return;\n }\n\n // Queued message indicator — the agent has pending input but is still\n // processing. This is NOT an idle state and must not trigger completion.\n if (QUEUED_MSG_RE.test(stripped)) {\n return;\n }\n\n // Definitive completion signal (e.g. plan confirmation dialog).\n // Checked BEFORE PERMISSION_DIALOG_RE — the plan confirm dialog\n // contains \"❯ 1. Yes\" which would otherwise trigger auto-confirm\n // and start code execution prematurely.\n if (options.completionSignal && hasSubstantiveOutput && options.completionSignal.test(stripped)) {\n logger.info('Completion signal detected', { sessionId });\n finish({ output: outputLines.join(''), timedOut: false });\n return;\n }\n\n // Claude Code session-end summary (e.g. \"✻Worked for 9m39s\" / \"✻Cogitated for 3m37s\").\n // This is a strong built-in completion signal — the agent has finished\n // and Claude Code is displaying its session summary before returning\n // to idle. Finish immediately instead of waiting for the debounce,\n // which can be blocked by subsequent stop-hook / banner output.\n //\n // Gate on artifactCheck: the broadened regex may also match mid-session\n // thinking summaries (e.g. after the first tool call). The artifact\n // gate prevents premature completion when the phase output file has\n // not been written yet.\n if (hasSubstantiveOutput && WORKED_SUMMARY_RE.test(stripped)) {\n const artifactReady = options.artifactCheck ? options.artifactCheck() : true;\n if (artifactReady) {\n logger.info('Session-end summary detected, finishing', { sessionId });\n finish({ output: outputLines.join(''), timedOut: false });\n return;\n }\n logger.info('Session summary detected but artifacts not ready, continuing', { sessionId });\n }\n\n // Auto-approve permission/confirmation dialogs (plan mode safety prompts).\n // Must be checked BEFORE isIdlePrompt — the ❯ in \"❯ 1. Yes\" would\n // otherwise trigger idle detection and start a premature debounce.\n if (PERMISSION_DIALOG_RE.test(stripped)) {\n logger.info('Permission dialog detected, auto-confirming', { sessionId });\n setTimeout(() => {\n if (!resolved) this.terminalManager.write(sessionId, '\\r');\n }, 500);\n return; // Don't count dialog frames as substantive output or idle prompts\n }\n\n // Interactive dialog (brainstorming interview, etc.) — forward to\n // frontend for user decision via onInputRequired callback.\n // Uses confidence scoring to avoid false positives from agent numbered lists.\n // When hook-based detection is active, regex is suppressed to avoid duplicates.\n if (options.onInputRequired && !dialogHandled && !dialogClassifier.shouldSuppressRegex() && isInteractiveDialog(stripped)) {\n const parsed = parseInteractiveDialog(stripped);\n if (parsed) {\n const confidence = getDialogConfidence(stripped);\n if (confidence === 'high') {\n dialogHandled = true;\n dialogBuffer.length = 0;\n if (dialogQuiesceTimer) { clearTimeout(dialogQuiesceTimer); dialogQuiesceTimer = undefined; }\n pauseTimersForInput();\n logger.info('Interactive dialog detected (high confidence), forwarding to handler', {\n sessionId, question: parsed.question.slice(0, 80),\n optionCount: parsed.options.length,\n });\n options.onInputRequired({\n type: 'interactive-dialog',\n content: parsed.question,\n options: parsed.options,\n }).then((response) => {\n resumeTimersAfterInput();\n dialogHandled = false;\n if (!resolved && response) {\n this.terminalManager.write(sessionId, response + '\\r');\n }\n }).catch((err) => {\n logger.warn('onInputRequired callback failed for interactive dialog', {\n error: (err as Error).message,\n });\n resumeTimersAfterInput();\n dialogHandled = false;\n });\n return;\n }\n // Low confidence: start quiesce timer — only forward if no new\n // substantive output arrives within the window.\n if (!dialogQuiesceTimer) {\n pendingDialogParsed = parsed;\n logger.info('Interactive dialog detected (low confidence), starting quiesce', {\n sessionId, question: parsed.question.slice(0, 80),\n optionCount: parsed.options.length,\n });\n dialogQuiesceTimer = setTimeout(() => {\n dialogQuiesceTimer = undefined;\n if (resolved || dialogHandled || !pendingDialogParsed) return;\n dialogHandled = true;\n dialogBuffer.length = 0;\n const dp = pendingDialogParsed;\n pendingDialogParsed = null;\n pauseTimersForInput();\n logger.info('Dialog quiesce elapsed — forwarding low-confidence dialog', {\n sessionId, question: dp.question.slice(0, 80),\n });\n options.onInputRequired!({\n type: 'interactive-dialog',\n content: dp.question,\n options: dp.options,\n }).then((response) => {\n resumeTimersAfterInput();\n dialogHandled = false;\n if (!resolved && response) {\n this.terminalManager.write(sessionId, response + '\\r');\n }\n }).catch((err) => {\n logger.warn('onInputRequired callback failed (quiesced dialog)', {\n error: (err as Error).message,\n });\n resumeTimersAfterInput();\n dialogHandled = false;\n });\n }, DIALOG_QUIESCE_MS);\n }\n return;\n }\n }\n\n // Substantive output must be non-noise (spinners don't count)\n if (echoConsumed && !hasSubstantiveOutput && !isNoise && stripped.trim().length > 0) {\n hasSubstantiveOutput = true;\n }\n\n outputLines.push(stripped);\n\n // Emit stream events for the dashboard (filter TUI noise)\n if (onStreamEvent) {\n for (const line of stripped.split('\\n').filter((l) => l.trim())) {\n if (!isTuiNoise(line)) {\n onStreamEvent({\n type: 'raw',\n content: line,\n timestamp: new Date().toISOString(),\n });\n }\n }\n }\n\n // Accumulate non-noise lines for cross-frame dialog detection.\n // The per-frame check above may miss dialogs split across PTY chunks.\n if (!dialogHandled && !isNoise) {\n for (const line of stripped.split('\\n').filter((l) => l.trim())) {\n if (!isTuiNoise(line)) {\n dialogBuffer.push(line);\n if (dialogBuffer.length > DIALOG_BUFFER_MAX) dialogBuffer.shift();\n }\n }\n }\n\n // Check for Claude Code idle prompt\n if (hasSubstantiveOutput && isIdlePrompt(stripped)) {\n // Mixed frame: ❯ co-rendered with active work (tool results, etc.)\n // This is NOT a genuine idle state — skip debounce entirely.\n if (isMixedFrame) {\n // Agent is still working — do not start debounce\n } else if (options.completionSignal) {\n // completionSignal is configured — idle prompt is NOT a valid\n // completion path. Only the signal itself (e.g. PLAN_CONFIRM_RE)\n // can trigger successful finish. Wall-clock and idle timeouts\n // still act as failure safety nets.\n logger.debug('Idle prompt ignored (waiting for completionSignal)', { sessionId });\n } else if (debounceTimer && isNoise) {\n // CRITICAL: If a debounce is already pending and this is a pure-noise\n // idle frame (TUI refresh with ❯), skip entirely. Otherwise the TUI's\n // continuous idle-prompt stream (every ~100ms) would clear+reschedule\n // the timer infinitely, preventing it from ever completing.\n } else {\n // Cross-frame dialog detection: the per-frame check may have missed\n // a dialog whose options arrived in separate PTY data chunks. Before\n // starting the idle debounce, check the accumulated buffer.\n if (options.onInputRequired && !dialogHandled && !dialogClassifier.shouldSuppressRegex() && dialogBuffer.length >= 2) {\n const combined = dialogBuffer.join('\\n');\n if (isInteractiveDialog(combined)) {\n const parsed = parseInteractiveDialog(combined);\n if (parsed) {\n const bufConfidence = getDialogConfidence(combined);\n if (bufConfidence === 'high') {\n dialogHandled = true;\n dialogBuffer.length = 0;\n if (dialogQuiesceTimer) { clearTimeout(dialogQuiesceTimer); dialogQuiesceTimer = undefined; }\n pauseTimersForInput();\n logger.info('Interactive dialog detected via accumulated buffer (high confidence)', {\n sessionId, question: parsed.question.slice(0, 80),\n optionCount: parsed.options.length,\n });\n options.onInputRequired({\n type: 'interactive-dialog',\n content: parsed.question,\n options: parsed.options,\n }).then((response) => {\n resumeTimersAfterInput();\n dialogHandled = false;\n if (!resolved && response) {\n this.terminalManager.write(sessionId, response + '\\r');\n }\n }).catch((err) => {\n logger.warn('onInputRequired callback failed (accumulated)', {\n error: (err as Error).message,\n });\n resumeTimersAfterInput();\n dialogHandled = false;\n });\n return;\n }\n // Low confidence via buffer: since we're already at an idle\n // prompt, this is likely genuine — but still use quiesce to\n // prevent false positives from accumulated numbered output.\n if (!dialogQuiesceTimer) {\n pendingDialogParsed = parsed;\n logger.info('Dialog detected via buffer (low confidence), starting quiesce', {\n sessionId, question: parsed.question.slice(0, 80),\n });\n dialogQuiesceTimer = setTimeout(() => {\n dialogQuiesceTimer = undefined;\n if (resolved || dialogHandled || !pendingDialogParsed) return;\n dialogHandled = true;\n dialogBuffer.length = 0;\n const dp = pendingDialogParsed;\n pendingDialogParsed = null;\n pauseTimersForInput();\n logger.info('Buffer dialog quiesce elapsed — forwarding', { sessionId });\n options.onInputRequired!({\n type: 'interactive-dialog',\n content: dp.question,\n options: dp.options,\n }).then((response) => {\n resumeTimersAfterInput();\n dialogHandled = false;\n if (!resolved && response) {\n this.terminalManager.write(sessionId, response + '\\r');\n }\n }).catch((err) => {\n logger.warn('onInputRequired callback failed (buffer quiesced)', {\n error: (err as Error).message,\n });\n resumeTimersAfterInput();\n dialogHandled = false;\n });\n }, DIALOG_QUIESCE_MS);\n }\n }\n }\n }\n\n // Real idle prompt (first one, or after real content interrupted debounce).\n // Start or restart the debounce timer.\n if (debounceTimer) clearTimeout(debounceTimer);\n const scheduleDebounce = () => {\n debounceTimer = setTimeout(() => {\n if (resolved) return;\n const recentActivityMs = Date.now() - lastOutputTime;\n if (recentActivityMs < 5_000) {\n // Recent activity — retry rather than giving up\n scheduleDebounce();\n return;\n }\n\n // Artifact hard-gate: if artifacts are expected but not ready, keep waiting\n const artifactReady = options.artifactCheck ? options.artifactCheck() : true;\n if (!artifactReady) {\n logger.info('Idle prompt detected but artifacts not ready, continuing to wait', {\n sessionId,\n });\n scheduleDebounce();\n return;\n }\n\n finish({\n output: outputLines.join(''),\n timedOut: false,\n timeoutType: undefined,\n });\n }, 5_000);\n };\n scheduleDebounce();\n }\n }\n });\n\n // PTY exit handler — Claude process crashed, was killed, or exited\n // after a stop hook. Always clean up the session mapping to prevent\n // ensureSession from trying to reuse a dead PTY.\n this.terminalManager.onExit(sessionId, (exitCode: number) => {\n const wasKilled = this.killedSessions.delete(sessionId);\n\n // Always remove the dead session from tracking\n for (const [wd, info] of this.sessions) {\n if (info.sessionId === sessionId) {\n this.sessions.delete(wd);\n break;\n }\n }\n\n if (!resolved) {\n logger.warn('PTY process exited during phase', { sessionId, exitCode, wasKilled });\n finish({\n output: outputLines.join(''),\n timedOut: wasKilled,\n timeoutType: wasKilled ? 'wall-clock' : undefined,\n });\n } else {\n // detectCompletion already resolved (e.g. via idle debounce or\n // Worked-for signal). The PTY exited afterwards (stop hook exit).\n // Session mapping already cleaned above — no further action needed.\n logger.info('PTY exited after phase completion (post stop-hook)', { sessionId, exitCode });\n }\n });\n\n const cleanup = () => {\n clearTimeout(wallTimer);\n clearInterval(idleCheck);\n if (debounceTimer) clearTimeout(debounceTimer);\n if (dialogQuiesceTimer) clearTimeout(dialogQuiesceTimer);\n subscription.dispose();\n hookSub?.dispose();\n hookWatcher?.stop();\n dialogClassifier.reset();\n };\n });\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { logger as rootLogger } from '../logger.js';\n\nconst logger = rootLogger.child('PlanFileResolver');\n\nexport interface ResolvedPlanFile {\n /** Absolute path to the plan file in the CLI plans directory */\n sourcePath: string;\n /** File content */\n content: string;\n}\n\n/** Known CLI plan directory paths by runner mode */\nconst PLAN_DIRS: Record<string, string> = {\n 'claude-internal': path.join(os.homedir(), '.claude-internal', 'plans'),\n 'codebuddy': path.join(os.homedir(), '.codebuddy', 'plans'),\n};\n\n/**\n * Resolves the correct plan file from CLI-native plan storage (e.g. ~/.claude/plans/)\n * after a plan-mode execution. Uses snapshot diffing (before/after file lists) with\n * optional content-based validation to handle concurrent plan generation.\n */\nexport class PlanFileResolver {\n private readonly plansDir: string;\n private beforeFiles: Map<string, number> = new Map();\n\n constructor(plansDir?: string) {\n this.plansDir = plansDir ?? path.join(os.homedir(), '.claude-internal', 'plans');\n }\n\n /** Create a resolver for a specific runner mode (e.g. 'claude-internal', 'codebuddy'). */\n static forRunner(agentMode: string): PlanFileResolver {\n const dir = PLAN_DIRS[agentMode];\n return new PlanFileResolver(dir);\n }\n\n /** Take a snapshot of existing plan files before the plan phase starts. */\n takeBeforeSnapshot(): void {\n this.beforeFiles = this.listFiles();\n logger.info('Plan file snapshot taken', {\n dir: this.plansDir,\n fileCount: this.beforeFiles.size,\n });\n }\n\n /**\n * Check if any new or modified plan files exist since the before-snapshot.\n * Used as an artifact gate by detectCompletion to prevent premature\n * completion when the agent is still exploring/thinking.\n */\n hasNewOrModifiedFiles(): boolean {\n const afterFiles = this.listFiles();\n for (const [filePath, mtime] of afterFiles) {\n const beforeMtime = this.beforeFiles.get(filePath);\n if (beforeMtime === undefined || mtime > beforeMtime) return true;\n }\n return false;\n }\n\n /**\n * After plan phase completes, find the newly created plan file.\n *\n * Strategy:\n * 1. Diff against beforeSnapshot to find new files\n * 2. Sort candidates by mtime (newest first)\n * 3. If contentHint provided, prefer the file whose content matches\n * 4. Return the best candidate\n *\n * @param contentHint - Optional string (e.g. issue IID or title) to validate content\n */\n resolve(contentHint?: string): ResolvedPlanFile | null {\n if (!fs.existsSync(this.plansDir)) {\n logger.warn('Plans directory does not exist', { dir: this.plansDir });\n return null;\n }\n\n const afterFiles = this.listFiles();\n const candidates = this.findNewFiles(afterFiles);\n\n if (candidates.length === 0) {\n logger.warn('No new plan files found after plan phase', {\n dir: this.plansDir,\n beforeCount: this.beforeFiles.size,\n afterCount: afterFiles.size,\n });\n return this.fallbackByMtime(afterFiles, contentHint);\n }\n\n if (candidates.length === 1) {\n return this.readCandidate(candidates[0].path, candidates[0].mtime);\n }\n\n // Multiple new files — try content matching first\n if (contentHint) {\n const matched = this.matchByContent(candidates, contentHint);\n if (matched) return matched;\n }\n\n // Fall back to most recent\n candidates.sort((a, b) => b.mtime - a.mtime);\n logger.info('Multiple new plan files found, using most recent', {\n count: candidates.length,\n selected: path.basename(candidates[0].path),\n });\n return this.readCandidate(candidates[0].path, candidates[0].mtime);\n }\n\n /**\n * Build a content hint string from issue metadata for content-based matching.\n */\n static buildContentHint(issueIid: number, issueTitle?: string): string {\n const parts = [`#${issueIid}`, `${issueIid}`];\n if (issueTitle) parts.push(issueTitle);\n return parts.join('|');\n }\n\n private listFiles(): Map<string, number> {\n const result = new Map<string, number>();\n if (!fs.existsSync(this.plansDir)) return result;\n\n try {\n const entries = fs.readdirSync(this.plansDir);\n for (const entry of entries) {\n if (!entry.endsWith('.md')) continue;\n const fullPath = path.join(this.plansDir, entry);\n try {\n const stat = fs.statSync(fullPath);\n if (stat.isFile()) {\n result.set(fullPath, stat.mtimeMs);\n }\n } catch {\n // skip unreadable files\n }\n }\n } catch (err) {\n logger.warn('Failed to list plans directory', { dir: this.plansDir, err });\n }\n return result;\n }\n\n private findNewFiles(afterFiles: Map<string, number>): Array<{ path: string; mtime: number }> {\n const candidates: Array<{ path: string; mtime: number }> = [];\n for (const [filePath, mtime] of afterFiles) {\n if (!this.beforeFiles.has(filePath)) {\n candidates.push({ path: filePath, mtime });\n }\n }\n return candidates;\n }\n\n /**\n * Fallback: if no new files found (rare case — plan might have overwritten\n * an existing file), find the most recently modified file.\n */\n private fallbackByMtime(\n afterFiles: Map<string, number>,\n contentHint?: string,\n ): ResolvedPlanFile | null {\n if (afterFiles.size === 0) return null;\n\n const sorted = [...afterFiles.entries()].sort((a, b) => b[1] - a[1]);\n\n if (contentHint) {\n for (const [filePath] of sorted.slice(0, 5)) {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n if (this.contentMatches(content, contentHint)) {\n logger.info('Fallback: found plan file by content match', {\n file: path.basename(filePath),\n });\n return { sourcePath: filePath, content };\n }\n } catch {\n continue;\n }\n }\n }\n\n const [newestPath, newestMtime] = sorted[0];\n logger.info('Fallback: using most recently modified plan file', {\n file: path.basename(newestPath),\n ageMs: Date.now() - newestMtime,\n });\n return this.readCandidate(newestPath, newestMtime);\n }\n\n private matchByContent(\n candidates: Array<{ path: string; mtime: number }>,\n contentHint: string,\n ): ResolvedPlanFile | null {\n for (const candidate of candidates) {\n try {\n const content = fs.readFileSync(candidate.path, 'utf-8');\n if (this.contentMatches(content, contentHint)) {\n logger.info('Plan file matched by content', {\n file: path.basename(candidate.path),\n hint: contentHint.slice(0, 50),\n });\n return { sourcePath: candidate.path, content };\n }\n } catch {\n continue;\n }\n }\n return null;\n }\n\n private contentMatches(content: string, hint: string): boolean {\n const parts = hint.split('|');\n return parts.some(part => content.includes(part));\n }\n\n private readCandidate(filePath: string, mtime: number): ResolvedPlanFile | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n logger.info('Resolved plan file', {\n file: path.basename(filePath),\n size: content.length,\n ageMs: Date.now() - mtime,\n });\n return { sourcePath: filePath, content };\n } catch (err) {\n logger.error('Failed to read plan file', { filePath, err });\n return null;\n }\n }\n}\n","import type { HookEvent, AskUserQuestionOption } from '../hooks/HookInjector.js';\nimport type { DialogOption } from './AIRunner.js';\nimport { logger as rootLogger } from '../logger.js';\n\nconst logger = rootLogger.child('DialogClassifier');\n\nconst HOOK_EVENT_TTL_MS = 5_000;\nconst PERMISSION_SUPPRESS_MS = 2_000;\nconst ASK_USER_SUPPRESS_MS = 3_000;\n\ninterface TimedEvent {\n event: HookEvent;\n receivedAt: number;\n}\n\nexport interface AskUserData {\n question: string;\n options: DialogOption[];\n}\n\n/**\n * Dual-channel dialog classifier: combines hook events (high confidence)\n * with regex detection (fallback) to identify interactive dialogs.\n *\n * Hook channel: PreToolUse(AskUserQuestion) provides structured question+options\n * before TUI rendering. Notification(permission_prompt) suppresses false positives.\n *\n * Regex channel: existing parseInteractiveDialog/isInteractiveDialog logic\n * as fallback for runners that don't support hooks.\n */\nexport class DialogClassifier {\n private recentEvents: TimedEvent[] = [];\n private handledIds = new Set<string>();\n\n ingestHookEvent(event: HookEvent): void {\n this.recentEvents.push({ event, receivedAt: Date.now() });\n this.pruneExpired();\n\n if (event.event === 'ask_user_question') {\n logger.info('Received ask_user_question hook event', {\n question: event.question.slice(0, 80),\n optionCount: event.options.length,\n });\n } else if (event.event === 'notification') {\n logger.debug('Received notification hook event', {\n type: event.notification_type,\n });\n }\n }\n\n /**\n * Check if there's a pending AskUserQuestion from hooks that hasn't been\n * handled yet. Returns structured data or null.\n */\n consumePendingAskUser(): AskUserData | null {\n const now = Date.now();\n for (let i = this.recentEvents.length - 1; i >= 0; i--) {\n const { event, receivedAt } = this.recentEvents[i];\n if (event.event !== 'ask_user_question') continue;\n if (now - receivedAt > HOOK_EVENT_TTL_MS) continue;\n\n const eventId = `ask_user_${event.ts}`;\n if (this.handledIds.has(eventId)) continue;\n\n this.handledIds.add(eventId);\n return {\n question: event.question,\n options: mapHookOptions(event.options),\n };\n }\n return null;\n }\n\n /**\n * Whether a recent permission_prompt notification exists, indicating\n * that regex-detected dialogs are likely false positives.\n */\n hasRecentPermissionPrompt(): boolean {\n const now = Date.now();\n return this.recentEvents.some(({ event, receivedAt }) =>\n event.event === 'notification'\n && event.notification_type === 'permission_prompt'\n && now - receivedAt < PERMISSION_SUPPRESS_MS,\n );\n }\n\n /**\n * Whether a recent ask_user_question hook event exists, indicating\n * that regex detection should be suppressed to avoid duplicate forwarding.\n */\n hasRecentAskUser(): boolean {\n const now = Date.now();\n return this.recentEvents.some(({ event, receivedAt }) =>\n event.event === 'ask_user_question'\n && now - receivedAt < ASK_USER_SUPPRESS_MS,\n );\n }\n\n /**\n * Whether regex-based dialog detection should be suppressed\n * (either due to recent hook-based AskUser or permission_prompt).\n */\n shouldSuppressRegex(): boolean {\n return this.hasRecentAskUser() || this.hasRecentPermissionPrompt();\n }\n\n reset(): void {\n this.recentEvents.length = 0;\n this.handledIds.clear();\n }\n\n private pruneExpired(): void {\n const cutoff = Date.now() - HOOK_EVENT_TTL_MS;\n this.recentEvents = this.recentEvents.filter(e => e.receivedAt > cutoff);\n }\n}\n\nfunction mapHookOptions(hookOptions: AskUserQuestionOption[]): DialogOption[] {\n return hookOptions.map((o, i) => ({\n index: o.index ?? i + 1,\n label: o.label,\n }));\n}\n","import fs from 'node:fs';\nimport type { HookEvent } from './HookInjector.js';\nimport { logger as rootLogger } from '../logger.js';\n\nconst logger = rootLogger.child('HookEventWatcher');\n\nexport interface Disposable {\n dispose(): void;\n}\n\n/**\n * 监听 hook 事件文件(JSONL),解析新增行并回调。\n *\n * 使用 fs.watch + 文件偏移量追踪实现增量读取。\n * 当 fs.watch 不可靠时(某些网络文件系统),通过定期 poll 兜底。\n */\nexport class HookEventWatcher {\n private readonly eventsFile: string;\n private watcher: fs.FSWatcher | null = null;\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private offset = 0;\n private listeners: Array<(event: HookEvent) => void> = [];\n private started = false;\n\n constructor(eventsFile: string) {\n this.eventsFile = eventsFile;\n }\n\n start(): void {\n if (this.started) return;\n this.started = true;\n\n this.offset = this.getCurrentSize();\n\n try {\n this.watcher = fs.watch(this.eventsFile, () => this.readNewEvents());\n } catch {\n logger.debug('fs.watch unavailable, using poll-only mode');\n }\n\n this.pollTimer = setInterval(() => this.readNewEvents(), 1_000);\n }\n\n stop(): void {\n if (!this.started) return;\n this.started = false;\n this.watcher?.close();\n this.watcher = null;\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n this.listeners = [];\n }\n\n onEvent(callback: (event: HookEvent) => void): Disposable {\n this.listeners.push(callback);\n return {\n dispose: () => {\n this.listeners = this.listeners.filter(l => l !== callback);\n },\n };\n }\n\n /**\n * 等待指定类型的事件,带超时。\n * 对于 'stop' 事件,只有 blocked=false 时才 resolve。\n */\n waitForEvent(\n eventType: HookEvent['event'],\n timeoutMs: number,\n ): Promise<HookEvent> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n sub.dispose();\n reject(new Error(`Timeout waiting for hook event \"${eventType}\" after ${timeoutMs}ms`));\n }, timeoutMs);\n\n const sub = this.onEvent((ev) => {\n if (ev.event !== eventType) return;\n if (ev.event === 'stop' && ev.blocked) return;\n clearTimeout(timer);\n sub.dispose();\n resolve(ev);\n });\n });\n }\n\n /** 获取已记录的所有产物写入事件摘要 */\n getArtifactSummary(): string {\n const events = this.readAll().filter(\n (e): e is Extract<HookEvent, { event: 'artifact_write' }> => e.event === 'artifact_write',\n );\n if (events.length === 0) return '';\n return events.map(e => `${e.file} (${e.bytes} bytes)`).join(', ');\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private readNewEvents(): void {\n if (!this.started) return;\n\n const size = this.getCurrentSize();\n if (size <= this.offset) return;\n\n try {\n const fd = fs.openSync(this.eventsFile, 'r');\n try {\n const buf = Buffer.alloc(size - this.offset);\n fs.readSync(fd, buf, 0, buf.length, this.offset);\n this.offset = size;\n\n const chunk = buf.toString('utf-8');\n for (const line of chunk.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const event = JSON.parse(trimmed) as HookEvent;\n this.emit(event);\n } catch {\n logger.debug('Skipping malformed event line', { line: trimmed });\n }\n }\n } finally {\n fs.closeSync(fd);\n }\n } catch (err) {\n logger.debug('Error reading events file', { error: (err as Error).message });\n }\n }\n\n private readAll(): HookEvent[] {\n if (!fs.existsSync(this.eventsFile)) return [];\n try {\n const content = fs.readFileSync(this.eventsFile, 'utf-8').trim();\n if (!content) return [];\n return content.split('\\n').reduce<HookEvent[]>((acc, line) => {\n const trimmed = line.trim();\n if (!trimmed) return acc;\n try {\n acc.push(JSON.parse(trimmed) as HookEvent);\n } catch { /* skip */ }\n return acc;\n }, []);\n } catch {\n return [];\n }\n }\n\n private emit(event: HookEvent): void {\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (err) {\n logger.warn('Event listener error', { error: (err as Error).message });\n }\n }\n }\n\n private getCurrentSize(): number {\n try {\n return fs.statSync(this.eventsFile).size;\n } catch {\n return 0;\n }\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { logger as rootLogger } from '../logger.js';\n\nconst logger = rootLogger.child('HookInjector');\n\nconst HOOKS_DIR = '.claude-plan/.hooks';\nconst EVENTS_FILE_NAME = '.hook-events.jsonl';\nconst MANIFEST_FILE_NAME = '.artifact-manifest.jsonl';\nconst CONTEXT_FILE_NAME = '.hook-context.json';\n\nexport interface HookContext {\n workDir: string;\n planDir: string;\n /** All pipeline artifacts (used by PostToolUse to track writes across phases). */\n expectedArtifacts: string[];\n /** Current phase artifacts only (used by Stop hook to gate completion). Falls back to expectedArtifacts if omitted. */\n phaseExpectedArtifacts?: string[];\n issueIid: number;\n phaseName?: string;\n issueTitle?: string;\n issueDescription?: string;\n}\n\nexport interface ArtifactEntry {\n ts: string;\n file: string;\n path: string;\n bytes: number;\n event: 'write';\n}\n\nexport interface StopEntry {\n ts: string;\n event: 'stop';\n blocked: boolean;\n missing?: string;\n}\n\nexport type ManifestEntry = ArtifactEntry | StopEntry;\n\n/**\n * 在 worktree 中动态注入 Claude Code hooks。\n *\n * 在 SetupStep 中调用 inject(),生成:\n * - {workDir}/.claude/settings.local.json(合并已有 permissions + 追加 hooks)\n * - {workDir}/.claude-plan/.hooks/*.sh(hook 脚本,上下文以绝对路径嵌入)\n * - {workDir}/.claude-plan/.hook-context.json(阶段上下文,供 compact restore 读取)\n *\n * Claude Code PTY 启动后自动加载 settings.local.json 中的 hooks。\n */\nexport class HookInjector {\n inject(ctx: HookContext): void {\n this.writeHookScripts(ctx);\n this.writeContextFile(ctx);\n this.writeSettingsLocal(ctx);\n this.initEventsFile(ctx);\n logger.info('Hooks injected', {\n workDir: ctx.workDir,\n issueIid: ctx.issueIid,\n phase: ctx.phaseName,\n artifacts: ctx.expectedArtifacts,\n });\n }\n\n /**\n * 阶段切换时更新 hooks 配置(重写脚本 + settings.local.json)。\n * 保留 events/manifest 文件(不截断),仅更新脚本和配置。\n */\n updateForPhase(ctx: HookContext): void {\n this.writeHookScripts(ctx);\n this.writeContextFile(ctx);\n this.writeSettingsLocal(ctx);\n logger.info('Hooks updated for phase', {\n workDir: ctx.workDir,\n issueIid: ctx.issueIid,\n phase: ctx.phaseName,\n });\n }\n\n readManifest(workDir: string): ManifestEntry[] {\n const manifestPath = path.join(workDir, '.claude-plan', MANIFEST_FILE_NAME);\n return readJsonl<ManifestEntry>(manifestPath);\n }\n\n readEvents(workDir: string): HookEvent[] {\n const eventsPath = path.join(workDir, '.claude-plan', EVENTS_FILE_NAME);\n return readJsonl<HookEvent>(eventsPath);\n }\n\n getEventsFilePath(workDir: string): string {\n return path.join(workDir, '.claude-plan', EVENTS_FILE_NAME);\n }\n\n getManifestFilePath(workDir: string): string {\n return path.join(workDir, '.claude-plan', MANIFEST_FILE_NAME);\n }\n\n cleanup(workDir: string): void {\n const hooksDir = path.join(workDir, HOOKS_DIR);\n try {\n if (fs.existsSync(hooksDir)) {\n fs.rmSync(hooksDir, { recursive: true });\n }\n } catch (err) {\n logger.warn('Failed to cleanup hooks', { error: (err as Error).message });\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private writeHookScripts(ctx: HookContext): void {\n const hooksDir = path.join(ctx.workDir, HOOKS_DIR);\n fs.mkdirSync(hooksDir, { recursive: true });\n\n const eventsFile = path.join(ctx.workDir, '.claude-plan', EVENTS_FILE_NAME);\n const manifestFile = path.join(ctx.workDir, '.claude-plan', MANIFEST_FILE_NAME);\n const contextFile = path.join(ctx.workDir, '.claude-plan', CONTEXT_FILE_NAME);\n const expected = ctx.expectedArtifacts.join(',');\n const phaseExpected = (ctx.phaseExpectedArtifacts ?? ctx.expectedArtifacts).join(',');\n\n const scripts: Array<{ name: string; content: string }> = [\n { name: 'session-start.sh', content: buildSessionStartScript(eventsFile) },\n { name: 'compact-restore.sh', content: buildCompactRestoreScript(eventsFile, contextFile) },\n { name: 'post-tool-use.sh', content: buildPostToolUseScript(eventsFile, manifestFile, expected) },\n { name: 'post-artifact.sh', content: buildPostArtifactScript(manifestFile, expected) },\n { name: 'exit-plan-mode.sh', content: buildExitPlanModeScript(eventsFile) },\n { name: 'permission.sh', content: buildPermissionScript(eventsFile) },\n { name: 'protect-files.sh', content: buildProtectFilesScript(eventsFile, ctx.phaseName, ctx.planDir) },\n { name: 'stop.sh', content: buildStopScript(eventsFile, ctx.planDir, phaseExpected) },\n { name: 'ask-user-hook.sh', content: buildAskUserHookScript(eventsFile) },\n { name: 'notification.sh', content: buildNotificationScript(eventsFile) },\n ];\n\n for (const { name, content } of scripts) {\n const scriptPath = path.join(hooksDir, name);\n fs.writeFileSync(scriptPath, content, { mode: 0o755 });\n }\n }\n\n private writeContextFile(ctx: HookContext): void {\n const contextPath = path.join(ctx.workDir, '.claude-plan', CONTEXT_FILE_NAME);\n const context = {\n issueIid: ctx.issueIid,\n issueTitle: ctx.issueTitle ?? '',\n issueDescription: ctx.issueDescription ?? '',\n phaseName: ctx.phaseName ?? '',\n expectedArtifacts: ctx.expectedArtifacts,\n planDir: ctx.planDir,\n };\n fs.writeFileSync(contextPath, JSON.stringify(context, null, 2), 'utf-8');\n }\n\n private writeSettingsLocal(ctx: HookContext): void {\n const claudeDir = path.join(ctx.workDir, '.claude');\n fs.mkdirSync(claudeDir, { recursive: true });\n\n const settingsPath = path.join(claudeDir, 'settings.local.json');\n let existing: Record<string, unknown> = {};\n if (fs.existsSync(settingsPath)) {\n try {\n existing = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));\n } catch {\n logger.warn('Failed to parse existing settings.local.json, overwriting');\n }\n }\n\n const hooksDir = path.join(ctx.workDir, HOOKS_DIR);\n const hooks = buildHooksConfig(hooksDir, ctx);\n\n const merged = { ...existing, hooks };\n fs.writeFileSync(settingsPath, JSON.stringify(merged, null, 2), 'utf-8');\n }\n\n private initEventsFile(ctx: HookContext): void {\n const eventsPath = path.join(ctx.workDir, '.claude-plan', EVENTS_FILE_NAME);\n const manifestPath = path.join(ctx.workDir, '.claude-plan', MANIFEST_FILE_NAME);\n fs.writeFileSync(eventsPath, '', 'utf-8');\n fs.writeFileSync(manifestPath, '', 'utf-8');\n }\n}\n\n// ---------------------------------------------------------------------------\n// Hook event types (used by HookEventWatcher)\n// ---------------------------------------------------------------------------\n\nexport interface AskUserQuestionOption {\n index: number;\n label: string;\n}\n\nexport type HookEvent =\n | { ts: string; event: 'session_start'; session_id: string }\n | { ts: string; event: 'compact_restore' }\n | { ts: string; event: 'artifact_write'; file: string; path: string; bytes: number }\n | { ts: string; event: 'permission_request'; tool: string }\n | { ts: string; event: 'exit_plan_mode' }\n | { ts: string; event: 'protect_blocked'; tool: string; file: string }\n | { ts: string; event: 'stop'; blocked: boolean; missing?: string }\n | { ts: string; event: 'ask_user_question'; question: string; options: AskUserQuestionOption[] }\n | { ts: string; event: 'notification'; notification_type: string; message: string };\n\n// ---------------------------------------------------------------------------\n// Hook config builder — phase-aware\n// ---------------------------------------------------------------------------\n\ninterface HookEntry {\n type: string;\n command: string;\n timeout: number;\n if?: string;\n}\n\ninterface HookGroup {\n matcher?: string;\n hooks: HookEntry[];\n}\n\nfunction buildHooksConfig(\n hooksDir: string,\n ctx: HookContext,\n): Record<string, HookGroup[]> {\n const isPlanPhase = ctx.phaseName === 'plan';\n const artifactIfPatterns = buildArtifactIfPatterns(ctx.expectedArtifacts);\n\n const config: Record<string, HookGroup[]> = {\n SessionStart: [\n {\n hooks: [{\n type: 'command',\n command: path.join(hooksDir, 'session-start.sh'),\n timeout: 5,\n }],\n },\n {\n matcher: 'compact',\n hooks: [{\n type: 'command',\n command: path.join(hooksDir, 'compact-restore.sh'),\n timeout: 5,\n }],\n },\n ],\n\n PreToolUse: [\n {\n matcher: 'AskUserQuestion',\n hooks: [{\n type: 'command',\n command: path.join(hooksDir, 'ask-user-hook.sh'),\n timeout: 5,\n }],\n },\n {\n matcher: 'Edit|Write',\n hooks: [{\n type: 'command',\n command: path.join(hooksDir, 'protect-files.sh'),\n timeout: 5,\n ...buildProtectIfClause(ctx.phaseName),\n }],\n },\n ],\n\n PostToolUse: buildPostToolUseConfig(hooksDir, artifactIfPatterns),\n\n PermissionRequest: buildPermissionRequestConfig(hooksDir, isPlanPhase),\n\n Notification: [{\n hooks: [{\n type: 'command',\n command: path.join(hooksDir, 'notification.sh'),\n timeout: 5,\n }],\n }],\n\n Stop: [{\n hooks: [{\n type: 'command',\n command: path.join(hooksDir, 'stop.sh'),\n timeout: 15,\n }],\n }],\n };\n\n return config;\n}\n\nfunction buildPermissionRequestConfig(\n hooksDir: string,\n isPlanPhase: boolean,\n): HookGroup[] {\n const groups: HookGroup[] = [];\n\n if (isPlanPhase) {\n groups.push({\n matcher: 'ExitPlanMode',\n hooks: [{\n type: 'command',\n command: path.join(hooksDir, 'exit-plan-mode.sh'),\n timeout: 5,\n }],\n });\n }\n\n groups.push({\n matcher: 'Bash|Edit|Write|Read|Glob|Grep|WebFetch|WebSearch|mcp__.*',\n hooks: [{\n type: 'command',\n command: path.join(hooksDir, 'permission.sh'),\n timeout: 5,\n }],\n });\n\n return groups;\n}\n\nfunction buildPostToolUseConfig(\n hooksDir: string,\n artifactIfPatterns: string | undefined,\n): HookGroup[] {\n const groups: HookGroup[] = [];\n\n if (artifactIfPatterns) {\n groups.push({\n matcher: 'Write|Edit',\n hooks: [{\n type: 'command',\n command: path.join(hooksDir, 'post-artifact.sh'),\n timeout: 10,\n if: artifactIfPatterns,\n }],\n });\n }\n\n groups.push({\n matcher: 'Write|Edit',\n hooks: [{\n type: 'command',\n command: path.join(hooksDir, 'post-tool-use.sh'),\n timeout: 10,\n }],\n });\n\n return groups;\n}\n\n/**\n * Build `if` clause patterns for artifact files.\n * e.g. \"Write(*01-plan.md)|Edit(*01-plan.md)|Write(*02-verify-report.md)|Edit(*02-verify-report.md)\"\n */\nfunction buildArtifactIfPatterns(artifacts: string[]): string | undefined {\n if (artifacts.length === 0) return undefined;\n return artifacts\n .flatMap(f => [`Write(*${f})`, `Edit(*${f})`])\n .join('|');\n}\n\n/**\n * Build `if` clause for PreToolUse protect hook.\n * Protects sensitive files (.env, lock files) from direct edits.\n */\nfunction buildProtectIfClause(_phaseName?: string): { if?: string } {\n const alwaysProtected = ['.env', '.env.*', 'package-lock.json', 'pnpm-lock.yaml'];\n const ifValue = alwaysProtected\n .flatMap(f => [`Edit(*${f})`, `Write(*${f})`])\n .join('|');\n return { if: ifValue };\n}\n\n// ---------------------------------------------------------------------------\n// Script templates\n// ---------------------------------------------------------------------------\n\nfunction buildSessionStartScript(eventsFile: string): string {\n return `#!/bin/bash\nset -euo pipefail\nINPUT=$(cat)\nSESSION_ID=$(echo \"$INPUT\" | jq -r '.session_id // empty')\nprintf '{\"ts\":\"%s\",\"event\":\"session_start\",\"session_id\":\"%s\"}\\\\n' \\\\\n \"$(date -u +%FT%TZ)\" \"$SESSION_ID\" >> ${quote(eventsFile)}\nexit 0\n`;\n}\n\nfunction buildCompactRestoreScript(\n eventsFile: string,\n contextFile: string,\n): string {\n return `#!/bin/bash\nset -euo pipefail\n\nCONTEXT_FILE=${quote(contextFile)}\nif [ ! -f \"$CONTEXT_FILE\" ]; then\n exit 0\nfi\n\nISSUE_IID=$(jq -r '.issueIid // empty' < \"$CONTEXT_FILE\")\nISSUE_TITLE=$(jq -r '.issueTitle // empty' < \"$CONTEXT_FILE\")\nISSUE_DESC=$(jq -r '.issueDescription // empty' < \"$CONTEXT_FILE\")\nPHASE=$(jq -r '.phaseName // empty' < \"$CONTEXT_FILE\")\nPLAN_DIR=$(jq -r '.planDir // empty' < \"$CONTEXT_FILE\")\nARTIFACTS=$(jq -r '.expectedArtifacts | join(\", \") // empty' < \"$CONTEXT_FILE\")\n\nREADY=\"\"\nMISSING=\"\"\nfor f in $(jq -r '.expectedArtifacts[]' < \"$CONTEXT_FILE\" 2>/dev/null); do\n FPATH=\"$PLAN_DIR/$f\"\n if [ -f \"$FPATH\" ] && [ \"$(wc -c < \"$FPATH\")\" -ge 50 ]; then\n READY=\"$READY $f\"\n else\n MISSING=\"$MISSING $f\"\n fi\ndone\nREADY=$(echo \"$READY\" | xargs)\nMISSING=$(echo \"$MISSING\" | xargs)\n\nprintf '{\"ts\":\"%s\",\"event\":\"compact_restore\"}\\\\n' \"$(date -u +%FT%TZ)\" >> ${quote(eventsFile)}\n\ncat <<CONTEXT\n[上下文恢复 — compaction 后自动注入]\nIssue #$ISSUE_IID: $ISSUE_TITLE\n当前阶段: $PHASE\n预期产物: $ARTIFACTS\n已就绪: \\${READY:-无}\n未完成: \\${MISSING:-无}\n\n需求描述:\n$ISSUE_DESC\nCONTEXT\nexit 0\n`;\n}\n\nfunction buildPostToolUseScript(\n eventsFile: string,\n manifestFile: string,\n expected: string,\n): string {\n return `#!/bin/bash\nset -euo pipefail\nINPUT=$(cat)\nFILE_PATH=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // empty')\n[ -z \"$FILE_PATH\" ] && exit 0\n\nEXPECTED=${quote(expected)}\nBASENAME=$(basename \"$FILE_PATH\")\n\nif echo \"$EXPECTED\" | tr ',' '\\\\n' | grep -qx \"$BASENAME\"; then\n BYTES=$(wc -c < \"$FILE_PATH\" 2>/dev/null || echo 0)\n printf '{\"ts\":\"%s\",\"event\":\"artifact_write\",\"file\":\"%s\",\"path\":\"%s\",\"bytes\":%s}\\\\n' \\\\\n \"$(date -u +%FT%TZ)\" \"$BASENAME\" \"$FILE_PATH\" \"$BYTES\" >> ${quote(manifestFile)}\nfi\n\nprintf '{\"ts\":\"%s\",\"event\":\"artifact_write\",\"file\":\"%s\",\"path\":\"%s\",\"bytes\":0}\\\\n' \\\\\n \"$(date -u +%FT%TZ)\" \"$BASENAME\" \"$FILE_PATH\" >> ${quote(eventsFile)}\nexit 0\n`;\n}\n\nfunction buildPostArtifactScript(\n manifestFile: string,\n expected: string,\n): string {\n return `#!/bin/bash\nset -euo pipefail\nINPUT=$(cat)\nFILE_PATH=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // empty')\n[ -z \"$FILE_PATH\" ] && exit 0\n\nEXPECTED=${quote(expected)}\nBASENAME=$(basename \"$FILE_PATH\")\n\nif echo \"$EXPECTED\" | tr ',' '\\\\n' | grep -qx \"$BASENAME\"; then\n BYTES=$(wc -c < \"$FILE_PATH\" 2>/dev/null || echo 0)\n printf '{\"ts\":\"%s\",\"event\":\"write\",\"file\":\"%s\",\"path\":\"%s\",\"bytes\":%s}\\\\n' \\\\\n \"$(date -u +%FT%TZ)\" \"$BASENAME\" \"$FILE_PATH\" \"$BYTES\" >> ${quote(manifestFile)}\nfi\nexit 0\n`;\n}\n\nfunction buildExitPlanModeScript(eventsFile: string): string {\n return `#!/bin/bash\nset -euo pipefail\nINPUT=$(cat)\n\nprintf '{\"ts\":\"%s\",\"event\":\"exit_plan_mode\"}\\\\n' \"$(date -u +%FT%TZ)\" >> ${quote(eventsFile)}\n\necho '{\"hookSpecificOutput\":{\"hookEventName\":\"PermissionRequest\",\"decision\":{\"behavior\":\"allow\"}}}'\nexit 0\n`;\n}\n\nfunction buildPermissionScript(eventsFile: string): string {\n return `#!/bin/bash\nset -euo pipefail\nINPUT=$(cat)\nTOOL=$(echo \"$INPUT\" | jq -r '.tool_name // empty')\nprintf '{\"ts\":\"%s\",\"event\":\"permission_request\",\"tool\":\"%s\"}\\\\n' \\\\\n \"$(date -u +%FT%TZ)\" \"$TOOL\" >> ${quote(eventsFile)}\necho '{\"hookSpecificOutput\":{\"hookEventName\":\"PermissionRequest\",\"decision\":{\"behavior\":\"allow\"}}}'\nexit 0\n`;\n}\n\nfunction buildProtectFilesScript(\n eventsFile: string,\n _phaseName: string | undefined,\n _planDir: string,\n): string {\n return `#!/bin/bash\nset -euo pipefail\nINPUT=$(cat)\nTOOL=$(echo \"$INPUT\" | jq -r '.tool_name // empty')\nFILE_PATH=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // empty')\n[ -z \"$FILE_PATH\" ] && exit 0\n\nBASENAME=$(basename \"$FILE_PATH\")\n\nblocked_reason() {\n printf '{\"ts\":\"%s\",\"event\":\"protect_blocked\",\"tool\":\"%s\",\"file\":\"%s\"}\\\\n' \\\\\n \"$(date -u +%FT%TZ)\" \"$TOOL\" \"$BASENAME\" >> ${quote(eventsFile)}\n echo \"$1\" >&2\n exit 2\n}\n\ncase \"$BASENAME\" in\n .env|.env.*)\n blocked_reason \"禁止修改环境配置文件 $BASENAME,请通过 .env.example 或文档说明配置变更。\"\n ;;\n package-lock.json|pnpm-lock.yaml)\n blocked_reason \"禁止直接修改锁文件 $BASENAME,请通过 npm install / pnpm install 更新依赖。\"\n ;;\nesac\n\nexit 0\n`;\n}\n\nfunction buildStopScript(\n eventsFile: string,\n planDir: string,\n phaseExpected: string,\n): string {\n return `#!/bin/bash\nset -euo pipefail\nINPUT=$(cat)\nSTOP_ACTIVE=$(echo \"$INPUT\" | jq -r '.stop_hook_active // false')\n\nPLAN_DIR=${quote(planDir)}\nMIN_BYTES=50\nPHASE_EXPECTED=${quote(phaseExpected)}\n\nMISSING=\"\"\nREADY=\"\"\nfor f in $(echo \"$PHASE_EXPECTED\" | tr ',' ' '); do\n [ -z \"$f\" ] && continue\n FPATH=\"$PLAN_DIR/$f\"\n if [ -f \"$FPATH\" ] && [ \"$(wc -c < \"$FPATH\")\" -ge \"$MIN_BYTES\" ]; then\n BYTES=$(wc -c < \"$FPATH\")\n READY=\"$READY $f(\\${BYTES} bytes)\"\n else\n MISSING=\"$MISSING $f\"\n fi\ndone\n\nMISSING=$(echo \"$MISSING\" | xargs)\nREADY=$(echo \"$READY\" | xargs)\n\nif [ -n \"$MISSING\" ] && [ \"$STOP_ACTIVE\" != \"true\" ]; then\n printf '{\"ts\":\"%s\",\"event\":\"stop\",\"blocked\":true,\"missing\":\"%s\"}\\\\n' \\\\\n \"$(date -u +%FT%TZ)\" \"$MISSING\" >> ${quote(eventsFile)}\n\n REASON=\"产物未就绪: $MISSING。请写入 $PLAN_DIR/ 下的对应文件。已就绪: \\${READY:-无}\"\n\n printf '{\"decision\":\"block\",\"reason\":\"%s\"}' \"$REASON\"\n exit 0\nfi\n\nprintf '{\"ts\":\"%s\",\"event\":\"stop\",\"blocked\":false,\"missing\":\"%s\"}\\\\n' \\\\\n \"$(date -u +%FT%TZ)\" \"\\${MISSING:-none}\" >> ${quote(eventsFile)}\nexit 0\n`;\n}\n\nfunction buildAskUserHookScript(eventsFile: string): string {\n return `#!/bin/bash\nset -euo pipefail\nINPUT=$(cat)\nQUESTION=$(echo \"$INPUT\" | jq -r '.tool_input.question // empty')\nOPTIONS=$(echo \"$INPUT\" | jq -c '[.tool_input.options[]? | {index: .index, label: .label}] // []' 2>/dev/null || echo '[]')\n[ -z \"$QUESTION\" ] && exit 0\nprintf '{\"ts\":\"%s\",\"event\":\"ask_user_question\",\"question\":\"%s\",\"options\":%s}\\\\n' \\\\\n \"$(date -u +%FT%TZ)\" \"$(echo \"$QUESTION\" | head -c 500 | tr '\"' \"'\")\" \"$OPTIONS\" >> ${quote(eventsFile)}\nexit 0\n`;\n}\n\nfunction buildNotificationScript(eventsFile: string): string {\n return `#!/bin/bash\nset -euo pipefail\nINPUT=$(cat)\nNTYPE=$(echo \"$INPUT\" | jq -r '.notification_type // empty')\nMSG=$(echo \"$INPUT\" | jq -r '.message // empty' | head -c 200 | tr '\"' \"'\")\n[ -z \"$NTYPE\" ] && exit 0\nprintf '{\"ts\":\"%s\",\"event\":\"notification\",\"notification_type\":\"%s\",\"message\":\"%s\"}\\\\n' \\\\\n \"$(date -u +%FT%TZ)\" \"$NTYPE\" \"$MSG\" >> ${quote(eventsFile)}\nexit 0\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nfunction quote(s: string): string {\n return `\"${s.replace(/\"/g, '\\\\\"')}\"`;\n}\n\nfunction readJsonl<T>(filePath: string): T[] {\n if (!fs.existsSync(filePath)) return [];\n try {\n const content = fs.readFileSync(filePath, 'utf-8').trim();\n if (!content) return [];\n return content.split('\\n').reduce<T[]>((acc, line) => {\n const trimmed = line.trim();\n if (!trimmed) return acc;\n try {\n acc.push(JSON.parse(trimmed) as T);\n } catch {\n logger.debug('Skipping malformed JSONL line', { line: trimmed });\n }\n return acc;\n }, []);\n } catch {\n return [];\n }\n}\n"],"mappings":"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","names":["fs","path","logger","logger","fs","logger","fs","fs","path","logger","path","fs","logger","result","path","fs"]}
|