aiwcli 0.15.5 → 0.15.7
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/README.md +6 -3
- package/dist/capabilities/branch/adapters.d.ts +2 -0
- package/dist/capabilities/branch/adapters.js +21 -0
- package/dist/capabilities/branch/contracts.d.ts +57 -0
- package/dist/capabilities/branch/contracts.js +1 -0
- package/dist/capabilities/branch/control-plane.d.ts +2 -0
- package/dist/capabilities/branch/control-plane.js +343 -0
- package/dist/capabilities/branch/runtime-core.d.ts +5 -0
- package/dist/capabilities/branch/runtime-core.js +36 -0
- package/dist/capabilities/installation/control-plane/clean-command.d.ts +41 -0
- package/dist/capabilities/installation/control-plane/clean-command.js +196 -0
- package/dist/capabilities/installation/control-plane/clear-command.d.ts +160 -0
- package/dist/capabilities/installation/control-plane/clear-command.js +1220 -0
- package/dist/capabilities/installation/control-plane/init-command.d.ts +81 -0
- package/dist/capabilities/installation/control-plane/init-command.js +449 -0
- package/dist/capabilities/launch/contracts.d.ts +51 -0
- package/dist/capabilities/launch/contracts.js +1 -0
- package/dist/capabilities/launch/control-plane/execute-launch.d.ts +2 -0
- package/dist/capabilities/launch/control-plane/execute-launch.js +222 -0
- package/dist/capabilities/launch/runtime-core/launch-options.d.ts +14 -0
- package/dist/capabilities/launch/runtime-core/launch-options.js +69 -0
- package/dist/cli/base-command.d.ts +18 -0
- package/dist/cli/base-command.js +55 -0
- package/dist/commands/branch.d.ts +0 -20
- package/dist/commands/branch.js +24 -416
- package/dist/commands/clean.d.ts +1 -41
- package/dist/commands/clean.js +1 -196
- package/dist/commands/clear.d.ts +1 -161
- package/dist/commands/clear.js +1 -1121
- package/dist/commands/init/index.d.ts +1 -98
- package/dist/commands/init/index.js +4 -478
- package/dist/commands/launch.d.ts +36 -11
- package/dist/commands/launch.js +135 -159
- package/dist/lib/base-command.d.ts +1 -114
- package/dist/lib/base-command.js +1 -153
- package/dist/lib/claude-settings-types.d.ts +31 -19
- package/dist/lib/context/context-formatter.d.ts +74 -0
- package/dist/lib/context/context-formatter.js +493 -0
- package/dist/lib/context/context-selector.d.ts +42 -0
- package/dist/lib/context/context-selector.js +451 -0
- package/dist/lib/context/context-store.d.ts +100 -0
- package/dist/lib/context/context-store.js +618 -0
- package/dist/lib/context/plan-manager.d.ts +54 -0
- package/dist/lib/context/plan-manager.js +282 -0
- package/dist/lib/context/task-tracker.d.ts +44 -0
- package/dist/lib/context/task-tracker.js +146 -0
- package/dist/lib/core-ide-base.d.ts +4 -0
- package/dist/lib/core-ide-base.js +77 -0
- package/dist/lib/core-installer.d.ts +5 -0
- package/dist/lib/core-installer.js +54 -0
- package/dist/lib/git-exclude-manager.d.ts +2 -2
- package/dist/lib/git-exclude-manager.js +3 -3
- package/dist/lib/hooks/hook-utils.d.ts +143 -0
- package/dist/lib/hooks/hook-utils.js +609 -0
- package/dist/lib/hooks/session-end-logic.d.ts +5 -0
- package/dist/lib/hooks/session-end-logic.js +63 -0
- package/dist/lib/hooks-merger.js +25 -19
- package/dist/lib/ide-path-resolver.d.ts +19 -7
- package/dist/lib/ide-path-resolver.js +25 -9
- package/dist/lib/install-state.d.ts +34 -0
- package/dist/lib/install-state.js +161 -0
- package/dist/lib/launch-options.d.ts +1 -0
- package/dist/lib/launch-options.js +1 -0
- package/dist/lib/lsp-patch.d.ts +12 -0
- package/dist/lib/lsp-patch.js +156 -0
- package/dist/lib/multiplexer.d.ts +57 -0
- package/dist/lib/multiplexer.js +19 -0
- package/dist/lib/multiplexers/psmux.d.ts +75 -0
- package/dist/lib/multiplexers/psmux.js +384 -0
- package/dist/lib/multiplexers/tmux.d.ts +44 -0
- package/dist/lib/multiplexers/tmux.js +262 -0
- package/dist/lib/mux-utils.d.ts +5 -0
- package/dist/lib/mux-utils.js +42 -0
- package/dist/lib/paths.d.ts +2 -2
- package/dist/lib/paths.js +2 -2
- package/dist/lib/platform-commands.d.ts +27 -0
- package/dist/lib/platform-commands.js +49 -0
- package/dist/lib/runtime/aiw-cli.d.ts +37 -0
- package/dist/lib/runtime/aiw-cli.js +74 -0
- package/dist/lib/runtime/atomic-write.d.ts +19 -0
- package/dist/lib/runtime/atomic-write.js +121 -0
- package/dist/lib/runtime/cli-args.d.ts +55 -0
- package/dist/lib/runtime/cli-args.js +185 -0
- package/dist/lib/runtime/constants.d.ts +56 -0
- package/dist/lib/runtime/constants.js +230 -0
- package/dist/lib/runtime/executable-policy.d.ts +16 -0
- package/dist/lib/runtime/executable-policy.js +57 -0
- package/dist/lib/runtime/git-state.d.ts +9 -0
- package/dist/lib/runtime/git-state.js +59 -0
- package/dist/lib/runtime/inference.d.ts +37 -0
- package/dist/lib/runtime/inference.js +262 -0
- package/dist/lib/runtime/lint-dispatch.d.ts +40 -0
- package/dist/lib/runtime/lint-dispatch.js +285 -0
- package/dist/lib/runtime/logger.d.ts +66 -0
- package/dist/lib/runtime/logger.js +201 -0
- package/dist/lib/runtime/models.d.ts +14 -0
- package/dist/lib/runtime/models.js +14 -0
- package/dist/lib/runtime/platform-adapter.d.ts +7 -0
- package/dist/lib/runtime/platform-adapter.js +21 -0
- package/dist/lib/runtime/preflight.d.ts +24 -0
- package/dist/lib/runtime/preflight.js +65 -0
- package/dist/lib/runtime/sentinel-ipc.d.ts +14 -0
- package/dist/lib/runtime/sentinel-ipc.js +67 -0
- package/dist/lib/runtime/state-io.d.ts +30 -0
- package/dist/lib/runtime/state-io.js +174 -0
- package/dist/lib/runtime/stop-words.d.ts +20 -0
- package/dist/lib/runtime/stop-words.js +150 -0
- package/dist/lib/runtime/subprocess-utils.d.ts +29 -0
- package/dist/lib/runtime/subprocess-utils.js +96 -0
- package/dist/lib/runtime/tmux-preflight.d.ts +13 -0
- package/dist/lib/runtime/tmux-preflight.js +78 -0
- package/dist/lib/runtime/utils.d.ts +54 -0
- package/dist/lib/runtime/utils.js +162 -0
- package/dist/lib/sentinel-wrapper.d.ts +9 -0
- package/dist/lib/sentinel-wrapper.js +20 -0
- package/dist/lib/shell-quoting.d.ts +5 -0
- package/dist/lib/shell-quoting.js +17 -0
- package/dist/lib/spawn-errors.d.ts +6 -0
- package/dist/lib/spawn-errors.js +15 -0
- package/dist/lib/spawn.js +5 -11
- package/dist/lib/template-installer.d.ts +4 -5
- package/dist/lib/template-installer.js +36 -34
- package/dist/lib/template-resolver.d.ts +6 -7
- package/dist/lib/template-resolver.js +26 -21
- package/dist/lib/template-settings-reconstructor.d.ts +7 -2
- package/dist/lib/template-settings-reconstructor.js +76 -45
- package/dist/lib/terminal-strategy.d.ts +11 -0
- package/dist/lib/terminal-strategy.js +49 -0
- package/dist/lib/terminal.d.ts +28 -0
- package/dist/lib/terminal.js +162 -112
- package/dist/lib/tmux-pane-placement.d.ts +17 -0
- package/dist/lib/tmux-pane-placement.js +58 -0
- package/dist/lib/tmux-primitives.d.ts +5 -0
- package/dist/lib/tmux-primitives.js +15 -0
- package/dist/lib/tmux-session.d.ts +32 -0
- package/dist/lib/tmux-session.js +86 -0
- package/dist/lib/tty-detection.js +1 -1
- package/dist/lib/types.d.ts +168 -0
- package/dist/lib/types.js +6 -0
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/platform/launch.d.ts +10 -0
- package/dist/platform/launch.js +10 -0
- package/dist/templates/CLAUDE.md +31 -40
- package/dist/templates/cc-native/.claude/settings.json +27 -27
- package/dist/templates/cc-native/CC-NATIVE-README.md +1 -1
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +10 -9
- package/dist/templates/cc-native/_cc-native/CLAUDE.md +18 -18
- package/dist/templates/cc-native/_cc-native/artifacts/CLAUDE.md +3 -3
- package/dist/templates/cc-native/_cc-native/artifacts/lib/format.ts +14 -14
- package/dist/templates/cc-native/_cc-native/artifacts/lib/tracker.ts +1 -1
- package/dist/templates/cc-native/_cc-native/artifacts/lib/write.ts +3 -3
- package/dist/templates/cc-native/_cc-native/cc-native.config.json +3 -3
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +16 -15
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +3 -3
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +2 -2
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +2 -2
- package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +3 -3
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +2 -2
- package/dist/templates/cc-native/_cc-native/hooks/validate_task_prompt.ts +3 -3
- package/dist/templates/cc-native/_cc-native/lib-ts/CLAUDE.md +8 -8
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +4 -4
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-discovery.ts +2 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/logger.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/retrieval-pipeline.ts +2 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/types.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/settings.ts +8 -8
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +3 -3
- package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +2 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +3 -3
- package/dist/templates/cc-native/_cc-native/plan-review/CLAUDE.md +3 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/__tests__/agent-selection.test.ts +345 -0
- package/dist/templates/cc-native/_cc-native/plan-review/lib/__tests__/preflight.test.ts +344 -0
- package/dist/templates/cc-native/_cc-native/plan-review/lib/agent-selection.ts +37 -15
- package/dist/templates/cc-native/_cc-native/plan-review/lib/corroboration.ts +16 -69
- package/dist/templates/cc-native/_cc-native/plan-review/lib/orchestrator.ts +1 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/output-builder.ts +1 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/plan-questions.ts +2 -2
- package/dist/templates/cc-native/_cc-native/plan-review/lib/preflight.ts +56 -26
- package/dist/templates/cc-native/_cc-native/plan-review/lib/review-pipeline.ts +7 -7
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/agent.ts +4 -4
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/base/base-agent.ts +3 -3
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/index.ts +1 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/claude-agent.ts +2 -2
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/codex-agent.ts +4 -4
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/gemini-agent.ts +1 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/orchestrator-claude-agent.ts +5 -6
- package/dist/templates/core/.codex/workflows/codex.md +17 -0
- package/dist/templates/core/.codex/workflows/handoff.md +5 -0
- package/dist/templates/core/.codex/workflows/meta-plan.md +7 -0
- package/dist/templates/core/.cognition/AGENTS.md +5 -0
- package/dist/templates/core/.cognition/config.json +12 -0
- package/dist/templates/{_shared → core}/.windsurf/workflows/handoff.md +1 -1
- package/dist/templates/{_shared → core}/.windsurf/workflows/meta-plan.md +1 -1
- package/dist/templates/core/hooks-ts/_utils/git-state.ts +2 -0
- package/dist/templates/{_shared → core}/hooks-ts/archive_plan.ts +14 -23
- package/dist/templates/core/hooks-ts/codex_explorer.ts +160 -0
- package/dist/templates/{_shared → core}/hooks-ts/context_monitor.ts +23 -55
- package/dist/templates/{_shared → core}/hooks-ts/file-suggestion.ts +4 -3
- package/dist/templates/{_shared → core}/hooks-ts/lint_after_edit.ts +7 -9
- package/dist/templates/{_shared → core}/hooks-ts/pre_compact.ts +5 -5
- package/dist/templates/{_shared → core}/hooks-ts/session_end.ts +38 -78
- package/dist/templates/{_shared → core}/hooks-ts/session_start.ts +5 -5
- package/dist/templates/core/hooks-ts/task_create_capture.ts +32 -0
- package/dist/templates/{_shared → core}/hooks-ts/task_update_capture.ts +9 -24
- package/dist/templates/core/hooks-ts/user_prompt_submit.ts +46 -0
- package/dist/templates/{_shared → core}/lib-ts/CLAUDE.md +27 -16
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/backends/headless.ts +3 -2
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/backends/tmux.ts +44 -15
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/base-agent.ts +6 -4
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/execution-backend.ts +1 -1
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/index.ts +2 -2
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/structured-output.ts +4 -5
- package/dist/templates/{_shared → core}/lib-ts/context/CLAUDE.md +9 -6
- package/dist/templates/{_shared → core}/lib-ts/context/context-formatter.ts +16 -21
- package/dist/templates/{_shared → core}/lib-ts/context/context-selector.ts +8 -6
- package/dist/templates/{_shared → core}/lib-ts/context/context-store.ts +32 -20
- package/dist/templates/{_shared → core}/lib-ts/context/plan-manager.ts +19 -15
- package/dist/templates/{_shared → core}/lib-ts/context/task-tracker.ts +3 -3
- package/dist/templates/core/lib-ts/hooks/context-monitor-logic.ts +32 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/hooks}/hook-utils.ts +168 -41
- package/dist/templates/core/lib-ts/hooks/prompt-binding-logic.ts +80 -0
- package/dist/templates/core/lib-ts/hooks/session-end-logic.ts +93 -0
- package/dist/templates/core/lib-ts/package.json +19 -0
- package/dist/templates/core/lib-ts/runtime/agent-launcher.ts +295 -0
- package/dist/templates/core/lib-ts/runtime/aiw-cli.ts +106 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/atomic-write.ts +12 -7
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/cli-args.ts +8 -6
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/constants.ts +326 -324
- package/dist/templates/core/lib-ts/runtime/executable-policy.ts +89 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/git-state.ts +6 -4
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/inference.ts +59 -10
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/lint-dispatch.ts +25 -23
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/logger.ts +32 -29
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/models.ts +2 -2
- package/dist/templates/core/lib-ts/runtime/platform-adapter.ts +33 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/preflight.ts +4 -3
- package/dist/templates/core/lib-ts/runtime/sentinel-ipc.ts +91 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/state-io.ts +11 -7
- package/dist/templates/core/lib-ts/runtime/stop-words.ts +185 -0
- package/dist/templates/core/lib-ts/runtime/subprocess-utils.ts +147 -0
- package/dist/templates/core/lib-ts/runtime/tmux-preflight.ts +93 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/utils.ts +4 -3
- package/dist/templates/{_shared → core}/lib-ts/templates/formatters.ts +7 -5
- package/dist/templates/{_shared → core}/lib-ts/templates/plan-context.ts +2 -1
- package/dist/templates/{_shared → core}/lib-ts/tsconfig.json +3 -1
- package/dist/templates/{_shared → core}/lib-ts/types.ts +78 -77
- package/dist/templates/core/scripts/resolve-run.ts +61 -0
- package/dist/templates/{_shared → core}/scripts/resolve_context.ts +3 -3
- package/dist/templates/{_shared → core}/scripts/status_line.ts +25 -20
- package/dist/templates/core/skills/codex/CLAUDE.md +78 -0
- package/dist/templates/{_shared → core}/skills/codex/SKILL.md +21 -18
- package/dist/templates/{_shared → core}/skills/codex/lib/codex-watcher.ts +76 -103
- package/dist/templates/{_shared → core}/skills/codex/scripts/launch-codex.ts +119 -133
- package/dist/templates/{_shared → core}/skills/codex/scripts/watch-codex.ts +6 -4
- package/dist/templates/core/skills/devin/CLAUDE.md +65 -0
- package/dist/templates/core/skills/devin/SKILL.md +73 -0
- package/dist/templates/core/skills/devin/lib/devin-watcher.ts +280 -0
- package/dist/templates/core/skills/devin/scripts/launch-devin.ts +257 -0
- package/dist/templates/{_shared → core}/skills/handoff-system/CLAUDE.md +436 -433
- package/dist/templates/{_shared → core}/skills/handoff-system/lib/document-generator.ts +9 -7
- package/dist/templates/{_shared → core}/skills/handoff-system/lib/handoff-reader.ts +6 -4
- package/dist/templates/{_shared → core}/skills/handoff-system/scripts/resume_handoff.ts +10 -8
- package/dist/templates/{_shared → core}/skills/handoff-system/scripts/save_handoff.ts +12 -10
- package/dist/templates/{_shared → core}/skills/handoff-system/workflows/handoff-resume.md +2 -2
- package/dist/templates/{_shared → core}/skills/handoff-system/workflows/handoff.md +6 -5
- package/dist/templates/{_shared → core}/skills/meta-plan/CLAUDE.md +2 -1
- package/dist/templates/{_shared → core}/skills/meta-plan/workflows/meta-plan.md +8 -7
- package/oclif.manifest.json +89 -13
- package/package.json +13 -12
- package/dist/templates/_shared/.claude/settings.json +0 -120
- package/dist/templates/_shared/.claude/skills/codex/SKILL.md +0 -35
- package/dist/templates/_shared/.claude/skills/handoff/SKILL.md +0 -13
- package/dist/templates/_shared/.claude/skills/handoff-resume/SKILL.md +0 -13
- package/dist/templates/_shared/.claude/skills/meta-plan/SKILL.md +0 -43
- package/dist/templates/_shared/.codex/workflows/codex.md +0 -11
- package/dist/templates/_shared/.codex/workflows/handoff.md +0 -226
- package/dist/templates/_shared/.codex/workflows/meta-plan.md +0 -347
- package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +0 -2
- package/dist/templates/_shared/hooks-ts/task_create_capture.ts +0 -48
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +0 -93
- package/dist/templates/_shared/lib-ts/base/launchers/tmux-launcher.ts +0 -173
- package/dist/templates/_shared/lib-ts/base/launchers/window-launcher.ts +0 -93
- package/dist/templates/_shared/lib-ts/base/launchers/wt-launcher.ts +0 -64
- package/dist/templates/_shared/lib-ts/base/pane-launcher.ts +0 -55
- package/dist/templates/_shared/lib-ts/base/sentinel-ipc.ts +0 -87
- package/dist/templates/_shared/lib-ts/base/stop-words.ts +0 -184
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +0 -249
- package/dist/templates/_shared/lib-ts/base/tmux-driver.ts +0 -341
- package/dist/templates/_shared/lib-ts/base/tmux-pane-placement.ts +0 -78
- package/dist/templates/_shared/lib-ts/package.json +0 -20
- package/dist/templates/_shared/scripts/resolve-run.ts +0 -62
- package/dist/templates/_shared/skills/codex/CLAUDE.md +0 -70
- /package/dist/templates/{_shared → core}/lib-ts/agent-exec/backends/index.ts +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process'
|
|
2
|
+
import type { ExecSyncOptionsWithStringEncoding } from 'node:child_process'
|
|
3
|
+
|
|
4
|
+
import { commandLookupBinary, isWindowsPlatform } from './platform-adapter.js'
|
|
5
|
+
|
|
6
|
+
export type WindowsLookupProfile = 'cmdOrExeFirst' | 'exeThenExtensionlessThenCmd'
|
|
7
|
+
|
|
8
|
+
export function parseLookupLines(stdout: string): string[] {
|
|
9
|
+
return stdout
|
|
10
|
+
.trim()
|
|
11
|
+
.split(/\r?\n/)
|
|
12
|
+
.map((line) => line.trim())
|
|
13
|
+
.filter(Boolean)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function pickWindowsPath(lines: string[], profile: WindowsLookupProfile): null | string {
|
|
17
|
+
if (lines.length === 0) return null
|
|
18
|
+
|
|
19
|
+
if (profile === 'cmdOrExeFirst') {
|
|
20
|
+
return lines.find((line) => /\.(cmd|exe)$/i.test(line)) ?? lines[0] ?? null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return lines.find((line) => /\.exe$/i.test(line))
|
|
24
|
+
?? lines.find((line) => !/\.(cmd|ps1)$/i.test(line))
|
|
25
|
+
?? lines.find((line) => /\.cmd$/i.test(line))
|
|
26
|
+
?? lines[0]
|
|
27
|
+
?? null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function selectLookupPath(
|
|
31
|
+
lines: string[],
|
|
32
|
+
platform: NodeJS.Platform = process.platform,
|
|
33
|
+
windowsProfile: WindowsLookupProfile = 'cmdOrExeFirst',
|
|
34
|
+
): null | string {
|
|
35
|
+
if (lines.length === 0) return null
|
|
36
|
+
if (!isWindowsPlatform(platform)) return lines[0] ?? null
|
|
37
|
+
return pickWindowsPath(lines, windowsProfile)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function resolveExecutable(
|
|
41
|
+
name: string,
|
|
42
|
+
options?: {
|
|
43
|
+
platform?: NodeJS.Platform | undefined
|
|
44
|
+
timeoutMs?: number | undefined
|
|
45
|
+
windowsHide?: boolean | undefined
|
|
46
|
+
windowsProfile?: undefined | WindowsLookupProfile
|
|
47
|
+
},
|
|
48
|
+
): null | string {
|
|
49
|
+
const platform = options?.platform ?? process.platform
|
|
50
|
+
const lines = lookupExecutables(name, {
|
|
51
|
+
platform,
|
|
52
|
+
timeoutMs: options?.timeoutMs,
|
|
53
|
+
windowsHide: options?.windowsHide,
|
|
54
|
+
})
|
|
55
|
+
return selectLookupPath(lines, platform, options?.windowsProfile ?? 'cmdOrExeFirst')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function isCommandAvailable(
|
|
59
|
+
name: string,
|
|
60
|
+
platform: NodeJS.Platform = process.platform,
|
|
61
|
+
): boolean {
|
|
62
|
+
return resolveExecutable(name, { platform }) !== null
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function lookupExecutables(
|
|
66
|
+
name: string,
|
|
67
|
+
options?: {
|
|
68
|
+
platform?: NodeJS.Platform | undefined
|
|
69
|
+
timeoutMs?: number | undefined
|
|
70
|
+
windowsHide?: boolean | undefined
|
|
71
|
+
},
|
|
72
|
+
): string[] {
|
|
73
|
+
const platform = options?.platform ?? process.platform
|
|
74
|
+
const lookupBin = commandLookupBinary(platform)
|
|
75
|
+
const cmd = `${lookupBin} ${name}`
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
return parseLookupLines(execSync(cmd, {
|
|
79
|
+
encoding: 'utf8',
|
|
80
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
81
|
+
shell: true,
|
|
82
|
+
timeout: options?.timeoutMs ?? 3000,
|
|
83
|
+
windowsHide: options?.windowsHide ?? true,
|
|
84
|
+
} as unknown as ExecSyncOptionsWithStringEncoding))
|
|
85
|
+
} catch {
|
|
86
|
+
return []
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
@@ -4,13 +4,13 @@ import { execFileSync } from "node:child_process";
|
|
|
4
4
|
* Capture current git state for session snapshots.
|
|
5
5
|
* All fields are optional — failures are silently ignored.
|
|
6
6
|
*/
|
|
7
|
-
export function getGitState(projectRoot: string): Record<string,
|
|
8
|
-
const gitState: Record<string,
|
|
7
|
+
export function getGitState(projectRoot: string): Record<string, unknown> {
|
|
8
|
+
const gitState: Record<string, unknown> = {};
|
|
9
9
|
const isWin = process.platform === "win32";
|
|
10
10
|
const opts = {
|
|
11
11
|
cwd: projectRoot,
|
|
12
12
|
timeout: 5000,
|
|
13
|
-
encoding: "
|
|
13
|
+
encoding: "utf8" as const,
|
|
14
14
|
stdio: ["pipe", "pipe", "pipe"] as ["pipe", "pipe", "pipe"],
|
|
15
15
|
shell: isWin,
|
|
16
16
|
};
|
|
@@ -45,7 +45,7 @@ export function getGitState(projectRoot: string): Record<string, any> {
|
|
|
45
45
|
export function getGitStatusShort(projectRoot?: string): string {
|
|
46
46
|
try {
|
|
47
47
|
const result = execFileSync("git", ["status", "--short"], {
|
|
48
|
-
encoding: "
|
|
48
|
+
encoding: "utf8",
|
|
49
49
|
timeout: 5000,
|
|
50
50
|
stdio: ["pipe", "pipe", "pipe"],
|
|
51
51
|
...(projectRoot ? { cwd: projectRoot } : {}),
|
|
@@ -56,3 +56,5 @@ export function getGitStatusShort(projectRoot?: string): string {
|
|
|
56
56
|
return "(git status unavailable)";
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
|
|
@@ -9,7 +9,6 @@ import { execFileSync } from "node:child_process";
|
|
|
9
9
|
import { logDebug, logWarn } from "./logger.js";
|
|
10
10
|
import { STOP_WORDS } from "./stop-words.js";
|
|
11
11
|
import type { InferenceResult } from "../types.js";
|
|
12
|
-
import { execFileAsync } from "./subprocess-utils.js";
|
|
13
12
|
import {
|
|
14
13
|
buildCliInvocation,
|
|
15
14
|
inferenceSpec,
|
|
@@ -19,6 +18,7 @@ import {
|
|
|
19
18
|
TIER_TIMEOUTS,
|
|
20
19
|
} from "./cli-args.js";
|
|
21
20
|
import { CODEX_MODELS } from "./models.js";
|
|
21
|
+
import { execFileAsync } from "./subprocess-utils.js";
|
|
22
22
|
|
|
23
23
|
const CONTEXT_ID_PRIMARY_MODEL = CODEX_MODELS.spark;
|
|
24
24
|
|
|
@@ -35,7 +35,6 @@ export function inference(
|
|
|
35
35
|
): InferenceResult {
|
|
36
36
|
const startTime = Date.now();
|
|
37
37
|
const modelInput = options?.model ?? level;
|
|
38
|
-
const model = resolveModel(modelInput);
|
|
39
38
|
const timeoutSec = timeout ?? (isModelTier(modelInput) ? getTierTimeout(modelInput) : TIER_TIMEOUTS.fast);
|
|
40
39
|
const fullPrompt = `${systemPrompt}\n\n${userPrompt}`;
|
|
41
40
|
|
|
@@ -55,7 +54,7 @@ export function inference(
|
|
|
55
54
|
{
|
|
56
55
|
timeout: timeoutSec * 1000,
|
|
57
56
|
env: invocation.env,
|
|
58
|
-
encoding: "
|
|
57
|
+
encoding: "utf8",
|
|
59
58
|
stdio: ["pipe", "pipe", "pipe"],
|
|
60
59
|
shell: isWin,
|
|
61
60
|
},
|
|
@@ -67,10 +66,17 @@ export function inference(
|
|
|
67
66
|
output: stdout.trim(),
|
|
68
67
|
latency_ms: latencyMs,
|
|
69
68
|
};
|
|
70
|
-
} catch (error:
|
|
69
|
+
} catch (error: unknown) {
|
|
71
70
|
const latencyMs = Date.now() - startTime;
|
|
71
|
+
const execError = error as {
|
|
72
|
+
code?: string;
|
|
73
|
+
killed?: boolean;
|
|
74
|
+
status?: number;
|
|
75
|
+
stderr?: unknown;
|
|
76
|
+
stdout?: unknown;
|
|
77
|
+
};
|
|
72
78
|
|
|
73
|
-
if (
|
|
79
|
+
if (execError.code === "ETIMEDOUT" || execError.killed) {
|
|
74
80
|
return {
|
|
75
81
|
success: false,
|
|
76
82
|
output: "",
|
|
@@ -79,7 +85,7 @@ export function inference(
|
|
|
79
85
|
};
|
|
80
86
|
}
|
|
81
87
|
|
|
82
|
-
if (
|
|
88
|
+
if (execError.code === "ENOENT") {
|
|
83
89
|
return {
|
|
84
90
|
success: false,
|
|
85
91
|
output: "",
|
|
@@ -89,11 +95,11 @@ export function inference(
|
|
|
89
95
|
}
|
|
90
96
|
|
|
91
97
|
// Non-zero exit code
|
|
92
|
-
if (
|
|
98
|
+
if (execError.status !== undefined && execError.status !== 0) {
|
|
93
99
|
return {
|
|
94
100
|
success: false,
|
|
95
|
-
output: (
|
|
96
|
-
error: (
|
|
101
|
+
output: String(execError.stdout ?? "").trim(),
|
|
102
|
+
error: String(execError.stderr ?? "").trim() || `Exit code: ${execError.status}`,
|
|
97
103
|
latency_ms: latencyMs,
|
|
98
104
|
};
|
|
99
105
|
}
|
|
@@ -182,7 +188,7 @@ Respond with ONLY a JSON object: {"slug": "your 8-12 word phrase here"}`;
|
|
|
182
188
|
|
|
183
189
|
/**
|
|
184
190
|
* Generate a 5-12 word context ID slug from a user prompt.
|
|
185
|
-
* Uses
|
|
191
|
+
* Uses Codex Spark first, then falls back to current fast tier for resilience.
|
|
186
192
|
* See SPEC.md §6.3
|
|
187
193
|
*/
|
|
188
194
|
export function generateContextIdSlug(
|
|
@@ -294,6 +300,47 @@ export async function inferenceAsync(
|
|
|
294
300
|
return { success: true, output: result.stdout.trim(), latency_ms: latencyMs };
|
|
295
301
|
}
|
|
296
302
|
|
|
303
|
+
export async function codexInferAsync(
|
|
304
|
+
prompt: string,
|
|
305
|
+
model: string,
|
|
306
|
+
options?: { sandbox?: string; timeout?: number },
|
|
307
|
+
): Promise<InferenceResult> {
|
|
308
|
+
const startTime = Date.now();
|
|
309
|
+
const args = ["exec", "--model", model, "--json"];
|
|
310
|
+
if (options?.sandbox) {
|
|
311
|
+
args.push("--sandbox", options.sandbox);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const result = await execFileAsync(
|
|
315
|
+
"codex",
|
|
316
|
+
args,
|
|
317
|
+
{
|
|
318
|
+
env: process.env,
|
|
319
|
+
input: prompt,
|
|
320
|
+
timeout: (options?.timeout ?? 30) * 1000,
|
|
321
|
+
},
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const latencyMs = Date.now() - startTime;
|
|
325
|
+
if (result.killed) {
|
|
326
|
+
return { success: false, output: "", error: `Timeout after ${options?.timeout ?? 30}s`, latency_ms: latencyMs };
|
|
327
|
+
}
|
|
328
|
+
if (result.exitCode !== 0) {
|
|
329
|
+
return {
|
|
330
|
+
success: false,
|
|
331
|
+
output: result.stdout.trim(),
|
|
332
|
+
error: result.stderr.trim() || `Exit code: ${result.exitCode}`,
|
|
333
|
+
latency_ms: latencyMs,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
success: true,
|
|
339
|
+
output: result.stdout.trim(),
|
|
340
|
+
latency_ms: latencyMs,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
297
344
|
/**
|
|
298
345
|
* Filter stop words from text.
|
|
299
346
|
* See SPEC.md §6.4
|
|
@@ -305,3 +352,5 @@ function filterStopWords(text: string): string {
|
|
|
305
352
|
.filter((w) => !STOP_WORDS.has(w) && w.length > 1)
|
|
306
353
|
.join(" ");
|
|
307
354
|
}
|
|
355
|
+
|
|
356
|
+
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* See root CLAUDE.md for template sync targets.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {spawnSync} from 'node:child_process'
|
|
7
8
|
import * as fs from "node:fs";
|
|
8
|
-
import
|
|
9
|
+
import path from "node:path";
|
|
9
10
|
|
|
10
11
|
import { logDebug, logWarn } from "./logger.js";
|
|
11
12
|
import { findExecutable } from "./subprocess-utils.js";
|
|
@@ -35,14 +36,14 @@ export interface LintError {
|
|
|
35
36
|
|
|
36
37
|
function parseBiomeOutput(stdout: string, _stderr: string, _exitCode: number): LintError[] {
|
|
37
38
|
try {
|
|
38
|
-
const data = JSON.parse(stdout);
|
|
39
|
-
const diagnostics
|
|
39
|
+
const data = JSON.parse(stdout) as { diagnostics?: any[] };
|
|
40
|
+
const diagnostics = data.diagnostics ?? [];
|
|
40
41
|
return diagnostics.map((d) => ({
|
|
41
42
|
line: d.location?.span?.start?.line ?? d.location?.sourceCode?.lineIndex ?? 0,
|
|
42
43
|
column: d.location?.span?.start?.character ?? undefined,
|
|
43
44
|
severity: d.severity === "error" || d.severity === "fatal" ? "error" as const : "warning" as const,
|
|
44
45
|
message: typeof d.description === "string" ? d.description : (d.message ?? "Unknown issue"),
|
|
45
|
-
rule: d.category
|
|
46
|
+
...(d.category ? {rule: d.category} : {}),
|
|
46
47
|
}));
|
|
47
48
|
} catch {
|
|
48
49
|
return [];
|
|
@@ -51,13 +52,13 @@ function parseBiomeOutput(stdout: string, _stderr: string, _exitCode: number): L
|
|
|
51
52
|
|
|
52
53
|
function parseRuffOutput(stdout: string, _stderr: string, _exitCode: number): LintError[] {
|
|
53
54
|
try {
|
|
54
|
-
const items
|
|
55
|
+
const items = JSON.parse(stdout) as any[];
|
|
55
56
|
return items.map((item) => ({
|
|
56
57
|
line: item.location?.row ?? 0,
|
|
57
58
|
column: item.location?.column ?? undefined,
|
|
58
59
|
severity: "error" as const,
|
|
59
60
|
message: item.message ?? "Unknown issue",
|
|
60
|
-
rule: item.code
|
|
61
|
+
...(item.code ? {rule: item.code} : {}),
|
|
61
62
|
}));
|
|
62
63
|
} catch {
|
|
63
64
|
return [];
|
|
@@ -66,14 +67,14 @@ function parseRuffOutput(stdout: string, _stderr: string, _exitCode: number): Li
|
|
|
66
67
|
|
|
67
68
|
function parseShellcheckOutput(stdout: string, _stderr: string, _exitCode: number): LintError[] {
|
|
68
69
|
try {
|
|
69
|
-
const data = JSON.parse(stdout);
|
|
70
|
-
const comments
|
|
70
|
+
const data = JSON.parse(stdout) as { comments?: any[] };
|
|
71
|
+
const comments = data.comments ?? [];
|
|
71
72
|
return comments.map((c) => ({
|
|
72
73
|
line: c.line ?? 0,
|
|
73
74
|
column: c.column ?? undefined,
|
|
74
75
|
severity: c.level === "error" ? "error" as const : "warning" as const,
|
|
75
76
|
message: c.message ?? "Unknown issue",
|
|
76
|
-
|
|
77
|
+
...(c.code ? {rule: `SC${c.code}`} : {}),
|
|
77
78
|
}));
|
|
78
79
|
} catch {
|
|
79
80
|
return [];
|
|
@@ -82,14 +83,14 @@ function parseShellcheckOutput(stdout: string, _stderr: string, _exitCode: numbe
|
|
|
82
83
|
|
|
83
84
|
function parseRubocopOutput(stdout: string, _stderr: string, _exitCode: number): LintError[] {
|
|
84
85
|
try {
|
|
85
|
-
const data = JSON.parse(stdout);
|
|
86
|
-
const offenses
|
|
86
|
+
const data = JSON.parse(stdout) as { files?: Array<{ offenses?: any[] }> };
|
|
87
|
+
const offenses = data.files?.[0]?.offenses ?? [];
|
|
87
88
|
return offenses.map((o) => ({
|
|
88
89
|
line: o.location?.line ?? 0,
|
|
89
90
|
column: o.location?.column ?? undefined,
|
|
90
91
|
severity: o.severity === "error" || o.severity === "fatal" ? "error" as const : "warning" as const,
|
|
91
92
|
message: o.message ?? "Unknown issue",
|
|
92
|
-
rule: o.cop_name
|
|
93
|
+
...(o.cop_name ? {rule: o.cop_name} : {}),
|
|
93
94
|
}));
|
|
94
95
|
} catch {
|
|
95
96
|
return [];
|
|
@@ -108,7 +109,7 @@ function parseCppcheckOutput(_stdout: string, stderr: string, _exitCode: number)
|
|
|
108
109
|
column: parseInt(m[3]!, 10),
|
|
109
110
|
severity: m[4] === "error" ? "error" : "warning",
|
|
110
111
|
message: m[5]!,
|
|
111
|
-
rule: m[6],
|
|
112
|
+
...(m[6] ? {rule: m[6]} : {}),
|
|
112
113
|
});
|
|
113
114
|
}
|
|
114
115
|
}
|
|
@@ -117,14 +118,14 @@ function parseCppcheckOutput(_stdout: string, stderr: string, _exitCode: number)
|
|
|
117
118
|
|
|
118
119
|
function parseEslintOutput(stdout: string, _stderr: string, _exitCode: number): LintError[] {
|
|
119
120
|
try {
|
|
120
|
-
const files
|
|
121
|
-
const messages
|
|
121
|
+
const files = JSON.parse(stdout) as Array<{ messages?: any[] }>;
|
|
122
|
+
const messages = files[0]?.messages ?? [];
|
|
122
123
|
return messages.map((m) => ({
|
|
123
124
|
line: m.line ?? 0,
|
|
124
125
|
column: m.column ?? undefined,
|
|
125
126
|
severity: m.severity === 2 ? "error" as const : "warning" as const,
|
|
126
127
|
message: m.message ?? "Unknown issue",
|
|
127
|
-
rule: m.ruleId
|
|
128
|
+
...(m.ruleId ? {rule: m.ruleId} : {}),
|
|
128
129
|
}));
|
|
129
130
|
} catch {
|
|
130
131
|
return [];
|
|
@@ -275,17 +276,16 @@ export function runLinter(
|
|
|
275
276
|
const args = config.buildArgs(filePath);
|
|
276
277
|
|
|
277
278
|
try {
|
|
278
|
-
|
|
279
|
-
const result = (Bun as any).spawnSync([binary, ...args], {
|
|
279
|
+
const result = spawnSync(binary, args, {
|
|
280
280
|
cwd: projectRoot,
|
|
281
281
|
timeout: 8000, // 8s soft limit (10s hook timeout is the hard kill)
|
|
282
|
-
|
|
283
|
-
|
|
282
|
+
stdio: 'pipe',
|
|
283
|
+
encoding: 'utf8',
|
|
284
284
|
});
|
|
285
285
|
|
|
286
|
-
const
|
|
287
|
-
const stdout = result.stdout
|
|
288
|
-
const stderr = result.stderr
|
|
286
|
+
const exitCode = result.status ?? 1;
|
|
287
|
+
const stdout = result.stdout ?? "";
|
|
288
|
+
const stderr = result.stderr ?? "";
|
|
289
289
|
|
|
290
290
|
// Exit 0 = clean
|
|
291
291
|
if (exitCode === 0) return { errors: [] };
|
|
@@ -337,3 +337,5 @@ export function formatLintErrors(
|
|
|
337
337
|
|
|
338
338
|
return lines.join("\n");
|
|
339
339
|
}
|
|
340
|
+
|
|
341
|
+
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import * as fs from "node:fs";
|
|
23
|
-
import
|
|
23
|
+
import path from "node:path";
|
|
24
24
|
|
|
25
25
|
const LEVELS: Record<string, number> = {
|
|
26
26
|
debug: 0,
|
|
@@ -29,7 +29,8 @@ const LEVELS: Record<string, number> = {
|
|
|
29
29
|
error: 3,
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
const
|
|
32
|
+
const MAX_LOG_BYTES = 2_000_000; // 2MB threshold before pruning
|
|
33
|
+
let _prunedThisProcess = false;
|
|
33
34
|
|
|
34
35
|
// Module-level session ID cache
|
|
35
36
|
let _cachedSessionId: string | null = null;
|
|
@@ -89,7 +90,7 @@ export function hookLog(
|
|
|
89
90
|
message: string,
|
|
90
91
|
opts?: {
|
|
91
92
|
component?: string;
|
|
92
|
-
data?:
|
|
93
|
+
data?: unknown;
|
|
93
94
|
traceback_str?: string;
|
|
94
95
|
stderr?: boolean;
|
|
95
96
|
},
|
|
@@ -121,7 +122,7 @@ export function hookLog(
|
|
|
121
122
|
// Build JSONL entry
|
|
122
123
|
const now = new Date();
|
|
123
124
|
const ts = now.toISOString().replace("Z", "").slice(0, 23);
|
|
124
|
-
const entry: Record<string,
|
|
125
|
+
const entry: Record<string, unknown> = {
|
|
125
126
|
ts,
|
|
126
127
|
level: levelLower,
|
|
127
128
|
hook: hookName,
|
|
@@ -148,42 +149,41 @@ export function hookLog(
|
|
|
148
149
|
const dir = path.dirname(logPath);
|
|
149
150
|
fs.mkdirSync(dir, { recursive: true });
|
|
150
151
|
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
if (
|
|
157
|
-
fs.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
152
|
+
// Size-based prune: runs at most once per bun process
|
|
153
|
+
if (!_prunedThisProcess) {
|
|
154
|
+
_prunedThisProcess = true;
|
|
155
|
+
try {
|
|
156
|
+
const stat = fs.statSync(logPath);
|
|
157
|
+
if (stat.size > MAX_LOG_BYTES) {
|
|
158
|
+
const content = fs.readFileSync(logPath, "utf8");
|
|
159
|
+
const pruneTarget = Math.floor(content.length * 0.1);
|
|
160
|
+
const keepFrom = content.indexOf("\n", pruneTarget);
|
|
161
|
+
if (keepFrom > 0) {
|
|
162
|
+
fs.writeFileSync(logPath, content.slice(keepFrom + 1), "utf8");
|
|
163
|
+
}
|
|
162
164
|
}
|
|
163
|
-
}
|
|
164
|
-
} catch {
|
|
165
|
-
// ignore
|
|
165
|
+
} catch { /* file doesn't exist yet — fine */ }
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
fs.appendFileSync(logPath, line, "
|
|
168
|
+
fs.appendFileSync(logPath, line, "utf8");
|
|
169
169
|
} catch {
|
|
170
170
|
// Never crash
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
export function logDebug(hookName: string, message: string, opts?: Record<string,
|
|
174
|
+
export function logDebug(hookName: string, message: string, opts?: Record<string, unknown>): void {
|
|
175
175
|
hookLog("debug", hookName, message, opts);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
export function logInfo(hookName: string, message: string, opts?: Record<string,
|
|
178
|
+
export function logInfo(hookName: string, message: string, opts?: Record<string, unknown>): void {
|
|
179
179
|
hookLog("info", hookName, message, opts);
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
export function logWarn(hookName: string, message: string, opts?: Record<string,
|
|
182
|
+
export function logWarn(hookName: string, message: string, opts?: Record<string, unknown>): void {
|
|
183
183
|
hookLog("warn", hookName, message, opts);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
export function logError(hookName: string, message: string, opts?: Record<string,
|
|
186
|
+
export function logError(hookName: string, message: string, opts?: Record<string, unknown>): void {
|
|
187
187
|
hookLog("error", hookName, message, opts);
|
|
188
188
|
}
|
|
189
189
|
|
|
@@ -191,7 +191,7 @@ export function logError(hookName: string, message: string, opts?: Record<string
|
|
|
191
191
|
* Log an error that SHOULD be visible to user/model via stderr.
|
|
192
192
|
* Use for real problems needing attention, not routine diagnostics.
|
|
193
193
|
*/
|
|
194
|
-
export function logBlocking(hookName: string, message: string, opts?: Record<string,
|
|
194
|
+
export function logBlocking(hookName: string, message: string, opts?: Record<string, unknown>): void {
|
|
195
195
|
hookLog("error", hookName, message, { ...opts, stderr: true });
|
|
196
196
|
}
|
|
197
197
|
|
|
@@ -204,14 +204,14 @@ export function logDiagnostic(
|
|
|
204
204
|
phase: string,
|
|
205
205
|
summary: string,
|
|
206
206
|
opts?: {
|
|
207
|
-
inputs?:
|
|
208
|
-
decision?:
|
|
209
|
-
reasoning?:
|
|
207
|
+
inputs?: unknown;
|
|
208
|
+
decision?: unknown;
|
|
209
|
+
reasoning?: unknown;
|
|
210
210
|
component?: string;
|
|
211
|
-
data?:
|
|
211
|
+
data?: unknown;
|
|
212
212
|
},
|
|
213
213
|
): void {
|
|
214
|
-
const diagData: Record<string,
|
|
214
|
+
const diagData: Record<string, unknown> = { phase };
|
|
215
215
|
if (opts?.inputs !== undefined) diagData.inputs = opts.inputs;
|
|
216
216
|
if (opts?.decision !== undefined) diagData.decision = opts.decision;
|
|
217
217
|
if (opts?.reasoning !== undefined) diagData.reasoning = opts.reasoning;
|
|
@@ -245,3 +245,6 @@ export function logHookError(
|
|
|
245
245
|
stderr: true,
|
|
246
246
|
});
|
|
247
247
|
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type TmuxColorMode = 'c256' | 'truecolor'
|
|
2
|
+
|
|
3
|
+
export function isWindowsPlatform(platform: NodeJS.Platform = process.platform): boolean {
|
|
4
|
+
return platform === 'win32'
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isNonWindowsPlatform(platform: NodeJS.Platform = process.platform): boolean {
|
|
8
|
+
return !isWindowsPlatform(platform)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function commandLookupBinary(platform: NodeJS.Platform = process.platform): 'where.exe' | 'which' {
|
|
12
|
+
return isWindowsPlatform(platform) ? 'where.exe' : 'which'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function shouldUseShell(platform: NodeJS.Platform = process.platform): boolean {
|
|
16
|
+
return isWindowsPlatform(platform)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function resolveTmuxColorModeForPlatform(
|
|
20
|
+
_platform?: NodeJS.Platform,
|
|
21
|
+
): TmuxColorMode {
|
|
22
|
+
// Always truecolor — Windows now uses psmux (native ConPTY) which supports truecolor natively.
|
|
23
|
+
// The c256 degradation was only needed for tmux-via-MSYS2-bash.
|
|
24
|
+
return 'truecolor'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function applyTmuxLaunchEnv(
|
|
28
|
+
envVars: Record<string, string>,
|
|
29
|
+
_platform?: NodeJS.Platform,
|
|
30
|
+
): Record<string, string> {
|
|
31
|
+
// Always inject truecolor — psmux on Windows and tmux on Unix both support it.
|
|
32
|
+
return { COLORTERM: 'truecolor', ...envVars }
|
|
33
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared preflight health check for provider+model availability.
|
|
3
|
-
* Extracted from plan-review/lib/preflight.ts for reuse by
|
|
3
|
+
* Extracted from plan-review/lib/preflight.ts for reuse by unknown hook.
|
|
4
4
|
*
|
|
5
5
|
* Validates that a CLI tool + model combo works by running a minimal "ping" request.
|
|
6
6
|
*/
|
|
@@ -89,10 +89,11 @@ export async function checkProviderModel(
|
|
|
89
89
|
|
|
90
90
|
logDebug(hook, `${provider}:${model} passed (${latencyMs}ms)`);
|
|
91
91
|
return { provider, model, available: true, latencyMs };
|
|
92
|
-
} catch (
|
|
92
|
+
} catch (error_) {
|
|
93
93
|
const latencyMs = Date.now() - start;
|
|
94
|
-
const error =
|
|
94
|
+
const error = error_ instanceof Error ? error_.message : String(error_);
|
|
95
95
|
logWarn(hook, `${provider}:${model} exception: ${error}`);
|
|
96
96
|
return { provider, model, available: false, latencyMs, error };
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as fs from 'node:fs'
|
|
2
|
+
import * as os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
export interface SentinelIpcPaths {
|
|
6
|
+
tmpDir: string
|
|
7
|
+
inputPath: string
|
|
8
|
+
stdoutPath: string
|
|
9
|
+
stderrPath: string
|
|
10
|
+
sentinelPath: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function sanitizePrefix(prefix: string): string {
|
|
14
|
+
return prefix.replaceAll(/[^a-zA-Z0-9_-]/g, '-')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createSentinelIpcPaths(prefix: string): SentinelIpcPaths {
|
|
18
|
+
const safePrefix = sanitizePrefix(prefix)
|
|
19
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `${safePrefix}-`))
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
tmpDir,
|
|
23
|
+
inputPath: path.join(tmpDir, 'input.txt'),
|
|
24
|
+
stdoutPath: path.join(tmpDir, 'stdout.txt'),
|
|
25
|
+
stderrPath: path.join(tmpDir, 'stderr.txt'),
|
|
26
|
+
sentinelPath: path.join(tmpDir, 'sentinel.txt'),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function buildShellCaptureScript(
|
|
31
|
+
command: string,
|
|
32
|
+
paths: Pick<SentinelIpcPaths, 'inputPath' | 'stdoutPath' | 'stderrPath' | 'sentinelPath'>,
|
|
33
|
+
quote: (input: string) => string,
|
|
34
|
+
): string {
|
|
35
|
+
return [
|
|
36
|
+
command,
|
|
37
|
+
`< ${quote(paths.inputPath)}`,
|
|
38
|
+
`> ${quote(paths.stdoutPath)}`,
|
|
39
|
+
`2> ${quote(paths.stderrPath)}`,
|
|
40
|
+
`; echo $? > ${quote(paths.sentinelPath)}`,
|
|
41
|
+
].join(' ')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function waitForSentinelFile(
|
|
45
|
+
sentinelPath: string,
|
|
46
|
+
timeoutMs: number,
|
|
47
|
+
pollIntervalMs = 250,
|
|
48
|
+
): Promise<boolean> {
|
|
49
|
+
const deadline = Date.now() + timeoutMs
|
|
50
|
+
while (Date.now() < deadline) {
|
|
51
|
+
if (fs.existsSync(sentinelPath)) return true
|
|
52
|
+
await new Promise<void>((resolve) => {
|
|
53
|
+
setTimeout(resolve, pollIntervalMs)
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return fs.existsSync(sentinelPath)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function readSentinelExitCode(sentinelPath: string, fallback = 1): number {
|
|
61
|
+
if (!fs.existsSync(sentinelPath)) return fallback
|
|
62
|
+
|
|
63
|
+
const raw = fs.readFileSync(sentinelPath, 'utf8').trim()
|
|
64
|
+
const parsed = Number.parseInt(raw, 10)
|
|
65
|
+
return Number.isFinite(parsed) ? parsed : fallback
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function readTextIfExists(filePath: string): string {
|
|
69
|
+
if (!filePath || !fs.existsSync(filePath)) return ''
|
|
70
|
+
return fs.readFileSync(filePath, 'utf8')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function cleanupSentinelIpc(paths: Pick<SentinelIpcPaths, 'tmpDir'>): void {
|
|
74
|
+
try {
|
|
75
|
+
fs.rmSync(paths.tmpDir, { recursive: true, force: true })
|
|
76
|
+
} catch {
|
|
77
|
+
// Best-effort cleanup.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function cleanupSentinelPath(sentinelPath: string | undefined): void {
|
|
82
|
+
if (!sentinelPath) return
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
fs.rmSync(path.dirname(sentinelPath), { recursive: true, force: true })
|
|
86
|
+
} catch {
|
|
87
|
+
// Best-effort cleanup.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|