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/README.md
CHANGED
|
@@ -28,7 +28,7 @@ aiwcli v0.9.0 includes:
|
|
|
28
28
|
- **CLI Commands**: `launch`, `init`, `branch`, `clean`, `clear`
|
|
29
29
|
- **Templates**:
|
|
30
30
|
- **cc-native** - Event-sourced context management with plan review, 25+ specialized agents
|
|
31
|
-
- **
|
|
31
|
+
- **core** - Common utilities (context manager, event logging, task sync)
|
|
32
32
|
- **IDE Support**: Claude Code, Windsurf, Codex
|
|
33
33
|
|
|
34
34
|
After installation, run `aiw init --method cc-native` to set up the template in your project.
|
|
@@ -38,7 +38,7 @@ After installation, run `aiw init --method cc-native` to set up the template in
|
|
|
38
38
|
AIW CLI provides the following commands:
|
|
39
39
|
|
|
40
40
|
### `aiw launch`
|
|
41
|
-
Launch Claude Code with AIW configuration (sandbox disabled, tmux-first
|
|
41
|
+
Launch Claude Code with AIW configuration (sandbox disabled, tmux-first when outside tmux).
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
44
|
aiw launch
|
|
@@ -49,11 +49,14 @@ aiw launch --tmux-session aiw-main # Reuse/attach a specific tmux session name
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
`aiw launch` now creates a fresh tmux session by default when auto-launching tmux.
|
|
52
|
+
On Windows (outside tmux), `aiw launch` opens a new mintty window first, then runs tmux there.
|
|
53
|
+
If window launch fails, it falls back to inline non-tmux launch.
|
|
52
54
|
|
|
53
|
-
If you prefer typing `codex`, route
|
|
55
|
+
If you prefer typing `codex` or `devin`, route them through AIW launch behavior with aliases:
|
|
54
56
|
|
|
55
57
|
```bash
|
|
56
58
|
alias codex='aiw launch --codex'
|
|
59
|
+
alias devin='aiw launch --devin'
|
|
57
60
|
```
|
|
58
61
|
|
|
59
62
|
### `aiw init`
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import clipboardy from 'clipboardy';
|
|
3
|
+
import { branchExists, createWorktree, deleteBranch, deleteWorktreeFolder, getAllWorktrees, getCurrentBranch, getMainBranch, getWorktreePath, hasMergeRequest, hasUnpushedCommits, } from '../../lib/git/index.js';
|
|
4
|
+
import { launchTerminal } from '../../lib/terminal.js';
|
|
5
|
+
export function createBranchCommandDependencies() {
|
|
6
|
+
return {
|
|
7
|
+
access: fs.access,
|
|
8
|
+
branchExists,
|
|
9
|
+
copyToClipboard: clipboardy.write,
|
|
10
|
+
createWorktree,
|
|
11
|
+
deleteBranch,
|
|
12
|
+
deleteWorktreeFolder,
|
|
13
|
+
getAllWorktrees,
|
|
14
|
+
getCurrentBranch,
|
|
15
|
+
getMainBranch,
|
|
16
|
+
getWorktreePath,
|
|
17
|
+
hasMergeRequest,
|
|
18
|
+
hasUnpushedCommits,
|
|
19
|
+
launchTerminal,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export interface BranchTerminalLaunchOptions {
|
|
2
|
+
command: string;
|
|
3
|
+
cwd: string;
|
|
4
|
+
debugLog?: (message: string) => void;
|
|
5
|
+
}
|
|
6
|
+
export interface BranchTerminalLaunchResult {
|
|
7
|
+
error?: string;
|
|
8
|
+
success: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface BranchCommandRequest {
|
|
11
|
+
args: {
|
|
12
|
+
branchName?: string | undefined;
|
|
13
|
+
};
|
|
14
|
+
cwd: string;
|
|
15
|
+
flags: {
|
|
16
|
+
all?: boolean | undefined;
|
|
17
|
+
delete?: boolean | undefined;
|
|
18
|
+
launch?: boolean | undefined;
|
|
19
|
+
main?: boolean | undefined;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface BranchCommandLogger {
|
|
23
|
+
debug: (...args: unknown[]) => void;
|
|
24
|
+
error: (message: string, options?: {
|
|
25
|
+
exit?: number | undefined;
|
|
26
|
+
}) => never;
|
|
27
|
+
log: (message?: string) => void;
|
|
28
|
+
logInfo: (message: string) => void;
|
|
29
|
+
logSuccess: (message: string) => void;
|
|
30
|
+
}
|
|
31
|
+
export interface WorktreeRecord {
|
|
32
|
+
branch: null | string;
|
|
33
|
+
path: string;
|
|
34
|
+
}
|
|
35
|
+
export interface BranchCommandDependencies {
|
|
36
|
+
access(path: string): Promise<void>;
|
|
37
|
+
branchExists(branchName: string): boolean;
|
|
38
|
+
copyToClipboard(text: string): Promise<void>;
|
|
39
|
+
createWorktree(branchName: string, worktreePath: string): Promise<void>;
|
|
40
|
+
deleteBranch(branchName: string, options?: {
|
|
41
|
+
debugLog?: ((message: string) => void) | undefined;
|
|
42
|
+
}): void;
|
|
43
|
+
deleteWorktreeFolder(worktreePath: string, options?: {
|
|
44
|
+
debugLog?: ((message: string) => void) | undefined;
|
|
45
|
+
}): Promise<void>;
|
|
46
|
+
getAllWorktrees(): WorktreeRecord[];
|
|
47
|
+
getCurrentBranch(): string;
|
|
48
|
+
getMainBranch(): null | string;
|
|
49
|
+
getWorktreePath(branchName: string): null | string;
|
|
50
|
+
hasMergeRequest(branchName: string, options?: {
|
|
51
|
+
debugLog?: ((message: string) => void) | undefined;
|
|
52
|
+
}): boolean;
|
|
53
|
+
hasUnpushedCommits(branchName: string, options?: {
|
|
54
|
+
debugLog?: ((message: string) => void) | undefined;
|
|
55
|
+
}): boolean;
|
|
56
|
+
launchTerminal(options: BranchTerminalLaunchOptions): Promise<BranchTerminalLaunchResult>;
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { join, resolve } from 'node:path';
|
|
2
|
+
import { deriveWorktreePath, determineBranchMode, validateLaunchBranchName } from './runtime-core.js';
|
|
3
|
+
import { EXIT_CODES } from '../../types/index.js';
|
|
4
|
+
export async function executeBranchCommand(request, logger, dependencies) {
|
|
5
|
+
try {
|
|
6
|
+
const mode = determineBranchMode(request);
|
|
7
|
+
if (mode === 'main') {
|
|
8
|
+
await handleMainBranch(request.cwd, logger, dependencies);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (mode === 'launch') {
|
|
12
|
+
await handleWorktreeLaunch(request.cwd, request.args.branchName, logger, dependencies);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (mode === 'delete-all') {
|
|
16
|
+
await handleDeleteAll(request.cwd, logger, dependencies);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
await handleDelete(request.cwd, request.args.branchName, logger, dependencies);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (isUsageFailure(error)) {
|
|
23
|
+
logger.error(error.message, { exit: error.exit });
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function handleDelete(cwd, branchName, logger, dependencies) {
|
|
29
|
+
try {
|
|
30
|
+
if (!branchName) {
|
|
31
|
+
logger.error('Branch name is required with --delete flag\n\nUsage: aiw branch --delete <branchName>', {
|
|
32
|
+
exit: EXIT_CODES.INVALID_USAGE,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
logger.debug('Checking if current directory is a git repository...');
|
|
36
|
+
const isGitRepo = await isGitRepository(cwd, dependencies);
|
|
37
|
+
if (!isGitRepo) {
|
|
38
|
+
logger.error('Not a git repository. This command only works inside a git repository.', {
|
|
39
|
+
exit: EXIT_CODES.ENVIRONMENT_ERROR,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
logger.debug('✓ Git repository detected');
|
|
43
|
+
if (branchName === 'main' || branchName === 'master') {
|
|
44
|
+
logger.error(`Cannot delete ${branchName} branch. This is a protected branch.`, {
|
|
45
|
+
exit: EXIT_CODES.INVALID_USAGE,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
logger.debug(`Checking if branch '${branchName}' exists...`);
|
|
49
|
+
if (!dependencies.branchExists(branchName)) {
|
|
50
|
+
logger.error(`Branch '${branchName}' does not exist.`, {
|
|
51
|
+
exit: EXIT_CODES.INVALID_USAGE,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
logger.debug(`✓ Branch '${branchName}' exists`);
|
|
55
|
+
logger.debug('Getting current branch name...');
|
|
56
|
+
let currentBranch;
|
|
57
|
+
try {
|
|
58
|
+
currentBranch = dependencies.getCurrentBranch();
|
|
59
|
+
}
|
|
60
|
+
catch (error_) {
|
|
61
|
+
const error = error_;
|
|
62
|
+
logger.error(`Failed to get current branch: ${error.message}`, {
|
|
63
|
+
exit: EXIT_CODES.ENVIRONMENT_ERROR,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
logger.debug(`Current branch: ${currentBranch}`);
|
|
67
|
+
if (currentBranch === branchName) {
|
|
68
|
+
try {
|
|
69
|
+
await dependencies.copyToClipboard('aiw branch --main');
|
|
70
|
+
logger.debug('✓ Copied "aiw branch --main" to clipboard');
|
|
71
|
+
}
|
|
72
|
+
catch (clipboardError) {
|
|
73
|
+
logger.debug('Failed to copy to clipboard:', clipboardError);
|
|
74
|
+
}
|
|
75
|
+
logger.error(`Cannot delete branch '${branchName}' because you are currently on it.\n\n` +
|
|
76
|
+
'Please switch to a different directory first.\n\n' +
|
|
77
|
+
"Suggestion: 'aiw branch --main' has been copied to your clipboard.", { exit: EXIT_CODES.INVALID_USAGE });
|
|
78
|
+
}
|
|
79
|
+
logger.debug(`✓ Not currently on branch '${branchName}'`);
|
|
80
|
+
logger.debug(`Finding worktree path for branch '${branchName}'...`);
|
|
81
|
+
const worktreePath = dependencies.getWorktreePath(branchName);
|
|
82
|
+
if (worktreePath) {
|
|
83
|
+
logger.logInfo(`Deleting worktree folder at ${worktreePath}...`);
|
|
84
|
+
await dependencies.deleteWorktreeFolder(worktreePath, { debugLog: (message) => logger.debug(message) });
|
|
85
|
+
logger.debug('✓ Worktree folder deleted');
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
logger.debug('No worktree folder found for this branch');
|
|
89
|
+
}
|
|
90
|
+
logger.logInfo(`Deleting git branch '${branchName}'...`);
|
|
91
|
+
dependencies.deleteBranch(branchName, { debugLog: (message) => logger.debug(message) });
|
|
92
|
+
logger.debug(`✓ Git branch '${branchName}' deleted`);
|
|
93
|
+
logger.logSuccess(`✓ Branch '${branchName}' and its worktree have been deleted`);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const err = error;
|
|
97
|
+
if (err.message?.includes('EEXIT'))
|
|
98
|
+
throw error;
|
|
99
|
+
logger.error(`Failed to delete branch: ${err.message}`, {
|
|
100
|
+
exit: EXIT_CODES.GENERAL_ERROR,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function handleDeleteAll(cwd, logger, dependencies) {
|
|
105
|
+
try {
|
|
106
|
+
logger.debug('Checking if current directory is a git repository...');
|
|
107
|
+
const isGitRepo = await isGitRepository(cwd, dependencies);
|
|
108
|
+
if (!isGitRepo) {
|
|
109
|
+
logger.error('Not a git repository. This command only works inside a git repository.', {
|
|
110
|
+
exit: EXIT_CODES.ENVIRONMENT_ERROR,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
logger.debug('✓ Git repository detected');
|
|
114
|
+
logger.logInfo('Scanning all worktrees in repository...');
|
|
115
|
+
const allWorktrees = dependencies.getAllWorktrees();
|
|
116
|
+
logger.debug(`Found ${allWorktrees.length} worktrees`);
|
|
117
|
+
const deleted = [];
|
|
118
|
+
const preserved = [];
|
|
119
|
+
const candidatesForAsyncCheck = [];
|
|
120
|
+
const normalizedCwd = resolve(cwd);
|
|
121
|
+
for (const worktree of allWorktrees) {
|
|
122
|
+
const { branch, path } = worktree;
|
|
123
|
+
if (!branch) {
|
|
124
|
+
logger.debug(`Skipping worktree at ${path} (detached HEAD)`);
|
|
125
|
+
preserved.push({ branch, path, reason: 'detached HEAD' });
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (branch === 'main' || branch === 'master') {
|
|
129
|
+
logger.debug(`Skipping protected branch: ${branch}`);
|
|
130
|
+
preserved.push({ branch, path, reason: 'protected branch (main/master)' });
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (normalizedCwd === resolve(path)) {
|
|
134
|
+
logger.debug(`Skipping current directory worktree: ${path}`);
|
|
135
|
+
preserved.push({ branch, path, reason: 'current directory (cannot delete while inside)' });
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
candidatesForAsyncCheck.push({ branch, path });
|
|
139
|
+
}
|
|
140
|
+
logger.debug(`Running safety checks on ${candidatesForAsyncCheck.length} candidates in parallel...`);
|
|
141
|
+
const debugLog = (message) => logger.debug(message);
|
|
142
|
+
const safetyCheckResults = await Promise.all(candidatesForAsyncCheck.map(async ({ branch, path }) => {
|
|
143
|
+
logger.debug(`Checking safety for branch '${branch}'...`);
|
|
144
|
+
if (dependencies.hasUnpushedCommits(branch, { debugLog })) {
|
|
145
|
+
logger.debug(`Branch '${branch}' has unpushed commits, skipping`);
|
|
146
|
+
return { branch, path, safe: false, reason: 'has unpushed commits' };
|
|
147
|
+
}
|
|
148
|
+
if (dependencies.hasMergeRequest(branch, { debugLog })) {
|
|
149
|
+
logger.debug(`Branch '${branch}' has an open PR, skipping`);
|
|
150
|
+
return { branch, path, safe: false, reason: 'has open pull request' };
|
|
151
|
+
}
|
|
152
|
+
logger.debug(`Branch '${branch}' is safe to delete`);
|
|
153
|
+
return { branch, path, safe: true, reason: null };
|
|
154
|
+
}));
|
|
155
|
+
const safeToDelete = safetyCheckResults.filter((result) => result.safe);
|
|
156
|
+
const unsafe = safetyCheckResults.filter((result) => !result.safe);
|
|
157
|
+
for (const { branch, path, reason } of unsafe) {
|
|
158
|
+
preserved.push({ branch, path, reason: reason });
|
|
159
|
+
}
|
|
160
|
+
for (const { branch, path } of safeToDelete) {
|
|
161
|
+
try {
|
|
162
|
+
// Sequential deletion required: worktree must be deleted before branch.
|
|
163
|
+
// eslint-disable-next-line no-await-in-loop
|
|
164
|
+
await dependencies.deleteWorktreeFolder(path, { debugLog });
|
|
165
|
+
dependencies.deleteBranch(branch, { debugLog });
|
|
166
|
+
deleted.push({ branch, path });
|
|
167
|
+
logger.debug(`✓ Deleted branch '${branch}' and worktree at ${path}`);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
const err = error;
|
|
171
|
+
logger.debug(`Failed to delete branch '${branch}': ${err.message}`);
|
|
172
|
+
preserved.push({ branch, path, reason: `deletion failed: ${err.message}` });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
logger.log('');
|
|
176
|
+
logger.logSuccess('✓ Worktree cleanup complete');
|
|
177
|
+
logger.log('');
|
|
178
|
+
if (deleted.length > 0) {
|
|
179
|
+
logger.logInfo(`Deleted ${deleted.length} worktree${deleted.length === 1 ? '' : 's'}:`);
|
|
180
|
+
for (const { branch, path } of deleted) {
|
|
181
|
+
logger.log(` - ${branch} (${path})`);
|
|
182
|
+
}
|
|
183
|
+
logger.log('');
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
logger.logInfo('No worktrees were deleted.');
|
|
187
|
+
logger.log('');
|
|
188
|
+
}
|
|
189
|
+
if (preserved.length > 0) {
|
|
190
|
+
logger.logInfo(`Preserved ${preserved.length} worktree${preserved.length === 1 ? '' : 's'}:`);
|
|
191
|
+
for (const { branch, path, reason } of preserved) {
|
|
192
|
+
logger.log(` - ${branch ?? 'detached'} (${path})`);
|
|
193
|
+
logger.log(` Reason: ${reason}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
const err = error;
|
|
199
|
+
if (err.message?.includes('EEXIT'))
|
|
200
|
+
throw error;
|
|
201
|
+
logger.error(`Failed to clean up worktrees: ${err.message}`, {
|
|
202
|
+
exit: EXIT_CODES.GENERAL_ERROR,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async function handleMainBranch(cwd, logger, dependencies) {
|
|
207
|
+
try {
|
|
208
|
+
logger.debug('Checking if current directory is a git repository...');
|
|
209
|
+
const isGitRepo = await isGitRepository(cwd, dependencies);
|
|
210
|
+
if (!isGitRepo) {
|
|
211
|
+
logger.error('Not a git repository. This command only works inside a git repository.', {
|
|
212
|
+
exit: EXIT_CODES.ENVIRONMENT_ERROR,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
logger.debug('✓ Git repository detected');
|
|
216
|
+
logger.debug('Getting current branch name...');
|
|
217
|
+
let currentBranch;
|
|
218
|
+
try {
|
|
219
|
+
currentBranch = dependencies.getCurrentBranch();
|
|
220
|
+
}
|
|
221
|
+
catch (error_) {
|
|
222
|
+
const error = error_;
|
|
223
|
+
logger.error(`Failed to get current branch: ${error.message}`, {
|
|
224
|
+
exit: EXIT_CODES.ENVIRONMENT_ERROR,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
logger.debug(`Current branch: ${currentBranch}`);
|
|
228
|
+
if (currentBranch === 'main' || currentBranch === 'master') {
|
|
229
|
+
logger.error(`Already on ${currentBranch} branch. This command is for switching to main/master from another branch.`, {
|
|
230
|
+
exit: EXIT_CODES.INVALID_USAGE,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
logger.debug('✓ Currently on a feature branch');
|
|
234
|
+
logger.debug('Checking which main branch exists...');
|
|
235
|
+
const mainBranch = dependencies.getMainBranch();
|
|
236
|
+
if (!mainBranch) {
|
|
237
|
+
logger.error('Neither "main" nor "master" branch exists in this repository.', {
|
|
238
|
+
exit: EXIT_CODES.INVALID_USAGE,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
logger.debug(`✓ Found main branch: ${mainBranch}`);
|
|
242
|
+
logger.debug(`Finding worktree path for ${mainBranch} branch...`);
|
|
243
|
+
const mainBranchPath = dependencies.getWorktreePath(mainBranch);
|
|
244
|
+
if (!mainBranchPath) {
|
|
245
|
+
logger.error(`Could not find worktree for ${mainBranch} branch.`, {
|
|
246
|
+
exit: EXIT_CODES.ENVIRONMENT_ERROR,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
logger.debug(`✓ Found ${mainBranch} worktree at: ${mainBranchPath}`);
|
|
250
|
+
logger.logInfo(`Opening new terminal with aiw launch in ${mainBranch} branch...`);
|
|
251
|
+
const result = await dependencies.launchTerminal({
|
|
252
|
+
command: 'aiw launch',
|
|
253
|
+
cwd: mainBranchPath,
|
|
254
|
+
debugLog: (message) => logger.debug(message),
|
|
255
|
+
});
|
|
256
|
+
if (!result.success) {
|
|
257
|
+
logger.error(`Failed to launch terminal: ${result.error}`, { exit: EXIT_CODES.GENERAL_ERROR });
|
|
258
|
+
}
|
|
259
|
+
logger.logSuccess(`✓ New terminal launched with aiw in ${mainBranch} branch`);
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
const err = error;
|
|
263
|
+
if (err.message?.includes('EEXIT'))
|
|
264
|
+
throw error;
|
|
265
|
+
logger.error(`Failed to launch terminal: ${err.message}`, {
|
|
266
|
+
exit: EXIT_CODES.GENERAL_ERROR,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
async function handleWorktreeLaunch(cwd, branchName, logger, dependencies) {
|
|
271
|
+
const validatedBranchName = validateLaunchBranchName(branchName);
|
|
272
|
+
try {
|
|
273
|
+
const isGitRepo = await isGitRepository(cwd, dependencies);
|
|
274
|
+
if (!isGitRepo) {
|
|
275
|
+
logger.error('Not a git repository. This command only works inside a git repository.', {
|
|
276
|
+
exit: EXIT_CODES.ENVIRONMENT_ERROR,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
const worktreePath = deriveWorktreePath(cwd, validatedBranchName);
|
|
280
|
+
logger.debug(`Checking for existing worktree at: ${worktreePath}`);
|
|
281
|
+
let worktreeExists = false;
|
|
282
|
+
try {
|
|
283
|
+
await dependencies.access(worktreePath);
|
|
284
|
+
worktreeExists = true;
|
|
285
|
+
logger.logInfo(`Worktree already exists at: ${worktreePath}`);
|
|
286
|
+
logger.logInfo('Opening terminal in existing worktree...');
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
logger.logInfo(`Creating worktree for branch: ${validatedBranchName}`);
|
|
290
|
+
logger.logInfo(`Worktree location: ${worktreePath}`);
|
|
291
|
+
}
|
|
292
|
+
if (!worktreeExists) {
|
|
293
|
+
await dependencies.createWorktree(validatedBranchName, worktreePath);
|
|
294
|
+
logger.logSuccess(`✓ Created worktree at ${worktreePath}`);
|
|
295
|
+
logger.logSuccess(`✓ Created and checked out branch: ${validatedBranchName}`);
|
|
296
|
+
}
|
|
297
|
+
const result = await dependencies.launchTerminal({
|
|
298
|
+
command: 'aiw launch',
|
|
299
|
+
cwd: worktreePath,
|
|
300
|
+
debugLog: (message) => logger.debug(message),
|
|
301
|
+
});
|
|
302
|
+
if (!result.success) {
|
|
303
|
+
logger.error(`Failed to launch terminal: ${result.error}`, { exit: EXIT_CODES.GENERAL_ERROR });
|
|
304
|
+
}
|
|
305
|
+
logger.logSuccess('✓ Launched terminal with aiw launch');
|
|
306
|
+
logger.log('');
|
|
307
|
+
logger.logInfo('New terminal window opened at worktree location.');
|
|
308
|
+
logger.logInfo('Claude Code should be launching automatically.');
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
const err = error;
|
|
312
|
+
if (err.message?.includes('EEXIT'))
|
|
313
|
+
throw error;
|
|
314
|
+
if (err.message?.includes('already exists')) {
|
|
315
|
+
logger.error(`Branch '${validatedBranchName}' already exists. Choose a different name.`, {
|
|
316
|
+
exit: EXIT_CODES.INVALID_USAGE,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
if (err.stderr?.includes('fatal: not a git repository')) {
|
|
320
|
+
logger.error('Not a git repository. Please run this command from a git repository root.', {
|
|
321
|
+
exit: EXIT_CODES.INVALID_USAGE,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
logger.error(`Failed to create/open worktree: ${err.message}`, { exit: EXIT_CODES.GENERAL_ERROR });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
async function isGitRepository(cwd, dependencies) {
|
|
328
|
+
try {
|
|
329
|
+
await dependencies.access(join(cwd, '.git'));
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function isUsageFailure(error) {
|
|
337
|
+
return typeof error === 'object'
|
|
338
|
+
&& error !== null
|
|
339
|
+
&& 'exit' in error
|
|
340
|
+
&& 'message' in error
|
|
341
|
+
&& typeof error.exit === 'number'
|
|
342
|
+
&& typeof error.message === 'string';
|
|
343
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { BranchCommandRequest } from './contracts.js';
|
|
2
|
+
export type BranchMode = 'delete' | 'delete-all' | 'launch' | 'main';
|
|
3
|
+
export declare function determineBranchMode(request: BranchCommandRequest): BranchMode;
|
|
4
|
+
export declare function validateLaunchBranchName(branchName: string | undefined): string;
|
|
5
|
+
export declare function deriveWorktreePath(cwd: string, branchName: string): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { basename, dirname, resolve } from 'node:path';
|
|
2
|
+
import { EXIT_CODES } from '../../types/index.js';
|
|
3
|
+
const VALID_BRANCH_NAME_PATTERN = /^[a-zA-Z0-9._/-]+$/;
|
|
4
|
+
export function determineBranchMode(request) {
|
|
5
|
+
const { args, flags } = request;
|
|
6
|
+
if (!flags.main && !flags.launch && !flags.delete) {
|
|
7
|
+
if (args.branchName) {
|
|
8
|
+
return 'launch';
|
|
9
|
+
}
|
|
10
|
+
throw usageFailure('Either provide a branch name directly (aiw branch <name>) or use --main, --launch, or --delete');
|
|
11
|
+
}
|
|
12
|
+
if (flags.main)
|
|
13
|
+
return 'main';
|
|
14
|
+
if (flags.launch)
|
|
15
|
+
return 'launch';
|
|
16
|
+
if (flags.delete && flags.all)
|
|
17
|
+
return 'delete-all';
|
|
18
|
+
return 'delete';
|
|
19
|
+
}
|
|
20
|
+
export function validateLaunchBranchName(branchName) {
|
|
21
|
+
if (!branchName || branchName.trim().length === 0) {
|
|
22
|
+
throw usageFailure('Branch name is required. Usage: aiw branch <name> or aiw branch --launch <name>');
|
|
23
|
+
}
|
|
24
|
+
if (!VALID_BRANCH_NAME_PATTERN.test(branchName)) {
|
|
25
|
+
throw usageFailure('Branch name contains invalid characters. Use only letters, numbers, dots, dashes, underscores, and slashes.');
|
|
26
|
+
}
|
|
27
|
+
return branchName;
|
|
28
|
+
}
|
|
29
|
+
export function deriveWorktreePath(cwd, branchName) {
|
|
30
|
+
const currentDirName = basename(cwd);
|
|
31
|
+
const parentDir = dirname(cwd);
|
|
32
|
+
return resolve(parentDir, `${currentDirName}-${branchName}`);
|
|
33
|
+
}
|
|
34
|
+
function usageFailure(message) {
|
|
35
|
+
return Object.assign(new Error(message), { exit: EXIT_CODES.INVALID_USAGE });
|
|
36
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import BaseCommand from '../../../cli/base-command.js';
|
|
2
|
+
/**
|
|
3
|
+
* Clean output folder(s) for methods.
|
|
4
|
+
* Removes all files and subdirectories from the method's output folder.
|
|
5
|
+
*/
|
|
6
|
+
export default class CleanCommand extends BaseCommand {
|
|
7
|
+
static description: string;
|
|
8
|
+
static examples: string[];
|
|
9
|
+
static flags: {
|
|
10
|
+
all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
method: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
debug: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
help: import("@oclif/core/interfaces").BooleanFlag<void>;
|
|
16
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Display contents of folders that will be deleted.
|
|
21
|
+
*
|
|
22
|
+
* @param allContents - Folders with their contents
|
|
23
|
+
* @param targetDir - Base directory for relative path display
|
|
24
|
+
*/
|
|
25
|
+
private displayContents;
|
|
26
|
+
/**
|
|
27
|
+
* Find all output folders in the target directory.
|
|
28
|
+
* Matches folders with pattern _*-output (e.g., _bmad-output, _gsd-output).
|
|
29
|
+
*
|
|
30
|
+
* @param targetDir - Directory to search in
|
|
31
|
+
* @returns Array of output folder names
|
|
32
|
+
*/
|
|
33
|
+
private findAllOutputFolders;
|
|
34
|
+
/**
|
|
35
|
+
* Get all top-level contents of a directory.
|
|
36
|
+
*
|
|
37
|
+
* @param dirPath - Directory to list contents of
|
|
38
|
+
* @returns Array of content items with path and type info
|
|
39
|
+
*/
|
|
40
|
+
private getContents;
|
|
41
|
+
}
|