aiwcli 0.15.5 → 0.15.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/dist/capabilities/branch/adapters.d.ts +2 -0
- package/dist/capabilities/branch/adapters.js +21 -0
- package/dist/capabilities/branch/contracts.d.ts +57 -0
- package/dist/capabilities/branch/contracts.js +1 -0
- package/dist/capabilities/branch/control-plane.d.ts +2 -0
- package/dist/capabilities/branch/control-plane.js +343 -0
- package/dist/capabilities/branch/runtime-core.d.ts +5 -0
- package/dist/capabilities/branch/runtime-core.js +36 -0
- package/dist/capabilities/installation/control-plane/clean-command.d.ts +41 -0
- package/dist/capabilities/installation/control-plane/clean-command.js +196 -0
- package/dist/capabilities/installation/control-plane/clear-command.d.ts +160 -0
- package/dist/capabilities/installation/control-plane/clear-command.js +1220 -0
- package/dist/capabilities/installation/control-plane/init-command.d.ts +81 -0
- package/dist/capabilities/installation/control-plane/init-command.js +449 -0
- package/dist/capabilities/launch/contracts.d.ts +51 -0
- package/dist/capabilities/launch/contracts.js +1 -0
- package/dist/capabilities/launch/control-plane/execute-launch.d.ts +2 -0
- package/dist/capabilities/launch/control-plane/execute-launch.js +222 -0
- package/dist/capabilities/launch/runtime-core/launch-options.d.ts +14 -0
- package/dist/capabilities/launch/runtime-core/launch-options.js +69 -0
- package/dist/cli/base-command.d.ts +18 -0
- package/dist/cli/base-command.js +55 -0
- package/dist/commands/branch.d.ts +0 -20
- package/dist/commands/branch.js +24 -416
- package/dist/commands/clean.d.ts +1 -41
- package/dist/commands/clean.js +1 -196
- package/dist/commands/clear.d.ts +1 -161
- package/dist/commands/clear.js +1 -1121
- package/dist/commands/init/index.d.ts +1 -98
- package/dist/commands/init/index.js +4 -478
- package/dist/commands/launch.d.ts +36 -11
- package/dist/commands/launch.js +135 -159
- package/dist/lib/base-command.d.ts +1 -114
- package/dist/lib/base-command.js +1 -153
- package/dist/lib/claude-settings-types.d.ts +31 -19
- package/dist/lib/context/context-formatter.d.ts +74 -0
- package/dist/lib/context/context-formatter.js +493 -0
- package/dist/lib/context/context-selector.d.ts +42 -0
- package/dist/lib/context/context-selector.js +451 -0
- package/dist/lib/context/context-store.d.ts +100 -0
- package/dist/lib/context/context-store.js +618 -0
- package/dist/lib/context/plan-manager.d.ts +54 -0
- package/dist/lib/context/plan-manager.js +282 -0
- package/dist/lib/context/task-tracker.d.ts +44 -0
- package/dist/lib/context/task-tracker.js +146 -0
- package/dist/lib/core-ide-base.d.ts +4 -0
- package/dist/lib/core-ide-base.js +77 -0
- package/dist/lib/core-installer.d.ts +5 -0
- package/dist/lib/core-installer.js +54 -0
- package/dist/lib/git-exclude-manager.d.ts +2 -2
- package/dist/lib/git-exclude-manager.js +3 -3
- package/dist/lib/hooks/hook-utils.d.ts +143 -0
- package/dist/lib/hooks/hook-utils.js +609 -0
- package/dist/lib/hooks/session-end-logic.d.ts +5 -0
- package/dist/lib/hooks/session-end-logic.js +63 -0
- package/dist/lib/hooks-merger.js +25 -19
- package/dist/lib/ide-path-resolver.d.ts +19 -7
- package/dist/lib/ide-path-resolver.js +25 -9
- package/dist/lib/install-state.d.ts +34 -0
- package/dist/lib/install-state.js +161 -0
- package/dist/lib/launch-options.d.ts +1 -0
- package/dist/lib/launch-options.js +1 -0
- package/dist/lib/lsp-patch.d.ts +12 -0
- package/dist/lib/lsp-patch.js +156 -0
- package/dist/lib/multiplexer.d.ts +57 -0
- package/dist/lib/multiplexer.js +19 -0
- package/dist/lib/multiplexers/psmux.d.ts +75 -0
- package/dist/lib/multiplexers/psmux.js +384 -0
- package/dist/lib/multiplexers/tmux.d.ts +44 -0
- package/dist/lib/multiplexers/tmux.js +262 -0
- package/dist/lib/mux-utils.d.ts +5 -0
- package/dist/lib/mux-utils.js +42 -0
- package/dist/lib/paths.d.ts +2 -2
- package/dist/lib/paths.js +2 -2
- package/dist/lib/platform-commands.d.ts +27 -0
- package/dist/lib/platform-commands.js +49 -0
- package/dist/lib/runtime/aiw-cli.d.ts +37 -0
- package/dist/lib/runtime/aiw-cli.js +74 -0
- package/dist/lib/runtime/atomic-write.d.ts +19 -0
- package/dist/lib/runtime/atomic-write.js +121 -0
- package/dist/lib/runtime/cli-args.d.ts +55 -0
- package/dist/lib/runtime/cli-args.js +185 -0
- package/dist/lib/runtime/constants.d.ts +56 -0
- package/dist/lib/runtime/constants.js +230 -0
- package/dist/lib/runtime/executable-policy.d.ts +16 -0
- package/dist/lib/runtime/executable-policy.js +57 -0
- package/dist/lib/runtime/git-state.d.ts +9 -0
- package/dist/lib/runtime/git-state.js +59 -0
- package/dist/lib/runtime/inference.d.ts +37 -0
- package/dist/lib/runtime/inference.js +262 -0
- package/dist/lib/runtime/lint-dispatch.d.ts +40 -0
- package/dist/lib/runtime/lint-dispatch.js +285 -0
- package/dist/lib/runtime/logger.d.ts +66 -0
- package/dist/lib/runtime/logger.js +201 -0
- package/dist/lib/runtime/models.d.ts +14 -0
- package/dist/lib/runtime/models.js +14 -0
- package/dist/lib/runtime/platform-adapter.d.ts +7 -0
- package/dist/lib/runtime/platform-adapter.js +21 -0
- package/dist/lib/runtime/preflight.d.ts +24 -0
- package/dist/lib/runtime/preflight.js +65 -0
- package/dist/lib/runtime/sentinel-ipc.d.ts +14 -0
- package/dist/lib/runtime/sentinel-ipc.js +67 -0
- package/dist/lib/runtime/state-io.d.ts +30 -0
- package/dist/lib/runtime/state-io.js +174 -0
- package/dist/lib/runtime/stop-words.d.ts +20 -0
- package/dist/lib/runtime/stop-words.js +150 -0
- package/dist/lib/runtime/subprocess-utils.d.ts +29 -0
- package/dist/lib/runtime/subprocess-utils.js +96 -0
- package/dist/lib/runtime/tmux-preflight.d.ts +13 -0
- package/dist/lib/runtime/tmux-preflight.js +78 -0
- package/dist/lib/runtime/utils.d.ts +54 -0
- package/dist/lib/runtime/utils.js +162 -0
- package/dist/lib/sentinel-wrapper.d.ts +9 -0
- package/dist/lib/sentinel-wrapper.js +20 -0
- package/dist/lib/shell-quoting.d.ts +5 -0
- package/dist/lib/shell-quoting.js +17 -0
- package/dist/lib/spawn-errors.d.ts +6 -0
- package/dist/lib/spawn-errors.js +15 -0
- package/dist/lib/spawn.js +5 -11
- package/dist/lib/template-installer.d.ts +4 -5
- package/dist/lib/template-installer.js +36 -34
- package/dist/lib/template-resolver.d.ts +6 -7
- package/dist/lib/template-resolver.js +26 -21
- package/dist/lib/template-settings-reconstructor.d.ts +7 -2
- package/dist/lib/template-settings-reconstructor.js +76 -45
- package/dist/lib/terminal-strategy.d.ts +11 -0
- package/dist/lib/terminal-strategy.js +49 -0
- package/dist/lib/terminal.d.ts +28 -0
- package/dist/lib/terminal.js +162 -112
- package/dist/lib/tmux-pane-placement.d.ts +17 -0
- package/dist/lib/tmux-pane-placement.js +58 -0
- package/dist/lib/tmux-primitives.d.ts +5 -0
- package/dist/lib/tmux-primitives.js +15 -0
- package/dist/lib/tmux-session.d.ts +32 -0
- package/dist/lib/tmux-session.js +86 -0
- package/dist/lib/tty-detection.js +1 -1
- package/dist/lib/types.d.ts +168 -0
- package/dist/lib/types.js +6 -0
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/platform/launch.d.ts +10 -0
- package/dist/platform/launch.js +10 -0
- package/dist/templates/CLAUDE.md +31 -40
- package/dist/templates/cc-native/.claude/settings.json +27 -27
- package/dist/templates/cc-native/CC-NATIVE-README.md +1 -1
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +10 -9
- package/dist/templates/cc-native/_cc-native/CLAUDE.md +18 -18
- package/dist/templates/cc-native/_cc-native/artifacts/CLAUDE.md +3 -3
- package/dist/templates/cc-native/_cc-native/artifacts/lib/format.ts +14 -14
- package/dist/templates/cc-native/_cc-native/artifacts/lib/tracker.ts +1 -1
- package/dist/templates/cc-native/_cc-native/artifacts/lib/write.ts +3 -3
- package/dist/templates/cc-native/_cc-native/cc-native.config.json +3 -3
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +16 -15
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +3 -3
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +2 -2
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +2 -2
- package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +3 -3
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +2 -2
- package/dist/templates/cc-native/_cc-native/hooks/validate_task_prompt.ts +3 -3
- package/dist/templates/cc-native/_cc-native/lib-ts/CLAUDE.md +8 -8
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +4 -4
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-discovery.ts +2 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/logger.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/retrieval-pipeline.ts +2 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/rlm/types.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/settings.ts +8 -8
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +3 -3
- package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +2 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +3 -3
- package/dist/templates/cc-native/_cc-native/plan-review/CLAUDE.md +3 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/__tests__/agent-selection.test.ts +345 -0
- package/dist/templates/cc-native/_cc-native/plan-review/lib/__tests__/preflight.test.ts +344 -0
- package/dist/templates/cc-native/_cc-native/plan-review/lib/agent-selection.ts +37 -15
- package/dist/templates/cc-native/_cc-native/plan-review/lib/corroboration.ts +16 -69
- package/dist/templates/cc-native/_cc-native/plan-review/lib/orchestrator.ts +1 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/output-builder.ts +1 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/plan-questions.ts +2 -2
- package/dist/templates/cc-native/_cc-native/plan-review/lib/preflight.ts +56 -26
- package/dist/templates/cc-native/_cc-native/plan-review/lib/review-pipeline.ts +7 -7
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/agent.ts +4 -4
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/base/base-agent.ts +3 -3
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/index.ts +1 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/claude-agent.ts +2 -2
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/codex-agent.ts +4 -4
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/gemini-agent.ts +1 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/orchestrator-claude-agent.ts +5 -6
- package/dist/templates/core/.codex/workflows/codex.md +17 -0
- package/dist/templates/core/.codex/workflows/handoff.md +5 -0
- package/dist/templates/core/.codex/workflows/meta-plan.md +7 -0
- package/dist/templates/core/.cognition/AGENTS.md +5 -0
- package/dist/templates/core/.cognition/config.json +12 -0
- package/dist/templates/{_shared → core}/.windsurf/workflows/handoff.md +1 -1
- package/dist/templates/{_shared → core}/.windsurf/workflows/meta-plan.md +1 -1
- package/dist/templates/core/hooks-ts/_utils/git-state.ts +2 -0
- package/dist/templates/{_shared → core}/hooks-ts/archive_plan.ts +14 -23
- package/dist/templates/core/hooks-ts/codex_explorer.ts +160 -0
- package/dist/templates/{_shared → core}/hooks-ts/context_monitor.ts +23 -55
- package/dist/templates/{_shared → core}/hooks-ts/file-suggestion.ts +4 -3
- package/dist/templates/{_shared → core}/hooks-ts/lint_after_edit.ts +7 -9
- package/dist/templates/{_shared → core}/hooks-ts/pre_compact.ts +5 -5
- package/dist/templates/{_shared → core}/hooks-ts/session_end.ts +38 -78
- package/dist/templates/{_shared → core}/hooks-ts/session_start.ts +5 -5
- package/dist/templates/core/hooks-ts/task_create_capture.ts +32 -0
- package/dist/templates/{_shared → core}/hooks-ts/task_update_capture.ts +9 -24
- package/dist/templates/core/hooks-ts/user_prompt_submit.ts +46 -0
- package/dist/templates/{_shared → core}/lib-ts/CLAUDE.md +27 -16
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/backends/headless.ts +3 -2
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/backends/tmux.ts +44 -15
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/base-agent.ts +6 -4
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/execution-backend.ts +1 -1
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/index.ts +2 -2
- package/dist/templates/{_shared → core}/lib-ts/agent-exec/structured-output.ts +4 -5
- package/dist/templates/{_shared → core}/lib-ts/context/CLAUDE.md +9 -6
- package/dist/templates/{_shared → core}/lib-ts/context/context-formatter.ts +16 -21
- package/dist/templates/{_shared → core}/lib-ts/context/context-selector.ts +8 -6
- package/dist/templates/{_shared → core}/lib-ts/context/context-store.ts +32 -20
- package/dist/templates/{_shared → core}/lib-ts/context/plan-manager.ts +19 -15
- package/dist/templates/{_shared → core}/lib-ts/context/task-tracker.ts +3 -3
- package/dist/templates/core/lib-ts/hooks/context-monitor-logic.ts +32 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/hooks}/hook-utils.ts +168 -41
- package/dist/templates/core/lib-ts/hooks/prompt-binding-logic.ts +80 -0
- package/dist/templates/core/lib-ts/hooks/session-end-logic.ts +93 -0
- package/dist/templates/core/lib-ts/package.json +19 -0
- package/dist/templates/core/lib-ts/runtime/agent-launcher.ts +295 -0
- package/dist/templates/core/lib-ts/runtime/aiw-cli.ts +106 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/atomic-write.ts +12 -7
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/cli-args.ts +8 -6
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/constants.ts +326 -324
- package/dist/templates/core/lib-ts/runtime/executable-policy.ts +89 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/git-state.ts +6 -4
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/inference.ts +59 -10
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/lint-dispatch.ts +25 -23
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/logger.ts +32 -29
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/models.ts +2 -2
- package/dist/templates/core/lib-ts/runtime/platform-adapter.ts +33 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/preflight.ts +4 -3
- package/dist/templates/core/lib-ts/runtime/sentinel-ipc.ts +91 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/state-io.ts +11 -7
- package/dist/templates/core/lib-ts/runtime/stop-words.ts +185 -0
- package/dist/templates/core/lib-ts/runtime/subprocess-utils.ts +147 -0
- package/dist/templates/core/lib-ts/runtime/tmux-preflight.ts +93 -0
- package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/utils.ts +4 -3
- package/dist/templates/{_shared → core}/lib-ts/templates/formatters.ts +7 -5
- package/dist/templates/{_shared → core}/lib-ts/templates/plan-context.ts +2 -1
- package/dist/templates/{_shared → core}/lib-ts/tsconfig.json +3 -1
- package/dist/templates/{_shared → core}/lib-ts/types.ts +78 -77
- package/dist/templates/core/scripts/resolve-run.ts +61 -0
- package/dist/templates/{_shared → core}/scripts/resolve_context.ts +3 -3
- package/dist/templates/{_shared → core}/scripts/status_line.ts +25 -20
- package/dist/templates/core/skills/codex/CLAUDE.md +78 -0
- package/dist/templates/{_shared → core}/skills/codex/SKILL.md +21 -18
- package/dist/templates/{_shared → core}/skills/codex/lib/codex-watcher.ts +76 -103
- package/dist/templates/{_shared → core}/skills/codex/scripts/launch-codex.ts +119 -133
- package/dist/templates/{_shared → core}/skills/codex/scripts/watch-codex.ts +6 -4
- package/dist/templates/core/skills/devin/CLAUDE.md +65 -0
- package/dist/templates/core/skills/devin/SKILL.md +73 -0
- package/dist/templates/core/skills/devin/lib/devin-watcher.ts +280 -0
- package/dist/templates/core/skills/devin/scripts/launch-devin.ts +257 -0
- package/dist/templates/{_shared → core}/skills/handoff-system/CLAUDE.md +436 -433
- package/dist/templates/{_shared → core}/skills/handoff-system/lib/document-generator.ts +9 -7
- package/dist/templates/{_shared → core}/skills/handoff-system/lib/handoff-reader.ts +6 -4
- package/dist/templates/{_shared → core}/skills/handoff-system/scripts/resume_handoff.ts +10 -8
- package/dist/templates/{_shared → core}/skills/handoff-system/scripts/save_handoff.ts +12 -10
- package/dist/templates/{_shared → core}/skills/handoff-system/workflows/handoff-resume.md +2 -2
- package/dist/templates/{_shared → core}/skills/handoff-system/workflows/handoff.md +6 -5
- package/dist/templates/{_shared → core}/skills/meta-plan/CLAUDE.md +2 -1
- package/dist/templates/{_shared → core}/skills/meta-plan/workflows/meta-plan.md +8 -7
- package/oclif.manifest.json +89 -13
- package/package.json +13 -12
- package/dist/templates/_shared/.claude/settings.json +0 -120
- package/dist/templates/_shared/.claude/skills/codex/SKILL.md +0 -35
- package/dist/templates/_shared/.claude/skills/handoff/SKILL.md +0 -13
- package/dist/templates/_shared/.claude/skills/handoff-resume/SKILL.md +0 -13
- package/dist/templates/_shared/.claude/skills/meta-plan/SKILL.md +0 -43
- package/dist/templates/_shared/.codex/workflows/codex.md +0 -11
- package/dist/templates/_shared/.codex/workflows/handoff.md +0 -226
- package/dist/templates/_shared/.codex/workflows/meta-plan.md +0 -347
- package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +0 -2
- package/dist/templates/_shared/hooks-ts/task_create_capture.ts +0 -48
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +0 -93
- package/dist/templates/_shared/lib-ts/base/launchers/tmux-launcher.ts +0 -173
- package/dist/templates/_shared/lib-ts/base/launchers/window-launcher.ts +0 -93
- package/dist/templates/_shared/lib-ts/base/launchers/wt-launcher.ts +0 -64
- package/dist/templates/_shared/lib-ts/base/pane-launcher.ts +0 -55
- package/dist/templates/_shared/lib-ts/base/sentinel-ipc.ts +0 -87
- package/dist/templates/_shared/lib-ts/base/stop-words.ts +0 -184
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +0 -249
- package/dist/templates/_shared/lib-ts/base/tmux-driver.ts +0 -341
- package/dist/templates/_shared/lib-ts/base/tmux-pane-placement.ts +0 -78
- package/dist/templates/_shared/lib-ts/package.json +0 -20
- package/dist/templates/_shared/scripts/resolve-run.ts +0 -62
- package/dist/templates/_shared/skills/codex/CLAUDE.md +0 -70
- /package/dist/templates/{_shared → core}/lib-ts/agent-exec/backends/index.ts +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { safeMaybeActivate, logInfo, logWarn } from "./hook-utils.js";
|
|
2
|
+
import { buildContextInventory } from "../context/context-formatter.js";
|
|
3
|
+
import { BlockRequest, determineContext } from "../context/context-selector.js";
|
|
4
|
+
import {
|
|
5
|
+
bindSession,
|
|
6
|
+
getContextBySessionId,
|
|
7
|
+
saveState,
|
|
8
|
+
} from "../context/context-store.js";
|
|
9
|
+
import type { ContextState } from "../types.js";
|
|
10
|
+
|
|
11
|
+
export interface PromptBindingResult {
|
|
12
|
+
outputs: string[];
|
|
13
|
+
blockedReason?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function shouldClearHandoff(state: ContextState): boolean {
|
|
17
|
+
return Boolean(state.handoff_path);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function executePromptBinding(
|
|
21
|
+
prompt: string | undefined,
|
|
22
|
+
sessionId: string,
|
|
23
|
+
permissionMode: string,
|
|
24
|
+
projectRoot: string,
|
|
25
|
+
): Promise<PromptBindingResult> {
|
|
26
|
+
const outputs: string[] = [];
|
|
27
|
+
const existingCtx = getContextBySessionId(sessionId, projectRoot);
|
|
28
|
+
|
|
29
|
+
if (existingCtx) {
|
|
30
|
+
safeMaybeActivate(
|
|
31
|
+
existingCtx.id,
|
|
32
|
+
permissionMode,
|
|
33
|
+
projectRoot,
|
|
34
|
+
"user_prompt_submit",
|
|
35
|
+
);
|
|
36
|
+
return { outputs };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!prompt) return { outputs };
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const [contextId, method, outputText] = await determineContext(
|
|
43
|
+
prompt,
|
|
44
|
+
sessionId,
|
|
45
|
+
projectRoot,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (contextId) {
|
|
49
|
+
bindSession(contextId, sessionId, projectRoot);
|
|
50
|
+
safeMaybeActivate(contextId, permissionMode, projectRoot, "user_prompt_submit");
|
|
51
|
+
|
|
52
|
+
const state = getContextBySessionId(sessionId, projectRoot);
|
|
53
|
+
if (state && shouldClearHandoff(state)) {
|
|
54
|
+
state.handoff_path = null;
|
|
55
|
+
saveState(state.id, state, projectRoot);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
logInfo("user_prompt_submit", `Context ${contextId} via ${method}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (outputText) outputs.push(outputText);
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const boundState = getContextBySessionId(sessionId, projectRoot);
|
|
65
|
+
if (boundState) {
|
|
66
|
+
const inventory = buildContextInventory(boundState, projectRoot);
|
|
67
|
+
if (inventory) outputs.push(inventory);
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
logWarn("user_prompt_submit", `Inventory failed (non-critical): ${error}`);
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (error instanceof BlockRequest) {
|
|
74
|
+
return { outputs, blockedReason: (error as Error).message };
|
|
75
|
+
}
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { outputs };
|
|
80
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
extractPlanAnchors,
|
|
5
|
+
generatePlanId,
|
|
6
|
+
normalizePlanContent,
|
|
7
|
+
} from "../context/plan-manager.js";
|
|
8
|
+
import { nowIso } from "../runtime/utils.js";
|
|
9
|
+
import type { ContextState, GitState, LastSession } from "../types.js";
|
|
10
|
+
|
|
11
|
+
function formatArchiveTimestamp(date: Date): string {
|
|
12
|
+
return (
|
|
13
|
+
`${date.getFullYear()}-` +
|
|
14
|
+
`${String(date.getMonth() + 1).padStart(2, "0")}-` +
|
|
15
|
+
`${String(date.getDate()).padStart(2, "0")}-` +
|
|
16
|
+
`${String(date.getHours()).padStart(2, "0")}` +
|
|
17
|
+
`${String(date.getMinutes()).padStart(2, "0")}`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function resolveArtifactType(state: ContextState): "plan" | "handoff" | null {
|
|
22
|
+
if (state.next_artifact_type) return state.next_artifact_type;
|
|
23
|
+
|
|
24
|
+
const hasPlan = Boolean(state.plan_path && state.plan_hash);
|
|
25
|
+
const hasHandoff = Boolean(state.handoff_path);
|
|
26
|
+
|
|
27
|
+
if (hasPlan && hasHandoff) return "plan";
|
|
28
|
+
if (hasPlan) return "plan";
|
|
29
|
+
if (hasHandoff) return "handoff";
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function computePlanFallback(
|
|
34
|
+
state: ContextState,
|
|
35
|
+
planContent: string,
|
|
36
|
+
): Partial<ContextState> {
|
|
37
|
+
const normalized = normalizePlanContent(planContent);
|
|
38
|
+
const planHash = crypto
|
|
39
|
+
.createHash("sha256")
|
|
40
|
+
.update(normalized, "utf8")
|
|
41
|
+
.digest("hex")
|
|
42
|
+
.slice(0, 12);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
plan_hash: planHash,
|
|
46
|
+
plan_signature: planContent.slice(0, 200),
|
|
47
|
+
plan_id: generatePlanId(),
|
|
48
|
+
plan_anchors: extractPlanAnchors(planContent),
|
|
49
|
+
work_consumed: state.work_consumed ?? false,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function shouldStage(
|
|
54
|
+
state: ContextState,
|
|
55
|
+
permissionMode: string,
|
|
56
|
+
): boolean {
|
|
57
|
+
const artifactType = resolveArtifactType(state);
|
|
58
|
+
const canStage = state.mode === "active" || permissionMode === "plan";
|
|
59
|
+
return Boolean(artifactType && canStage && !state.work_consumed);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function buildSessionMetadata(
|
|
63
|
+
sessionId: string,
|
|
64
|
+
source: string,
|
|
65
|
+
transcriptPath: string | undefined,
|
|
66
|
+
gitState?: GitState,
|
|
67
|
+
): LastSession {
|
|
68
|
+
return {
|
|
69
|
+
session_id: sessionId,
|
|
70
|
+
save_reason: source,
|
|
71
|
+
saved_at: nowIso(),
|
|
72
|
+
...(transcriptPath ? { transcript_path: transcriptPath } : {}),
|
|
73
|
+
git_state: gitState ?? {},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function generateArchiveFilename(
|
|
78
|
+
sessionId: string,
|
|
79
|
+
date: Date,
|
|
80
|
+
existingNames: Iterable<string>,
|
|
81
|
+
): string {
|
|
82
|
+
const existing = new Set(existingNames);
|
|
83
|
+
const timestamp = formatArchiveTimestamp(date);
|
|
84
|
+
|
|
85
|
+
let archiveName = `${timestamp}-${sessionId}.jsonl`;
|
|
86
|
+
let counter = 2;
|
|
87
|
+
while (existing.has(archiveName)) {
|
|
88
|
+
archiveName = `${timestamp}-${sessionId}-${counter}.jsonl`;
|
|
89
|
+
counter += 1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return archiveName;
|
|
93
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lib-ts-tests",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "vitest run",
|
|
7
|
+
"test:unit": "vitest run '__tests__/runtime/**/*.test.ts' '__tests__/templates/**/*.test.ts'",
|
|
8
|
+
"test:contract": "vitest run '__tests__/context/**/*.test.ts' '__tests__/handoff/**/*.test.ts'",
|
|
9
|
+
"test:integration": "vitest run '__tests__/integration/**/*.test.ts'",
|
|
10
|
+
"test:parity": "vitest run '__tests__/integration/python-parity.test.ts'"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"chai": "^5.0.0",
|
|
14
|
+
"@types/sinon": "^17.0.0",
|
|
15
|
+
"sinon": "^17.0.0",
|
|
16
|
+
"typescript": "^5.0.0",
|
|
17
|
+
"vitest": "^3.2.4"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared agent-launcher utilities used by Codex, Devin, and future agent skills.
|
|
3
|
+
* Extracted from codex skill to avoid duplication across agent launch scripts.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import * as os from "node:os";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
|
|
10
|
+
import { logDebug, logWarn } from "./logger.js";
|
|
11
|
+
import { execFileAsync, findExecutable } from "./subprocess-utils.js";
|
|
12
|
+
import { buildExternalAgentContext } from "../context/context-formatter.js";
|
|
13
|
+
import { getContextBySessionId, getContext } from "../context/context-store.js";
|
|
14
|
+
import type { ContextState } from "../types.js";
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Constants
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
export const POLL_INTERVAL_MS = 2000;
|
|
21
|
+
export const POLL_TIMEOUT_MS = 3000;
|
|
22
|
+
export const WAIT_TIMEOUT_MS_DEFAULT = 14_400_000;
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Types
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
type PaneBackend = "tmux" | "window" | "exec";
|
|
29
|
+
|
|
30
|
+
export interface PaneWatchTarget {
|
|
31
|
+
backend?: PaneBackend;
|
|
32
|
+
paneId?: string;
|
|
33
|
+
sentinelPath?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Generic Helpers
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
export function sleep(ms: number): Promise<void> {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
setTimeout(resolve, ms);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function persistSummary(
|
|
47
|
+
summary: string,
|
|
48
|
+
prefix: string,
|
|
49
|
+
sessionId?: string,
|
|
50
|
+
): string | null {
|
|
51
|
+
try {
|
|
52
|
+
const suffix = sessionId
|
|
53
|
+
? sessionId.replaceAll(/[^a-zA-Z0-9_-]/g, "").slice(0, 8)
|
|
54
|
+
: String(process.pid);
|
|
55
|
+
const filePath = path.join(
|
|
56
|
+
os.tmpdir(),
|
|
57
|
+
`${prefix}-summary-${Date.now()}-${suffix}.md`,
|
|
58
|
+
);
|
|
59
|
+
fs.writeFileSync(filePath, summary, "utf8");
|
|
60
|
+
return filePath.replaceAll("\\", "/");
|
|
61
|
+
} catch (error) {
|
|
62
|
+
logWarn("agent-launcher", `Failed to persist summary: ${String(error)}`);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Plan Discovery
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
/** Fallback plan discovery: scan all context plan dirs by mtime. */
|
|
72
|
+
export function findLatestPlanByMtime(projectRoot: string): string | null {
|
|
73
|
+
const contextsDir = path.join(projectRoot, "_output", "contexts");
|
|
74
|
+
if (!fs.existsSync(contextsDir)) return null;
|
|
75
|
+
|
|
76
|
+
let best: { path: string; mtime: number } | null = null;
|
|
77
|
+
|
|
78
|
+
for (const ctxEntry of fs.readdirSync(contextsDir)) {
|
|
79
|
+
if (ctxEntry.startsWith("_")) continue;
|
|
80
|
+
const plansDir = path.join(contextsDir, ctxEntry, "plans");
|
|
81
|
+
if (!fs.existsSync(plansDir)) continue;
|
|
82
|
+
|
|
83
|
+
for (const file of fs.readdirSync(plansDir)) {
|
|
84
|
+
if (!file.endsWith(".md")) continue;
|
|
85
|
+
const fullPath = path.join(plansDir, file);
|
|
86
|
+
try {
|
|
87
|
+
const mtime = fs.statSync(fullPath).mtimeMs;
|
|
88
|
+
if (!best || mtime > best.mtime) {
|
|
89
|
+
best = { path: fullPath, mtime };
|
|
90
|
+
}
|
|
91
|
+
} catch { /* skip unreadable */ }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return best?.path ?? null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// Bootstrap Prompt Construction
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
export function buildFileModeBootstrapPrompt(
|
|
103
|
+
targetPath: string,
|
|
104
|
+
sourceLabel: "plan" | "file",
|
|
105
|
+
extraPrompt?: string,
|
|
106
|
+
orientation?: string,
|
|
107
|
+
): string {
|
|
108
|
+
const absolutePath = path.resolve(targetPath);
|
|
109
|
+
const sourceTitle = sourceLabel === "plan" ? "Plan Source" : "File Source";
|
|
110
|
+
const sections: string[] = ["## Startup Brief", ""];
|
|
111
|
+
|
|
112
|
+
if (orientation?.trim()) {
|
|
113
|
+
sections.push(orientation.trim(), "", "---", "");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
sections.push(
|
|
117
|
+
`## ${sourceTitle}`,
|
|
118
|
+
"",
|
|
119
|
+
`Primary input path: ${absolutePath}`,
|
|
120
|
+
"",
|
|
121
|
+
"Read this file directly from disk before taking action.",
|
|
122
|
+
"Treat its contents as the source of truth.",
|
|
123
|
+
"Do not ask for the file contents to be pasted inline.",
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
if (extraPrompt?.trim()) {
|
|
127
|
+
sections.push("", "## Additional Instructions", "", extraPrompt.trim());
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return sections.join("\n");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Context Resolution
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
export function resolveContextForLaunch(
|
|
138
|
+
contextFlag: string | undefined,
|
|
139
|
+
projectRoot: string,
|
|
140
|
+
): ContextState | null {
|
|
141
|
+
if (contextFlag) {
|
|
142
|
+
return getContext(contextFlag, projectRoot) ?? null;
|
|
143
|
+
}
|
|
144
|
+
const sessionId = process.env.CLAUDE_SESSION_ID;
|
|
145
|
+
if (sessionId) {
|
|
146
|
+
return getContextBySessionId(sessionId, projectRoot) ?? null;
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Prompt File Writers
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
export interface WritePromptFileOpts {
|
|
156
|
+
ctx?: ContextState | null;
|
|
157
|
+
extraPrompt?: string;
|
|
158
|
+
projectRoot: string;
|
|
159
|
+
tempFilePrefix: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface WriteFileRefPromptFileOpts extends WritePromptFileOpts {
|
|
163
|
+
fileReferencePath: string;
|
|
164
|
+
label: "plan" | "file";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface WriteInlinePromptFileOpts extends WritePromptFileOpts {
|
|
168
|
+
text: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Build a file-reference bootstrap prompt and write it to a temp file.
|
|
173
|
+
* Returns the temp file path.
|
|
174
|
+
*/
|
|
175
|
+
export function writeFileRefPromptFile(opts: WriteFileRefPromptFileOpts): string {
|
|
176
|
+
let orientation = "";
|
|
177
|
+
if (opts.ctx) {
|
|
178
|
+
try {
|
|
179
|
+
orientation = buildExternalAgentContext(opts.ctx, opts.projectRoot);
|
|
180
|
+
} catch {
|
|
181
|
+
logWarn("agent-launcher", `Context orientation build failed for ${opts.ctx.id}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const bootstrap = buildFileModeBootstrapPrompt(
|
|
186
|
+
opts.fileReferencePath,
|
|
187
|
+
opts.label,
|
|
188
|
+
opts.extraPrompt,
|
|
189
|
+
orientation,
|
|
190
|
+
);
|
|
191
|
+
const tempFile = path.join(os.tmpdir(), `${opts.tempFilePrefix}-file-ref-${Date.now()}.md`);
|
|
192
|
+
fs.writeFileSync(tempFile, bootstrap, "utf8");
|
|
193
|
+
return tempFile;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Write inline text to a temp file, prepend context orientation, append extra prompt.
|
|
198
|
+
* Returns the temp file path.
|
|
199
|
+
*/
|
|
200
|
+
export function writeInlinePromptFile(opts: WriteInlinePromptFileOpts): string {
|
|
201
|
+
let content = opts.text;
|
|
202
|
+
|
|
203
|
+
if (opts.ctx) {
|
|
204
|
+
try {
|
|
205
|
+
const orientation = buildExternalAgentContext(opts.ctx, opts.projectRoot);
|
|
206
|
+
content = `${orientation}\n\n---\n\n${content}`;
|
|
207
|
+
} catch {
|
|
208
|
+
logWarn("agent-launcher", `Context orientation prepend failed for ${opts.ctx.id}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (opts.extraPrompt?.trim()) {
|
|
213
|
+
content = `${content}\n\n---\n\n## Additional Instructions\n\n${opts.extraPrompt}`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const tempFile = path.join(os.tmpdir(), `${opts.tempFilePrefix}-prompt-${Date.now()}.md`);
|
|
217
|
+
fs.writeFileSync(tempFile, content, "utf8");
|
|
218
|
+
return tempFile;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
// Pane Watching
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
async function waitForSentinelClose(sentinelPath: string, timeoutMs: number): Promise<void> {
|
|
226
|
+
const deadline = Date.now() + timeoutMs;
|
|
227
|
+
while (true) {
|
|
228
|
+
if (fs.existsSync(sentinelPath)) return;
|
|
229
|
+
if (Date.now() >= deadline) {
|
|
230
|
+
logDebug("agent-launcher", `watch timeout reached waiting for sentinel ${sentinelPath}`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const remainingMs = deadline - Date.now();
|
|
235
|
+
await sleep(Math.max(0, Math.min(POLL_INTERVAL_MS, remainingMs)));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function normalizeWatchTarget(target: string | PaneWatchTarget): PaneWatchTarget {
|
|
240
|
+
if (typeof target === "string") {
|
|
241
|
+
return { backend: "tmux", paneId: target };
|
|
242
|
+
}
|
|
243
|
+
return target;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export async function waitForPaneClose(
|
|
247
|
+
target: string | PaneWatchTarget,
|
|
248
|
+
timeoutMs = WAIT_TIMEOUT_MS_DEFAULT,
|
|
249
|
+
): Promise<void> {
|
|
250
|
+
const watch = normalizeWatchTarget(target);
|
|
251
|
+
|
|
252
|
+
if (watch.sentinelPath) {
|
|
253
|
+
await waitForSentinelClose(watch.sentinelPath, timeoutMs);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const backend = watch.backend ?? "tmux";
|
|
258
|
+
const paneId = watch.paneId ?? "";
|
|
259
|
+
|
|
260
|
+
if (backend !== "tmux") {
|
|
261
|
+
logDebug("agent-launcher", `No pane watcher for backend=${backend}; continuing without wait`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (!paneId) return;
|
|
266
|
+
|
|
267
|
+
const tmuxPath = findExecutable("tmux");
|
|
268
|
+
if (!tmuxPath) {
|
|
269
|
+
logWarn("agent-launcher", `tmux unavailable while watching pane ${paneId}`);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const deadline = Date.now() + timeoutMs;
|
|
274
|
+
while (true) {
|
|
275
|
+
if (Date.now() >= deadline) {
|
|
276
|
+
logDebug("agent-launcher", `watch timeout reached for pane ${paneId} after ${timeoutMs}ms`);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const result = await execFileAsync(tmuxPath, ["list-panes", "-a", "-F", "#{pane_id}"], {
|
|
281
|
+
timeout: POLL_TIMEOUT_MS,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
if (result.exitCode !== 0) {
|
|
285
|
+
logDebug("agent-launcher", `list-panes failed; assuming pane closed (${result.stderr.trim() || "no stderr"})`);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const activePaneIds = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
290
|
+
if (!activePaneIds.includes(paneId)) return;
|
|
291
|
+
|
|
292
|
+
const remainingMs = deadline - Date.now();
|
|
293
|
+
await sleep(Math.max(0, Math.min(POLL_INTERVAL_MS, remainingMs)));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper to discover and invoke the `aiw` CLI binary from template scripts.
|
|
3
|
+
*
|
|
4
|
+
* Reads the resolved binary path from `.aiwcli/.aiw-bin-path` (written by `aiw init`).
|
|
5
|
+
* Falls back to `aiw` on PATH if the file is missing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
|
|
11
|
+
import { getProjectRoot } from "./constants.js";
|
|
12
|
+
import { execFileAsync, type ExecResult } from "./subprocess-utils.js";
|
|
13
|
+
|
|
14
|
+
function resolveAiwBin(cwd?: string): string {
|
|
15
|
+
const projectRoot = getProjectRoot(cwd ?? process.cwd());
|
|
16
|
+
const binPathFile = path.join(projectRoot, ".aiwcli", ".aiw-bin-path");
|
|
17
|
+
try {
|
|
18
|
+
const binPath = fs.readFileSync(binPathFile, "utf8").trim();
|
|
19
|
+
if (binPath && fs.existsSync(binPath)) return binPath;
|
|
20
|
+
} catch {
|
|
21
|
+
// Fall through to PATH lookup
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return "aiw";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AiwLaunchOptions {
|
|
28
|
+
/** Launch codex instead of claude. */
|
|
29
|
+
codex?: boolean;
|
|
30
|
+
/** Working directory. */
|
|
31
|
+
cwd?: string;
|
|
32
|
+
/** Extra env vars to inject. */
|
|
33
|
+
env?: Record<string, string>;
|
|
34
|
+
/** Return JSON output. */
|
|
35
|
+
json?: boolean;
|
|
36
|
+
/** Path to prompt file. */
|
|
37
|
+
promptPath?: string;
|
|
38
|
+
/** Split direction: auto, h, or v. */
|
|
39
|
+
split?: "auto" | "h" | "v";
|
|
40
|
+
/** Timeout in ms (only relevant with --wait). */
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
/** Block until pane exits. */
|
|
43
|
+
wait?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface AiwLaunchResult {
|
|
47
|
+
backend: string;
|
|
48
|
+
exitCode: null | number;
|
|
49
|
+
launched: boolean;
|
|
50
|
+
paneId: null | string;
|
|
51
|
+
reason: null | string;
|
|
52
|
+
sentinelPath: null | string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Shell out to `aiw launch` with structured options.
|
|
57
|
+
* Returns parsed JSON result when --json is used.
|
|
58
|
+
*/
|
|
59
|
+
export async function aiwLaunch(options: AiwLaunchOptions): Promise<AiwLaunchResult> {
|
|
60
|
+
const bin = resolveAiwBin(options.cwd);
|
|
61
|
+
const args = ["launch"];
|
|
62
|
+
|
|
63
|
+
if (options.codex) args.push("--codex");
|
|
64
|
+
if (options.wait) args.push("--wait");
|
|
65
|
+
args.push("--json");
|
|
66
|
+
if (options.split) args.push("--split", options.split);
|
|
67
|
+
if (options.env && Object.keys(options.env).length > 0) {
|
|
68
|
+
args.push("--env", JSON.stringify(options.env));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (options.promptPath) args.push("--prompt-path", options.promptPath);
|
|
72
|
+
|
|
73
|
+
const result = await execFileAsync(bin, args, {
|
|
74
|
+
timeout: options.timeoutMs ?? 14_400_000,
|
|
75
|
+
env: process.env as Record<string, string>,
|
|
76
|
+
shell: process.platform === "win32",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return parseJsonResult(result);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function parseJsonResult(result: ExecResult): AiwLaunchResult {
|
|
83
|
+
try {
|
|
84
|
+
const lines = result.stdout.trim().split(/\r?\n/);
|
|
85
|
+
const lastLine = lines.at(-1) ?? "";
|
|
86
|
+
const parsed = JSON.parse(lastLine);
|
|
87
|
+
return {
|
|
88
|
+
launched: Boolean(parsed.launched),
|
|
89
|
+
backend: String(parsed.backend ?? "exec"),
|
|
90
|
+
paneId: parsed.paneId ?? null,
|
|
91
|
+
sentinelPath: parsed.sentinelPath ?? null,
|
|
92
|
+
exitCode: typeof parsed.exitCode === "number" ? parsed.exitCode : null,
|
|
93
|
+
reason: parsed.reason ?? null,
|
|
94
|
+
};
|
|
95
|
+
} catch {
|
|
96
|
+
return {
|
|
97
|
+
launched: false,
|
|
98
|
+
backend: "exec",
|
|
99
|
+
paneId: null,
|
|
100
|
+
sentinelPath: null,
|
|
101
|
+
exitCode: result.exitCode,
|
|
102
|
+
reason: `Failed to parse aiw launch output: ${result.stderr || result.stdout}`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import * as crypto from "node:crypto";
|
|
9
9
|
import * as fs from "node:fs";
|
|
10
10
|
import * as _os from "node:os";
|
|
11
|
-
import
|
|
11
|
+
import path from "node:path";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Write file atomically with retry logic.
|
|
@@ -21,6 +21,7 @@ export function atomicWrite(
|
|
|
21
21
|
content: string,
|
|
22
22
|
maxAttempts = 2,
|
|
23
23
|
backoffMs: number[] = [500, 1000],
|
|
24
|
+
fsync = true,
|
|
24
25
|
): [boolean, null | string] {
|
|
25
26
|
// Ensure parent directory exists
|
|
26
27
|
const dir = path.dirname(filePath);
|
|
@@ -36,8 +37,8 @@ export function atomicWrite(
|
|
|
36
37
|
// Write to temp file
|
|
37
38
|
const fd = fs.openSync(tmpPath, "w");
|
|
38
39
|
try {
|
|
39
|
-
fs.writeSync(fd, content, undefined, "
|
|
40
|
-
fs.fsyncSync(fd);
|
|
40
|
+
fs.writeSync(fd, content, undefined, "utf8");
|
|
41
|
+
if (fsync) fs.fsyncSync(fd);
|
|
41
42
|
} finally {
|
|
42
43
|
fs.closeSync(fd);
|
|
43
44
|
}
|
|
@@ -53,7 +54,7 @@ export function atomicWrite(
|
|
|
53
54
|
fs.renameSync(tmpPath, filePath);
|
|
54
55
|
|
|
55
56
|
return [true, null];
|
|
56
|
-
} catch (error:
|
|
57
|
+
} catch (error: unknown) {
|
|
57
58
|
// Clean up temp file
|
|
58
59
|
try {
|
|
59
60
|
fs.unlinkSync(tmpPath);
|
|
@@ -85,6 +86,7 @@ export function atomicAppend(
|
|
|
85
86
|
content: string,
|
|
86
87
|
maxAttempts = 2,
|
|
87
88
|
backoffMs: number[] = [500, 1000],
|
|
89
|
+
fsync = true,
|
|
88
90
|
): [boolean, null | string] {
|
|
89
91
|
// Ensure parent directory exists
|
|
90
92
|
const dir = path.dirname(filePath);
|
|
@@ -96,8 +98,8 @@ export function atomicAppend(
|
|
|
96
98
|
try {
|
|
97
99
|
const fd = fs.openSync(filePath, "a");
|
|
98
100
|
try {
|
|
99
|
-
fs.writeSync(fd, content, undefined, "
|
|
100
|
-
fs.fsyncSync(fd);
|
|
101
|
+
fs.writeSync(fd, content, undefined, "utf8");
|
|
102
|
+
if (fsync) fs.fsyncSync(fd);
|
|
101
103
|
} finally {
|
|
102
104
|
fs.closeSync(fd);
|
|
103
105
|
}
|
|
@@ -112,7 +114,7 @@ export function atomicAppend(
|
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
return [true, null];
|
|
115
|
-
} catch (error:
|
|
117
|
+
} catch (error: unknown) {
|
|
116
118
|
if (attempt < maxAttempts - 1) {
|
|
117
119
|
const waitMs = backoffMs[Math.min(attempt, backoffMs.length - 1)] ?? backoffMs.at(-1) ?? 500;
|
|
118
120
|
sleepSync(waitMs);
|
|
@@ -136,3 +138,6 @@ function sleepSync(ms: number): void {
|
|
|
136
138
|
const i32 = new Int32Array(sab);
|
|
137
139
|
Atomics.wait(i32, 0, 0, ms);
|
|
138
140
|
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* platform quoting, model tier resolution, and env setup.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { CLAUDE_MODELS, CODEX_MODELS } from "./models.js";
|
|
7
8
|
import type { PreflightCommandConfig } from "./preflight.js";
|
|
8
9
|
import { getInternalSubprocessEnv, shellQuoteWin } from "./subprocess-utils.js";
|
|
9
|
-
import { CLAUDE_MODELS, CODEX_MODELS } from "./models.js";
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
// Types
|
|
@@ -258,7 +258,7 @@ export function reviewSpec(
|
|
|
258
258
|
model,
|
|
259
259
|
mode: "structured",
|
|
260
260
|
jsonSchema: schema,
|
|
261
|
-
systemPrompt,
|
|
261
|
+
...(systemPrompt ? {systemPrompt} : {}),
|
|
262
262
|
};
|
|
263
263
|
}
|
|
264
264
|
|
|
@@ -270,9 +270,9 @@ export function codexReplSpec(
|
|
|
270
270
|
return {
|
|
271
271
|
provider: "codex",
|
|
272
272
|
mode: "repl",
|
|
273
|
-
model,
|
|
274
|
-
sandbox,
|
|
275
|
-
yolo,
|
|
273
|
+
...(model ? {model} : {}),
|
|
274
|
+
...(sandbox ? {sandbox} : {}),
|
|
275
|
+
...(yolo !== undefined ? {yolo} : {}),
|
|
276
276
|
};
|
|
277
277
|
}
|
|
278
278
|
|
|
@@ -285,3 +285,5 @@ export function preflightCommandConfig(provider: CliProvider): PreflightCommandC
|
|
|
285
285
|
input,
|
|
286
286
|
};
|
|
287
287
|
}
|
|
288
|
+
|
|
289
|
+
export {CLAUDE_MODELS, CODEX_MODELS} from "./models.js";
|