aiwcli 0.15.4 → 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/hooks-merger.js
CHANGED
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
import { mergeArraysWithDedup, mergeConfigByEventType } from './generic-merge.js';
|
|
2
2
|
/**
|
|
3
|
-
* Check if two hook
|
|
3
|
+
* Check if two hook actions are equivalent
|
|
4
4
|
*/
|
|
5
|
-
function
|
|
6
|
-
|
|
5
|
+
function areHookActionsEqual(a, b) {
|
|
6
|
+
if (a.type !== b.type)
|
|
7
|
+
return false;
|
|
8
|
+
if (a.type === 'command') {
|
|
9
|
+
return (b.type === 'command' &&
|
|
10
|
+
a.command === b.command &&
|
|
11
|
+
a.timeout === b.timeout &&
|
|
12
|
+
a.async === b.async &&
|
|
13
|
+
a.blockOnFail === b.blockOnFail);
|
|
14
|
+
}
|
|
15
|
+
return b.type === 'prompt' && a.prompt === b.prompt && a.timeout === b.timeout && a.async === b.async;
|
|
7
16
|
}
|
|
8
17
|
/**
|
|
9
18
|
* Check if two hook matchers are equivalent
|
|
10
19
|
*/
|
|
11
20
|
function areHookMatchersEqual(a, b) {
|
|
12
|
-
|
|
21
|
+
const matcherA = a.matcher ?? '*';
|
|
22
|
+
const matcherB = b.matcher ?? '*';
|
|
23
|
+
if (matcherA !== matcherB || a.once !== b.once) {
|
|
13
24
|
return false;
|
|
14
25
|
}
|
|
15
26
|
if (a.hooks.length !== b.hooks.length) {
|
|
16
27
|
return false;
|
|
17
28
|
}
|
|
18
29
|
// Check if all hooks are equivalent (order-independent)
|
|
19
|
-
return a.hooks.every((hookA) => b.hooks.some((hookB) =>
|
|
30
|
+
return a.hooks.every((hookA) => b.hooks.some((hookB) => areHookActionsEqual(hookA, hookB)));
|
|
20
31
|
}
|
|
21
32
|
/**
|
|
22
33
|
* Merge hooks configurations from template into existing
|
|
@@ -65,30 +76,25 @@ export function mergeClaudeSettings(existing, template) {
|
|
|
65
76
|
...existing.env,
|
|
66
77
|
...template.env,
|
|
67
78
|
};
|
|
68
|
-
// Merge enabled plugins
|
|
69
|
-
const mergedEnabledPlugins =
|
|
70
|
-
...existing.enabledPlugins,
|
|
71
|
-
|
|
72
|
-
};
|
|
79
|
+
// Merge enabled plugins only if template explicitly defines them
|
|
80
|
+
const mergedEnabledPlugins = template.enabledPlugins && Object.keys(template.enabledPlugins).length > 0
|
|
81
|
+
? { ...existing.enabledPlugins, ...template.enabledPlugins }
|
|
82
|
+
: existing.enabledPlugins;
|
|
73
83
|
// Merge hooks using dedicated function
|
|
74
84
|
const mergedHooks = mergeHooks(existing.hooks, template.hooks);
|
|
75
|
-
// Merge methods tracking (existing takes precedence, template adds new)
|
|
76
|
-
const mergedMethods = {
|
|
77
|
-
...template.methods,
|
|
78
|
-
...existing.methods,
|
|
79
|
-
};
|
|
80
85
|
// Create merged settings
|
|
81
86
|
const merged = {
|
|
82
87
|
...existing,
|
|
83
88
|
...template,
|
|
84
89
|
permissions: mergedPermissions,
|
|
85
90
|
env: mergedEnv,
|
|
86
|
-
enabledPlugins: mergedEnabledPlugins,
|
|
87
91
|
hooks: mergedHooks,
|
|
88
92
|
};
|
|
89
|
-
// Only add
|
|
90
|
-
if (Object.keys(
|
|
91
|
-
merged.
|
|
93
|
+
// Only add enabledPlugins if there are unknown (avoid overriding user-scoped plugins)
|
|
94
|
+
if (mergedEnabledPlugins && Object.keys(mergedEnabledPlugins).length > 0) {
|
|
95
|
+
merged.enabledPlugins = mergedEnabledPlugins;
|
|
92
96
|
}
|
|
97
|
+
// Drop legacy install metadata from prior settings files.
|
|
98
|
+
delete merged.methods;
|
|
93
99
|
return merged;
|
|
94
100
|
}
|
|
@@ -30,10 +30,16 @@ export declare class IdePathResolver {
|
|
|
30
30
|
/**
|
|
31
31
|
* Get path to a folder within the .aiwcli container
|
|
32
32
|
*
|
|
33
|
-
* @param folderName - Folder name within .aiwcli (e.g., '
|
|
33
|
+
* @param folderName - Folder name within .aiwcli (e.g., '_core', '_bmad')
|
|
34
34
|
* @returns Absolute path to the folder
|
|
35
35
|
*/
|
|
36
36
|
getAiwcliFolder(folderName: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Get the .aiwcli/state directory path
|
|
39
|
+
*
|
|
40
|
+
* @returns Absolute path to .aiwcli/state directory
|
|
41
|
+
*/
|
|
42
|
+
getAiwcliStateDir(): string;
|
|
37
43
|
/**
|
|
38
44
|
* Get path to a file/folder within .claude directory
|
|
39
45
|
*
|
|
@@ -53,6 +59,12 @@ export declare class IdePathResolver {
|
|
|
53
59
|
* @returns Absolute path to Claude settings file
|
|
54
60
|
*/
|
|
55
61
|
getClaudeSettings(): string;
|
|
62
|
+
/**
|
|
63
|
+
* Get the core runtime folder path within .aiwcli
|
|
64
|
+
*
|
|
65
|
+
* @returns Absolute path to _core directory
|
|
66
|
+
*/
|
|
67
|
+
getCoreFolder(): string;
|
|
56
68
|
/**
|
|
57
69
|
* Get IDE directory path by IDE name
|
|
58
70
|
*
|
|
@@ -60,6 +72,12 @@ export declare class IdePathResolver {
|
|
|
60
72
|
* @returns Absolute path to IDE directory
|
|
61
73
|
*/
|
|
62
74
|
getIdeDir(ideName: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Get install state file path.
|
|
77
|
+
*
|
|
78
|
+
* @returns Absolute path to install-state.json
|
|
79
|
+
*/
|
|
80
|
+
getInstallStatePath(): string;
|
|
63
81
|
/**
|
|
64
82
|
* Get method-specific folder path within .aiwcli
|
|
65
83
|
* Convention: _{methodName} (e.g., _bmad, _gsd, _cc-native)
|
|
@@ -74,12 +92,6 @@ export declare class IdePathResolver {
|
|
|
74
92
|
* @returns Absolute path to project root
|
|
75
93
|
*/
|
|
76
94
|
getProjectRoot(): string;
|
|
77
|
-
/**
|
|
78
|
-
* Get the shared folder path within .aiwcli
|
|
79
|
-
*
|
|
80
|
-
* @returns Absolute path to _shared directory
|
|
81
|
-
*/
|
|
82
|
-
getSharedFolder(): string;
|
|
83
95
|
/**
|
|
84
96
|
* Get path to a file/folder within .windsurf directory
|
|
85
97
|
*
|
|
@@ -35,12 +35,20 @@ export class IdePathResolver {
|
|
|
35
35
|
/**
|
|
36
36
|
* Get path to a folder within the .aiwcli container
|
|
37
37
|
*
|
|
38
|
-
* @param folderName - Folder name within .aiwcli (e.g., '
|
|
38
|
+
* @param folderName - Folder name within .aiwcli (e.g., '_core', '_bmad')
|
|
39
39
|
* @returns Absolute path to the folder
|
|
40
40
|
*/
|
|
41
41
|
getAiwcliFolder(folderName) {
|
|
42
42
|
return join(this.getAiwcliContainer(), folderName);
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the .aiwcli/state directory path
|
|
46
|
+
*
|
|
47
|
+
* @returns Absolute path to .aiwcli/state directory
|
|
48
|
+
*/
|
|
49
|
+
getAiwcliStateDir() {
|
|
50
|
+
return join(this.getAiwcliContainer(), 'state');
|
|
51
|
+
}
|
|
44
52
|
/**
|
|
45
53
|
* Get path to a file/folder within .claude directory
|
|
46
54
|
*
|
|
@@ -66,6 +74,14 @@ export class IdePathResolver {
|
|
|
66
74
|
getClaudeSettings() {
|
|
67
75
|
return this.getClaude('settings.json');
|
|
68
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Get the core runtime folder path within .aiwcli
|
|
79
|
+
*
|
|
80
|
+
* @returns Absolute path to _core directory
|
|
81
|
+
*/
|
|
82
|
+
getCoreFolder() {
|
|
83
|
+
return this.getAiwcliFolder('_core');
|
|
84
|
+
}
|
|
69
85
|
/**
|
|
70
86
|
* Get IDE directory path by IDE name
|
|
71
87
|
*
|
|
@@ -75,6 +91,14 @@ export class IdePathResolver {
|
|
|
75
91
|
getIdeDir(ideName) {
|
|
76
92
|
return join(this.projectRoot, `.${ideName}`);
|
|
77
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Get install state file path.
|
|
96
|
+
*
|
|
97
|
+
* @returns Absolute path to install-state.json
|
|
98
|
+
*/
|
|
99
|
+
getInstallStatePath() {
|
|
100
|
+
return join(this.getAiwcliStateDir(), 'install-state.json');
|
|
101
|
+
}
|
|
78
102
|
/**
|
|
79
103
|
* Get method-specific folder path within .aiwcli
|
|
80
104
|
* Convention: _{methodName} (e.g., _bmad, _gsd, _cc-native)
|
|
@@ -93,14 +117,6 @@ export class IdePathResolver {
|
|
|
93
117
|
getProjectRoot() {
|
|
94
118
|
return this.projectRoot;
|
|
95
119
|
}
|
|
96
|
-
/**
|
|
97
|
-
* Get the shared folder path within .aiwcli
|
|
98
|
-
*
|
|
99
|
-
* @returns Absolute path to _shared directory
|
|
100
|
-
*/
|
|
101
|
-
getSharedFolder() {
|
|
102
|
-
return this.getAiwcliFolder('_shared');
|
|
103
|
-
}
|
|
104
120
|
/**
|
|
105
121
|
* Get path to a file/folder within .windsurf directory
|
|
106
122
|
*
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
interface CoreState {
|
|
2
|
+
assetVersion: string;
|
|
3
|
+
installed: boolean;
|
|
4
|
+
installedAt: string;
|
|
5
|
+
}
|
|
6
|
+
interface IdeState {
|
|
7
|
+
managed: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface MethodState {
|
|
10
|
+
idePaths: string[];
|
|
11
|
+
ides: string[];
|
|
12
|
+
installed: boolean;
|
|
13
|
+
installedAt: string;
|
|
14
|
+
runtimePaths: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface InstallState {
|
|
17
|
+
core: CoreState;
|
|
18
|
+
ides: Record<string, IdeState>;
|
|
19
|
+
initializedAt: string;
|
|
20
|
+
methods: Record<string, MethodState>;
|
|
21
|
+
updatedAt: string;
|
|
22
|
+
version: 1;
|
|
23
|
+
}
|
|
24
|
+
export declare function readInstallState(targetDir: string): Promise<InstallState | undefined>;
|
|
25
|
+
export declare function writeInstallState(targetDir: string, state: InstallState): Promise<void>;
|
|
26
|
+
export declare function ensureInstallState(targetDir: string): Promise<InstallState>;
|
|
27
|
+
export declare function markCoreInstalled(targetDir: string, ides: string[]): Promise<void>;
|
|
28
|
+
export declare function markCoreRemoved(targetDir: string): Promise<void>;
|
|
29
|
+
export declare function markMethodInstalled(targetDir: string, method: string, ides: string[]): Promise<void>;
|
|
30
|
+
export declare function markMethodRemoved(targetDir: string, method: string): Promise<void>;
|
|
31
|
+
export declare function getInstalledMethodsFromState(targetDir: string): Promise<string[]>;
|
|
32
|
+
export declare function getInstalledMethods(targetDir: string): Promise<string[]>;
|
|
33
|
+
export declare function deleteInstallStateIfPresent(targetDir: string): Promise<void>;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { IdePathResolver } from './ide-path-resolver.js';
|
|
3
|
+
import { pathExists } from './paths.js';
|
|
4
|
+
const CORE_RUNTIME_FOLDERS = new Set(['_core']);
|
|
5
|
+
const RESERVED_AIWCLI_FOLDERS = new Set(['_output', 'state', ...CORE_RUNTIME_FOLDERS]);
|
|
6
|
+
const VERSION = 1;
|
|
7
|
+
export async function readInstallState(targetDir) {
|
|
8
|
+
const statePath = new IdePathResolver(targetDir).getInstallStatePath();
|
|
9
|
+
try {
|
|
10
|
+
const content = await fs.readFile(statePath, 'utf8');
|
|
11
|
+
const parsed = JSON.parse(content);
|
|
12
|
+
if (parsed.version !== VERSION || !parsed.core || !parsed.methods || !parsed.ides)
|
|
13
|
+
return undefined;
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function writeInstallState(targetDir, state) {
|
|
21
|
+
const resolver = new IdePathResolver(targetDir);
|
|
22
|
+
const statePath = resolver.getInstallStatePath();
|
|
23
|
+
await fs.mkdir(resolver.getAiwcliStateDir(), { recursive: true });
|
|
24
|
+
await fs.writeFile(statePath, JSON.stringify(state, null, 2), 'utf8');
|
|
25
|
+
}
|
|
26
|
+
export async function ensureInstallState(targetDir) {
|
|
27
|
+
const existing = await readInstallState(targetDir);
|
|
28
|
+
if (existing)
|
|
29
|
+
return existing;
|
|
30
|
+
const now = new Date().toISOString();
|
|
31
|
+
const initial = {
|
|
32
|
+
version: VERSION,
|
|
33
|
+
initializedAt: now,
|
|
34
|
+
updatedAt: now,
|
|
35
|
+
core: {
|
|
36
|
+
installed: await hasLegacyCoreRuntime(targetDir),
|
|
37
|
+
assetVersion: 'v1',
|
|
38
|
+
installedAt: now,
|
|
39
|
+
},
|
|
40
|
+
methods: await discoverLegacyMethods(targetDir, now),
|
|
41
|
+
ides: {},
|
|
42
|
+
};
|
|
43
|
+
return initial;
|
|
44
|
+
}
|
|
45
|
+
export async function markCoreInstalled(targetDir, ides) {
|
|
46
|
+
const state = await ensureInstallState(targetDir);
|
|
47
|
+
const now = new Date().toISOString();
|
|
48
|
+
await backfillMissingMethodsFromDisk(targetDir, state, now);
|
|
49
|
+
state.core = { installed: true, assetVersion: 'v1', installedAt: now };
|
|
50
|
+
for (const ide of ides) {
|
|
51
|
+
state.ides[ide] = { managed: true };
|
|
52
|
+
}
|
|
53
|
+
state.updatedAt = now;
|
|
54
|
+
await writeInstallState(targetDir, state);
|
|
55
|
+
}
|
|
56
|
+
export async function markCoreRemoved(targetDir) {
|
|
57
|
+
const state = await readInstallState(targetDir);
|
|
58
|
+
if (!state)
|
|
59
|
+
return;
|
|
60
|
+
state.core.installed = false;
|
|
61
|
+
state.updatedAt = new Date().toISOString();
|
|
62
|
+
await writeInstallState(targetDir, state);
|
|
63
|
+
}
|
|
64
|
+
export async function markMethodInstalled(targetDir, method, ides) {
|
|
65
|
+
const state = await ensureInstallState(targetDir);
|
|
66
|
+
const now = new Date().toISOString();
|
|
67
|
+
await backfillMissingMethodsFromDisk(targetDir, state, now);
|
|
68
|
+
state.methods[method] = {
|
|
69
|
+
installed: true,
|
|
70
|
+
installedAt: now,
|
|
71
|
+
ides: [...ides],
|
|
72
|
+
runtimePaths: [`.aiwcli/_${method}`],
|
|
73
|
+
idePaths: ides.flatMap((ide) => ideRootPaths(ide, method)),
|
|
74
|
+
};
|
|
75
|
+
for (const ide of ides) {
|
|
76
|
+
state.ides[ide] = { managed: true };
|
|
77
|
+
}
|
|
78
|
+
state.updatedAt = now;
|
|
79
|
+
await writeInstallState(targetDir, state);
|
|
80
|
+
}
|
|
81
|
+
export async function markMethodRemoved(targetDir, method) {
|
|
82
|
+
const state = await ensureInstallState(targetDir);
|
|
83
|
+
const now = new Date().toISOString();
|
|
84
|
+
await backfillMissingMethodsFromDisk(targetDir, state, now);
|
|
85
|
+
if (method in state.methods) {
|
|
86
|
+
delete state.methods[method];
|
|
87
|
+
state.updatedAt = now;
|
|
88
|
+
await writeInstallState(targetDir, state);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export async function getInstalledMethodsFromState(targetDir) {
|
|
92
|
+
const state = await readInstallState(targetDir);
|
|
93
|
+
if (!state)
|
|
94
|
+
return [];
|
|
95
|
+
return Object.entries(state.methods)
|
|
96
|
+
.filter(([, methodState]) => methodState.installed)
|
|
97
|
+
.map(([name]) => name);
|
|
98
|
+
}
|
|
99
|
+
export async function getInstalledMethods(targetDir) {
|
|
100
|
+
const installed = await getInstalledMethodsFromState(targetDir);
|
|
101
|
+
if (installed.length > 0)
|
|
102
|
+
return installed;
|
|
103
|
+
return discoverLegacyMethodNames(targetDir);
|
|
104
|
+
}
|
|
105
|
+
export async function deleteInstallStateIfPresent(targetDir) {
|
|
106
|
+
const resolver = new IdePathResolver(targetDir);
|
|
107
|
+
const stateDir = resolver.getAiwcliStateDir();
|
|
108
|
+
const statePath = resolver.getInstallStatePath();
|
|
109
|
+
if (!(await pathExists(statePath)))
|
|
110
|
+
return;
|
|
111
|
+
await fs.rm(statePath, { force: true });
|
|
112
|
+
try {
|
|
113
|
+
if ((await fs.readdir(stateDir)).length === 0)
|
|
114
|
+
await fs.rmdir(stateDir);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Ignore cleanup errors for missing/non-empty state directories.
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async function backfillMissingMethodsFromDisk(targetDir, state, installedAt) {
|
|
121
|
+
const discoveredMethods = await discoverLegacyMethods(targetDir, installedAt);
|
|
122
|
+
for (const [method, methodState] of Object.entries(discoveredMethods)) {
|
|
123
|
+
state.methods[method] ??= methodState;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function discoverLegacyMethods(targetDir, installedAt) {
|
|
127
|
+
const methodNames = await discoverLegacyMethodNames(targetDir);
|
|
128
|
+
return Object.fromEntries(methodNames.map((method) => [method, {
|
|
129
|
+
installed: true,
|
|
130
|
+
installedAt,
|
|
131
|
+
ides: [],
|
|
132
|
+
runtimePaths: [`.aiwcli/_${method}`],
|
|
133
|
+
idePaths: [],
|
|
134
|
+
}]));
|
|
135
|
+
}
|
|
136
|
+
async function discoverLegacyMethodNames(targetDir) {
|
|
137
|
+
const containerDir = new IdePathResolver(targetDir).getAiwcliContainer();
|
|
138
|
+
try {
|
|
139
|
+
const entries = await fs.readdir(containerDir, { withFileTypes: true });
|
|
140
|
+
return entries
|
|
141
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith('_') && !RESERVED_AIWCLI_FOLDERS.has(entry.name))
|
|
142
|
+
.map((entry) => entry.name.slice(1))
|
|
143
|
+
.sort((left, right) => left.localeCompare(right));
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function hasLegacyCoreRuntime(targetDir) {
|
|
150
|
+
const resolver = new IdePathResolver(targetDir);
|
|
151
|
+
return pathExists(resolver.getCoreFolder());
|
|
152
|
+
}
|
|
153
|
+
function ideRootPaths(ide, method) {
|
|
154
|
+
if (ide === 'claude')
|
|
155
|
+
return [`.claude/commands/${method}`, `.claude/agents/${method}`];
|
|
156
|
+
if (ide === 'codex')
|
|
157
|
+
return [];
|
|
158
|
+
if (ide === 'windsurf')
|
|
159
|
+
return [`.windsurf/workflows/${method}`];
|
|
160
|
+
return [`.${ide}/${method}`];
|
|
161
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { buildSpawnedWindowArgs, type BuildSpawnedWindowArgsParams, buildUniqueSessionName, parseExtraEnv, resolvePromptText, sanitizeSessionName, } from '../capabilities/launch/runtime-core/launch-options.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { buildSpawnedWindowArgs, buildUniqueSessionName, parseExtraEnv, resolvePromptText, sanitizeSessionName, } from '../capabilities/launch/runtime-core/launch-options.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patches the npm-installed Claude Code cli.js to add `shell: true` on Windows
|
|
3
|
+
* for LSP subprocess spawning, then renames the native binary so the npm shim
|
|
4
|
+
* takes precedence on PATH.
|
|
5
|
+
*
|
|
6
|
+
* Safe ordering: locate → backup → patch → verify → rename.
|
|
7
|
+
* Never throws. On unknown failure, emits a warning and returns.
|
|
8
|
+
*/
|
|
9
|
+
export declare function ensureLspPatch(options: {
|
|
10
|
+
debugLog: (msg: string) => void;
|
|
11
|
+
warn: (msg: string) => void;
|
|
12
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { copyFileSync, existsSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
// Temporary workaround until upstream LSP spawn bug is fixed.
|
|
6
|
+
// Tracks: https://github.com/anthropics/claude-code/issues/17136
|
|
7
|
+
// https://github.com/anthropics/claude-code/issues/19658
|
|
8
|
+
/**
|
|
9
|
+
* Regex matching the LSP spawn options in Claude Code's bundled cli.js.
|
|
10
|
+
* The single-letter variable name changes across versions (X in v2.1.63, D in v2.1.69),
|
|
11
|
+
* so we capture the variable name dynamically.
|
|
12
|
+
*/
|
|
13
|
+
const SPAWN_PATTERN = /\{stdio:\["pipe","pipe","pipe"\],env:([A-Za-z_$][\w$]*)\?\.env\?\{\.\.\.globalThis\.process\.env,\.\.\.\1\.env\}:void 0,cwd:\1\?\.cwd,windowsHide:!0\}/;
|
|
14
|
+
/** Build the replacement string with the captured variable name and shell:true. */
|
|
15
|
+
function buildReplacement(varName) {
|
|
16
|
+
return `{stdio:["pipe","pipe","pipe"],env:${varName}?.env?{...globalThis.process.env,...${varName}.env}:void 0,cwd:${varName}?.cwd,windowsHide:!0,shell:process.platform==="win32"}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Patches the npm-installed Claude Code cli.js to add `shell: true` on Windows
|
|
20
|
+
* for LSP subprocess spawning, then renames the native binary so the npm shim
|
|
21
|
+
* takes precedence on PATH.
|
|
22
|
+
*
|
|
23
|
+
* Safe ordering: locate → backup → patch → verify → rename.
|
|
24
|
+
* Never throws. On unknown failure, emits a warning and returns.
|
|
25
|
+
*/
|
|
26
|
+
export async function ensureLspPatch(options) {
|
|
27
|
+
const { debugLog, warn } = options;
|
|
28
|
+
try {
|
|
29
|
+
// Step 1 — Guard: only applies on Windows
|
|
30
|
+
if (process.platform !== 'win32')
|
|
31
|
+
return;
|
|
32
|
+
// Step 2 — Locate npm cli.js
|
|
33
|
+
let npmPrefix;
|
|
34
|
+
try {
|
|
35
|
+
npmPrefix = execSync('npm config get prefix', { timeout: 3000, encoding: 'utf8' }).trim();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
debugLog('LSP patch: could not determine npm prefix, skipping');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const cliJsPath = path.join(npmPrefix, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
|
|
42
|
+
if (!existsSync(cliJsPath)) {
|
|
43
|
+
warn('Windows LSP fix requires: npm i -g @anthropic-ai/claude-code');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Step 3 — Locate npm shim
|
|
47
|
+
const shimPath = path.join(npmPrefix, 'claude.cmd');
|
|
48
|
+
if (!existsSync(shimPath)) {
|
|
49
|
+
warn('npm claude.cmd shim not found, skipping LSP patch');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Step 4 — Read cli.js and check patch status
|
|
53
|
+
let content;
|
|
54
|
+
try {
|
|
55
|
+
content = readFileSync(cliJsPath, 'utf8');
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
warn('LSP patch: cannot read cli.js, skipping');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (content.includes('shell:process.platform==="win32"')) {
|
|
62
|
+
debugLog('LSP patch: already applied');
|
|
63
|
+
renamNativeBinary(debugLog, warn);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// Step 5 — Patch cli.js (with diagnostic detection)
|
|
67
|
+
const match = SPAWN_PATTERN.exec(content);
|
|
68
|
+
if (!match) {
|
|
69
|
+
// Distinguish: upstream fixed vs pattern restructured
|
|
70
|
+
if (content.includes('shell:process.platform==="win32"') || content.includes("shell:process.platform==='win32'")) {
|
|
71
|
+
// Upstream added shell:true natively — patch is obsolete
|
|
72
|
+
debugLog('LSP patch: upstream now includes shell:true — patch no longer needed!');
|
|
73
|
+
renamNativeBinary(debugLog, warn);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Pattern restructured — needs manual update
|
|
77
|
+
const spawnSnippet = content.match(/\{stdio:\["pipe","pipe","pipe"\][^}]{0,200}\}/)?.[0] ?? '(spawn pattern not found)';
|
|
78
|
+
warn(`LSP patch: spawn pattern changed in new Claude Code version — manual update needed.\n` +
|
|
79
|
+
` Current pattern: ${spawnSnippet}\n` +
|
|
80
|
+
` Update SPAWN_PATTERN in: packages/cli/src/lib/lsp-patch.ts`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const patched = content.replace(SPAWN_PATTERN, buildReplacement(match[1]));
|
|
84
|
+
// Backup original (only if .bak doesn't already exist)
|
|
85
|
+
const bakPath = cliJsPath + '.bak';
|
|
86
|
+
if (!existsSync(bakPath)) {
|
|
87
|
+
try {
|
|
88
|
+
copyFileSync(cliJsPath, bakPath);
|
|
89
|
+
debugLog(`LSP patch: backed up cli.js to ${bakPath}`);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Non-fatal — continue with patch
|
|
93
|
+
debugLog('LSP patch: could not create backup, continuing');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Atomic write: write to tmp, then rename over original
|
|
97
|
+
const tmpPath = cliJsPath + '.tmp';
|
|
98
|
+
try {
|
|
99
|
+
writeFileSync(tmpPath, patched, 'utf8');
|
|
100
|
+
renameSync(tmpPath, cliJsPath);
|
|
101
|
+
debugLog('LSP patch: applied successfully');
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
const { code } = error;
|
|
105
|
+
if (code === 'EACCES' || code === 'EPERM') {
|
|
106
|
+
warn('LSP patch: permission denied writing cli.js');
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
warn(`LSP patch: write failed — ${code ?? error}`);
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Step 6 — Rename native binary
|
|
114
|
+
renamNativeBinary(debugLog, warn);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
// Catch-all: never let this function throw
|
|
118
|
+
warn(`LSP patch: unexpected error — ${error}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Renames the native Claude binary in ~/.local/bin/ so the npm shim
|
|
123
|
+
* (which points to the patched cli.js) takes precedence on PATH.
|
|
124
|
+
*/
|
|
125
|
+
function renamNativeBinary(debugLog, warn) {
|
|
126
|
+
const localBin = path.join(os.homedir(), '.local', 'bin');
|
|
127
|
+
for (const name of ['claude.exe', 'claude']) {
|
|
128
|
+
const src = path.join(localBin, name);
|
|
129
|
+
if (!existsSync(src))
|
|
130
|
+
continue;
|
|
131
|
+
try {
|
|
132
|
+
const stat = statSync(src);
|
|
133
|
+
// Native Bun binary is ~242MB; npm shim is tiny
|
|
134
|
+
if (stat.size <= 1_000_000)
|
|
135
|
+
continue;
|
|
136
|
+
const ext = path.extname(name);
|
|
137
|
+
const base = path.basename(name, ext);
|
|
138
|
+
const target = path.join(localBin, `${base}-native${ext}`);
|
|
139
|
+
if (existsSync(target)) {
|
|
140
|
+
debugLog(`LSP patch: ${target} already exists, skipping rename`);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
renameSync(src, target);
|
|
144
|
+
debugLog(`LSP patch: renamed ${src} → ${target}`);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
const { code } = error;
|
|
148
|
+
if (code === 'EACCES' || code === 'EPERM') {
|
|
149
|
+
warn(`Cannot rename ${src} — rename manually to ${src.replace(name, name.replace('claude', 'claude-native'))}`);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
debugLog(`LSP patch: rename failed for ${src} — ${code ?? error}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified multiplexer abstraction.
|
|
3
|
+
* Single interface for tmux (Unix) and psmux (Windows) backends.
|
|
4
|
+
* Factory: detectMultiplexer() → Multiplexer | null
|
|
5
|
+
*/
|
|
6
|
+
export type MultiplexerBackend = 'psmux' | 'tmux';
|
|
7
|
+
export type SplitDirection = 'auto' | 'h' | 'v';
|
|
8
|
+
export interface SplitPaneOptions {
|
|
9
|
+
args: string[];
|
|
10
|
+
autoClose?: boolean | undefined;
|
|
11
|
+
cwd?: string | undefined;
|
|
12
|
+
env?: Record<string, string> | undefined;
|
|
13
|
+
holdMessage?: string | undefined;
|
|
14
|
+
holdPane?: boolean | undefined;
|
|
15
|
+
mode?: 'exec' | 'repl' | undefined;
|
|
16
|
+
promptPath?: string | undefined;
|
|
17
|
+
sentinel?: boolean | undefined;
|
|
18
|
+
split?: SplitDirection | undefined;
|
|
19
|
+
splitTarget?: string | undefined;
|
|
20
|
+
toolName: string;
|
|
21
|
+
}
|
|
22
|
+
export interface SplitPaneResult {
|
|
23
|
+
backend: MultiplexerBackend;
|
|
24
|
+
exitCode?: number | undefined;
|
|
25
|
+
launched: boolean;
|
|
26
|
+
paneId?: string | undefined;
|
|
27
|
+
reason?: string | undefined;
|
|
28
|
+
sentinelPath?: string | undefined;
|
|
29
|
+
stderr?: string | undefined;
|
|
30
|
+
}
|
|
31
|
+
export interface CreateSessionOptions {
|
|
32
|
+
enableMouse?: boolean | undefined;
|
|
33
|
+
promptText?: string | undefined;
|
|
34
|
+
reattach?: boolean | undefined;
|
|
35
|
+
sessionName: string;
|
|
36
|
+
toolArgs: string[];
|
|
37
|
+
toolPath: string;
|
|
38
|
+
}
|
|
39
|
+
export interface CreateSessionResult {
|
|
40
|
+
exitCode: number;
|
|
41
|
+
reason?: string | undefined;
|
|
42
|
+
usedMux: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface Multiplexer {
|
|
45
|
+
readonly backend: MultiplexerBackend;
|
|
46
|
+
createSession(options: CreateSessionOptions): Promise<CreateSessionResult>;
|
|
47
|
+
isInsideSession(): boolean;
|
|
48
|
+
kill(paneId: string): Promise<void>;
|
|
49
|
+
splitPane(options: SplitPaneOptions): Promise<SplitPaneResult>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Detect the best available multiplexer for the current platform.
|
|
53
|
+
* Windows → PsmuxMultiplexer (if installed and meets version requirement)
|
|
54
|
+
* Unix → TmuxMultiplexer (if tmux binary on PATH)
|
|
55
|
+
* Returns null if no multiplexer is available.
|
|
56
|
+
*/
|
|
57
|
+
export declare function detectMultiplexer(platform?: NodeJS.Platform): Promise<Multiplexer | null>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified multiplexer abstraction.
|
|
3
|
+
* Single interface for tmux (Unix) and psmux (Windows) backends.
|
|
4
|
+
* Factory: detectMultiplexer() → Multiplexer | null
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Detect the best available multiplexer for the current platform.
|
|
8
|
+
* Windows → PsmuxMultiplexer (if installed and meets version requirement)
|
|
9
|
+
* Unix → TmuxMultiplexer (if tmux binary on PATH)
|
|
10
|
+
* Returns null if no multiplexer is available.
|
|
11
|
+
*/
|
|
12
|
+
export async function detectMultiplexer(platform = process.platform) {
|
|
13
|
+
if (platform === 'win32') {
|
|
14
|
+
const { PsmuxMultiplexer } = await import('./multiplexers/psmux.js');
|
|
15
|
+
return PsmuxMultiplexer.create();
|
|
16
|
+
}
|
|
17
|
+
const { TmuxMultiplexer } = await import('./multiplexers/tmux.js');
|
|
18
|
+
return TmuxMultiplexer.create();
|
|
19
|
+
}
|