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
package/dist/lib/spawn.js
CHANGED
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
import { execSync, spawn as nodeSpawn } from 'node:child_process';
|
|
42
42
|
import { debug, debugSpawn } from './debug.js';
|
|
43
43
|
import { ProcessSpawnError } from './errors.js';
|
|
44
|
+
import { classifySpawnError, resolveWindowsSpawnArgs } from './spawn-errors.js';
|
|
44
45
|
/**
|
|
45
46
|
* Spawn an external process and return its exit code.
|
|
46
47
|
*
|
|
@@ -94,9 +95,10 @@ export async function spawnProcess(command, args = [], options = {}) {
|
|
|
94
95
|
catch (error) {
|
|
95
96
|
// If command not found and .cmd file exists, use cmd.exe wrapper
|
|
96
97
|
// This avoids DEP0190 deprecation warning while supporting npm-installed commands
|
|
97
|
-
|
|
98
|
+
const windowsSpawnArgs = resolveWindowsSpawnArgs(command, args, commandExistsInPath);
|
|
99
|
+
if (error instanceof ProcessSpawnError && error.code === 'ENOENT' && windowsSpawnArgs) {
|
|
98
100
|
// Use cmd.exe /c to execute .cmd file without shell mode or deprecation warning
|
|
99
|
-
return attemptSpawn(
|
|
101
|
+
return attemptSpawn(windowsSpawnArgs.command, windowsSpawnArgs.args, { cwd, stdio, detached, shell: false });
|
|
100
102
|
}
|
|
101
103
|
throw error;
|
|
102
104
|
}
|
|
@@ -126,15 +128,7 @@ function attemptSpawn(command, args, spawnOptions) {
|
|
|
126
128
|
const childProcess = nodeSpawn(command, args, spawnOptions);
|
|
127
129
|
// Handle spawn errors (ENOENT, EACCES, etc.)
|
|
128
130
|
childProcess.on('error', (error) => {
|
|
129
|
-
|
|
130
|
-
reject(new ProcessSpawnError(`Command not found: ${command}. Install Claude Code from https://claude.ai/download.`, 'ENOENT'));
|
|
131
|
-
}
|
|
132
|
-
else if (error.code === 'EACCES') {
|
|
133
|
-
reject(new ProcessSpawnError(`Permission denied: ${command}. Check file permissions.`, 'EACCES'));
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
reject(new ProcessSpawnError(`Failed to spawn ${command}: ${error.message}. Check that the command exists and is executable.`, error.code));
|
|
137
|
-
}
|
|
131
|
+
reject(classifySpawnError(command, error));
|
|
138
132
|
});
|
|
139
133
|
// Capture exit code on process close
|
|
140
134
|
childProcess.on('close', (code, signal) => {
|
|
@@ -34,10 +34,10 @@ interface TemplateInstallationStatus {
|
|
|
34
34
|
existing: TemplateItemStatus[];
|
|
35
35
|
/** Items that are missing from target directory */
|
|
36
36
|
missing: TemplateItemStatus[];
|
|
37
|
-
/** The method-specific
|
|
38
|
-
|
|
39
|
-
/** Whether the
|
|
40
|
-
|
|
37
|
+
/** The method-specific runtime folder name (e.g., '_gsd', '_bmad') */
|
|
38
|
+
runtimeFolder: null | string;
|
|
39
|
+
/** Whether the runtime folder exists */
|
|
40
|
+
runtimeFolderExists: boolean;
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* Result of template installation
|
|
@@ -79,7 +79,6 @@ export declare function copyDir(src: string, dest: string, excludeIdeFolders?: b
|
|
|
79
79
|
*
|
|
80
80
|
* Template structure:
|
|
81
81
|
* - Non-dot folders (e.g., _bmad/, GSR/) → .aiwcli/ (always overwritten)
|
|
82
|
-
* - _shared/ → .aiwcli/_shared/ (always overwritten)
|
|
83
82
|
* - IDE dot folders (e.g., .claude/) → decomposed into method-owned subdirs
|
|
84
83
|
*
|
|
85
84
|
* Settings reconstruction is handled separately by the caller via reconstructIdeSettings().
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
|
-
import {
|
|
2
|
+
import { join } from 'node:path';
|
|
3
3
|
import { IdePathResolver } from './ide-path-resolver.js';
|
|
4
4
|
import { pathExists } from './paths.js';
|
|
5
5
|
/**
|
|
@@ -17,11 +17,11 @@ export async function checkTemplateStatus(templatePath, targetDir, ides, templat
|
|
|
17
17
|
const missing = [];
|
|
18
18
|
// Scan template directory
|
|
19
19
|
const entries = await fs.readdir(templatePath, { withFileTypes: true });
|
|
20
|
-
// Identify
|
|
20
|
+
// Identify method runtime folder based on template name
|
|
21
21
|
// Convention: _templatename (e.g., _gsd, _bmad)
|
|
22
|
-
const
|
|
23
|
-
let
|
|
24
|
-
let
|
|
22
|
+
const runtimeFolderName = `_${templateName}`;
|
|
23
|
+
let runtimeFolder = null;
|
|
24
|
+
let runtimeFolderExists = false;
|
|
25
25
|
// Filter entries to only include relevant items (skip non-selected IDE folders and excluded patterns)
|
|
26
26
|
const relevantEntries = entries.filter((entry) => {
|
|
27
27
|
// Skip excluded patterns (test files, cache, etc.)
|
|
@@ -58,17 +58,17 @@ export async function checkTemplateStatus(templatePath, targetDir, ides, templat
|
|
|
58
58
|
else {
|
|
59
59
|
missing.push(status);
|
|
60
60
|
}
|
|
61
|
-
// Track
|
|
62
|
-
if (status.name ===
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
// Track method runtime folder
|
|
62
|
+
if (status.name === runtimeFolderName) {
|
|
63
|
+
runtimeFolder = runtimeFolderName;
|
|
64
|
+
runtimeFolderExists = status.exists;
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
return {
|
|
68
68
|
existing,
|
|
69
69
|
missing,
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
runtimeFolder,
|
|
71
|
+
runtimeFolderExists,
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
@@ -101,7 +101,7 @@ export async function copyDir(src, dest, excludeIdeFolders = false) {
|
|
|
101
101
|
if (shouldExclude(entry.name)) {
|
|
102
102
|
return false;
|
|
103
103
|
}
|
|
104
|
-
// Exclude IDE config folders if requested (used for
|
|
104
|
+
// Exclude IDE config folders if requested (used for core folder)
|
|
105
105
|
// These folders are used for settings merging, not direct installation
|
|
106
106
|
if (excludeIdeFolders && entry.isDirectory() && entry.name.startsWith('.')) {
|
|
107
107
|
return false;
|
|
@@ -150,7 +150,6 @@ async function mergeDirectory(src, dest) {
|
|
|
150
150
|
*
|
|
151
151
|
* Template structure:
|
|
152
152
|
* - Non-dot folders (e.g., _bmad/, GSR/) → .aiwcli/ (always overwritten)
|
|
153
|
-
* - _shared/ → .aiwcli/_shared/ (always overwritten)
|
|
154
153
|
* - IDE dot folders (e.g., .claude/) → decomposed into method-owned subdirs
|
|
155
154
|
*
|
|
156
155
|
* Settings reconstruction is handled separately by the caller via reconstructIdeSettings().
|
|
@@ -205,27 +204,6 @@ export async function installTemplate(config) {
|
|
|
205
204
|
});
|
|
206
205
|
const nonDotResults = await Promise.all(nonDotInstalls);
|
|
207
206
|
installedFolders.push(...nonDotResults);
|
|
208
|
-
// Install root-level _shared directory (shared across all templates)
|
|
209
|
-
// Exclude IDE config folders (.claude, .windsurf) - they are used for settings merging only
|
|
210
|
-
const templatesRoot = dirname(templatePath);
|
|
211
|
-
const rootSharedSrc = join(templatesRoot, '_shared');
|
|
212
|
-
const rootSharedDest = join(containerDir, '_shared');
|
|
213
|
-
if (await pathExists(rootSharedSrc)) {
|
|
214
|
-
await copyDir(rootSharedSrc, rootSharedDest, true); // excludeIdeFolders = true
|
|
215
|
-
installedFolders.push('_shared');
|
|
216
|
-
// Copy shared IDE content (e.g., _shared/.claude/commands/handoff.md)
|
|
217
|
-
// These are non-method-owned files that live in IDE folders
|
|
218
|
-
const sharedIdeInstalls = ides.map(async (ide) => {
|
|
219
|
-
const sharedIdeFolder = join(rootSharedSrc, `.${ide}`);
|
|
220
|
-
if (await pathExists(sharedIdeFolder)) {
|
|
221
|
-
const destIdeFolder = resolver.getIdeDir(ide);
|
|
222
|
-
await fs.mkdir(destIdeFolder, { recursive: true });
|
|
223
|
-
// Merge shared IDE content, skipping files that already exist
|
|
224
|
-
await mergeDirectory(sharedIdeFolder, destIdeFolder);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
await Promise.all(sharedIdeInstalls);
|
|
228
|
-
}
|
|
229
207
|
// Install method-owned IDE content (decomposed approach)
|
|
230
208
|
// Instead of copying entire .claude/ from template, only copy method-namespaced subdirectories
|
|
231
209
|
const ideInstalls = ides.map(async (ide) => {
|
|
@@ -260,9 +238,33 @@ export async function installTemplate(config) {
|
|
|
260
238
|
const ideResults = (await Promise.all(ideInstalls)).filter((result) => result !== null);
|
|
261
239
|
installedFolders.push(...ideResults);
|
|
262
240
|
// Settings reconstruction is handled by the caller via reconstructIdeSettings()
|
|
241
|
+
// Write resolved CLI binary path so template scripts can shell out to `aiw`
|
|
242
|
+
await writeAiwBinPath(containerDir);
|
|
263
243
|
return {
|
|
264
244
|
installedFolders,
|
|
265
245
|
sharedSettingsMerged: false,
|
|
266
246
|
templatePath,
|
|
267
247
|
};
|
|
268
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Write the resolved `aiw` binary path to `.aiwcli/.aiw-bin-path`.
|
|
251
|
+
* Template scripts read this file to find the CLI binary for `aiw launch`.
|
|
252
|
+
*/
|
|
253
|
+
async function writeAiwBinPath(containerDir) {
|
|
254
|
+
try {
|
|
255
|
+
// process.argv[1] is the main script entry point (e.g., /path/to/aiw/bin/run.js)
|
|
256
|
+
// Resolve to the bin directory to find the actual `aiw` executable
|
|
257
|
+
const { execSync } = await import('node:child_process');
|
|
258
|
+
const cmd = process.platform === 'win32' ? 'where aiw' : 'which aiw';
|
|
259
|
+
const resolved = execSync(cmd, { encoding: 'utf8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] })
|
|
260
|
+
.trim()
|
|
261
|
+
.split(/\r?\n/)[0]
|
|
262
|
+
?.trim();
|
|
263
|
+
if (resolved) {
|
|
264
|
+
await fs.writeFile(join(containerDir, '.aiw-bin-path'), resolved, 'utf8');
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
// Best-effort — aiw will still be found on PATH at runtime
|
|
269
|
+
}
|
|
270
|
+
}
|
|
@@ -12,15 +12,14 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function getTemplatePath(templateName: string): Promise<string>;
|
|
14
14
|
/**
|
|
15
|
-
* Get list of available template names by scanning the templates directory.
|
|
15
|
+
* Get list of available method template names by scanning the templates directory.
|
|
16
16
|
*
|
|
17
|
-
* @returns Array of template names (e.g., ['bmad', '
|
|
17
|
+
* @returns Array of method template names (e.g., ['bmad', 'cc-native'])
|
|
18
18
|
* @throws Error if templates directory cannot be read (indicates corrupted installation)
|
|
19
19
|
*/
|
|
20
|
+
export declare function getAvailableTemplates(): Promise<string[]>;
|
|
20
21
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* @returns Absolute path to the _shared template
|
|
22
|
+
* Discover IDE names available in a template path by scanning top-level dot-folders.
|
|
23
|
+
* Example: .claude, .codex -> ['claude', 'codex']
|
|
24
24
|
*/
|
|
25
|
-
export declare function
|
|
26
|
-
export declare function getAvailableTemplates(): Promise<string[]>;
|
|
25
|
+
export declare function getTemplateIdeNamesByPath(templatePath: string): Promise<string[]>;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
function getTemplatesRootDir() {
|
|
5
|
+
const currentFileUrl = import.meta.url;
|
|
6
|
+
const currentFilePath = fileURLToPath(currentFileUrl);
|
|
7
|
+
const currentDir = dirname(currentFilePath);
|
|
8
|
+
return join(currentDir, '..', 'templates');
|
|
9
|
+
}
|
|
4
10
|
/**
|
|
5
11
|
* Resolve the absolute path to a bundled template root.
|
|
6
12
|
* Works in both development (src/) and production (dist/) contexts.
|
|
@@ -21,13 +27,10 @@ export async function getTemplatePath(templateName) {
|
|
|
21
27
|
// Get the directory of this file
|
|
22
28
|
// In dev: .../aiwcli/src/lib/
|
|
23
29
|
// In prod: .../aiwcli/dist/lib/
|
|
24
|
-
const currentFileUrl = import.meta.url;
|
|
25
|
-
const currentFilePath = fileURLToPath(currentFileUrl);
|
|
26
|
-
const currentDir = dirname(currentFilePath);
|
|
27
30
|
// Go up one level and into templates/<templateName>
|
|
28
31
|
// src/lib/ → src/templates/<templateName>/
|
|
29
32
|
// dist/lib/ → dist/templates/<templateName>/
|
|
30
|
-
const templatePath = join(
|
|
33
|
+
const templatePath = join(getTemplatesRootDir(), templateName);
|
|
31
34
|
// Validate template exists
|
|
32
35
|
try {
|
|
33
36
|
await fs.access(templatePath);
|
|
@@ -38,29 +41,18 @@ export async function getTemplatePath(templateName) {
|
|
|
38
41
|
return templatePath;
|
|
39
42
|
}
|
|
40
43
|
/**
|
|
41
|
-
* Get list of available template names by scanning the templates directory.
|
|
44
|
+
* Get list of available method template names by scanning the templates directory.
|
|
42
45
|
*
|
|
43
|
-
* @returns Array of template names (e.g., ['bmad', '
|
|
46
|
+
* @returns Array of method template names (e.g., ['bmad', 'cc-native'])
|
|
44
47
|
* @throws Error if templates directory cannot be read (indicates corrupted installation)
|
|
45
48
|
*/
|
|
46
|
-
/**
|
|
47
|
-
* Get the absolute path to the _shared template directory.
|
|
48
|
-
*
|
|
49
|
-
* @returns Absolute path to the _shared template
|
|
50
|
-
*/
|
|
51
|
-
export function getSharedTemplatePath() {
|
|
52
|
-
const currentFilePath = fileURLToPath(import.meta.url);
|
|
53
|
-
const currentDir = dirname(currentFilePath);
|
|
54
|
-
return join(currentDir, '..', 'templates', '_shared');
|
|
55
|
-
}
|
|
56
49
|
export async function getAvailableTemplates() {
|
|
57
|
-
const
|
|
58
|
-
const currentFilePath = fileURLToPath(currentFileUrl);
|
|
59
|
-
const currentDir = dirname(currentFilePath);
|
|
60
|
-
const templatesDir = join(currentDir, '..', 'templates');
|
|
50
|
+
const templatesDir = getTemplatesRootDir();
|
|
61
51
|
try {
|
|
62
52
|
const entries = await fs.readdir(templatesDir, { withFileTypes: true });
|
|
63
|
-
return entries
|
|
53
|
+
return entries
|
|
54
|
+
.filter((entry) => entry.isDirectory() && !entry.name.startsWith('_') && !RESERVED_TEMPLATE_NAMES.has(entry.name))
|
|
55
|
+
.map((entry) => entry.name);
|
|
64
56
|
}
|
|
65
57
|
catch (error) {
|
|
66
58
|
const err = error;
|
|
@@ -68,3 +60,16 @@ export async function getAvailableTemplates() {
|
|
|
68
60
|
`This indicates a corrupted installation. Please reinstall aiwcli.`);
|
|
69
61
|
}
|
|
70
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Discover IDE names available in a template path by scanning top-level dot-folders.
|
|
65
|
+
* Example: .claude, .codex -> ['claude', 'codex']
|
|
66
|
+
*/
|
|
67
|
+
export async function getTemplateIdeNamesByPath(templatePath) {
|
|
68
|
+
const entries = await fs.readdir(templatePath, { withFileTypes: true });
|
|
69
|
+
return entries
|
|
70
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith('.'))
|
|
71
|
+
.map((entry) => entry.name.slice(1))
|
|
72
|
+
.filter((name) => name.length > 0)
|
|
73
|
+
.sort((a, b) => a.localeCompare(b));
|
|
74
|
+
}
|
|
75
|
+
const RESERVED_TEMPLATE_NAMES = new Set(['core']);
|
|
@@ -13,13 +13,17 @@
|
|
|
13
13
|
*
|
|
14
14
|
* @module lib/template-settings-reconstructor
|
|
15
15
|
*/
|
|
16
|
+
import type { ClaudeSettings } from './claude-settings-types.js';
|
|
16
17
|
/**
|
|
17
18
|
* Reconstruct .claude/settings.json and .windsurf/hooks.json from the union
|
|
18
19
|
* of all specified templates.
|
|
19
20
|
*
|
|
21
|
+
* Note: Codex content is file-based today (`.codex/skills/*`) and does not
|
|
22
|
+
* have a merged settings artifact, so it is intentionally ignored here.
|
|
23
|
+
*
|
|
20
24
|
* The function:
|
|
21
25
|
* 1. Starts with empty settings
|
|
22
|
-
* 2. Merges
|
|
26
|
+
* 2. Merges core template settings (when active templates exist)
|
|
23
27
|
* 3. For each active template, merges its template-source settings
|
|
24
28
|
* 4. Writes the result to the IDE settings file
|
|
25
29
|
*
|
|
@@ -30,6 +34,7 @@
|
|
|
30
34
|
*
|
|
31
35
|
* @param targetDir - Project root directory
|
|
32
36
|
* @param activeTemplates - Template names to include (e.g., ['cc-native', 'bmad'])
|
|
33
|
-
* @param ides - IDEs to reconstruct for (
|
|
37
|
+
* @param ides - IDEs to reconstruct for (currently claude/windsurf)
|
|
34
38
|
*/
|
|
35
39
|
export declare function reconstructIdeSettings(targetDir: string, activeTemplates: string[], ides: string[]): Promise<void>;
|
|
40
|
+
export declare function normalizeTemplateSettingsPaths(settings: ClaudeSettings): ClaudeSettings;
|
|
@@ -14,19 +14,24 @@
|
|
|
14
14
|
* @module lib/template-settings-reconstructor
|
|
15
15
|
*/
|
|
16
16
|
import { join } from 'node:path';
|
|
17
|
+
import { getCoreClaudeSettingsBase, getCoreWindsurfHooksBase } from './core-ide-base.js';
|
|
17
18
|
import { mergeClaudeSettings } from './hooks-merger.js';
|
|
18
19
|
import { IdePathResolver } from './ide-path-resolver.js';
|
|
20
|
+
import { adaptHookCommand, validateCommandsForPlatform } from './platform-commands.js';
|
|
19
21
|
import { readClaudeSettings, writeClaudeSettings } from './settings-hierarchy.js';
|
|
20
|
-
import {
|
|
22
|
+
import { getTemplatePath } from './template-resolver.js';
|
|
21
23
|
import { getTargetHooksFile, readWindsurfHooks, writeWindsurfHooks } from './windsurf-hooks-hierarchy.js';
|
|
22
24
|
import { mergeWindsurfHooks } from './windsurf-hooks-merger.js';
|
|
23
25
|
/**
|
|
24
26
|
* Reconstruct .claude/settings.json and .windsurf/hooks.json from the union
|
|
25
27
|
* of all specified templates.
|
|
26
28
|
*
|
|
29
|
+
* Note: Codex content is file-based today (`.codex/skills/*`) and does not
|
|
30
|
+
* have a merged settings artifact, so it is intentionally ignored here.
|
|
31
|
+
*
|
|
27
32
|
* The function:
|
|
28
33
|
* 1. Starts with empty settings
|
|
29
|
-
* 2. Merges
|
|
34
|
+
* 2. Merges core template settings (when active templates exist)
|
|
30
35
|
* 3. For each active template, merges its template-source settings
|
|
31
36
|
* 4. Writes the result to the IDE settings file
|
|
32
37
|
*
|
|
@@ -37,87 +42,113 @@ import { mergeWindsurfHooks } from './windsurf-hooks-merger.js';
|
|
|
37
42
|
*
|
|
38
43
|
* @param targetDir - Project root directory
|
|
39
44
|
* @param activeTemplates - Template names to include (e.g., ['cc-native', 'bmad'])
|
|
40
|
-
* @param ides - IDEs to reconstruct for (
|
|
45
|
+
* @param ides - IDEs to reconstruct for (currently claude/windsurf)
|
|
41
46
|
*/
|
|
42
47
|
export async function reconstructIdeSettings(targetDir, activeTemplates, ides) {
|
|
43
|
-
const sharedTemplatePath = getSharedTemplatePath();
|
|
44
48
|
if (ides.includes('claude')) {
|
|
45
|
-
await reconstructClaudeSettings(targetDir, activeTemplates
|
|
49
|
+
await reconstructClaudeSettings(targetDir, activeTemplates);
|
|
46
50
|
}
|
|
47
51
|
if (ides.includes('windsurf')) {
|
|
48
|
-
await reconstructWindsurfHooks(targetDir, activeTemplates
|
|
52
|
+
await reconstructWindsurfHooks(targetDir, activeTemplates);
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
/**
|
|
52
56
|
* Reconstruct .claude/settings.json from scratch using template sources.
|
|
53
57
|
*/
|
|
54
|
-
async function reconstructClaudeSettings(targetDir, activeTemplates
|
|
58
|
+
async function reconstructClaudeSettings(targetDir, activeTemplates) {
|
|
55
59
|
const resolver = new IdePathResolver(targetDir);
|
|
56
60
|
const settingsPath = resolver.getClaudeSettings();
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
const methodsTracking = existingSettings?.methods;
|
|
61
|
-
// Start from empty and merge all template settings
|
|
62
|
-
let reconstructed = {};
|
|
63
|
-
// 1. Merge _shared template settings (only when active templates exist that need them)
|
|
64
|
-
if (activeTemplates.length > 0) {
|
|
65
|
-
const sharedSettingsPath = join(sharedTemplatePath, '.claude', 'settings.json');
|
|
66
|
-
const sharedSettings = await readClaudeSettings(sharedSettingsPath);
|
|
67
|
-
if (sharedSettings) {
|
|
68
|
-
reconstructed = mergeClaudeSettings(reconstructed, sharedSettings);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// 2. Merge each active template's settings (sequential for deterministic merge order)
|
|
61
|
+
// Start from core-owned base settings, then merge method templates.
|
|
62
|
+
let reconstructed = getCoreClaudeSettingsBase();
|
|
63
|
+
// Merge each active template's settings (sequential for deterministic merge order)
|
|
72
64
|
for (const template of activeTemplates) {
|
|
65
|
+
let templateSettingsPath = `<unresolved:${template}>/.claude/settings.json`;
|
|
73
66
|
try {
|
|
74
67
|
const templatePath = await getTemplatePath(template); // eslint-disable-line no-await-in-loop
|
|
75
|
-
|
|
68
|
+
templateSettingsPath = join(templatePath, '.claude', 'settings.json');
|
|
76
69
|
const templateSettings = await readClaudeSettings(templateSettingsPath); // eslint-disable-line no-await-in-loop
|
|
77
70
|
if (templateSettings) {
|
|
78
|
-
reconstructed = mergeClaudeSettings(reconstructed, templateSettings);
|
|
71
|
+
reconstructed = mergeClaudeSettings(reconstructed, normalizeTemplateSettingsPaths(templateSettings));
|
|
79
72
|
}
|
|
80
73
|
}
|
|
81
|
-
catch {
|
|
82
|
-
|
|
74
|
+
catch (error) {
|
|
75
|
+
reportTemplateMergeFailure('claude', template, templateSettingsPath, error);
|
|
83
76
|
}
|
|
84
77
|
}
|
|
85
|
-
// 3.
|
|
86
|
-
|
|
87
|
-
reconstructed.methods = methodsTracking;
|
|
88
|
-
}
|
|
78
|
+
// 3. Platform-adapt hook commands (Windows cmd.exe compatibility)
|
|
79
|
+
reconstructed = adaptSettingsForPlatform(reconstructed);
|
|
89
80
|
// 4. Write reconstructed settings
|
|
90
81
|
await writeClaudeSettings(settingsPath, reconstructed);
|
|
91
82
|
}
|
|
92
83
|
/**
|
|
93
84
|
* Reconstruct .windsurf/hooks.json from scratch using template sources.
|
|
94
85
|
*/
|
|
95
|
-
async function reconstructWindsurfHooks(targetDir, activeTemplates
|
|
86
|
+
async function reconstructWindsurfHooks(targetDir, activeTemplates) {
|
|
96
87
|
const hooksPath = getTargetHooksFile(targetDir);
|
|
97
|
-
// Start from
|
|
98
|
-
let reconstructed =
|
|
99
|
-
//
|
|
100
|
-
if (activeTemplates.length > 0) {
|
|
101
|
-
const sharedHooksPath = join(sharedTemplatePath, '.windsurf', 'hooks.json');
|
|
102
|
-
const sharedHooks = await readWindsurfHooks(sharedHooksPath);
|
|
103
|
-
if (sharedHooks) {
|
|
104
|
-
reconstructed = mergeWindsurfHooks(reconstructed, sharedHooks);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// 2. Merge each active template's hooks (sequential for deterministic merge order)
|
|
88
|
+
// Start from core-owned base hooks.
|
|
89
|
+
let reconstructed = getCoreWindsurfHooksBase();
|
|
90
|
+
// Merge each active template's hooks (sequential for deterministic merge order)
|
|
108
91
|
for (const template of activeTemplates) {
|
|
92
|
+
let templateHooksPath = `<unresolved:${template}>/.windsurf/hooks.json`;
|
|
109
93
|
try {
|
|
110
94
|
const templatePath = await getTemplatePath(template); // eslint-disable-line no-await-in-loop
|
|
111
|
-
|
|
95
|
+
templateHooksPath = join(templatePath, '.windsurf', 'hooks.json');
|
|
112
96
|
const templateHooks = await readWindsurfHooks(templateHooksPath); // eslint-disable-line no-await-in-loop
|
|
113
97
|
if (templateHooks) {
|
|
114
98
|
reconstructed = mergeWindsurfHooks(reconstructed, templateHooks);
|
|
115
99
|
}
|
|
116
100
|
}
|
|
117
|
-
catch {
|
|
118
|
-
|
|
101
|
+
catch (error) {
|
|
102
|
+
reportTemplateMergeFailure('windsurf', template, templateHooksPath, error);
|
|
119
103
|
}
|
|
120
104
|
}
|
|
121
105
|
// 3. Write reconstructed hooks
|
|
122
106
|
await writeWindsurfHooks(hooksPath, reconstructed);
|
|
123
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Adapt all command strings in settings for the current platform.
|
|
110
|
+
* On Windows: rewrites commands for cmd.exe compatibility.
|
|
111
|
+
* Validates adapted commands and fails fast if unknown remain non-portable.
|
|
112
|
+
*/
|
|
113
|
+
function adaptSettingsForPlatform(settings) {
|
|
114
|
+
const result = { ...settings };
|
|
115
|
+
const allCommands = [];
|
|
116
|
+
// Adapt top-level command configs
|
|
117
|
+
if (result.statusLine && 'command' in result.statusLine) {
|
|
118
|
+
result.statusLine = { ...result.statusLine, command: adaptHookCommand(result.statusLine.command) };
|
|
119
|
+
allCommands.push(result.statusLine.command);
|
|
120
|
+
}
|
|
121
|
+
if (result.fileSuggestion && 'command' in result.fileSuggestion) {
|
|
122
|
+
result.fileSuggestion = { ...result.fileSuggestion, command: adaptHookCommand(result.fileSuggestion.command) };
|
|
123
|
+
allCommands.push(result.fileSuggestion.command);
|
|
124
|
+
}
|
|
125
|
+
// Adapt all hook commands
|
|
126
|
+
if (result.hooks) {
|
|
127
|
+
const adapted = {};
|
|
128
|
+
for (const [event, matchers] of Object.entries(result.hooks)) {
|
|
129
|
+
if (!matchers)
|
|
130
|
+
continue;
|
|
131
|
+
adapted[event] = matchers.map((matcher) => ({
|
|
132
|
+
...matcher,
|
|
133
|
+
hooks: matcher.hooks.map((hook) => {
|
|
134
|
+
if (hook.type !== 'command')
|
|
135
|
+
return hook;
|
|
136
|
+
const adaptedCmd = adaptHookCommand(hook.command);
|
|
137
|
+
allCommands.push(adaptedCmd);
|
|
138
|
+
return { ...hook, command: adaptedCmd };
|
|
139
|
+
}),
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
result.hooks = adapted;
|
|
143
|
+
}
|
|
144
|
+
// Validate: fail fast if unknown command still contains bash-only syntax on Windows
|
|
145
|
+
validateCommandsForPlatform(allCommands);
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
function reportTemplateMergeFailure(ide, template, settingsPath, error) {
|
|
149
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
150
|
+
process.stderr.write(`[warn] Failed to merge ${ide} template "${template}" from ${settingsPath}: ${reason}\n`);
|
|
151
|
+
}
|
|
152
|
+
export function normalizeTemplateSettingsPaths(settings) {
|
|
153
|
+
return structuredClone(settings);
|
|
154
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface TerminalConfig {
|
|
2
|
+
cmd: string;
|
|
3
|
+
getArgs: (command: string) => string[];
|
|
4
|
+
}
|
|
5
|
+
export type WindowsShellPreference = 'default' | 'git-bash' | 'mintty';
|
|
6
|
+
export type WindowsTerminalStrategy = 'git-bash-in-wt' | 'mintty' | 'powershell-fallback' | 'windows-terminal';
|
|
7
|
+
export declare const LINUX_TERMINALS: TerminalConfig[];
|
|
8
|
+
export declare function resolveWindowsTerminalStrategy(preference: WindowsShellPreference, gitBashPath: null | string, minttyExists: boolean, powershellCmd: string): WindowsTerminalStrategy[];
|
|
9
|
+
export declare function detectPowerShell(isAvailable: (command: string) => boolean): string;
|
|
10
|
+
export declare function isWSL(env?: NodeJS.ProcessEnv): boolean;
|
|
11
|
+
export declare function findAvailableLinuxTerminal(isAvailable: (command: string, platform?: NodeJS.Platform) => boolean): null | TerminalConfig;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const LINUX_TERMINALS = [
|
|
2
|
+
{ cmd: 'gnome-terminal', getArgs: (command) => ['--', 'bash', '-c', `${command}; exec bash`] },
|
|
3
|
+
{ cmd: 'konsole', getArgs: (command) => ['-e', `bash -c "${command}; exec bash"`] },
|
|
4
|
+
{ cmd: 'xterm', getArgs: (command) => ['-e', `bash -c "${command}; exec bash"`] },
|
|
5
|
+
{ cmd: 'x-terminal-emulator', getArgs: (command) => ['-e', `bash -c "${command}; exec bash"`] },
|
|
6
|
+
];
|
|
7
|
+
export function resolveWindowsTerminalStrategy(preference, gitBashPath, minttyExists, powershellCmd) {
|
|
8
|
+
const strategies = [];
|
|
9
|
+
if (preference === 'mintty') {
|
|
10
|
+
if (minttyExists && gitBashPath) {
|
|
11
|
+
strategies.push('mintty');
|
|
12
|
+
}
|
|
13
|
+
if (gitBashPath) {
|
|
14
|
+
strategies.push('git-bash-in-wt');
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
strategies.push('windows-terminal');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
else if (preference === 'git-bash') {
|
|
21
|
+
if (gitBashPath) {
|
|
22
|
+
strategies.push('git-bash-in-wt');
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
strategies.push('windows-terminal');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
strategies.push('windows-terminal');
|
|
30
|
+
}
|
|
31
|
+
if (powershellCmd) {
|
|
32
|
+
strategies.push('powershell-fallback');
|
|
33
|
+
}
|
|
34
|
+
return [...new Set(strategies)];
|
|
35
|
+
}
|
|
36
|
+
export function detectPowerShell(isAvailable) {
|
|
37
|
+
return isAvailable('pwsh') ? 'pwsh' : 'powershell';
|
|
38
|
+
}
|
|
39
|
+
export function isWSL(env = process.env) {
|
|
40
|
+
return Boolean(env['WSL_DISTRO_NAME']);
|
|
41
|
+
}
|
|
42
|
+
export function findAvailableLinuxTerminal(isAvailable) {
|
|
43
|
+
for (const terminal of LINUX_TERMINALS) {
|
|
44
|
+
if (isAvailable(terminal.cmd, 'linux')) {
|
|
45
|
+
return terminal;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
package/dist/lib/terminal.d.ts
CHANGED
|
@@ -27,6 +27,27 @@
|
|
|
27
27
|
*
|
|
28
28
|
* @module lib/terminal
|
|
29
29
|
*/
|
|
30
|
+
import { type WindowsShellPreference } from './terminal-strategy.js';
|
|
31
|
+
/** @internal */
|
|
32
|
+
export interface SpawnArgs {
|
|
33
|
+
args: string[];
|
|
34
|
+
command: string;
|
|
35
|
+
}
|
|
36
|
+
/** @internal */
|
|
37
|
+
export declare function resolveTerminalPlatform(platform: NodeJS.Platform, isWSLResult: boolean): 'darwin' | 'linux' | 'windows' | 'wsl';
|
|
38
|
+
/** @internal */
|
|
39
|
+
export declare function buildMacTerminalSpawnArgs(cwd: string, command: string): SpawnArgs;
|
|
40
|
+
/** @internal */
|
|
41
|
+
export declare function buildWindowsTerminalSpawnArgs(cwd: string, command: string, powershellCmd: string): SpawnArgs;
|
|
42
|
+
/** @internal */
|
|
43
|
+
export declare function buildPowerShellFallbackSpawnArgs(cwd: string, command: string, powershellCmd: string): SpawnArgs;
|
|
44
|
+
/** @internal */
|
|
45
|
+
export declare function buildLinuxTerminalSpawnArgs(cwd: string, command: string, terminalInfo: {
|
|
46
|
+
cmd: string;
|
|
47
|
+
getArgs: (cmd: string) => string[];
|
|
48
|
+
}): SpawnArgs;
|
|
49
|
+
/** @internal */
|
|
50
|
+
export declare function buildWSLTerminalSpawnArgs(cwd: string, command: string): SpawnArgs;
|
|
30
51
|
/**
|
|
31
52
|
* Options for launching a new terminal window.
|
|
32
53
|
*/
|
|
@@ -44,6 +65,13 @@ interface TerminalLaunchOptions {
|
|
|
44
65
|
* If provided, debug messages will be passed to this function.
|
|
45
66
|
*/
|
|
46
67
|
debugLog?: (message: string) => void;
|
|
68
|
+
/**
|
|
69
|
+
* Preferred shell when launching on Windows.
|
|
70
|
+
* - default: Existing behavior (PowerShell in wt or fallback)
|
|
71
|
+
* - mintty: Prefer mintty + Git Bash, fallback to git-bash in wt, then PowerShell
|
|
72
|
+
* - git-bash: Prefer Git Bash in wt, fallback to PowerShell if unavailable
|
|
73
|
+
*/
|
|
74
|
+
windowsShellPreference?: WindowsShellPreference;
|
|
47
75
|
}
|
|
48
76
|
/**
|
|
49
77
|
* Result of a terminal launch operation.
|