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
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan lifecycle management — archival, lookup, and path extraction.
|
|
3
|
+
* See SPEC.md §9
|
|
4
|
+
*
|
|
5
|
+
* Provides pure-data operations on plan files:
|
|
6
|
+
* - archivePlan: copy plan to context plans/ folder, compute hash + signature
|
|
7
|
+
* - findLatestPlan: locate the most relevant plan for a context
|
|
8
|
+
* - extractPlanPathFromResult: parse plan path from ExitPlanMode output
|
|
9
|
+
*/
|
|
10
|
+
import * as crypto from "node:crypto";
|
|
11
|
+
import * as fs from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { atomicWrite } from "../runtime/atomic-write.js";
|
|
14
|
+
import { getContextPlansDir, sanitizeTitle } from "../runtime/constants.js";
|
|
15
|
+
import { logDebug, logInfo, logWarn, logError } from "../runtime/logger.js";
|
|
16
|
+
import { generateSlug } from "../runtime/utils.js";
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Plan archival
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/**
|
|
21
|
+
* Archive a plan file to the context's plans/ folder.
|
|
22
|
+
* Computes a content hash and signature.
|
|
23
|
+
* Does NOT modify state.json or mode.
|
|
24
|
+
* See SPEC.md §9.2
|
|
25
|
+
*
|
|
26
|
+
* Returns [archivedPath, planHash, planSignature] on success,
|
|
27
|
+
* or [null, null, null] on error.
|
|
28
|
+
*/
|
|
29
|
+
export function archivePlan(planPath, contextId, projectRoot) {
|
|
30
|
+
if (!fs.existsSync(planPath)) {
|
|
31
|
+
logWarn("plan_manager", `Plan file not found: ${planPath}`);
|
|
32
|
+
return [null, null, null];
|
|
33
|
+
}
|
|
34
|
+
let content;
|
|
35
|
+
try {
|
|
36
|
+
content = fs.readFileSync(planPath, "utf8");
|
|
37
|
+
}
|
|
38
|
+
catch (error_) {
|
|
39
|
+
logError("plan_manager", `Failed to read plan: ${error_}`);
|
|
40
|
+
return [null, null, null];
|
|
41
|
+
}
|
|
42
|
+
// Compute hash and signature
|
|
43
|
+
const planHash = crypto.createHash("sha256").update(content, "utf8").digest("hex").slice(0, 12);
|
|
44
|
+
const planSignature = content.slice(0, 200);
|
|
45
|
+
// Ensure plans directory exists
|
|
46
|
+
const plansDir = getContextPlansDir(contextId, projectRoot);
|
|
47
|
+
fs.mkdirSync(plansDir, { recursive: true });
|
|
48
|
+
// Generate archive filename: YYYY-MM-DD-HHMM-<slug>.md
|
|
49
|
+
const now = new Date();
|
|
50
|
+
const dateStr = [
|
|
51
|
+
now.getFullYear(),
|
|
52
|
+
"-",
|
|
53
|
+
String(now.getMonth() + 1).padStart(2, "0"),
|
|
54
|
+
"-",
|
|
55
|
+
String(now.getDate()).padStart(2, "0"),
|
|
56
|
+
"-",
|
|
57
|
+
String(now.getHours()).padStart(2, "0"),
|
|
58
|
+
String(now.getMinutes()).padStart(2, "0"),
|
|
59
|
+
].join("");
|
|
60
|
+
// Extract a clean summary from plan content for slug generation.
|
|
61
|
+
// Headings describe the plan's intent better than raw markdown body.
|
|
62
|
+
const summary = extractPlanSummary(content);
|
|
63
|
+
const slug = generateSlug(summary, 60, sanitizeTitle(path.basename(planPath, path.extname(planPath)), 30));
|
|
64
|
+
let archiveName = `${dateStr}-${slug}.md`;
|
|
65
|
+
let archivePath = path.join(plansDir, archiveName);
|
|
66
|
+
// Handle filename collisions
|
|
67
|
+
let counter = 2;
|
|
68
|
+
while (fs.existsSync(archivePath)) {
|
|
69
|
+
archiveName = `${dateStr}-${slug}-${counter}.md`;
|
|
70
|
+
archivePath = path.join(plansDir, archiveName);
|
|
71
|
+
counter++;
|
|
72
|
+
}
|
|
73
|
+
// Write archived plan atomically
|
|
74
|
+
const [success, error] = atomicWrite(archivePath, content);
|
|
75
|
+
if (!success) {
|
|
76
|
+
logError("plan_manager", `Failed to write archive: ${error}`);
|
|
77
|
+
return [null, null, null];
|
|
78
|
+
}
|
|
79
|
+
logInfo("plan_manager", `Archived plan to: ${archivePath}`);
|
|
80
|
+
return [archivePath, planHash, planSignature];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Extract a human-readable summary from plan markdown content.
|
|
84
|
+
* Pulls headings and the first substantial paragraph, producing
|
|
85
|
+
* text suitable for the AI slug generator (which expects conversational input).
|
|
86
|
+
*/
|
|
87
|
+
function extractPlanSummary(content) {
|
|
88
|
+
const lines = content.split(/\r?\n/);
|
|
89
|
+
const parts = [];
|
|
90
|
+
let firstParagraph = "";
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
const trimmed = line.trim();
|
|
93
|
+
// Collect markdown headings (strip # prefix)
|
|
94
|
+
if (trimmed.startsWith("#")) {
|
|
95
|
+
const heading = trimmed.replace(/^#+\s*/, "");
|
|
96
|
+
if (heading.length > 2)
|
|
97
|
+
parts.push(heading);
|
|
98
|
+
}
|
|
99
|
+
// Grab first substantial non-heading line as context
|
|
100
|
+
if (!firstParagraph && !trimmed.startsWith("#") && trimmed.length > 20) {
|
|
101
|
+
firstParagraph = trimmed.slice(0, 120);
|
|
102
|
+
}
|
|
103
|
+
// Enough material for the AI
|
|
104
|
+
if (parts.length >= 5)
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
if (firstParagraph)
|
|
108
|
+
parts.push(firstParagraph);
|
|
109
|
+
return parts.join(" ").slice(0, 500) || content.slice(0, 500);
|
|
110
|
+
}
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Plan lookup
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
/**
|
|
115
|
+
* Find the most relevant plan file for a context.
|
|
116
|
+
* Priority: state.json plan_path > most recent .md in plans/ dir.
|
|
117
|
+
* See SPEC.md §9.3
|
|
118
|
+
*/
|
|
119
|
+
export function findLatestPlan(contextId, projectRoot) {
|
|
120
|
+
// 1. Check state.json plan_path first
|
|
121
|
+
try {
|
|
122
|
+
// Dynamic import to avoid circular dependency at module level
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, no-undef -- dynamic require to avoid circular dependency
|
|
124
|
+
const stateIo = require("../runtime/state-io.js");
|
|
125
|
+
const state = stateIo.readStateJson(contextId, projectRoot);
|
|
126
|
+
if (state?.plan_path && fs.existsSync(state.plan_path)) {
|
|
127
|
+
return state.plan_path;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
logWarn("plan_manager", `Failed to check state.json plan_path: ${error}`);
|
|
132
|
+
}
|
|
133
|
+
// 2. Fall back to most recent .md in plans/ dir
|
|
134
|
+
const plansDir = getContextPlansDir(contextId, projectRoot);
|
|
135
|
+
if (fs.existsSync(plansDir)) {
|
|
136
|
+
try {
|
|
137
|
+
const files = fs.readdirSync(plansDir)
|
|
138
|
+
.filter(f => f.endsWith(".md"))
|
|
139
|
+
.map(f => {
|
|
140
|
+
const fullPath = path.join(plansDir, f);
|
|
141
|
+
return { path: fullPath, mtime: fs.statSync(fullPath).mtimeMs };
|
|
142
|
+
})
|
|
143
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
144
|
+
if (files.length > 0) {
|
|
145
|
+
return files[0].path;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch { /* ignore */ }
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// Plan identification and normalization
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
/**
|
|
156
|
+
* Generate a short unique plan identifier (8 hex chars).
|
|
157
|
+
* See SPEC.md §9.4
|
|
158
|
+
*/
|
|
159
|
+
export function generatePlanId() {
|
|
160
|
+
return crypto.randomUUID().replaceAll('-', "").slice(0, 8);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Aggressively normalize plan content for hashing.
|
|
164
|
+
* Strips all XML/HTML tags and collapses whitespace.
|
|
165
|
+
* See SPEC.md §9.5
|
|
166
|
+
*/
|
|
167
|
+
export function normalizePlanContent(text) {
|
|
168
|
+
let result = text.replaceAll(/<[^>]+>/g, "");
|
|
169
|
+
result = result.replaceAll(/\s+/g, " ").trim();
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Extract structural anchors from plan content.
|
|
174
|
+
* Returns markdown headings + first substantial paragraph as short strings.
|
|
175
|
+
* See SPEC.md §9.6
|
|
176
|
+
*/
|
|
177
|
+
export function extractPlanAnchors(content, maxAnchors = 5) {
|
|
178
|
+
const anchors = [];
|
|
179
|
+
for (const line of content.split(/\r?\n/)) {
|
|
180
|
+
const trimmed = line.trim();
|
|
181
|
+
if (trimmed.startsWith("#") && trimmed.length > 3) {
|
|
182
|
+
anchors.push(trimmed.slice(0, 80));
|
|
183
|
+
}
|
|
184
|
+
else if (anchors.length === 0 && trimmed.length > 20) {
|
|
185
|
+
anchors.push(trimmed.slice(0, 80));
|
|
186
|
+
}
|
|
187
|
+
if (anchors.length >= maxAnchors)
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
return anchors;
|
|
191
|
+
}
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// Transcript-based plan path extraction
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
const MAX_TRANSCRIPT_SIZE = 50 * 1024 * 1024; // 50 MB
|
|
196
|
+
/**
|
|
197
|
+
* Find the plan file path by parsing the session transcript JSONL.
|
|
198
|
+
* Searches in reverse for the most recent Write tool call targeting .claude/plans/.
|
|
199
|
+
* See SPEC.md §9.7
|
|
200
|
+
*/
|
|
201
|
+
export function findPlanPathInTranscript(transcriptPath) {
|
|
202
|
+
if (!transcriptPath)
|
|
203
|
+
return null;
|
|
204
|
+
if (!fs.existsSync(transcriptPath)) {
|
|
205
|
+
logDebug("plan_manager", `Transcript not found: ${transcriptPath}`);
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
let size;
|
|
209
|
+
try {
|
|
210
|
+
size = fs.statSync(transcriptPath).size;
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
if (size > MAX_TRANSCRIPT_SIZE) {
|
|
216
|
+
logWarn("plan_manager", `Transcript too large (${size} bytes), skipping`);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
let lines;
|
|
220
|
+
try {
|
|
221
|
+
lines = fs.readFileSync(transcriptPath, "utf8").split(/\r?\n/);
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
logWarn("plan_manager", `Failed to read transcript: ${error}`);
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
228
|
+
const line = lines[i].trim();
|
|
229
|
+
if (!line)
|
|
230
|
+
continue;
|
|
231
|
+
let data;
|
|
232
|
+
try {
|
|
233
|
+
data = JSON.parse(line);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
let contentArr;
|
|
239
|
+
try {
|
|
240
|
+
const message = data.message;
|
|
241
|
+
contentArr = message?.content;
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (!Array.isArray(contentArr))
|
|
247
|
+
continue;
|
|
248
|
+
for (const block of contentArr) {
|
|
249
|
+
if (typeof block !== "object" || block === null)
|
|
250
|
+
continue;
|
|
251
|
+
if (block.type !== "tool_use" || block.name !== "Write")
|
|
252
|
+
continue;
|
|
253
|
+
const filePath = block.input?.file_path;
|
|
254
|
+
if (!filePath)
|
|
255
|
+
continue;
|
|
256
|
+
// Check if path contains .claude/plans/ as consecutive parts
|
|
257
|
+
const parts = filePath.replaceAll('\\', "/").split("/");
|
|
258
|
+
for (let j = 0; j < parts.length - 1; j++) {
|
|
259
|
+
if (parts[j] === ".claude" && parts[j + 1] === "plans") {
|
|
260
|
+
logInfo("plan_manager", `Extracted plan path from transcript: ${filePath}`);
|
|
261
|
+
return filePath;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
logDebug("plan_manager", "No plan Write found in transcript");
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
// Path extraction from tool output
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
/**
|
|
273
|
+
* Extract plan file path from ExitPlanMode tool result.
|
|
274
|
+
* Parses the pattern: "Your plan has been saved to: <path>"
|
|
275
|
+
* See SPEC.md §9.8
|
|
276
|
+
*/
|
|
277
|
+
export function extractPlanPathFromResult(toolResult) {
|
|
278
|
+
if (!toolResult)
|
|
279
|
+
return null;
|
|
280
|
+
const match = toolResult.match(/Your plan has been saved to:\s*(.+\.md)/);
|
|
281
|
+
return match ? match[1].trim() : null;
|
|
282
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task tracker — direct state.json CRUD for tasks.
|
|
3
|
+
* See SPEC.md §10
|
|
4
|
+
*
|
|
5
|
+
* Writes tasks directly to the tasks[] array in state.json.
|
|
6
|
+
* Uses state-io for I/O to avoid circular imports with context-store.
|
|
7
|
+
*/
|
|
8
|
+
import type { Task } from "../types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Scan tasks[] for highest aiw-N, return aiw-(N+1).
|
|
11
|
+
* See SPEC.md §10.2
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateNextTaskId(contextId: string, projectRoot?: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Add a new task to state.json tasks[] and return the task object.
|
|
16
|
+
* See SPEC.md §10.3
|
|
17
|
+
*/
|
|
18
|
+
export declare function addTask(contextId: string, subject: string, description?: string, activeForm?: string, sessionId?: string, projectRoot?: string): null | Task;
|
|
19
|
+
/**
|
|
20
|
+
* Find task by task_id in tasks[], update fields, return true on success.
|
|
21
|
+
* See SPEC.md §10.4
|
|
22
|
+
*/
|
|
23
|
+
export declare function updateTask(contextId: string, taskId: string, opts?: {
|
|
24
|
+
evidence?: string;
|
|
25
|
+
files_changed?: string[];
|
|
26
|
+
session_id?: string;
|
|
27
|
+
status?: string;
|
|
28
|
+
work_summary?: string;
|
|
29
|
+
}, projectRoot?: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Remove task from tasks[] and return true on success.
|
|
32
|
+
* See SPEC.md §10.5
|
|
33
|
+
*/
|
|
34
|
+
export declare function deleteTask(contextId: string, taskId: string, projectRoot?: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Return tasks[] from state.json.
|
|
37
|
+
* See SPEC.md §10.6
|
|
38
|
+
*/
|
|
39
|
+
export declare function getTasks(contextId: string, projectRoot?: string): Task[];
|
|
40
|
+
/**
|
|
41
|
+
* Partition tasks and format as markdown checklist.
|
|
42
|
+
* See SPEC.md §10.7
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateTaskSummary(contextId: string, projectRoot?: string): string;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task tracker — direct state.json CRUD for tasks.
|
|
3
|
+
* See SPEC.md §10
|
|
4
|
+
*
|
|
5
|
+
* Writes tasks directly to the tasks[] array in state.json.
|
|
6
|
+
* Uses state-io for I/O to avoid circular imports with context-store.
|
|
7
|
+
*/
|
|
8
|
+
import { logWarn } from "../runtime/logger.js";
|
|
9
|
+
import { readStateJson, writeStateJson } from "../runtime/state-io.js";
|
|
10
|
+
import { nowIso } from "../runtime/utils.js";
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Public API
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
/**
|
|
15
|
+
* Scan tasks[] for highest aiw-N, return aiw-(N+1).
|
|
16
|
+
* See SPEC.md §10.2
|
|
17
|
+
*/
|
|
18
|
+
export function generateNextTaskId(contextId, projectRoot) {
|
|
19
|
+
const state = readStateJson(contextId, projectRoot);
|
|
20
|
+
const tasks = state?.tasks ?? [];
|
|
21
|
+
let maxNum = 0;
|
|
22
|
+
for (const t of tasks) {
|
|
23
|
+
const match = /^aiw-(\d+)$/.exec(t.id);
|
|
24
|
+
if (match) {
|
|
25
|
+
const num = Number.parseInt(match[1], 10);
|
|
26
|
+
if (num > maxNum)
|
|
27
|
+
maxNum = num;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return `aiw-${maxNum + 1}`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Add a new task to state.json tasks[] and return the task object.
|
|
34
|
+
* See SPEC.md §10.3
|
|
35
|
+
*/
|
|
36
|
+
export function addTask(contextId, subject, description = "", activeForm = "", sessionId = "", projectRoot) {
|
|
37
|
+
const state = readStateJson(contextId, projectRoot);
|
|
38
|
+
if (!state)
|
|
39
|
+
return null;
|
|
40
|
+
const taskId = generateNextTaskId(contextId, projectRoot);
|
|
41
|
+
const task = {
|
|
42
|
+
id: taskId,
|
|
43
|
+
subject,
|
|
44
|
+
description,
|
|
45
|
+
active_form: activeForm,
|
|
46
|
+
status: "pending",
|
|
47
|
+
created_at: nowIso(),
|
|
48
|
+
completed_at: null,
|
|
49
|
+
evidence: "",
|
|
50
|
+
work_summary: "",
|
|
51
|
+
files_changed: [],
|
|
52
|
+
session_id: sessionId,
|
|
53
|
+
};
|
|
54
|
+
state.tasks.push(task);
|
|
55
|
+
state.last_active = nowIso();
|
|
56
|
+
const [success] = writeStateJson(contextId, state, projectRoot);
|
|
57
|
+
return success ? task : null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Find task by task_id in tasks[], update fields, return true on success.
|
|
61
|
+
* See SPEC.md §10.4
|
|
62
|
+
*/
|
|
63
|
+
export function updateTask(contextId, taskId, opts, projectRoot) {
|
|
64
|
+
const state = readStateJson(contextId, projectRoot);
|
|
65
|
+
if (!state)
|
|
66
|
+
return false;
|
|
67
|
+
for (const task of state.tasks) {
|
|
68
|
+
if (task.id === taskId) {
|
|
69
|
+
if (opts?.status !== undefined) {
|
|
70
|
+
task.status = opts.status;
|
|
71
|
+
if (opts.status === "completed") {
|
|
72
|
+
task.completed_at = nowIso();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (opts?.evidence)
|
|
76
|
+
task.evidence = opts.evidence;
|
|
77
|
+
if (opts?.work_summary)
|
|
78
|
+
task.work_summary = opts.work_summary;
|
|
79
|
+
if (opts?.files_changed !== undefined)
|
|
80
|
+
task.files_changed = opts.files_changed;
|
|
81
|
+
if (opts?.session_id)
|
|
82
|
+
task.session_id = opts.session_id;
|
|
83
|
+
state.last_active = nowIso();
|
|
84
|
+
const [success] = writeStateJson(contextId, state, projectRoot);
|
|
85
|
+
return success;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
logWarn("task_tracker", `Task '${taskId}' not found in context '${contextId}'`);
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Remove task from tasks[] and return true on success.
|
|
93
|
+
* See SPEC.md §10.5
|
|
94
|
+
*/
|
|
95
|
+
export function deleteTask(contextId, taskId, projectRoot) {
|
|
96
|
+
const state = readStateJson(contextId, projectRoot);
|
|
97
|
+
if (!state)
|
|
98
|
+
return false;
|
|
99
|
+
const originalLen = state.tasks.length;
|
|
100
|
+
state.tasks = state.tasks.filter(t => t.id !== taskId);
|
|
101
|
+
if (state.tasks.length === originalLen) {
|
|
102
|
+
logWarn("task_tracker", `Task '${taskId}' not found in context '${contextId}'`);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
state.last_active = nowIso();
|
|
106
|
+
const [success] = writeStateJson(contextId, state, projectRoot);
|
|
107
|
+
return success;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Return tasks[] from state.json.
|
|
111
|
+
* See SPEC.md §10.6
|
|
112
|
+
*/
|
|
113
|
+
export function getTasks(contextId, projectRoot) {
|
|
114
|
+
const state = readStateJson(contextId, projectRoot);
|
|
115
|
+
if (!state)
|
|
116
|
+
return [];
|
|
117
|
+
return state.tasks;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Partition tasks and format as markdown checklist.
|
|
121
|
+
* See SPEC.md §10.7
|
|
122
|
+
*/
|
|
123
|
+
export function generateTaskSummary(contextId, projectRoot) {
|
|
124
|
+
const tasks = getTasks(contextId, projectRoot);
|
|
125
|
+
if (tasks.length === 0)
|
|
126
|
+
return "No tasks in this context.";
|
|
127
|
+
const completed = tasks.filter(t => t.status === "completed");
|
|
128
|
+
const inProgress = tasks.filter(t => t.status === "in_progress");
|
|
129
|
+
const pending = tasks.filter(t => t.status === "pending");
|
|
130
|
+
const blocked = tasks.filter(t => t.status === "blocked");
|
|
131
|
+
const lines = [`### Tasks (${tasks.length} total)`, ""];
|
|
132
|
+
for (const t of completed) {
|
|
133
|
+
const ws = t.work_summary ? `\n Work: ${t.work_summary}` : "";
|
|
134
|
+
lines.push(`- [x] ${t.id}: ${t.subject}${ws}`);
|
|
135
|
+
}
|
|
136
|
+
for (const t of inProgress) {
|
|
137
|
+
lines.push(`- [~] ${t.id}: ${t.subject}`);
|
|
138
|
+
}
|
|
139
|
+
for (const t of pending) {
|
|
140
|
+
lines.push(`- [ ] ${t.id}: ${t.subject}`);
|
|
141
|
+
}
|
|
142
|
+
for (const t of blocked) {
|
|
143
|
+
lines.push(`- [!] ${t.id}: ${t.subject}`);
|
|
144
|
+
}
|
|
145
|
+
return lines.join("\n");
|
|
146
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const RESOLVER = 'bun .aiwcli/_core/scripts/resolve-run.ts';
|
|
2
|
+
const CORE_ROOT = '.aiwcli/_core';
|
|
3
|
+
function cmd(relativePath) {
|
|
4
|
+
return `${RESOLVER} ${CORE_ROOT}/${relativePath}`;
|
|
5
|
+
}
|
|
6
|
+
export function getCoreClaudeSettingsBase() {
|
|
7
|
+
return {
|
|
8
|
+
statusLine: {
|
|
9
|
+
type: 'command',
|
|
10
|
+
command: cmd('scripts/status_line.ts'),
|
|
11
|
+
},
|
|
12
|
+
fileSuggestion: {
|
|
13
|
+
type: 'command',
|
|
14
|
+
command: cmd('hooks-ts/file-suggestion.ts'),
|
|
15
|
+
},
|
|
16
|
+
hooks: {
|
|
17
|
+
UserPromptSubmit: [
|
|
18
|
+
{
|
|
19
|
+
matcher: '*',
|
|
20
|
+
hooks: [{ type: 'command', command: cmd('hooks-ts/user_prompt_submit.ts'), timeout: 10 * 1000 }],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
hooks: [{ type: 'command', command: cmd('hooks-ts/codex_explorer.ts'), timeout: 55 * 1000 }],
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
PostToolUse: [
|
|
27
|
+
{
|
|
28
|
+
matcher: '*',
|
|
29
|
+
hooks: [{ type: 'command', command: cmd('hooks-ts/context_monitor.ts'), timeout: 5000 }],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
matcher: 'TaskCreate',
|
|
33
|
+
hooks: [{ type: 'command', command: cmd('hooks-ts/task_create_capture.ts'), timeout: 3000 }],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
matcher: 'TaskUpdate',
|
|
37
|
+
hooks: [{ type: 'command', command: cmd('hooks-ts/task_update_capture.ts'), timeout: 3000 }],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
matcher: 'ExitPlanMode',
|
|
41
|
+
hooks: [{ type: 'command', command: cmd('hooks-ts/archive_plan.ts'), timeout: 5000 }],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
matcher: 'Write|Edit',
|
|
45
|
+
hooks: [{ type: 'command', command: cmd('hooks-ts/lint_after_edit.ts'), timeout: 10 * 1000 }],
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
SessionStart: [
|
|
49
|
+
{
|
|
50
|
+
matcher: '*',
|
|
51
|
+
hooks: [{ type: 'command', command: cmd('hooks-ts/session_start.ts'), timeout: 5000 }],
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
SessionEnd: [
|
|
55
|
+
{
|
|
56
|
+
matcher: '*',
|
|
57
|
+
hooks: [{ type: 'command', command: cmd('hooks-ts/session_end.ts'), timeout: 5000 }],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
PreCompact: [
|
|
61
|
+
{
|
|
62
|
+
matcher: '*',
|
|
63
|
+
hooks: [{ type: 'command', command: cmd('hooks-ts/pre_compact.ts'), timeout: 5000 }],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
PermissionRequest: [
|
|
67
|
+
{
|
|
68
|
+
matcher: 'ExitPlanMode',
|
|
69
|
+
hooks: [{ type: 'command', command: cmd('hooks-ts/archive_plan.ts'), timeout: 5000 }],
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function getCoreWindsurfHooksBase() {
|
|
76
|
+
return { hooks: {} };
|
|
77
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { IdePathResolver } from './ide-path-resolver.js';
|
|
5
|
+
import { pathExists } from './paths.js';
|
|
6
|
+
import { copyDir } from './template-installer.js';
|
|
7
|
+
/**
|
|
8
|
+
* Install core runtime assets into .aiwcli/_core.
|
|
9
|
+
*/
|
|
10
|
+
export async function installCoreAssets(targetDir, ides) {
|
|
11
|
+
const resolver = new IdePathResolver(targetDir);
|
|
12
|
+
const containerDir = resolver.getAiwcliContainer();
|
|
13
|
+
const coreDir = resolver.getCoreFolder();
|
|
14
|
+
await fs.mkdir(containerDir, { recursive: true });
|
|
15
|
+
const sourceRoot = getCoreAssetSource();
|
|
16
|
+
if (!(await pathExists(sourceRoot))) {
|
|
17
|
+
throw new Error(`Core assets not found at ${sourceRoot}. This indicates a corrupted installation.`);
|
|
18
|
+
}
|
|
19
|
+
// Copy runtime payload into .aiwcli/_core
|
|
20
|
+
await copyDir(sourceRoot, coreDir, true);
|
|
21
|
+
// Copy core IDE content (Codex skills, Windsurf workflows, etc.) from source dot folders.
|
|
22
|
+
for (const ide of ides) {
|
|
23
|
+
const srcIdeDir = join(sourceRoot, `.${ide}`);
|
|
24
|
+
if (!(await pathExists(srcIdeDir)))
|
|
25
|
+
continue; // eslint-disable-line no-await-in-loop
|
|
26
|
+
const dstIdeDir = resolver.getIdeDir(ide);
|
|
27
|
+
await mergeDirectory(srcIdeDir, dstIdeDir); // eslint-disable-line no-await-in-loop
|
|
28
|
+
}
|
|
29
|
+
return ['_core'];
|
|
30
|
+
}
|
|
31
|
+
export function getCoreResolverSourcePath() {
|
|
32
|
+
return join(getCoreAssetSource(), 'scripts', 'resolve-run.ts');
|
|
33
|
+
}
|
|
34
|
+
function getCoreAssetSource() {
|
|
35
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
36
|
+
const currentDir = dirname(currentFilePath);
|
|
37
|
+
return join(currentDir, '..', 'templates', 'core');
|
|
38
|
+
}
|
|
39
|
+
async function mergeDirectory(src, dest) {
|
|
40
|
+
await fs.mkdir(dest, { recursive: true });
|
|
41
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
42
|
+
const operations = entries.map(async (entry) => {
|
|
43
|
+
const srcPath = join(src, entry.name);
|
|
44
|
+
const destPath = join(dest, entry.name);
|
|
45
|
+
if (entry.isDirectory()) {
|
|
46
|
+
await mergeDirectory(srcPath, destPath);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!(await pathExists(destPath))) {
|
|
50
|
+
await fs.copyFile(srcPath, destPath);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
await Promise.all(operations);
|
|
54
|
+
}
|
|
@@ -7,7 +7,7 @@ export declare const AIW_EXCLUDE_ENTRIES: string[];
|
|
|
7
7
|
* @param targetDir - Directory to resolve git dir for
|
|
8
8
|
* @returns Absolute path to the git directory, or null if not a git repo
|
|
9
9
|
*/
|
|
10
|
-
export declare function resolveGitDir(targetDir: string): Promise<
|
|
10
|
+
export declare function resolveGitDir(targetDir: string): Promise<null | string>;
|
|
11
11
|
/**
|
|
12
12
|
* Prune stale entries from the AIW Installation section in git exclude file.
|
|
13
13
|
* Checks each entry against disk existence and removes entries whose paths don't exist.
|
|
@@ -15,7 +15,7 @@ export declare function resolveGitDir(targetDir: string): Promise<string | null>
|
|
|
15
15
|
*
|
|
16
16
|
* @param gitDir - Git directory (contains info/exclude)
|
|
17
17
|
* @param targetDir - Project root directory (for disk-existence checks)
|
|
18
|
-
* @returns True if
|
|
18
|
+
* @returns True if unknown entries were pruned
|
|
19
19
|
*/
|
|
20
20
|
export declare function pruneExcludeStaleEntries(gitDir: string, targetDir: string): Promise<boolean>;
|
|
21
21
|
/**
|
|
@@ -9,7 +9,7 @@ const execFileAsync = promisify(execFile);
|
|
|
9
9
|
*/
|
|
10
10
|
const AIW_EXCLUDE_HEADER = '# AIW Installation';
|
|
11
11
|
/** Standard exclude entries managed by AIW */
|
|
12
|
-
export const AIW_EXCLUDE_ENTRIES = ['.aiwcli', '_output', '.claude', '.windsurf'];
|
|
12
|
+
export const AIW_EXCLUDE_ENTRIES = ['.aiwcli', '_output', '.claude', '.codex', '.windsurf', '.cognition'];
|
|
13
13
|
/** Entries that should NEVER be removed from exclude, even on clear */
|
|
14
14
|
const AIW_PERMANENT_ENTRIES = ['_output'];
|
|
15
15
|
/**
|
|
@@ -48,7 +48,7 @@ async function ensureInfoDir(gitDir) {
|
|
|
48
48
|
*
|
|
49
49
|
* @param gitDir - Git directory (contains info/exclude)
|
|
50
50
|
* @param targetDir - Project root directory (for disk-existence checks)
|
|
51
|
-
* @returns True if
|
|
51
|
+
* @returns True if unknown entries were pruned
|
|
52
52
|
*/
|
|
53
53
|
export async function pruneExcludeStaleEntries(gitDir, targetDir) {
|
|
54
54
|
const excludePath = getExcludePath(gitDir);
|
|
@@ -142,7 +142,7 @@ function cleanupEmptySections(content) {
|
|
|
142
142
|
for (let i = 0; i < lines.length; i++) {
|
|
143
143
|
const line = lines[i];
|
|
144
144
|
if (line === AIW_EXCLUDE_HEADER) {
|
|
145
|
-
// Look ahead to see if there are
|
|
145
|
+
// Look ahead to see if there are unknown patterns
|
|
146
146
|
const nextLine = lines[i + 1];
|
|
147
147
|
if (nextLine === undefined || nextLine === '' || nextLine.startsWith('#')) {
|
|
148
148
|
// Skip the header — section is empty
|