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
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subprocess environment utilities.
|
|
3
|
-
* See SPEC.md §5.10
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { execSync, execFile } from "node:child_process";
|
|
7
|
-
import type { ChildProcess, ExecSyncOptionsWithStringEncoding } from "node:child_process";
|
|
8
|
-
|
|
9
|
-
// ─── Child Process Cleanup ─────────────────────────────────────────────────
|
|
10
|
-
//
|
|
11
|
-
// Track all spawned child processes to prevent orphaned Node.js processes.
|
|
12
|
-
//
|
|
13
|
-
// Problem: When parent process exits (Ctrl+C, abnormal termination), child
|
|
14
|
-
// processes spawned via execFile can become orphaned if they haven't completed.
|
|
15
|
-
//
|
|
16
|
-
// Solution: Track children in a Set, register exit/signal handlers to kill all
|
|
17
|
-
// tracked children before parent exits.
|
|
18
|
-
//
|
|
19
|
-
// Windows behavior: On Windows with shell:true, execFile spawns cmd.exe which
|
|
20
|
-
// spawns the actual node.exe. Using SIGKILL ensures both are terminated.
|
|
21
|
-
|
|
22
|
-
const childProcesses = new Set<ChildProcess>();
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Cleanup all tracked child processes.
|
|
26
|
-
* Called by exit and signal handlers.
|
|
27
|
-
*/
|
|
28
|
-
function cleanupChildren(): void {
|
|
29
|
-
for (const child of childProcesses) {
|
|
30
|
-
try {
|
|
31
|
-
// Use SIGKILL for forceful termination (important on Windows with shell)
|
|
32
|
-
child.kill("SIGKILL");
|
|
33
|
-
} catch {
|
|
34
|
-
// Ignore errors (child may have already exited)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
childProcesses.clear();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Register exit handler (runs when process exits normally)
|
|
41
|
-
process.on("exit", () => {
|
|
42
|
-
cleanupChildren();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// Register signal handlers (Ctrl+C, kill command)
|
|
46
|
-
process.on("SIGINT", () => {
|
|
47
|
-
cleanupChildren();
|
|
48
|
-
process.exit(130); // Standard exit code for SIGINT
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
process.on("SIGTERM", () => {
|
|
52
|
-
cleanupChildren();
|
|
53
|
-
process.exit(143); // Standard exit code for SIGTERM
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Check if this is an internal subprocess call.
|
|
58
|
-
* All hooks should check this and return early to prevent recursion.
|
|
59
|
-
*/
|
|
60
|
-
export function isInternalCall(): boolean {
|
|
61
|
-
return process.env.AIWCLI_INTERNAL_CALL === "true";
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Get environment for internal subprocess calls.
|
|
66
|
-
* Returns a copy of process.env with AIWCLI_INTERNAL_CALL=true and
|
|
67
|
-
* Claude Code nesting-detection env vars removed so subprocess
|
|
68
|
-
* claude instances can run without being blocked.
|
|
69
|
-
*/
|
|
70
|
-
export function getInternalSubprocessEnv(): Record<string, string | undefined> {
|
|
71
|
-
const env: Record<string, string | undefined> = {
|
|
72
|
-
...process.env,
|
|
73
|
-
AIWCLI_INTERNAL_CALL: "true",
|
|
74
|
-
};
|
|
75
|
-
// Explicitly delete vars that block subprocess calls (set to undefined does not work)
|
|
76
|
-
delete env.CLAUDECODE;
|
|
77
|
-
delete env.CLAUDE_CODE_ENTRYPOINT;
|
|
78
|
-
return env;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Find an executable on the system PATH.
|
|
82
|
-
* Uses `where` on Windows, `which` on Unix.
|
|
83
|
-
* On Windows, prefers .cmd/.exe over extensionless shims since
|
|
84
|
-
* execFileSync cannot spawn extensionless shell scripts.
|
|
85
|
-
* Returns the first match or null if not found.
|
|
86
|
-
*/
|
|
87
|
-
export function findExecutable(name: string): string | null {
|
|
88
|
-
try {
|
|
89
|
-
const cmd = process.platform === "win32" ? `where ${name}` : `which ${name}`;
|
|
90
|
-
// shell: true is valid at runtime but Node's TS types restrict it to string for this overload
|
|
91
|
-
const lines = execSync(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], shell: true } as unknown as ExecSyncOptionsWithStringEncoding)
|
|
92
|
-
.trim()
|
|
93
|
-
.split(/\r?\n/)
|
|
94
|
-
.map((l) => l.trim())
|
|
95
|
-
.filter(Boolean);
|
|
96
|
-
|
|
97
|
-
if (lines.length === 0) return null;
|
|
98
|
-
|
|
99
|
-
// On Windows, `where` may return an extensionless shim first (e.g. npm creates
|
|
100
|
-
// both `claude` and `claude.cmd`). execFileSync can't spawn the extensionless
|
|
101
|
-
// one, so prefer .cmd or .exe.
|
|
102
|
-
if (process.platform === "win32") {
|
|
103
|
-
const preferred = lines.find((l) => /\.(cmd|exe)$/i.test(l));
|
|
104
|
-
return preferred ?? lines[0] ?? null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return lines[0] ?? null;
|
|
108
|
-
} catch {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Type guard for Node.js child_process exec errors.
|
|
115
|
-
* ExecSync throws objects with these extra properties on non-zero exit or timeout.
|
|
116
|
-
*/
|
|
117
|
-
export interface ExecSyncError {
|
|
118
|
-
killed: boolean;
|
|
119
|
-
signal: string | null;
|
|
120
|
-
stdout: Buffer | string;
|
|
121
|
-
stderr: Buffer | string;
|
|
122
|
-
status: number | null;
|
|
123
|
-
message: string;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** Check if an unknown error is an ExecSync error with process info. */
|
|
127
|
-
export function isExecSyncError(e: unknown): e is ExecSyncError {
|
|
128
|
-
return (
|
|
129
|
-
typeof e === "object" &&
|
|
130
|
-
e !== null &&
|
|
131
|
-
"killed" in e &&
|
|
132
|
-
"signal" in e
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Quote a string for use as a cmd.exe argument when shell: true.
|
|
138
|
-
* Wraps in double quotes and escapes inner double quotes as "".
|
|
139
|
-
* On non-Windows platforms, returns the string unchanged (execFile
|
|
140
|
-
* handles quoting automatically without shell).
|
|
141
|
-
*/
|
|
142
|
-
/**
|
|
143
|
-
* Normalize a file path for CLI subprocess usage.
|
|
144
|
-
* Replaces backslashes with forward slashes on Windows to avoid
|
|
145
|
-
* cmd.exe escape interpretation issues. No-op on other platforms.
|
|
146
|
-
* Forward slashes are valid path separators on Windows in all modern CLIs.
|
|
147
|
-
*/
|
|
148
|
-
export function normalizePathForCli(p: string): string {
|
|
149
|
-
if (process.platform !== "win32") return p;
|
|
150
|
-
return p.replaceAll("\\", "/");
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export function shellQuoteWin(arg: string): string {
|
|
154
|
-
if (process.platform !== "win32") return arg;
|
|
155
|
-
return '"' + arg.replaceAll('"', '""') + '"';
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// ---------------------------------------------------------------------------
|
|
159
|
-
// Async Subprocess Execution
|
|
160
|
-
// ---------------------------------------------------------------------------
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Result from an async subprocess execution.
|
|
164
|
-
* Never throws — callers inspect fields to determine outcome.
|
|
165
|
-
*/
|
|
166
|
-
export interface ExecResult {
|
|
167
|
-
stdout: string;
|
|
168
|
-
stderr: string;
|
|
169
|
-
exitCode: number;
|
|
170
|
-
killed: boolean;
|
|
171
|
-
signal: string | null;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** Options for execFileAsync. */
|
|
175
|
-
export interface ExecAsyncOptions {
|
|
176
|
-
/** Data piped to the child's stdin. */
|
|
177
|
-
input?: string;
|
|
178
|
-
/** Timeout in milliseconds (not seconds). */
|
|
179
|
-
timeout?: number;
|
|
180
|
-
/** Environment variables for the child process. */
|
|
181
|
-
env?: Record<string, string | undefined>;
|
|
182
|
-
/** Maximum bytes on stdout/stderr. Default: 10 MB. */
|
|
183
|
-
maxBuffer?: number;
|
|
184
|
-
/** Use shell for execution. Required on Windows for .cmd files. */
|
|
185
|
-
shell?: boolean;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Async subprocess execution that does NOT block the event loop.
|
|
190
|
-
* Drop-in replacement for execFileSync in Promise-based parallel patterns.
|
|
191
|
-
*
|
|
192
|
-
* Returns ExecResult on both success and non-zero exit.
|
|
193
|
-
* On timeout: result.killed = true, result.signal = "SIGTERM".
|
|
194
|
-
* On spawn failure: result.exitCode = -1, result.stderr contains error.
|
|
195
|
-
*/
|
|
196
|
-
export function execFileAsync(
|
|
197
|
-
file: string,
|
|
198
|
-
args: string[],
|
|
199
|
-
options?: ExecAsyncOptions,
|
|
200
|
-
): Promise<ExecResult> {
|
|
201
|
-
return new Promise((resolve) => {
|
|
202
|
-
const child = execFile(
|
|
203
|
-
file,
|
|
204
|
-
args,
|
|
205
|
-
{
|
|
206
|
-
encoding: "utf-8",
|
|
207
|
-
timeout: options?.timeout ?? 0,
|
|
208
|
-
env: options?.env as NodeJS.ProcessEnv,
|
|
209
|
-
maxBuffer: options?.maxBuffer ?? 10 * 1024 * 1024,
|
|
210
|
-
shell: options?.shell,
|
|
211
|
-
},
|
|
212
|
-
(error, stdout, stderr) => {
|
|
213
|
-
if (error) {
|
|
214
|
-
// execFile callback error includes process exit info
|
|
215
|
-
const errObj = error as unknown as Record<string, unknown>;
|
|
216
|
-
resolve({
|
|
217
|
-
stdout: String(stdout ?? ""),
|
|
218
|
-
stderr: String(stderr ?? ""),
|
|
219
|
-
exitCode: typeof errObj.code === "number" ? errObj.code : (error as any).status ?? 1,
|
|
220
|
-
killed: Boolean(errObj.killed),
|
|
221
|
-
signal: typeof errObj.signal === "string" ? errObj.signal : null,
|
|
222
|
-
});
|
|
223
|
-
} else {
|
|
224
|
-
resolve({
|
|
225
|
-
stdout: String(stdout ?? ""),
|
|
226
|
-
stderr: String(stderr ?? ""),
|
|
227
|
-
exitCode: 0,
|
|
228
|
-
killed: false,
|
|
229
|
-
signal: null,
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
},
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
// Track child process for cleanup on abnormal parent exit
|
|
236
|
-
childProcesses.add(child);
|
|
237
|
-
|
|
238
|
-
// Remove from tracking when child exits (normal completion)
|
|
239
|
-
child.on("exit", () => {
|
|
240
|
-
childProcesses.delete(child);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
// Pipe input to stdin if provided
|
|
244
|
-
if (options?.input !== null && options?.input !== undefined && child.stdin) {
|
|
245
|
-
child.stdin.write(options.input);
|
|
246
|
-
child.stdin.end();
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
}
|
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared pane driver helpers for launching CLI tools in visible panes.
|
|
3
|
-
*
|
|
4
|
-
* This module now delegates pane creation to platform-specific launchers:
|
|
5
|
-
* - tmux (Linux/macOS in tmux sessions)
|
|
6
|
-
* - Windows Terminal split-pane
|
|
7
|
-
* - Windows new-window fallback
|
|
8
|
-
*
|
|
9
|
-
* Prompt delivery is handled at launch time by passing prompt text as a CLI
|
|
10
|
-
* argument for REPL mode, rather than injecting into an already-running pane.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import * as fs from "node:fs";
|
|
14
|
-
|
|
15
|
-
import { createPaneLauncher, type PaneBackend, type PaneSplitDirection } from "./pane-launcher.js";
|
|
16
|
-
import { cleanupSentinelIpc, createSentinelIpcPaths } from "./sentinel-ipc.js";
|
|
17
|
-
import { execFileAsync, findExecutable } from "./subprocess-utils.js";
|
|
18
|
-
import {
|
|
19
|
-
getTmuxAvailability,
|
|
20
|
-
normalizeSplitFlag,
|
|
21
|
-
quoteForSh,
|
|
22
|
-
type TmuxAvailability,
|
|
23
|
-
type TmuxSplitFlag,
|
|
24
|
-
} from "./launchers/tmux-launcher.js";
|
|
25
|
-
|
|
26
|
-
export type { TmuxAvailability, TmuxSplitFlag };
|
|
27
|
-
export { getTmuxAvailability, normalizeSplitFlag, quoteForSh };
|
|
28
|
-
|
|
29
|
-
export type DriverMode = "exec" | "repl";
|
|
30
|
-
export type TmuxSplitOption = TmuxSplitFlag | "auto";
|
|
31
|
-
|
|
32
|
-
export interface DriverPreflightResult {
|
|
33
|
-
available: boolean;
|
|
34
|
-
error?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export type DriverPreflight =
|
|
38
|
-
(toolPath: string) => Promise<DriverPreflightResult> | DriverPreflightResult;
|
|
39
|
-
|
|
40
|
-
export interface LaunchDriverOptions {
|
|
41
|
-
toolName: string;
|
|
42
|
-
toolBin?: string;
|
|
43
|
-
mode?: DriverMode;
|
|
44
|
-
args?: string[];
|
|
45
|
-
env?: Record<string, string>;
|
|
46
|
-
promptPath?: string;
|
|
47
|
-
splitFlag?: TmuxSplitOption;
|
|
48
|
-
splitTarget?: string;
|
|
49
|
-
autoClose?: boolean;
|
|
50
|
-
holdPane?: boolean;
|
|
51
|
-
holdMessage?: string;
|
|
52
|
-
allowExecFallback?: boolean;
|
|
53
|
-
preflight?: DriverPreflight;
|
|
54
|
-
timeoutMs?: number;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface LaunchDriverResult {
|
|
58
|
-
launched: boolean;
|
|
59
|
-
usedTmux: boolean;
|
|
60
|
-
backend: PaneBackend;
|
|
61
|
-
mode: DriverMode;
|
|
62
|
-
toolPath?: string;
|
|
63
|
-
paneId?: string;
|
|
64
|
-
sentinelPath?: string;
|
|
65
|
-
exitCode?: number;
|
|
66
|
-
stdout?: string;
|
|
67
|
-
reason?: string;
|
|
68
|
-
stderr?: string;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function buildEnvPrefix(env: Record<string, string>): string {
|
|
72
|
-
return Object.entries(env)
|
|
73
|
-
.map(([key, value]) => `${key}=${quoteForSh(value)}`)
|
|
74
|
-
.join(" ");
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function quoteForPowerShell(input: string): string {
|
|
78
|
-
return `'${input.replaceAll("'", "''")}'`;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function buildCommandArgs(
|
|
82
|
-
args: string[],
|
|
83
|
-
mode: DriverMode,
|
|
84
|
-
promptPath?: string,
|
|
85
|
-
): string[] {
|
|
86
|
-
if (mode !== "repl" || !promptPath) return args;
|
|
87
|
-
const promptText = fs.readFileSync(promptPath, "utf-8");
|
|
88
|
-
return [...args, promptText];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function buildShToolCommand(
|
|
92
|
-
toolPath: string,
|
|
93
|
-
args: string[],
|
|
94
|
-
env: Record<string, string>,
|
|
95
|
-
mode: DriverMode,
|
|
96
|
-
promptPath?: string,
|
|
97
|
-
): string {
|
|
98
|
-
const envPrefix = buildEnvPrefix(env);
|
|
99
|
-
const commandArgs = buildCommandArgs(args, mode, promptPath);
|
|
100
|
-
const argPart = commandArgs.map((arg) => quoteForSh(arg)).join(" ");
|
|
101
|
-
const base = [envPrefix, quoteForSh(toolPath), argPart]
|
|
102
|
-
.filter(Boolean)
|
|
103
|
-
.join(" ");
|
|
104
|
-
|
|
105
|
-
if (mode === "exec" && promptPath) {
|
|
106
|
-
return `${base} < ${quoteForSh(promptPath)}`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return base;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function buildPowerShellToolCommand(
|
|
113
|
-
toolPath: string,
|
|
114
|
-
args: string[],
|
|
115
|
-
env: Record<string, string>,
|
|
116
|
-
mode: DriverMode,
|
|
117
|
-
promptPath?: string,
|
|
118
|
-
): string {
|
|
119
|
-
const envPrefix = Object.entries(env)
|
|
120
|
-
.map(([key, value]) => `$env:${key}=${quoteForPowerShell(value)}`)
|
|
121
|
-
.join("; ");
|
|
122
|
-
|
|
123
|
-
const commandArgs = buildCommandArgs(args, mode, promptPath);
|
|
124
|
-
const argArray = commandArgs.map((arg) => quoteForPowerShell(arg)).join(", ");
|
|
125
|
-
const invocation = `& ${quoteForPowerShell(toolPath)}${argArray ? ` @(${argArray})` : ""}`;
|
|
126
|
-
|
|
127
|
-
const body = mode === "exec" && promptPath
|
|
128
|
-
? `Get-Content -Raw -Path ${quoteForPowerShell(promptPath)} | ${invocation}`
|
|
129
|
-
: invocation;
|
|
130
|
-
|
|
131
|
-
return [envPrefix, body].filter(Boolean).join("; ");
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function wrapPaneCommand(
|
|
135
|
-
backend: PaneBackend,
|
|
136
|
-
command: string,
|
|
137
|
-
sentinelPath: string,
|
|
138
|
-
autoClose: boolean,
|
|
139
|
-
holdPane: boolean,
|
|
140
|
-
holdMessage: string,
|
|
141
|
-
): string {
|
|
142
|
-
if (backend === "tmux") {
|
|
143
|
-
const base = `${command}; code=$?; printf '%s' "$code" > ${quoteForSh(sentinelPath)}`;
|
|
144
|
-
|
|
145
|
-
if (autoClose) {
|
|
146
|
-
return `${base}; tmux kill-pane -t "$TMUX_PANE" >/dev/null 2>&1 || true; exit $code`;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (holdPane) {
|
|
150
|
-
return `${base}; echo; echo ${quoteForSh(holdMessage)}; exec bash`;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return `${base}; exit $code`;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const base = `${command}; $code = $LASTEXITCODE; Set-Content -Path ${quoteForPowerShell(sentinelPath)} -Value $code -NoNewline`;
|
|
157
|
-
|
|
158
|
-
if (holdPane && !autoClose) {
|
|
159
|
-
return `${base}; Write-Host ''; Write-Host ${quoteForPowerShell(holdMessage)}; Read-Host -Prompt 'Press Enter to close' | Out-Null`;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return `${base}; exit $code`;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function mapSplitDirection(splitFlag: TmuxSplitOption | undefined): PaneSplitDirection {
|
|
166
|
-
if (splitFlag === "auto") return "auto";
|
|
167
|
-
if (splitFlag === "-v") return "v";
|
|
168
|
-
return "h";
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export function isTruthy(value: string | undefined): boolean {
|
|
172
|
-
if (!value) return false;
|
|
173
|
-
const normalized = value.trim().toLowerCase();
|
|
174
|
-
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export function resolveToolPath(toolName: string, toolBin?: string): string | null {
|
|
178
|
-
const bin = toolBin?.trim() || toolName;
|
|
179
|
-
return findExecutable(bin);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function buildCommandForBackend(
|
|
183
|
-
backend: PaneBackend,
|
|
184
|
-
toolPath: string,
|
|
185
|
-
args: string[],
|
|
186
|
-
envVars: Record<string, string>,
|
|
187
|
-
mode: DriverMode,
|
|
188
|
-
promptPath?: string,
|
|
189
|
-
): string {
|
|
190
|
-
if (backend === "tmux") {
|
|
191
|
-
return buildShToolCommand(toolPath, args, envVars, mode, promptPath);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return buildPowerShellToolCommand(toolPath, args, envVars, mode, promptPath);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export async function launchDriverInTmuxOrFallback(
|
|
198
|
-
options: LaunchDriverOptions,
|
|
199
|
-
): Promise<LaunchDriverResult> {
|
|
200
|
-
const mode = options.mode ?? "exec";
|
|
201
|
-
const args = options.args ?? [];
|
|
202
|
-
const envVars = options.env ?? {};
|
|
203
|
-
const timeoutMs = options.timeoutMs ?? 0;
|
|
204
|
-
const toolPath = resolveToolPath(options.toolName, options.toolBin);
|
|
205
|
-
|
|
206
|
-
if (!toolPath) {
|
|
207
|
-
return {
|
|
208
|
-
launched: false,
|
|
209
|
-
usedTmux: false,
|
|
210
|
-
backend: "exec",
|
|
211
|
-
mode,
|
|
212
|
-
reason: `${options.toolBin ?? options.toolName} not found on PATH`,
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (options.preflight) {
|
|
217
|
-
const preflight = await options.preflight(toolPath);
|
|
218
|
-
if (!preflight.available) {
|
|
219
|
-
return {
|
|
220
|
-
launched: false,
|
|
221
|
-
usedTmux: false,
|
|
222
|
-
backend: "exec",
|
|
223
|
-
mode,
|
|
224
|
-
toolPath,
|
|
225
|
-
reason: preflight.error ?? "driver preflight failed",
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (options.promptPath && !fs.existsSync(options.promptPath)) {
|
|
231
|
-
return {
|
|
232
|
-
launched: false,
|
|
233
|
-
usedTmux: false,
|
|
234
|
-
backend: "exec",
|
|
235
|
-
mode,
|
|
236
|
-
toolPath,
|
|
237
|
-
reason: `prompt file not found: ${options.promptPath}`,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const paneLauncher = await createPaneLauncher({ requireTmuxSession: true });
|
|
242
|
-
if (paneLauncher) {
|
|
243
|
-
const sentinel = createSentinelIpcPaths(`aiwcli-pane-${options.toolName}`);
|
|
244
|
-
|
|
245
|
-
try {
|
|
246
|
-
const baseCommand = buildCommandForBackend(
|
|
247
|
-
paneLauncher.backend,
|
|
248
|
-
toolPath,
|
|
249
|
-
args,
|
|
250
|
-
envVars,
|
|
251
|
-
mode,
|
|
252
|
-
options.promptPath,
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
const holdMessage = options.holdMessage ?? "[aiwcli] Driver exited. Pane held open.";
|
|
256
|
-
const paneCommand = wrapPaneCommand(
|
|
257
|
-
paneLauncher.backend,
|
|
258
|
-
baseCommand,
|
|
259
|
-
sentinel.sentinelPath,
|
|
260
|
-
Boolean(options.autoClose),
|
|
261
|
-
Boolean(options.holdPane) && !Boolean(options.autoClose),
|
|
262
|
-
holdMessage,
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
const paneResult = await paneLauncher.launch({
|
|
266
|
-
command: paneCommand,
|
|
267
|
-
splitDirection: mapSplitDirection(options.splitFlag),
|
|
268
|
-
splitTarget: options.splitTarget,
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
if (!paneResult.launched) {
|
|
272
|
-
cleanupSentinelIpc(sentinel);
|
|
273
|
-
return {
|
|
274
|
-
launched: false,
|
|
275
|
-
usedTmux: paneLauncher.backend === "tmux",
|
|
276
|
-
backend: paneLauncher.backend,
|
|
277
|
-
mode,
|
|
278
|
-
toolPath,
|
|
279
|
-
reason: paneResult.reason ?? "pane launch failed",
|
|
280
|
-
stderr: paneResult.stderr,
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return {
|
|
285
|
-
launched: true,
|
|
286
|
-
usedTmux: paneLauncher.backend === "tmux",
|
|
287
|
-
backend: paneLauncher.backend,
|
|
288
|
-
mode,
|
|
289
|
-
toolPath,
|
|
290
|
-
paneId: paneResult.paneId,
|
|
291
|
-
sentinelPath: sentinel.sentinelPath,
|
|
292
|
-
};
|
|
293
|
-
} catch (error) {
|
|
294
|
-
cleanupSentinelIpc(sentinel);
|
|
295
|
-
return {
|
|
296
|
-
launched: false,
|
|
297
|
-
usedTmux: paneLauncher.backend === "tmux",
|
|
298
|
-
backend: paneLauncher.backend,
|
|
299
|
-
mode,
|
|
300
|
-
toolPath,
|
|
301
|
-
reason: `pane launch failed: ${String(error)}`,
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (options.allowExecFallback) {
|
|
307
|
-
const commandArgs = buildCommandArgs(args, mode, options.promptPath);
|
|
308
|
-
const input = mode === "exec" && options.promptPath
|
|
309
|
-
? fs.readFileSync(options.promptPath, "utf-8")
|
|
310
|
-
: undefined;
|
|
311
|
-
|
|
312
|
-
const result = await execFileAsync(toolPath, commandArgs, {
|
|
313
|
-
input,
|
|
314
|
-
timeout: timeoutMs,
|
|
315
|
-
env: { ...process.env, ...envVars },
|
|
316
|
-
shell: process.platform === "win32",
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
return {
|
|
320
|
-
launched: true,
|
|
321
|
-
usedTmux: false,
|
|
322
|
-
backend: "exec",
|
|
323
|
-
mode,
|
|
324
|
-
toolPath,
|
|
325
|
-
exitCode: result.exitCode,
|
|
326
|
-
stdout: result.stdout,
|
|
327
|
-
stderr: result.stderr,
|
|
328
|
-
reason: result.exitCode === 0 ? undefined : `fallback exec exited ${result.exitCode}`,
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const tmux = getTmuxAvailability({ requireSessionEnv: true });
|
|
333
|
-
return {
|
|
334
|
-
launched: false,
|
|
335
|
-
usedTmux: false,
|
|
336
|
-
backend: "exec",
|
|
337
|
-
mode,
|
|
338
|
-
toolPath,
|
|
339
|
-
reason: `${tmux.reason ?? "no available pane launcher"}; fallback disabled`,
|
|
340
|
-
};
|
|
341
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { execFileAsync } from "./subprocess-utils.js";
|
|
2
|
-
|
|
3
|
-
export type TmuxSplitFlag = "-h" | "-v";
|
|
4
|
-
|
|
5
|
-
export interface TmuxPaneInfo {
|
|
6
|
-
paneId: string;
|
|
7
|
-
width: number;
|
|
8
|
-
height: number;
|
|
9
|
-
active: boolean;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface PlacementResult {
|
|
13
|
-
targetPane: string;
|
|
14
|
-
splitFlag: TmuxSplitFlag;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const LIST_PANES_FORMAT = "#{pane_id} #{pane_width} #{pane_height} #{pane_active}";
|
|
18
|
-
|
|
19
|
-
export async function listPanes(tmuxPath: string): Promise<TmuxPaneInfo[]> {
|
|
20
|
-
const result = await execFileAsync(tmuxPath, ["list-panes", "-F", LIST_PANES_FORMAT], {
|
|
21
|
-
timeout: 3000,
|
|
22
|
-
});
|
|
23
|
-
if (result.exitCode !== 0) return [];
|
|
24
|
-
|
|
25
|
-
const panes: TmuxPaneInfo[] = [];
|
|
26
|
-
for (const rawLine of result.stdout.split(/\r?\n/)) {
|
|
27
|
-
const line = rawLine.trim();
|
|
28
|
-
if (!line) continue;
|
|
29
|
-
|
|
30
|
-
const parts = line.split(/\s+/);
|
|
31
|
-
if (parts.length < 4) continue;
|
|
32
|
-
|
|
33
|
-
const paneId = parts[0] ?? "";
|
|
34
|
-
const width = Number.parseInt(parts[1] ?? "", 10);
|
|
35
|
-
const height = Number.parseInt(parts[2] ?? "", 10);
|
|
36
|
-
const activeRaw = parts[3] ?? "";
|
|
37
|
-
|
|
38
|
-
if (!paneId || !Number.isFinite(width) || !Number.isFinite(height)) continue;
|
|
39
|
-
panes.push({
|
|
40
|
-
paneId,
|
|
41
|
-
width,
|
|
42
|
-
height,
|
|
43
|
-
active: activeRaw === "1",
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return panes;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function findBestSplit(panes: TmuxPaneInfo[]): PlacementResult | null {
|
|
51
|
-
if (panes.length === 0) return null;
|
|
52
|
-
|
|
53
|
-
let best = panes[0];
|
|
54
|
-
let bestArea = best.width * best.height;
|
|
55
|
-
|
|
56
|
-
for (let i = 1; i < panes.length; i++) {
|
|
57
|
-
const pane = panes[i];
|
|
58
|
-
if (!pane) continue;
|
|
59
|
-
const area = pane.width * pane.height;
|
|
60
|
-
if (area > bestArea) {
|
|
61
|
-
best = pane;
|
|
62
|
-
bestArea = area;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Terminal cells are ~2x taller than wide (typical monospace font aspect ratio).
|
|
67
|
-
// Correct for this so BSP splits the visually longer axis, not just the higher
|
|
68
|
-
// character count. Without this, a 77x68 pane looks "wider" in chars but is
|
|
69
|
-
// actually much taller in pixels, and should split top/bottom (-v), not left/right.
|
|
70
|
-
const CELL_ASPECT_RATIO = 2.0;
|
|
71
|
-
const visualWidth = best.width;
|
|
72
|
-
const visualHeight = best.height * CELL_ASPECT_RATIO;
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
targetPane: best.paneId,
|
|
76
|
-
splitFlag: visualWidth >= visualHeight ? "-h" : "-v",
|
|
77
|
-
};
|
|
78
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "lib-ts-tests",
|
|
3
|
-
"private": true,
|
|
4
|
-
"type": "module",
|
|
5
|
-
"scripts": {
|
|
6
|
-
"test": "mocha",
|
|
7
|
-
"test:unit": "mocha '__tests__/base/**/*.test.ts' '__tests__/templates/**/*.test.ts'",
|
|
8
|
-
"test:contract": "mocha '__tests__/context/**/*.test.ts' '__tests__/handoff/**/*.test.ts'",
|
|
9
|
-
"test:integration": "mocha '__tests__/integration/**/*.test.ts'",
|
|
10
|
-
"test:parity": "mocha '__tests__/integration/python-parity.test.ts'"
|
|
11
|
-
},
|
|
12
|
-
"devDependencies": {
|
|
13
|
-
"mocha": "^10.0.0",
|
|
14
|
-
"chai": "^5.0.0",
|
|
15
|
-
"@types/mocha": "^10.0.0",
|
|
16
|
-
"@types/sinon": "^17.0.0",
|
|
17
|
-
"sinon": "^17.0.0",
|
|
18
|
-
"typescript": "^5.0.0"
|
|
19
|
-
}
|
|
20
|
-
}
|