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,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context selection — determines which context a prompt belongs to.
|
|
3
|
+
* See SPEC.md §8
|
|
4
|
+
*
|
|
5
|
+
* Single entry point: determineContext(prompt, sessionId, projectRoot)
|
|
6
|
+
* Returns [contextId, method, outputText].
|
|
7
|
+
*
|
|
8
|
+
* Selection priority:
|
|
9
|
+
* 1. session_match — session_id found in index.json sessions map
|
|
10
|
+
* 2. caret_command — prompt starts with ^ → parse and execute
|
|
11
|
+
* 3. plan_content_match — FALLBACK: match against has_plan contexts
|
|
12
|
+
* 3b. handoff_match — FALLBACK: match against has_handoff contexts
|
|
13
|
+
* 4. default — create new context
|
|
14
|
+
*/
|
|
15
|
+
import type { ContextState, CaretCommand } from "../types.js";
|
|
16
|
+
/**
|
|
17
|
+
* Raised when the request should be blocked with a message to user.
|
|
18
|
+
* See SPEC.md §8.2
|
|
19
|
+
*/
|
|
20
|
+
export declare class BlockRequest extends Error {
|
|
21
|
+
constructor(message: string);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a context ID query to an index (1-based) using tiered matching.
|
|
25
|
+
* Match priority: exact > prefix > substring (all case-insensitive).
|
|
26
|
+
* Returns [index, null] on unique match, [null, error] on 0 or 2+ matches.
|
|
27
|
+
* See SPEC.md §8.3
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveContextByPrefix(query: string, contexts: ContextState[]): [number | null, string | null];
|
|
30
|
+
/**
|
|
31
|
+
* Parse chained caret commands from user prompt.
|
|
32
|
+
* See SPEC.md §8.4
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseChainedCaret(prompt: string, contexts: ContextState[]): [CaretCommand | null, string | null];
|
|
35
|
+
/**
|
|
36
|
+
* Determine which context this prompt belongs to.
|
|
37
|
+
* See SPEC.md §8.5
|
|
38
|
+
*
|
|
39
|
+
* Returns [contextId, method, outputText].
|
|
40
|
+
* Throws BlockRequest when request should be blocked to show picker.
|
|
41
|
+
*/
|
|
42
|
+
export declare function determineContext(prompt: string, sessionId?: string, projectRoot?: string): [string | null, string, string | null];
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context selection — determines which context a prompt belongs to.
|
|
3
|
+
* See SPEC.md §8
|
|
4
|
+
*
|
|
5
|
+
* Single entry point: determineContext(prompt, sessionId, projectRoot)
|
|
6
|
+
* Returns [contextId, method, outputText].
|
|
7
|
+
*
|
|
8
|
+
* Selection priority:
|
|
9
|
+
* 1. session_match — session_id found in index.json sessions map
|
|
10
|
+
* 2. caret_command — prompt starts with ^ → parse and execute
|
|
11
|
+
* 3. plan_content_match — FALLBACK: match against has_plan contexts
|
|
12
|
+
* 3b. handoff_match — FALLBACK: match against has_handoff contexts
|
|
13
|
+
* 4. default — create new context
|
|
14
|
+
*/
|
|
15
|
+
import * as crypto from "node:crypto";
|
|
16
|
+
import { formatActiveContextReminder, formatContextCreated, formatContextPickerStderr, formatCommandFeedback, formatHandoffContinuation, formatPlanContinuation, } from "./context-formatter.js";
|
|
17
|
+
import { getContext, getAllContexts, getContextBySessionId, createContextFromPrompt, createContext, completeContext, bindSession, updateMode, determineArtifactType, } from "./context-store.js";
|
|
18
|
+
import { normalizePlanContent } from "./plan-manager.js";
|
|
19
|
+
import { logDebug, logInfo, logError } from "../runtime/logger.js";
|
|
20
|
+
import { isInternalCall } from "../runtime/subprocess-utils.js";
|
|
21
|
+
/** Minimum characters required for new context description. */
|
|
22
|
+
const MIN_NEW_CONTEXT_CHARS = 10;
|
|
23
|
+
/**
|
|
24
|
+
* Raised when the request should be blocked with a message to user.
|
|
25
|
+
* See SPEC.md §8.2
|
|
26
|
+
*/
|
|
27
|
+
export class BlockRequest extends Error {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "BlockRequest";
|
|
31
|
+
// Maintains proper prototype chain when transpiled to ES5
|
|
32
|
+
Object.setPrototypeOf(this, BlockRequest.prototype);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Context prefix resolution
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a context ID query to an index (1-based) using tiered matching.
|
|
40
|
+
* Match priority: exact > prefix > substring (all case-insensitive).
|
|
41
|
+
* Returns [index, null] on unique match, [null, error] on 0 or 2+ matches.
|
|
42
|
+
* See SPEC.md §8.3
|
|
43
|
+
*/
|
|
44
|
+
export function resolveContextByPrefix(query, contexts) {
|
|
45
|
+
const q = query.toLowerCase();
|
|
46
|
+
const available = contexts.map(c => c.id).join(", ");
|
|
47
|
+
// Tier 1: Exact match
|
|
48
|
+
const exact = contexts
|
|
49
|
+
.map((ctx, i) => [i + 1, ctx])
|
|
50
|
+
.filter(([, ctx]) => ctx.id.toLowerCase() === q);
|
|
51
|
+
if (exact.length === 1)
|
|
52
|
+
return [exact[0][0], null];
|
|
53
|
+
// Tier 2: Prefix match
|
|
54
|
+
const prefix = contexts
|
|
55
|
+
.map((ctx, i) => [i + 1, ctx])
|
|
56
|
+
.filter(([, ctx]) => ctx.id.toLowerCase().startsWith(q));
|
|
57
|
+
if (prefix.length === 1)
|
|
58
|
+
return [prefix[0][0], null];
|
|
59
|
+
if (prefix.length > 1) {
|
|
60
|
+
return [null, `Ambiguous match '${query}' — ${prefix.length} prefix matches: ${prefix.map(([, c]) => c.id).join(", ")}. Be more specific.`];
|
|
61
|
+
}
|
|
62
|
+
// Tier 3: Substring match
|
|
63
|
+
const substr = contexts
|
|
64
|
+
.map((ctx, i) => [i + 1, ctx])
|
|
65
|
+
.filter(([, ctx]) => ctx.id.toLowerCase().includes(q));
|
|
66
|
+
if (substr.length === 1)
|
|
67
|
+
return [substr[0][0], null];
|
|
68
|
+
if (substr.length > 1) {
|
|
69
|
+
return [null, `Ambiguous match '${query}' — ${substr.length} substring matches: ${substr.map(([, c]) => c.id).join(", ")}. Be more specific.`];
|
|
70
|
+
}
|
|
71
|
+
return [null, `No context matches '${query}'. Available: ${available}`];
|
|
72
|
+
}
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Caret command parsing
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
/**
|
|
77
|
+
* Parse chained caret commands from user prompt.
|
|
78
|
+
* See SPEC.md §8.4
|
|
79
|
+
*/
|
|
80
|
+
export function parseChainedCaret(prompt, contexts) {
|
|
81
|
+
if (!prompt.startsWith("^"))
|
|
82
|
+
return [null, null];
|
|
83
|
+
const match = prompt.match(/^\^(\S+)(?:\s+(.*))?$/s);
|
|
84
|
+
if (!match) {
|
|
85
|
+
return [null, "Invalid prefix. Use ^E<N> to end, ^S<N> to select, or ^0 <desc> for new context."];
|
|
86
|
+
}
|
|
87
|
+
const commandStr = match[1];
|
|
88
|
+
const remaining = (match[2] ?? "").trim();
|
|
89
|
+
// ^N shorthand
|
|
90
|
+
if (/^\d+$/.test(commandStr)) {
|
|
91
|
+
const num = parseInt(commandStr, 10);
|
|
92
|
+
if (num === 0) {
|
|
93
|
+
if (remaining.length < MIN_NEW_CONTEXT_CHARS) {
|
|
94
|
+
return [null,
|
|
95
|
+
`Please provide a longer description for your new context.\n` +
|
|
96
|
+
`Your description '${remaining}' is only ${remaining.length} characters.\n` +
|
|
97
|
+
`Minimum required: ${MIN_NEW_CONTEXT_CHARS} characters.\n` +
|
|
98
|
+
`Example: ^0 implement user authentication with JWT tokens`,
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
return [{ ends: [], select: null, new_context_desc: remaining, remaining_prompt: "" }, null];
|
|
102
|
+
}
|
|
103
|
+
if (num < 1 || num > contexts.length) {
|
|
104
|
+
if (contexts.length === 0) {
|
|
105
|
+
return [null, "No existing contexts. Use ^0 <description> to create a new one."];
|
|
106
|
+
}
|
|
107
|
+
return [null, `Invalid selection. Choose 1-${contexts.length} for existing contexts, or ^0 for new.`];
|
|
108
|
+
}
|
|
109
|
+
const ctx = contexts[num - 1];
|
|
110
|
+
return [{ ends: [], select: ctx.id, new_context_desc: null, remaining_prompt: remaining }, null];
|
|
111
|
+
}
|
|
112
|
+
// Parse chained commands
|
|
113
|
+
const ends = [];
|
|
114
|
+
let select = null;
|
|
115
|
+
let pos = 0;
|
|
116
|
+
while (pos < commandStr.length) {
|
|
117
|
+
const ch = commandStr[pos].toUpperCase();
|
|
118
|
+
if (ch === "E") {
|
|
119
|
+
pos++;
|
|
120
|
+
if (pos < commandStr.length && commandStr[pos] === "*") {
|
|
121
|
+
pos++;
|
|
122
|
+
if (contexts.length === 0)
|
|
123
|
+
return [null, "No contexts to end."];
|
|
124
|
+
for (const ctx of contexts) {
|
|
125
|
+
if (!ends.includes(ctx.id))
|
|
126
|
+
ends.push(ctx.id);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else if (pos < commandStr.length && commandStr[pos] === ":") {
|
|
130
|
+
pos++;
|
|
131
|
+
const prefixStart = pos;
|
|
132
|
+
while (pos < commandStr.length && !/[EeSs]/.test(commandStr[pos]))
|
|
133
|
+
pos++;
|
|
134
|
+
const pfx = commandStr.slice(prefixStart, pos);
|
|
135
|
+
if (!pfx)
|
|
136
|
+
return [null, "Expected ID query after 'E:'"];
|
|
137
|
+
const [idx, err] = resolveContextByPrefix(pfx, contexts);
|
|
138
|
+
if (err)
|
|
139
|
+
return [null, err];
|
|
140
|
+
const ctx = contexts[idx - 1];
|
|
141
|
+
if (!ends.includes(ctx.id))
|
|
142
|
+
ends.push(ctx.id);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
const numStart = pos;
|
|
146
|
+
while (pos < commandStr.length && /\d/.test(commandStr[pos]))
|
|
147
|
+
pos++;
|
|
148
|
+
if (numStart === pos) {
|
|
149
|
+
return [null, `Expected number, '*', or ':prefix' after 'E' at position ${numStart + 1}`];
|
|
150
|
+
}
|
|
151
|
+
const num = parseInt(commandStr.slice(numStart, pos), 10);
|
|
152
|
+
if (num < 1 || num > contexts.length) {
|
|
153
|
+
if (contexts.length === 0)
|
|
154
|
+
return [null, "No contexts to end."];
|
|
155
|
+
return [null, `Context ^E${num} invalid. Choose 1-${contexts.length}.`];
|
|
156
|
+
}
|
|
157
|
+
if (pos < commandStr.length && commandStr[pos] === "+") {
|
|
158
|
+
pos++;
|
|
159
|
+
for (let i = num; i <= contexts.length; i++) {
|
|
160
|
+
const ctx = contexts[i - 1];
|
|
161
|
+
if (!ends.includes(ctx.id))
|
|
162
|
+
ends.push(ctx.id);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
const ctx = contexts[num - 1];
|
|
167
|
+
if (!ends.includes(ctx.id))
|
|
168
|
+
ends.push(ctx.id);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else if (ch === "S") {
|
|
173
|
+
pos++;
|
|
174
|
+
let ctx;
|
|
175
|
+
if (pos < commandStr.length && commandStr[pos] === ":") {
|
|
176
|
+
pos++;
|
|
177
|
+
const prefixStart = pos;
|
|
178
|
+
while (pos < commandStr.length && !/[EeSs]/.test(commandStr[pos]))
|
|
179
|
+
pos++;
|
|
180
|
+
const pfx = commandStr.slice(prefixStart, pos);
|
|
181
|
+
if (!pfx)
|
|
182
|
+
return [null, "Expected ID query after 'S:'"];
|
|
183
|
+
const [idx, err] = resolveContextByPrefix(pfx, contexts);
|
|
184
|
+
if (err)
|
|
185
|
+
return [null, err];
|
|
186
|
+
ctx = contexts[idx - 1];
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
const numStart = pos;
|
|
190
|
+
while (pos < commandStr.length && /\d/.test(commandStr[pos]))
|
|
191
|
+
pos++;
|
|
192
|
+
if (numStart === pos) {
|
|
193
|
+
return [null, `Expected number or ':prefix' after 'S' at position ${numStart + 1}`];
|
|
194
|
+
}
|
|
195
|
+
const num = parseInt(commandStr.slice(numStart, pos), 10);
|
|
196
|
+
if (num < 1 || num > contexts.length) {
|
|
197
|
+
if (contexts.length === 0)
|
|
198
|
+
return [null, "No contexts to select."];
|
|
199
|
+
return [null, `Context ^S${num} invalid. Choose 1-${contexts.length}.`];
|
|
200
|
+
}
|
|
201
|
+
ctx = contexts[num - 1];
|
|
202
|
+
}
|
|
203
|
+
if (select === null)
|
|
204
|
+
select = ctx.id;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
return [null,
|
|
208
|
+
`Invalid command '${commandStr[pos]}' at position ${pos + 1}.\n` +
|
|
209
|
+
`Use E<N> to end, E<N>+ to end N and after, E* to end all, S<N> to select.\n` +
|
|
210
|
+
`Example: ^E1S2 (end 1, select 2), ^E2+ (end 2 and older), ^E* (end all)`,
|
|
211
|
+
];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (select !== null && ends.includes(select)) {
|
|
215
|
+
return [null, `Cannot select context '${select}' because it's being ended.`];
|
|
216
|
+
}
|
|
217
|
+
return [{ ends, select, new_context_desc: null, remaining_prompt: remaining }, null];
|
|
218
|
+
}
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
// Plan content matching (fallback)
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
function matchPlanContent(prompt, hasPlanContexts) {
|
|
223
|
+
if (hasPlanContexts.length === 0)
|
|
224
|
+
return null;
|
|
225
|
+
// Tier 1: Plan ID match
|
|
226
|
+
const idMatch = prompt.match(/<!-- plan-id: ([a-f0-9]+) -->/);
|
|
227
|
+
if (idMatch) {
|
|
228
|
+
const foundId = idMatch[1];
|
|
229
|
+
for (const ctx of hasPlanContexts) {
|
|
230
|
+
if (ctx.plan_id === foundId) {
|
|
231
|
+
logDebug("context_selector", `Tier 1 plan-id match: ${ctx.id} (id: ${foundId})`);
|
|
232
|
+
return ctx;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Tier 2: Normalized hash match
|
|
237
|
+
const normalized = normalizePlanContent(prompt);
|
|
238
|
+
const normHash = crypto.createHash("sha256").update(normalized, "utf8").digest("hex").slice(0, 12);
|
|
239
|
+
for (const ctx of hasPlanContexts) {
|
|
240
|
+
if (ctx.plan_hash && ctx.plan_hash === normHash) {
|
|
241
|
+
logDebug("context_selector", `Tier 2 normalized hash match: ${ctx.id} (hash: ${normHash})`);
|
|
242
|
+
return ctx;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Tier 3: Multi-anchor signature match
|
|
246
|
+
for (const ctx of hasPlanContexts) {
|
|
247
|
+
const anchors = ctx.plan_anchors ?? [];
|
|
248
|
+
if (anchors.length > 0) {
|
|
249
|
+
const hits = anchors.filter(a => prompt.includes(a)).length;
|
|
250
|
+
if (hits >= 2 && hits >= Math.floor(anchors.length / 2)) {
|
|
251
|
+
logDebug("context_selector", `Tier 3 anchor match: ${ctx.id} (${hits}/${anchors.length} anchors)`);
|
|
252
|
+
return ctx;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Tier 4 (legacy): Signature match
|
|
257
|
+
const promptHead = new Set(prompt.slice(0, 500));
|
|
258
|
+
for (const ctx of hasPlanContexts) {
|
|
259
|
+
if (ctx.plan_signature && promptHead.has(ctx.plan_signature)) {
|
|
260
|
+
logDebug("context_selector", `Tier 4 legacy signature match: ${ctx.id}`);
|
|
261
|
+
return ctx;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
// Context creation helper
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
function createNewContext(prompt, projectRoot) {
|
|
270
|
+
try {
|
|
271
|
+
const newCtx = createContextFromPrompt(prompt, projectRoot);
|
|
272
|
+
updateMode(newCtx.id, "active", projectRoot);
|
|
273
|
+
newCtx.mode = "active";
|
|
274
|
+
logInfo("context_selector", `Auto-created context: ${newCtx.id}`);
|
|
275
|
+
return [newCtx.id, "auto_created", formatContextCreated(newCtx)];
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
logError("context_selector", `Primary context creation failed: ${error}`);
|
|
279
|
+
try {
|
|
280
|
+
const now = new Date();
|
|
281
|
+
const yy = String(now.getFullYear()).slice(2);
|
|
282
|
+
const mm = String(now.getMonth() + 1).padStart(2, "0");
|
|
283
|
+
const dd = String(now.getDate()).padStart(2, "0");
|
|
284
|
+
const hh = String(now.getHours()).padStart(2, "0");
|
|
285
|
+
const min = String(now.getMinutes()).padStart(2, "0");
|
|
286
|
+
const fallbackId = `${yy}${mm}${dd}-${hh}${min}-context`;
|
|
287
|
+
const newCtx = createContext(fallbackId, prompt.trim().slice(0, 200) || "New context", "auto-created-fallback", projectRoot, ["auto-created", "fallback"]);
|
|
288
|
+
updateMode(newCtx.id, "active", projectRoot);
|
|
289
|
+
newCtx.mode = "active";
|
|
290
|
+
logInfo("context_selector", `Fallback context created: ${newCtx.id}`);
|
|
291
|
+
return [newCtx.id, "auto_created_fallback", formatContextCreated(newCtx)];
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
logError("context_selector", `ALL context creation failed: ${error}`);
|
|
295
|
+
return [null, "creation_failed", null];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
// Caret command handler
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
function handleCaretCommand(prompt, contexts, projectRoot) {
|
|
303
|
+
if (contexts.length === 0) {
|
|
304
|
+
const match = prompt.match(/^\^(\S+)(?:\s+(.*))?$/s);
|
|
305
|
+
if (!match) {
|
|
306
|
+
throw new BlockRequest("Invalid prefix. Use ^0 <description> to create a new context.\n" +
|
|
307
|
+
"Example: ^0 implement user authentication system");
|
|
308
|
+
}
|
|
309
|
+
const prefixValue = match[1];
|
|
310
|
+
const remaining = match[2] ?? "";
|
|
311
|
+
if (!/^\d+$/.test(prefixValue) || parseInt(prefixValue, 10) !== 0) {
|
|
312
|
+
throw new BlockRequest("No existing contexts to select. Use ^0 <description> to create a new context.\n" +
|
|
313
|
+
"Example: ^0 implement user authentication system");
|
|
314
|
+
}
|
|
315
|
+
const description = remaining.trim();
|
|
316
|
+
if (description.length < MIN_NEW_CONTEXT_CHARS) {
|
|
317
|
+
throw new BlockRequest(`Please provide a longer description for your new context.\n` +
|
|
318
|
+
`Your description '${description}' is only ${description.length} characters.\n` +
|
|
319
|
+
`Minimum required: ${MIN_NEW_CONTEXT_CHARS} characters.\n` +
|
|
320
|
+
`Example: ^0 implement user authentication with JWT tokens`);
|
|
321
|
+
}
|
|
322
|
+
return createNewContext(description, projectRoot);
|
|
323
|
+
}
|
|
324
|
+
const [cmd, error] = parseChainedCaret(prompt, contexts);
|
|
325
|
+
if (error)
|
|
326
|
+
throw new BlockRequest(error + "\n" + formatContextPickerStderr(contexts));
|
|
327
|
+
if (!cmd)
|
|
328
|
+
throw new BlockRequest(formatContextPickerStderr(contexts));
|
|
329
|
+
const endedContexts = [];
|
|
330
|
+
for (const ctxId of cmd.ends) {
|
|
331
|
+
const ctxToEnd = contexts.find(c => c.id === ctxId);
|
|
332
|
+
if (!ctxToEnd) {
|
|
333
|
+
throw new BlockRequest(`Context '${ctxId}' no longer exists.\n` + formatContextPickerStderr(contexts));
|
|
334
|
+
}
|
|
335
|
+
completeContext(ctxToEnd.id, projectRoot);
|
|
336
|
+
endedContexts.push(ctxToEnd);
|
|
337
|
+
logInfo("context_selector", `Ended context: ${ctxToEnd.id}`);
|
|
338
|
+
}
|
|
339
|
+
if (cmd.new_context_desc) {
|
|
340
|
+
const [ctxId, method, output] = createNewContext(cmd.new_context_desc, projectRoot);
|
|
341
|
+
if (ctxId && endedContexts.length > 0) {
|
|
342
|
+
const newCtx = getContext(ctxId, projectRoot);
|
|
343
|
+
const feedback = formatCommandFeedback(endedContexts, newCtx);
|
|
344
|
+
return [ctxId, method !== "creation_failed" ? "caret_new" : method, feedback];
|
|
345
|
+
}
|
|
346
|
+
return [ctxId, method !== "creation_failed" ? "caret_new" : method, output];
|
|
347
|
+
}
|
|
348
|
+
if (cmd.select) {
|
|
349
|
+
const selectedCtx = contexts.find(c => c.id === cmd.select);
|
|
350
|
+
if (!selectedCtx) {
|
|
351
|
+
throw new BlockRequest(`Context '${cmd.select}' no longer exists.\n` + formatContextPickerStderr(contexts));
|
|
352
|
+
}
|
|
353
|
+
logInfo("context_selector", `Caret-selected context: ${selectedCtx.id}`);
|
|
354
|
+
return [selectedCtx.id, "caret_select", formatCommandFeedback(endedContexts, selectedCtx)];
|
|
355
|
+
}
|
|
356
|
+
if (endedContexts.length > 0) {
|
|
357
|
+
const remainingContexts = getAllContexts("active", projectRoot);
|
|
358
|
+
const feedback = formatCommandFeedback(endedContexts, null);
|
|
359
|
+
if (remainingContexts.length === 0) {
|
|
360
|
+
throw new BlockRequest(feedback + "\nAll contexts have been ended. No context selected.\n\n" +
|
|
361
|
+
"Just type your task to start a new context.\n" +
|
|
362
|
+
"Example: implement user authentication system");
|
|
363
|
+
}
|
|
364
|
+
throw new BlockRequest(feedback + "\nNo context selected.\n\nSelect a context to continue:\n" +
|
|
365
|
+
formatContextPickerStderr(remainingContexts));
|
|
366
|
+
}
|
|
367
|
+
throw new BlockRequest(formatContextPickerStderr(contexts));
|
|
368
|
+
}
|
|
369
|
+
// ---------------------------------------------------------------------------
|
|
370
|
+
// Main entry point
|
|
371
|
+
// ---------------------------------------------------------------------------
|
|
372
|
+
/**
|
|
373
|
+
* Determine which context this prompt belongs to.
|
|
374
|
+
* See SPEC.md §8.5
|
|
375
|
+
*
|
|
376
|
+
* Returns [contextId, method, outputText].
|
|
377
|
+
* Throws BlockRequest when request should be blocked to show picker.
|
|
378
|
+
*/
|
|
379
|
+
export function determineContext(prompt, sessionId, projectRoot) {
|
|
380
|
+
if (isInternalCall()) {
|
|
381
|
+
logDebug("context_selector", "Skipping: internal subprocess call");
|
|
382
|
+
return [null, "skip_internal", null];
|
|
383
|
+
}
|
|
384
|
+
// --- Case 1: session_match ---
|
|
385
|
+
if (sessionId) {
|
|
386
|
+
const sessionContext = getContextBySessionId(sessionId, projectRoot);
|
|
387
|
+
if (sessionContext) {
|
|
388
|
+
logInfo("context_selector", `Session match: ${sessionContext.id}`);
|
|
389
|
+
return [
|
|
390
|
+
sessionContext.id,
|
|
391
|
+
"session_match",
|
|
392
|
+
formatActiveContextReminder(sessionContext, projectRoot),
|
|
393
|
+
];
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
// --- Case 2: caret_command ---
|
|
397
|
+
if (prompt.trim() === "^") {
|
|
398
|
+
const contexts = getAllContexts("active", projectRoot);
|
|
399
|
+
if (contexts.length === 0) {
|
|
400
|
+
throw new BlockRequest("No contexts exist.\n\nJust type your task to start a new context.\n" +
|
|
401
|
+
"Example: implement user authentication system");
|
|
402
|
+
}
|
|
403
|
+
throw new BlockRequest(formatContextPickerStderr(contexts));
|
|
404
|
+
}
|
|
405
|
+
if (prompt.startsWith("^")) {
|
|
406
|
+
const contexts = getAllContexts("active", projectRoot);
|
|
407
|
+
return handleCaretCommand(prompt, contexts, projectRoot);
|
|
408
|
+
}
|
|
409
|
+
// --- Case 3: Staged work match (CHANGED: unified mode) ---
|
|
410
|
+
const stagedContexts = getAllContexts("active", projectRoot).filter((c) => c.mode === "has_staged_work");
|
|
411
|
+
if (stagedContexts.length > 0) {
|
|
412
|
+
// Separate by artifact type
|
|
413
|
+
const planContexts = stagedContexts.filter((c) => determineArtifactType(c) === "plan");
|
|
414
|
+
const handoffContexts = stagedContexts.filter((c) => determineArtifactType(c) === "handoff");
|
|
415
|
+
// Try plan matching first (content-based matching)
|
|
416
|
+
if (planContexts.length > 0) {
|
|
417
|
+
const matched = matchPlanContent(prompt, planContexts);
|
|
418
|
+
if (matched) {
|
|
419
|
+
if (sessionId)
|
|
420
|
+
bindSession(matched.id, sessionId, projectRoot);
|
|
421
|
+
updateMode(matched.id, "active", projectRoot, {
|
|
422
|
+
work_consumed: true, // CHANGED: unified flag
|
|
423
|
+
...(matched.plan_hash ? { plan_hash_consumed: matched.plan_hash } : {}),
|
|
424
|
+
});
|
|
425
|
+
matched.mode = "active";
|
|
426
|
+
logInfo("context_selector", `Plan match (fallback): ${matched.id}`);
|
|
427
|
+
return [
|
|
428
|
+
matched.id,
|
|
429
|
+
"plan_content_match",
|
|
430
|
+
formatPlanContinuation(matched, projectRoot),
|
|
431
|
+
];
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// Fallback to handoff (pick first - no content matching)
|
|
435
|
+
if (handoffContexts.length > 0) {
|
|
436
|
+
const target = handoffContexts[0];
|
|
437
|
+
if (sessionId)
|
|
438
|
+
bindSession(target.id, sessionId, projectRoot);
|
|
439
|
+
updateMode(target.id, "active", projectRoot, { work_consumed: true }); // CHANGED
|
|
440
|
+
target.mode = "active";
|
|
441
|
+
logInfo("context_selector", `Handoff match (fallback): ${target.id}`);
|
|
442
|
+
return [
|
|
443
|
+
target.id,
|
|
444
|
+
"handoff_match",
|
|
445
|
+
formatHandoffContinuation(target, projectRoot),
|
|
446
|
+
];
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// --- Case 4: default ---
|
|
450
|
+
return createNewContext(prompt, projectRoot);
|
|
451
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context store — 2-layer CRUD for context state management.
|
|
3
|
+
* See SPEC.md §7
|
|
4
|
+
*
|
|
5
|
+
* Replaces context_manager's 3-layer approach with a simpler 2-layer model:
|
|
6
|
+
* state.json (per context folder — SOURCE OF TRUTH)
|
|
7
|
+
* index.json (at _output/ root — fast session→context lookup)
|
|
8
|
+
*/
|
|
9
|
+
import type { ContextState, Mode } from "../types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Determine artifact type from context state.
|
|
12
|
+
* Checks explicit next_artifact_type field first, falls back to field detection.
|
|
13
|
+
*
|
|
14
|
+
* Edge cases:
|
|
15
|
+
* - Both artifacts exist: Log warning, return "plan" (deterministic fallback for corrupted state)
|
|
16
|
+
* - No artifacts: Return null (caller handles gracefully)
|
|
17
|
+
*/
|
|
18
|
+
export declare function determineArtifactType(state: ContextState): "plan" | "handoff" | null;
|
|
19
|
+
/**
|
|
20
|
+
* Read state.json for a context. Falls back to context.json for migration.
|
|
21
|
+
* See SPEC.md §7.2
|
|
22
|
+
*/
|
|
23
|
+
export declare function loadState(contextId: string, projectRoot?: string): ContextState | null;
|
|
24
|
+
/**
|
|
25
|
+
* Atomically write state.json AND update index.json.
|
|
26
|
+
* See SPEC.md §7.3
|
|
27
|
+
*/
|
|
28
|
+
export declare function saveState(contextId: string, state: ContextState, projectRoot?: string): [boolean, string | null];
|
|
29
|
+
/**
|
|
30
|
+
* Create a new context folder + state.json + index entry.
|
|
31
|
+
* Throws ValueError-equivalent if context already exists.
|
|
32
|
+
* See SPEC.md §7.4
|
|
33
|
+
*/
|
|
34
|
+
export declare function createContext(contextId: string | null, summary: string, method?: string, projectRoot?: string, tags?: string[]): ContextState;
|
|
35
|
+
/**
|
|
36
|
+
* Load a single context by ID.
|
|
37
|
+
* See SPEC.md §7.5
|
|
38
|
+
*/
|
|
39
|
+
export declare function getContext(contextId: string, projectRoot?: string): ContextState | null;
|
|
40
|
+
/**
|
|
41
|
+
* List contexts from index.json, loading each state.json.
|
|
42
|
+
* Falls back to scanning context folders if index is missing.
|
|
43
|
+
* Results sorted by last_active descending.
|
|
44
|
+
* See SPEC.md §7.6
|
|
45
|
+
*/
|
|
46
|
+
export declare function getAllContexts(status?: string, projectRoot?: string): ContextState[];
|
|
47
|
+
/**
|
|
48
|
+
* Update allowed metadata fields (summary, tags, method) on a context.
|
|
49
|
+
* See SPEC.md §7.7
|
|
50
|
+
*/
|
|
51
|
+
export declare function updateContext(contextId: string, updates: Partial<Pick<ContextState, "summary" | "tags" | "method">>, projectRoot?: string): ContextState | null;
|
|
52
|
+
/**
|
|
53
|
+
* O(1) lookup: check index.json sessions map first.
|
|
54
|
+
* Side effect: sets logger context path for per-context log routing.
|
|
55
|
+
* See SPEC.md §7.8
|
|
56
|
+
*/
|
|
57
|
+
export declare function getContextBySessionId(sessionId: string, projectRoot?: string): ContextState | null;
|
|
58
|
+
/**
|
|
59
|
+
* Add session_id to both index.json sessions map and state.json session_ids.
|
|
60
|
+
* See SPEC.md §7.9
|
|
61
|
+
*/
|
|
62
|
+
export declare function bindSession(contextId: string, sessionId: string, projectRoot?: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Change the mode field, optionally setting plan/handoff fields.
|
|
65
|
+
* See SPEC.md §7.10
|
|
66
|
+
*/
|
|
67
|
+
export declare function updateMode(contextId: string, mode: Mode, projectRoot?: string, opts?: {
|
|
68
|
+
plan_path?: string;
|
|
69
|
+
plan_hash?: string;
|
|
70
|
+
plan_signature?: string;
|
|
71
|
+
plan_id?: string;
|
|
72
|
+
plan_anchors?: string[];
|
|
73
|
+
work_consumed?: boolean;
|
|
74
|
+
plan_hash_consumed?: string;
|
|
75
|
+
}): ContextState | null;
|
|
76
|
+
/**
|
|
77
|
+
* Transition idle/has_staged_work → active, unless in plan mode.
|
|
78
|
+
* See SPEC.md §7.11
|
|
79
|
+
*/
|
|
80
|
+
export declare function maybeActivate(contextId: string, permissionMode: string, projectRoot?: string, caller?: string): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Mark context completed and archive it.
|
|
83
|
+
* See SPEC.md §7.12
|
|
84
|
+
*/
|
|
85
|
+
export declare function completeContext(contextId: string, projectRoot?: string): ContextState | null;
|
|
86
|
+
/**
|
|
87
|
+
* Move completed context folder to _archive/, update indices.
|
|
88
|
+
* See SPEC.md §7.13
|
|
89
|
+
*/
|
|
90
|
+
export declare function archiveContext(contextId: string, projectRoot?: string): ContextState | null;
|
|
91
|
+
/**
|
|
92
|
+
* Reopen a completed/archived context.
|
|
93
|
+
* See SPEC.md §7.14
|
|
94
|
+
*/
|
|
95
|
+
export declare function reopenContext(contextId: string, projectRoot?: string): ContextState | null;
|
|
96
|
+
/**
|
|
97
|
+
* Auto-create a context from the user's prompt.
|
|
98
|
+
* See SPEC.md §7.15
|
|
99
|
+
*/
|
|
100
|
+
export declare function createContextFromPrompt(userPrompt: string, projectRoot?: string): ContextState;
|