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
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import * as fs from "node:fs";
|
|
11
|
-
import
|
|
11
|
+
import path from "node:path";
|
|
12
12
|
|
|
13
|
-
import { atomicWrite } from "../
|
|
13
|
+
import { atomicWrite } from "../runtime/atomic-write.js";
|
|
14
14
|
import {
|
|
15
15
|
getContextDir,
|
|
16
16
|
getContextsDir,
|
|
@@ -19,14 +19,17 @@ import {
|
|
|
19
19
|
getArchiveContextDir,
|
|
20
20
|
getArchiveIndexPath,
|
|
21
21
|
validateContextId,
|
|
22
|
-
} from "../
|
|
23
|
-
import { logInfo, logWarn, logError, setContextPath } from "../
|
|
24
|
-
import { readStateJson, writeStateJson } from "../
|
|
25
|
-
import { nowIso, generateContextId } from "../
|
|
22
|
+
} from "../runtime/constants.js";
|
|
23
|
+
import { logInfo, logWarn, logError, setContextPath } from "../runtime/logger.js";
|
|
24
|
+
import { readStateJson, writeStateJson } from "../runtime/state-io.js";
|
|
25
|
+
import { nowIso, generateContextId } from "../runtime/utils.js";
|
|
26
26
|
import type { ContextState, IndexFile, IndexEntry, Mode } from "../types.js";
|
|
27
27
|
|
|
28
28
|
const INDEX_VERSION = "3.0";
|
|
29
29
|
|
|
30
|
+
// Module-level index cache — safe because each hook is a separate bun process
|
|
31
|
+
let _indexCache: IndexFile | null = null;
|
|
32
|
+
|
|
30
33
|
// ---------------------------------------------------------------------------
|
|
31
34
|
// Public utilities
|
|
32
35
|
// ---------------------------------------------------------------------------
|
|
@@ -73,25 +76,31 @@ export function determineArtifactType(
|
|
|
73
76
|
// ---------------------------------------------------------------------------
|
|
74
77
|
|
|
75
78
|
function loadIndex(projectRoot?: string): IndexFile {
|
|
79
|
+
if (_indexCache) return _indexCache;
|
|
76
80
|
const indexPath = getIndexPath(projectRoot);
|
|
77
81
|
if (fs.existsSync(indexPath)) {
|
|
78
82
|
try {
|
|
79
|
-
const raw = fs.readFileSync(indexPath, "
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
const raw = fs.readFileSync(indexPath, "utf8");
|
|
84
|
+
_indexCache = JSON.parse(raw) as IndexFile;
|
|
85
|
+
return _indexCache;
|
|
86
|
+
} catch (error: unknown) {
|
|
82
87
|
logWarn("context_store", `Failed to read index, recreating: ${error}`);
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
|
-
|
|
90
|
+
const fresh: IndexFile = { version: INDEX_VERSION, updated_at: nowIso(), sessions: {}, contexts: {} };
|
|
91
|
+
_indexCache = fresh;
|
|
92
|
+
return fresh;
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
function saveIndex(index: IndexFile, projectRoot?: string): boolean {
|
|
89
96
|
index.updated_at = nowIso();
|
|
90
97
|
const content = JSON.stringify(index, null, 2);
|
|
91
|
-
|
|
98
|
+
// fsync: false — index.json is reconstructable by scanning _output/contexts/
|
|
99
|
+
const [success, error] = atomicWrite(getIndexPath(projectRoot), content, 2, [500, 1000], false);
|
|
92
100
|
if (!success) {
|
|
93
101
|
logWarn("context_store", `Failed to write index: ${error}`);
|
|
94
102
|
}
|
|
103
|
+
_indexCache = index;
|
|
95
104
|
return success;
|
|
96
105
|
}
|
|
97
106
|
|
|
@@ -111,7 +120,7 @@ function migrateContextJson(contextId: string, projectRoot?: string): ContextSta
|
|
|
111
120
|
if (!fs.existsSync(legacyPath)) return null;
|
|
112
121
|
|
|
113
122
|
try {
|
|
114
|
-
const data = JSON.parse(fs.readFileSync(legacyPath, "
|
|
123
|
+
const data = JSON.parse(fs.readFileSync(legacyPath, "utf8"));
|
|
115
124
|
const inFlight = data.in_flight ?? {};
|
|
116
125
|
const oldMode = inFlight.mode ?? "none";
|
|
117
126
|
const MODE_MIGRATION: Record<string, string> = {
|
|
@@ -149,7 +158,7 @@ function migrateContextJson(contextId: string, projectRoot?: string): ContextSta
|
|
|
149
158
|
last_session: null,
|
|
150
159
|
tasks: [],
|
|
151
160
|
};
|
|
152
|
-
} catch (error:
|
|
161
|
+
} catch (error: unknown) {
|
|
153
162
|
logWarn("context_store", `Failed to migrate context.json for '${contextId}': ${error}`);
|
|
154
163
|
return null;
|
|
155
164
|
}
|
|
@@ -493,7 +502,7 @@ export function maybeActivate(
|
|
|
493
502
|
|
|
494
503
|
if (state.mode === "idle" || state.mode === "has_staged_work") {
|
|
495
504
|
const oldMode = state.mode;
|
|
496
|
-
const opts: Record<string,
|
|
505
|
+
const opts: Record<string, unknown> = {};
|
|
497
506
|
if (oldMode === "has_staged_work") opts.work_consumed = true; // CHANGED: unified flag
|
|
498
507
|
updateMode(contextId, "active", projectRoot, opts);
|
|
499
508
|
logInfo(
|
|
@@ -560,7 +569,7 @@ export function archiveContext(contextId: string, projectRoot?: string): Context
|
|
|
560
569
|
|
|
561
570
|
try {
|
|
562
571
|
fs.renameSync(sourceDir, archiveDest);
|
|
563
|
-
} catch (error:
|
|
572
|
+
} catch (error: unknown) {
|
|
564
573
|
logError("context_store", `Failed to move context to archive: ${error}`);
|
|
565
574
|
return null;
|
|
566
575
|
}
|
|
@@ -650,8 +659,8 @@ function updateArchiveIndex(state: ContextState, projectRoot?: string): boolean
|
|
|
650
659
|
|
|
651
660
|
if (fs.existsSync(archiveIndexPath)) {
|
|
652
661
|
try {
|
|
653
|
-
archiveIndex = JSON.parse(fs.readFileSync(archiveIndexPath, "
|
|
654
|
-
} catch (error_:
|
|
662
|
+
archiveIndex = JSON.parse(fs.readFileSync(archiveIndexPath, "utf8"));
|
|
663
|
+
} catch (error_: unknown) {
|
|
655
664
|
logWarn("context_store", `Failed to read archive index, recreating: ${error_}`);
|
|
656
665
|
}
|
|
657
666
|
}
|
|
@@ -679,7 +688,7 @@ function restoreFromArchive(contextId: string, projectRoot?: string): ContextSta
|
|
|
679
688
|
|
|
680
689
|
try {
|
|
681
690
|
fs.renameSync(archiveDir, activeDir);
|
|
682
|
-
} catch (error:
|
|
691
|
+
} catch (error: unknown) {
|
|
683
692
|
logError("context_store", `Failed to restore context from archive: ${error}`);
|
|
684
693
|
return null;
|
|
685
694
|
}
|
|
@@ -697,7 +706,7 @@ function removeFromArchiveIndex(contextId: string, projectRoot?: string): boolea
|
|
|
697
706
|
if (!fs.existsSync(archiveIndexPath)) return true;
|
|
698
707
|
|
|
699
708
|
try {
|
|
700
|
-
const archiveIndex = JSON.parse(fs.readFileSync(archiveIndexPath, "
|
|
709
|
+
const archiveIndex = JSON.parse(fs.readFileSync(archiveIndexPath, "utf8")) as IndexFile;
|
|
701
710
|
if (archiveIndex.contexts[contextId]) {
|
|
702
711
|
delete archiveIndex.contexts[contextId];
|
|
703
712
|
archiveIndex.updated_at = nowIso();
|
|
@@ -709,8 +718,11 @@ function removeFromArchiveIndex(contextId: string, projectRoot?: string): boolea
|
|
|
709
718
|
}
|
|
710
719
|
}
|
|
711
720
|
return true;
|
|
712
|
-
} catch (error:
|
|
721
|
+
} catch (error: unknown) {
|
|
713
722
|
logWarn("context_store", `Failed to read archive index: ${error}`);
|
|
714
723
|
return false;
|
|
715
724
|
}
|
|
716
725
|
}
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
import * as crypto from "node:crypto";
|
|
12
12
|
import * as fs from "node:fs";
|
|
13
|
-
import
|
|
13
|
+
import path from "node:path";
|
|
14
14
|
|
|
15
|
-
import { atomicWrite } from "../
|
|
16
|
-
import { getContextPlansDir, sanitizeTitle } from "../
|
|
17
|
-
import { logDebug, logInfo, logWarn, logError } from "../
|
|
18
|
-
import { generateSlug } from "../
|
|
15
|
+
import { atomicWrite } from "../runtime/atomic-write.js";
|
|
16
|
+
import { getContextPlansDir, sanitizeTitle } from "../runtime/constants.js";
|
|
17
|
+
import { logDebug, logInfo, logWarn, logError } from "../runtime/logger.js";
|
|
18
|
+
import { generateSlug } from "../runtime/utils.js";
|
|
19
19
|
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
// Plan archival
|
|
@@ -42,14 +42,14 @@ export function archivePlan(
|
|
|
42
42
|
|
|
43
43
|
let content: string;
|
|
44
44
|
try {
|
|
45
|
-
content = fs.readFileSync(planPath, "
|
|
46
|
-
} catch (error_:
|
|
45
|
+
content = fs.readFileSync(planPath, "utf8");
|
|
46
|
+
} catch (error_: unknown) {
|
|
47
47
|
logError("plan_manager", `Failed to read plan: ${error_}`);
|
|
48
48
|
return [null, null, null];
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// Compute hash and signature
|
|
52
|
-
const planHash = crypto.createHash("sha256").update(content, "
|
|
52
|
+
const planHash = crypto.createHash("sha256").update(content, "utf8").digest("hex").slice(0, 12);
|
|
53
53
|
const planSignature = content.slice(0, 200);
|
|
54
54
|
|
|
55
55
|
// Ensure plans directory exists
|
|
@@ -144,12 +144,12 @@ export function findLatestPlan(
|
|
|
144
144
|
try {
|
|
145
145
|
// Dynamic import to avoid circular dependency at module level
|
|
146
146
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, no-undef -- dynamic require to avoid circular dependency
|
|
147
|
-
const stateIo = require("../
|
|
147
|
+
const stateIo = require("../runtime/state-io.js");
|
|
148
148
|
const state = stateIo.readStateJson(contextId, projectRoot);
|
|
149
149
|
if (state?.plan_path && fs.existsSync(state.plan_path)) {
|
|
150
150
|
return state.plan_path;
|
|
151
151
|
}
|
|
152
|
-
} catch (error:
|
|
152
|
+
} catch (error: unknown) {
|
|
153
153
|
logWarn("plan_manager", `Failed to check state.json plan_path: ${error}`);
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -249,8 +249,8 @@ export function findPlanPathInTranscript(transcriptPath: string): string | null
|
|
|
249
249
|
|
|
250
250
|
let lines: string[];
|
|
251
251
|
try {
|
|
252
|
-
lines = fs.readFileSync(transcriptPath, "
|
|
253
|
-
} catch (error:
|
|
252
|
+
lines = fs.readFileSync(transcriptPath, "utf8").split(/\r?\n/);
|
|
253
|
+
} catch (error: unknown) {
|
|
254
254
|
logWarn("plan_manager", `Failed to read transcript: ${error}`);
|
|
255
255
|
return null;
|
|
256
256
|
}
|
|
@@ -259,16 +259,17 @@ export function findPlanPathInTranscript(transcriptPath: string): string | null
|
|
|
259
259
|
const line = lines[i]!.trim();
|
|
260
260
|
if (!line) continue;
|
|
261
261
|
|
|
262
|
-
let data:
|
|
262
|
+
let data: unknown;
|
|
263
263
|
try {
|
|
264
264
|
data = JSON.parse(line);
|
|
265
265
|
} catch {
|
|
266
266
|
continue;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
let contentArr:
|
|
269
|
+
let contentArr: unknown;
|
|
270
270
|
try {
|
|
271
|
-
|
|
271
|
+
const message = (data as { message?: { content?: unknown } }).message;
|
|
272
|
+
contentArr = message?.content;
|
|
272
273
|
} catch {
|
|
273
274
|
continue;
|
|
274
275
|
}
|
|
@@ -311,3 +312,6 @@ export function extractPlanPathFromResult(toolResult: string): string | null {
|
|
|
311
312
|
const match = toolResult.match(/Your plan has been saved to:\s*(.+\.md)/);
|
|
312
313
|
return match ? match[1]!.trim() : null;
|
|
313
314
|
}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* Uses state-io for I/O to avoid circular imports with context-store.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { logWarn } from "../
|
|
10
|
-
import { readStateJson, toDict as _toDict, writeStateJson } from "../
|
|
11
|
-
import { nowIso } from "../
|
|
9
|
+
import { logWarn } from "../runtime/logger.js";
|
|
10
|
+
import { readStateJson, toDict as _toDict, writeStateJson } from "../runtime/state-io.js";
|
|
11
|
+
import { nowIso } from "../runtime/utils.js";
|
|
12
12
|
import type { ContextState as _ContextState, Task } from "../types.js";
|
|
13
13
|
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const CONTEXT_WARNING_30 = "## Context Window: ~30% Remaining\n\n" +
|
|
2
|
+
"This session is approaching its context limit. Consider:\n\n" +
|
|
3
|
+
"- Completing your current task, then pausing for the user to decide next steps\n" +
|
|
4
|
+
"- If significant work remains, mention that `/aiwcli-shared:handoff` can capture progress " +
|
|
5
|
+
"for a fresh session\n\n" +
|
|
6
|
+
"Do not rush or cut corners — finish the current task properly. " +
|
|
7
|
+
"Just be aware that starting large new tasks may not complete before context runs out.";
|
|
8
|
+
|
|
9
|
+
export const CONTEXT_WARNING_15 = "## Context Window: ~15% Remaining — Wrap Up Now\n\n" +
|
|
10
|
+
"Context is critically low. After completing your current step:\n\n" +
|
|
11
|
+
"1. **Stop taking on new work**\n" +
|
|
12
|
+
"2. Summarize what was accomplished and what remains\n" +
|
|
13
|
+
"3. Offer to run `/aiwcli-shared:handoff` so progress transfers to a fresh session\n\n" +
|
|
14
|
+
"Do not start new multi-step tasks. Focus on clean closure.";
|
|
15
|
+
|
|
16
|
+
const WARNING_THRESHOLDS = [
|
|
17
|
+
{ pct: 15, msg: CONTEXT_WARNING_15 },
|
|
18
|
+
{ pct: 30, msg: CONTEXT_WARNING_30 },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export function selectWarningMessage(
|
|
22
|
+
pctRemaining: number,
|
|
23
|
+
alreadyFired: number[],
|
|
24
|
+
): { pct: number; msg: string } | null {
|
|
25
|
+
for (const warning of WARNING_THRESHOLDS) {
|
|
26
|
+
if (pctRemaining <= warning.pct && !alreadyFired.includes(warning.pct)) {
|
|
27
|
+
return warning;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import type { HookInput, HookOutput, PermissionRequestOutput } from "../types.js";
|
|
9
|
+
import { getContextBySessionId, maybeActivate } from "../context/context-store.js";
|
|
10
|
+
import { getProjectRoot } from "../runtime/constants.js";
|
|
11
|
+
import { logDebug, logWarn, hookLog, setSessionId, getContextPath as _getContextPath } from "../runtime/logger.js";
|
|
12
|
+
import type { ContextState, HookInput, HookOutput, PermissionRequestOutput } from "../types.js";
|
|
13
13
|
|
|
14
14
|
// Re-export logger functions for convenience (matches Python hook_utils re-exports)
|
|
15
15
|
|
|
@@ -24,7 +24,120 @@ let _lastToolName: string | null = null;
|
|
|
24
24
|
let _cachedHookName: string | null = null;
|
|
25
25
|
|
|
26
26
|
// Pre-fetched input stash
|
|
27
|
-
let _prefetchedInput: Record<string,
|
|
27
|
+
let _prefetchedInput: Record<string, unknown> | null = null;
|
|
28
|
+
|
|
29
|
+
function readStringField(value: Record<string, unknown>, key: string): null | string {
|
|
30
|
+
const field = value[key];
|
|
31
|
+
return typeof field === "string" ? field : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface BoundSessionContext {
|
|
35
|
+
payload: HookInput;
|
|
36
|
+
projectRoot: string;
|
|
37
|
+
sessionId: string;
|
|
38
|
+
state: ContextState;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface PersistenceContext extends BoundSessionContext {
|
|
42
|
+
toolInput: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ToolInputContext {
|
|
46
|
+
payload: HookInput;
|
|
47
|
+
toolInput: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Load the current payload, resolve its project root, and require an already-bound session.
|
|
52
|
+
* Returns null when the hook should quietly no-op.
|
|
53
|
+
*/
|
|
54
|
+
export function requireBoundSession(
|
|
55
|
+
hookName: string,
|
|
56
|
+
prefetchedPayload?: HookInput | null,
|
|
57
|
+
): BoundSessionContext | null {
|
|
58
|
+
const payload = prefetchedPayload ?? loadHookInput();
|
|
59
|
+
if (!payload) return null;
|
|
60
|
+
|
|
61
|
+
const sessionId = payload.session_id;
|
|
62
|
+
if (!sessionId) {
|
|
63
|
+
logDebug(hookName, "No session_id available");
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const projectRoot = getProjectRoot(payload.cwd);
|
|
68
|
+
const state = getContextBySessionId(sessionId, projectRoot);
|
|
69
|
+
if (!state) {
|
|
70
|
+
logDebug(hookName, `No bound context for session ${sessionId.slice(0, 8)}`);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { payload, projectRoot, sessionId, state };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Require a bound session plus tool_input for PostToolUse task persistence hooks.
|
|
79
|
+
*/
|
|
80
|
+
export function requirePersistenceContext(
|
|
81
|
+
expectedTool: string,
|
|
82
|
+
hookName: string,
|
|
83
|
+
): PersistenceContext | null {
|
|
84
|
+
const bound = requireBoundSession(hookName);
|
|
85
|
+
if (!bound) return null;
|
|
86
|
+
|
|
87
|
+
const { payload } = bound;
|
|
88
|
+
if (payload.hook_event_name !== "PostToolUse") {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
if (payload.tool_name !== expectedTool) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
if (checkSkipPersistence(payload, hookName)) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const toolInput = getToolInput(payload);
|
|
99
|
+
if (!toolInput) {
|
|
100
|
+
logWarn(hookName, `${expectedTool} missing tool_input payload`);
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { ...bound, toolInput };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Require a tool_input payload for tool-oriented hooks without needing a bound session.
|
|
109
|
+
*/
|
|
110
|
+
export function requireToolInput(
|
|
111
|
+
hookName: string,
|
|
112
|
+
): ToolInputContext | null {
|
|
113
|
+
const payload = loadHookInput();
|
|
114
|
+
if (!payload) return null;
|
|
115
|
+
|
|
116
|
+
const toolInput = getToolInput(payload);
|
|
117
|
+
if (!toolInput) {
|
|
118
|
+
logDebug(hookName, "No tool_input payload");
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return { payload, toolInput };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Best-effort wrapper around maybeActivate. Hooks should never fail closed on activation.
|
|
127
|
+
*/
|
|
128
|
+
export function safeMaybeActivate(
|
|
129
|
+
contextId: string,
|
|
130
|
+
permissionMode: string,
|
|
131
|
+
projectRoot?: string,
|
|
132
|
+
hookName = "hook",
|
|
133
|
+
): boolean {
|
|
134
|
+
try {
|
|
135
|
+
return maybeActivate(contextId, permissionMode, projectRoot, hookName);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
logWarn(hookName, `maybeActivate failed for ${contextId}: ${error}`);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
28
141
|
|
|
29
142
|
/**
|
|
30
143
|
* Load and parse JSON from stdin (or return prefetched input if set).
|
|
@@ -36,23 +149,23 @@ export function loadHookInput(): HookInput | null {
|
|
|
36
149
|
const result = _prefetchedInput;
|
|
37
150
|
_prefetchedInput = null; // consume once
|
|
38
151
|
if (result && typeof result === "object") {
|
|
39
|
-
_lastHookEvent = result
|
|
40
|
-
_lastToolName = result
|
|
152
|
+
_lastHookEvent = readStringField(result, "hook_event_name");
|
|
153
|
+
_lastToolName = readStringField(result, "tool_name");
|
|
41
154
|
}
|
|
42
|
-
return result as HookInput;
|
|
155
|
+
return result as unknown as HookInput;
|
|
43
156
|
}
|
|
44
157
|
|
|
45
158
|
try {
|
|
46
159
|
// Read entire stdin using fd 0 (cross-platform, works on Windows)
|
|
47
|
-
const inputData = fs.readFileSync(0, "
|
|
160
|
+
const inputData = fs.readFileSync(0, "utf8").trim();
|
|
48
161
|
if (!inputData) return null;
|
|
49
162
|
|
|
50
|
-
const result = JSON.parse(inputData)
|
|
163
|
+
const result = JSON.parse(inputData) as Record<string, unknown>;
|
|
51
164
|
if (result && typeof result === "object") {
|
|
52
|
-
_lastHookEvent = result
|
|
53
|
-
_lastToolName = result
|
|
165
|
+
_lastHookEvent = readStringField(result, "hook_event_name");
|
|
166
|
+
_lastToolName = readStringField(result, "tool_name");
|
|
54
167
|
}
|
|
55
|
-
return result as HookInput;
|
|
168
|
+
return result as unknown as HookInput;
|
|
56
169
|
} catch {
|
|
57
170
|
return null;
|
|
58
171
|
}
|
|
@@ -78,7 +191,7 @@ export function validateHookEvent(
|
|
|
78
191
|
*/
|
|
79
192
|
export function getToolInput(
|
|
80
193
|
payload: HookInput,
|
|
81
|
-
): Record<string,
|
|
194
|
+
): Record<string, unknown> | null {
|
|
82
195
|
const toolInput = payload.tool_input;
|
|
83
196
|
return toolInput && typeof toolInput === "object" ? toolInput : null;
|
|
84
197
|
}
|
|
@@ -95,7 +208,11 @@ export function checkSkipPersistence(
|
|
|
95
208
|
if (!toolInput) return false;
|
|
96
209
|
|
|
97
210
|
const {metadata} = toolInput;
|
|
98
|
-
if (
|
|
211
|
+
if (
|
|
212
|
+
metadata &&
|
|
213
|
+
typeof metadata === "object" &&
|
|
214
|
+
Boolean((metadata as Record<string, unknown>).skip_persistence)
|
|
215
|
+
) {
|
|
99
216
|
logDebug(hookName, "Skipping persistence (skip_persistence flag set)");
|
|
100
217
|
return true;
|
|
101
218
|
}
|
|
@@ -167,12 +284,12 @@ export function emitContextAndBlock(
|
|
|
167
284
|
}
|
|
168
285
|
|
|
169
286
|
/** Log hook output (context, systemMessage, or block) to hook-log.jsonl for visibility. */
|
|
170
|
-
function _logEmit(type: "context" | "systemMessage" | "block", chars: number, payload: Record<string,
|
|
287
|
+
function _logEmit(type: "context" | "systemMessage" | "block", chars: number, payload: Record<string, unknown>): void {
|
|
171
288
|
const hook = _cachedHookName ?? "unknown";
|
|
172
289
|
const event = payload.event ?? "unknown";
|
|
173
290
|
const mechanism = payload.mechanism ? ` via ${payload.mechanism}` : "";
|
|
174
291
|
const msg = type === "block"
|
|
175
|
-
? `HOOK_OUTPUT [${type}] ${event} ${chars} chars${mechanism}, reason="${(payload.blockReason ?? "").slice(0, 80)}"`
|
|
292
|
+
? `HOOK_OUTPUT [${type}] ${event} ${chars} chars${mechanism}, reason="${String(payload.blockReason ?? "").slice(0, 80)}"`
|
|
176
293
|
: `HOOK_OUTPUT [${type}] ${event} ${chars} chars`;
|
|
177
294
|
hookLog("info", hook, msg, { data: payload });
|
|
178
295
|
}
|
|
@@ -316,8 +433,13 @@ export function emitBlock(reason: string, context?: string): void {
|
|
|
316
433
|
*/
|
|
317
434
|
function detectTemplate(scriptPath = ""): string {
|
|
318
435
|
const p = (scriptPath || (process.argv[1] ?? "")).replaceAll('\\', "/");
|
|
319
|
-
if (
|
|
320
|
-
|
|
436
|
+
if (
|
|
437
|
+
p.includes("/_core/hooks-ts/") ||
|
|
438
|
+
p.startsWith("_core/hooks-ts/") ||
|
|
439
|
+
p.includes("/core/hooks-ts/") ||
|
|
440
|
+
p.startsWith("core/hooks-ts/")
|
|
441
|
+
) {
|
|
442
|
+
return "core";
|
|
321
443
|
}
|
|
322
444
|
const match = p.match(/_([a-z][a-z0-9-]*)\/hooks\//);
|
|
323
445
|
if (match?.[1]) return match[1]; // e.g., "cc-native"
|
|
@@ -390,32 +512,34 @@ export function getContextPercentRemaining(
|
|
|
390
512
|
* Read stdin early and extract session_id + event metadata.
|
|
391
513
|
* Stashes parsed input for loadHookInput() to consume later.
|
|
392
514
|
*/
|
|
393
|
-
function _earlyReadInput(prefetchedInput?: Record<string,
|
|
515
|
+
function _earlyReadInput(prefetchedInput?: Record<string, unknown>): void {
|
|
394
516
|
if (prefetchedInput !== undefined) {
|
|
395
517
|
_prefetchedInput = prefetchedInput;
|
|
396
518
|
}
|
|
397
519
|
|
|
398
520
|
// If we already have prefetched input, extract metadata from it
|
|
399
521
|
if (_prefetchedInput && typeof _prefetchedInput === "object") {
|
|
400
|
-
_lastHookEvent = _prefetchedInput
|
|
401
|
-
_lastToolName = _prefetchedInput
|
|
402
|
-
|
|
403
|
-
|
|
522
|
+
_lastHookEvent = readStringField(_prefetchedInput, "hook_event_name");
|
|
523
|
+
_lastToolName = readStringField(_prefetchedInput, "tool_name");
|
|
524
|
+
const sessionId = readStringField(_prefetchedInput, "session_id");
|
|
525
|
+
if (sessionId) {
|
|
526
|
+
setSessionId(sessionId);
|
|
404
527
|
}
|
|
405
528
|
return;
|
|
406
529
|
}
|
|
407
530
|
|
|
408
531
|
// Read stdin now so HOOK_START can include sid
|
|
409
532
|
try {
|
|
410
|
-
const inputData = fs.readFileSync(0, "
|
|
533
|
+
const inputData = fs.readFileSync(0, "utf8").trim();
|
|
411
534
|
if (inputData) {
|
|
412
|
-
const parsed = JSON.parse(inputData)
|
|
535
|
+
const parsed = JSON.parse(inputData) as Record<string, unknown>;
|
|
413
536
|
if (parsed && typeof parsed === "object") {
|
|
414
537
|
_prefetchedInput = parsed;
|
|
415
|
-
_lastHookEvent = parsed
|
|
416
|
-
_lastToolName = parsed
|
|
417
|
-
|
|
418
|
-
|
|
538
|
+
_lastHookEvent = readStringField(parsed, "hook_event_name");
|
|
539
|
+
_lastToolName = readStringField(parsed, "tool_name");
|
|
540
|
+
const sessionId = readStringField(parsed, "session_id");
|
|
541
|
+
if (sessionId) {
|
|
542
|
+
setSessionId(sessionId);
|
|
419
543
|
}
|
|
420
544
|
}
|
|
421
545
|
}
|
|
@@ -431,7 +555,7 @@ function _earlyReadInput(prefetchedInput?: Record<string, any>): void {
|
|
|
431
555
|
export function runHook(
|
|
432
556
|
mainFunc: () => number | void,
|
|
433
557
|
hookName = "unknown",
|
|
434
|
-
prefetchedInput?: Record<string,
|
|
558
|
+
prefetchedInput?: Record<string, unknown>,
|
|
435
559
|
): never {
|
|
436
560
|
_earlyReadInput(prefetchedInput);
|
|
437
561
|
_cachedHookName = hookName;
|
|
@@ -439,7 +563,8 @@ export function runHook(
|
|
|
439
563
|
// Ensure cwd is project root so relative paths in hooks resolve correctly,
|
|
440
564
|
// even when cwd has drifted via `cd` in a Bash tool call.
|
|
441
565
|
try {
|
|
442
|
-
const
|
|
566
|
+
const cwd = _prefetchedInput ? readStringField(_prefetchedInput, "cwd") ?? undefined : undefined;
|
|
567
|
+
const projectRoot = getProjectRoot(cwd);
|
|
443
568
|
if (process.cwd() !== projectRoot) process.chdir(projectRoot);
|
|
444
569
|
} catch { /* non-fatal — proceed with current cwd */ }
|
|
445
570
|
|
|
@@ -448,7 +573,7 @@ export function runHook(
|
|
|
448
573
|
const event = _lastHookEvent ?? "unknown";
|
|
449
574
|
const tool = _lastToolName;
|
|
450
575
|
|
|
451
|
-
const startData: Record<string,
|
|
576
|
+
const startData: Record<string, unknown> = {
|
|
452
577
|
lifecycle: "start",
|
|
453
578
|
template,
|
|
454
579
|
event,
|
|
@@ -464,7 +589,7 @@ export function runHook(
|
|
|
464
589
|
const result = mainFunc();
|
|
465
590
|
exitCode = typeof result === "number" ? result : 0;
|
|
466
591
|
status = exitCode !== 0 ? "blocked" : "success";
|
|
467
|
-
} catch (error:
|
|
592
|
+
} catch (error: unknown) {
|
|
468
593
|
if (error instanceof Error && error.message.startsWith("SystemExit:")) {
|
|
469
594
|
const code = parseInt(error.message.slice(11), 10);
|
|
470
595
|
exitCode = isNaN(code) ? (error.message.slice(11) ? 1 : 0) : code;
|
|
@@ -489,7 +614,7 @@ export function runHook(
|
|
|
489
614
|
export function runHookAsync(
|
|
490
615
|
mainFunc: () => Promise<number | void>,
|
|
491
616
|
hookName = "unknown",
|
|
492
|
-
prefetchedInput?: Record<string,
|
|
617
|
+
prefetchedInput?: Record<string, unknown>,
|
|
493
618
|
): void {
|
|
494
619
|
_earlyReadInput(prefetchedInput);
|
|
495
620
|
_cachedHookName = hookName;
|
|
@@ -497,7 +622,8 @@ export function runHookAsync(
|
|
|
497
622
|
// Ensure cwd is project root so relative paths in hooks resolve correctly,
|
|
498
623
|
// even when cwd has drifted via `cd` in a Bash tool call.
|
|
499
624
|
try {
|
|
500
|
-
const
|
|
625
|
+
const cwd = _prefetchedInput ? readStringField(_prefetchedInput, "cwd") ?? undefined : undefined;
|
|
626
|
+
const projectRoot = getProjectRoot(cwd);
|
|
501
627
|
if (process.cwd() !== projectRoot) process.chdir(projectRoot);
|
|
502
628
|
} catch { /* non-fatal — proceed with current cwd */ }
|
|
503
629
|
|
|
@@ -506,7 +632,7 @@ export function runHookAsync(
|
|
|
506
632
|
const event = _lastHookEvent ?? "unknown";
|
|
507
633
|
const tool = _lastToolName;
|
|
508
634
|
|
|
509
|
-
const startData: Record<string,
|
|
635
|
+
const startData: Record<string, unknown> = {
|
|
510
636
|
lifecycle: "start",
|
|
511
637
|
template,
|
|
512
638
|
event,
|
|
@@ -520,7 +646,7 @@ export function runHookAsync(
|
|
|
520
646
|
_emitHookEnd(hookName, startTime, exitCode, exitCode !== 0 ? "blocked" : "success", null, startData, event, tool, template);
|
|
521
647
|
_drainAndExit(exitCode);
|
|
522
648
|
})
|
|
523
|
-
.catch((error:
|
|
649
|
+
.catch((error: unknown) => {
|
|
524
650
|
let exitCode = 0;
|
|
525
651
|
let status = "error";
|
|
526
652
|
let errorInfo: [Error, string] | null = null;
|
|
@@ -547,7 +673,7 @@ function _emitHookEnd(
|
|
|
547
673
|
exitCode: number,
|
|
548
674
|
status: string,
|
|
549
675
|
errorInfo: [Error, string] | null,
|
|
550
|
-
startData: Record<string,
|
|
676
|
+
startData: Record<string, unknown>,
|
|
551
677
|
event: string,
|
|
552
678
|
tool: string | null,
|
|
553
679
|
template: string,
|
|
@@ -561,7 +687,7 @@ function _emitHookEnd(
|
|
|
561
687
|
const durationMs = Math.round((performance.now() - startTime) * 10) / 10;
|
|
562
688
|
const endEvent = _lastHookEvent ?? event;
|
|
563
689
|
const endTool = _lastToolName ?? tool;
|
|
564
|
-
const endData: Record<string,
|
|
690
|
+
const endData: Record<string, unknown> = {
|
|
565
691
|
lifecycle: "end",
|
|
566
692
|
status,
|
|
567
693
|
duration_ms: durationMs,
|
|
@@ -602,4 +728,5 @@ function _drainAndExit(code: number): void {
|
|
|
602
728
|
});
|
|
603
729
|
}
|
|
604
730
|
|
|
605
|
-
export {hookLog, logBlocking, logDebug, logDiagnostic, logError, logHookError, logInfo, logWarn, setContextPath, setSessionId} from "
|
|
731
|
+
export {hookLog, logBlocking, logDebug, logDiagnostic, logError, logHookError, logInfo, logWarn, setContextPath, setSessionId} from "../runtime/logger.js";
|
|
732
|
+
|