aiwcli 0.11.0 → 0.12.0
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/dist/commands/clear.d.ts +8 -0
- package/dist/commands/clear.js +86 -0
- package/dist/lib/bmad-installer.d.ts +2 -27
- package/dist/lib/bmad-installer.js +3 -43
- package/dist/lib/claude-settings-types.d.ts +2 -1
- package/dist/lib/env-compat.d.ts +0 -8
- package/dist/lib/env-compat.js +0 -12
- package/dist/lib/git/index.d.ts +0 -1
- package/dist/lib/gitignore-manager.d.ts +0 -2
- package/dist/lib/gitignore-manager.js +1 -1
- package/dist/lib/hooks-merger.d.ts +1 -15
- package/dist/lib/hooks-merger.js +1 -1
- package/dist/lib/index.d.ts +3 -7
- package/dist/lib/index.js +3 -11
- package/dist/lib/output.d.ts +2 -1
- package/dist/lib/settings-hierarchy.d.ts +1 -13
- package/dist/lib/settings-hierarchy.js +1 -1
- package/dist/lib/template-installer.d.ts +5 -9
- package/dist/lib/template-installer.js +2 -12
- package/dist/lib/template-linter.d.ts +3 -10
- package/dist/lib/template-linter.js +2 -2
- package/dist/lib/template-resolver.d.ts +6 -0
- package/dist/lib/template-resolver.js +10 -0
- package/dist/lib/template-settings-reconstructor.d.ts +1 -1
- package/dist/lib/template-settings-reconstructor.js +17 -24
- package/dist/lib/terminal.d.ts +3 -14
- package/dist/lib/terminal.js +0 -4
- package/dist/lib/version.d.ts +2 -11
- package/dist/lib/version.js +2 -2
- package/dist/lib/windsurf-hooks-merger.d.ts +1 -15
- package/dist/lib/windsurf-hooks-merger.js +1 -1
- package/dist/templates/_shared/.codex/workflows/handoff.md +1 -1
- package/dist/templates/_shared/.windsurf/workflows/handoff.md +1 -1
- package/dist/templates/_shared/hooks-ts/session_end.ts +4 -1
- package/dist/templates/_shared/hooks-ts/session_start.ts +15 -20
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +12 -14
- package/dist/templates/_shared/lib-ts/CLAUDE.md +56 -7
- package/dist/templates/_shared/lib-ts/base/git-state.ts +1 -1
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +174 -43
- package/dist/templates/_shared/lib-ts/base/logger.ts +15 -18
- package/dist/templates/_shared/lib-ts/base/state-io.ts +11 -2
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +181 -162
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +26 -30
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +12 -13
- package/dist/templates/_shared/lib-ts/package.json +1 -2
- package/dist/templates/_shared/lib-ts/templates/plan-context.ts +27 -34
- package/dist/templates/_shared/lib-ts/types.ts +17 -2
- package/dist/templates/_shared/scripts/resume_handoff.ts +62 -38
- package/dist/templates/_shared/scripts/save_handoff.ts +24 -24
- package/dist/templates/_shared/scripts/status_line.ts +102 -148
- package/dist/templates/_shared/workflows/handoff.md +1 -1
- package/dist/templates/cc-native/.claude/settings.json +183 -175
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +23 -1
- package/dist/templates/cc-native/_cc-native/agents/plan-questions/PLAN-QUESTIONER.md +70 -0
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +6 -1
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +316 -176
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +38 -0
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +51 -0
- package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +53 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +15 -15
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +11 -12
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +227 -114
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +64 -16
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +23 -3
- package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +119 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +1 -4
- package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +7 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +40 -218
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-enhancement.ts +41 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-questions.ts +101 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +27 -111
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/base/base-agent.ts +217 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +5 -3
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/claude-agent.ts +65 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/codex-agent.ts +184 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/gemini-agent.ts +39 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/orchestrator-claude-agent.ts +195 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/schemas.ts +201 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +3 -5
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +30 -33
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +104 -132
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +22 -13
- package/oclif.manifest.json +1 -1
- package/package.json +2 -3
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +0 -119
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +0 -130
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +0 -106
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-EVOLUTION.md → plan-review/ARCH-EVOLUTION.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-PATTERNS.md → plan-review/ARCH-PATTERNS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-STRUCTURE.md → plan-review/ARCH-STRUCTURE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ASSUMPTION-TRACER.md → plan-review/ASSUMPTION-TRACER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{CLARITY-AUDITOR.md → plan-review/CLARITY-AUDITOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-FEASIBILITY.md → plan-review/COMPLETENESS-FEASIBILITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-GAPS.md → plan-review/COMPLETENESS-GAPS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-ORDERING.md → plan-review/COMPLETENESS-ORDERING.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{CONSTRAINT-VALIDATOR.md → plan-review/CONSTRAINT-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DESIGN-ADR-VALIDATOR.md → plan-review/DESIGN-ADR-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DESIGN-SCALE-MATCHER.md → plan-review/DESIGN-SCALE-MATCHER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DEVILS-ADVOCATE.md → plan-review/DEVILS-ADVOCATE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DOCUMENTATION-PHILOSOPHY.md → plan-review/DOCUMENTATION-PHILOSOPHY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{HANDOFF-READINESS.md → plan-review/HANDOFF-READINESS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{HIDDEN-COMPLEXITY.md → plan-review/HIDDEN-COMPLEXITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{INCREMENTAL-DELIVERY.md → plan-review/INCREMENTAL-DELIVERY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-DEPENDENCY.md → plan-review/RISK-DEPENDENCY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-FMEA.md → plan-review/RISK-FMEA.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-PREMORTEM.md → plan-review/RISK-PREMORTEM.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-REVERSIBILITY.md → plan-review/RISK-REVERSIBILITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SCOPE-BOUNDARY.md → plan-review/SCOPE-BOUNDARY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SIMPLICITY-GUARDIAN.md → plan-review/SIMPLICITY-GUARDIAN.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SKEPTIC.md → plan-review/SKEPTIC.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-BEHAVIOR-AUDITOR.md → plan-review/TESTDRIVEN-BEHAVIOR-AUDITOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-CHARACTERIZATION.md → plan-review/TESTDRIVEN-CHARACTERIZATION.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-FIRST-VALIDATOR.md → plan-review/TESTDRIVEN-FIRST-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-PYRAMID-ANALYZER.md → plan-review/TESTDRIVEN-PYRAMID-ANALYZER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TRADEOFF-COSTS.md → plan-review/TRADEOFF-COSTS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TRADEOFF-STAKEHOLDERS.md → plan-review/TRADEOFF-STAKEHOLDERS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{VERIFY-COVERAGE.md → plan-review/VERIFY-COVERAGE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{VERIFY-STRENGTH.md → plan-review/VERIFY-STRENGTH.md} +0 -0
|
@@ -13,31 +13,20 @@
|
|
|
13
13
|
*
|
|
14
14
|
* @module lib/template-settings-reconstructor
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
17
|
-
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { join } from 'node:path';
|
|
18
17
|
import { mergeClaudeSettings } from './hooks-merger.js';
|
|
19
18
|
import { IdePathResolver } from './ide-path-resolver.js';
|
|
20
19
|
import { readClaudeSettings, writeClaudeSettings } from './settings-hierarchy.js';
|
|
21
|
-
import { getTemplatePath } from './template-resolver.js';
|
|
20
|
+
import { getSharedTemplatePath, getTemplatePath } from './template-resolver.js';
|
|
22
21
|
import { getTargetHooksFile, readWindsurfHooks, writeWindsurfHooks } from './windsurf-hooks-hierarchy.js';
|
|
23
22
|
import { mergeWindsurfHooks } from './windsurf-hooks-merger.js';
|
|
24
|
-
/**
|
|
25
|
-
* Get the path to the _shared template directory.
|
|
26
|
-
*
|
|
27
|
-
* @returns Absolute path to the _shared template
|
|
28
|
-
*/
|
|
29
|
-
function getSharedTemplatePath() {
|
|
30
|
-
const currentFilePath = fileURLToPath(import.meta.url);
|
|
31
|
-
const currentDir = dirname(currentFilePath);
|
|
32
|
-
return join(currentDir, '..', 'templates', '_shared');
|
|
33
|
-
}
|
|
34
23
|
/**
|
|
35
24
|
* Reconstruct .claude/settings.json and .windsurf/hooks.json from the union
|
|
36
25
|
* of all specified templates.
|
|
37
26
|
*
|
|
38
27
|
* The function:
|
|
39
28
|
* 1. Starts with empty settings
|
|
40
|
-
* 2. Merges _shared template settings (
|
|
29
|
+
* 2. Merges _shared template settings (when active templates exist)
|
|
41
30
|
* 3. For each active template, merges its template-source settings
|
|
42
31
|
* 4. Writes the result to the IDE settings file
|
|
43
32
|
*
|
|
@@ -71,11 +60,13 @@ async function reconstructClaudeSettings(targetDir, activeTemplates, sharedTempl
|
|
|
71
60
|
const methodsTracking = existingSettings?.methods;
|
|
72
61
|
// Start from empty and merge all template settings
|
|
73
62
|
let reconstructed = {};
|
|
74
|
-
// 1. Merge _shared template settings
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
63
|
+
// 1. Merge _shared template settings (only when active templates exist that need them)
|
|
64
|
+
if (activeTemplates.length > 0) {
|
|
65
|
+
const sharedSettingsPath = join(sharedTemplatePath, '.claude', 'settings.json');
|
|
66
|
+
const sharedSettings = await readClaudeSettings(sharedSettingsPath);
|
|
67
|
+
if (sharedSettings) {
|
|
68
|
+
reconstructed = mergeClaudeSettings(reconstructed, sharedSettings);
|
|
69
|
+
}
|
|
79
70
|
}
|
|
80
71
|
// 2. Merge each active template's settings (sequential for deterministic merge order)
|
|
81
72
|
for (const template of activeTemplates) {
|
|
@@ -105,11 +96,13 @@ async function reconstructWindsurfHooks(targetDir, activeTemplates, sharedTempla
|
|
|
105
96
|
const hooksPath = getTargetHooksFile(targetDir);
|
|
106
97
|
// Start from empty
|
|
107
98
|
let reconstructed = { hooks: {} };
|
|
108
|
-
// 1. Merge _shared template hooks
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
99
|
+
// 1. Merge _shared template hooks (only when active templates exist that need them)
|
|
100
|
+
if (activeTemplates.length > 0) {
|
|
101
|
+
const sharedHooksPath = join(sharedTemplatePath, '.windsurf', 'hooks.json');
|
|
102
|
+
const sharedHooks = await readWindsurfHooks(sharedHooksPath);
|
|
103
|
+
if (sharedHooks) {
|
|
104
|
+
reconstructed = mergeWindsurfHooks(reconstructed, sharedHooks);
|
|
105
|
+
}
|
|
113
106
|
}
|
|
114
107
|
// 2. Merge each active template's hooks (sequential for deterministic merge order)
|
|
115
108
|
for (const template of activeTemplates) {
|
package/dist/lib/terminal.d.ts
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
/**
|
|
30
30
|
* Options for launching a new terminal window.
|
|
31
31
|
*/
|
|
32
|
-
|
|
32
|
+
interface TerminalLaunchOptions {
|
|
33
33
|
/**
|
|
34
34
|
* Command to execute in the new terminal.
|
|
35
35
|
*/
|
|
@@ -47,7 +47,7 @@ export interface TerminalLaunchOptions {
|
|
|
47
47
|
/**
|
|
48
48
|
* Result of a terminal launch operation.
|
|
49
49
|
*/
|
|
50
|
-
|
|
50
|
+
interface TerminalLaunchResult {
|
|
51
51
|
/**
|
|
52
52
|
* Error message if launch failed.
|
|
53
53
|
*/
|
|
@@ -57,14 +57,6 @@ export interface TerminalLaunchResult {
|
|
|
57
57
|
*/
|
|
58
58
|
success: boolean;
|
|
59
59
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Escape a shell argument for safe execution.
|
|
62
|
-
* Wraps path in double quotes and escapes internal quotes.
|
|
63
|
-
*
|
|
64
|
-
* @param arg - Argument to escape
|
|
65
|
-
* @returns Escaped argument safe for shell execution
|
|
66
|
-
*/
|
|
67
|
-
declare function escapeShellArg(arg: string): string;
|
|
68
60
|
/**
|
|
69
61
|
* Launch a new terminal window with the specified command.
|
|
70
62
|
*
|
|
@@ -96,7 +88,4 @@ declare function escapeShellArg(arg: string): string;
|
|
|
96
88
|
* ```
|
|
97
89
|
*/
|
|
98
90
|
export declare function launchTerminal(options: TerminalLaunchOptions): Promise<TerminalLaunchResult>;
|
|
99
|
-
|
|
100
|
-
* Escape shell argument utility - exported for use in command construction.
|
|
101
|
-
*/
|
|
102
|
-
export { escapeShellArg };
|
|
91
|
+
export {};
|
package/dist/lib/terminal.js
CHANGED
package/dist/lib/version.d.ts
CHANGED
|
@@ -25,20 +25,10 @@
|
|
|
25
25
|
*
|
|
26
26
|
* @module lib/version
|
|
27
27
|
*/
|
|
28
|
-
/**
|
|
29
|
-
* Minimum supported Claude Code version.
|
|
30
|
-
* Versions below this will trigger a compatibility warning.
|
|
31
|
-
*/
|
|
32
|
-
export declare const MIN_CLAUDE_CODE_VERSION = "0.1.0";
|
|
33
|
-
/**
|
|
34
|
-
* Known incompatible Claude Code versions.
|
|
35
|
-
* These versions have confirmed issues with AI Workflow CLI.
|
|
36
|
-
*/
|
|
37
|
-
export declare const INCOMPATIBLE_VERSIONS: string[];
|
|
38
28
|
/**
|
|
39
29
|
* Result of version compatibility check.
|
|
40
30
|
*/
|
|
41
|
-
|
|
31
|
+
interface VersionCheckResult {
|
|
42
32
|
/**
|
|
43
33
|
* Whether the version is compatible with AI Workflow CLI.
|
|
44
34
|
* If version is unknown, assumes compatible (graceful degradation).
|
|
@@ -97,3 +87,4 @@ export declare function getClaudeCodeVersion(): Promise<null | string>;
|
|
|
97
87
|
* ```
|
|
98
88
|
*/
|
|
99
89
|
export declare function checkVersionCompatibility(version: null | string | undefined): VersionCheckResult;
|
|
90
|
+
export {};
|
package/dist/lib/version.js
CHANGED
|
@@ -32,12 +32,12 @@ const execAsync = promisify(exec);
|
|
|
32
32
|
* Minimum supported Claude Code version.
|
|
33
33
|
* Versions below this will trigger a compatibility warning.
|
|
34
34
|
*/
|
|
35
|
-
|
|
35
|
+
const MIN_CLAUDE_CODE_VERSION = '0.1.0';
|
|
36
36
|
/**
|
|
37
37
|
* Known incompatible Claude Code versions.
|
|
38
38
|
* These versions have confirmed issues with AI Workflow CLI.
|
|
39
39
|
*/
|
|
40
|
-
|
|
40
|
+
const INCOMPATIBLE_VERSIONS = ['0.0.9'];
|
|
41
41
|
/**
|
|
42
42
|
* Detects Claude Code version by executing `claude --version`.
|
|
43
43
|
*
|
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
import type { WindsurfHooks
|
|
2
|
-
/**
|
|
3
|
-
* Merge hooks configurations from template into existing
|
|
4
|
-
*
|
|
5
|
-
* Strategy:
|
|
6
|
-
* - For each event type in template hooks
|
|
7
|
-
* - Concatenate with existing hooks for that event
|
|
8
|
-
* - Deduplicate based on command configuration
|
|
9
|
-
* - Maintain order: existing hooks first, then template hooks
|
|
10
|
-
*
|
|
11
|
-
* @param existing - Existing hooks configuration (will not be modified)
|
|
12
|
-
* @param template - Template hooks configuration to merge
|
|
13
|
-
* @returns New merged hooks configuration
|
|
14
|
-
*/
|
|
15
|
-
export declare function mergeWindsurfHooksConfig(existing: undefined | WindsurfHooksConfig, template: undefined | WindsurfHooksConfig): WindsurfHooksConfig;
|
|
1
|
+
import type { WindsurfHooks } from './windsurf-hooks-types.js';
|
|
16
2
|
/**
|
|
17
3
|
* Merge complete Windsurf hooks configurations
|
|
18
4
|
*
|
|
@@ -21,7 +21,7 @@ function areHookCommandsEqual(a, b) {
|
|
|
21
21
|
* @param template - Template hooks configuration to merge
|
|
22
22
|
* @returns New merged hooks configuration
|
|
23
23
|
*/
|
|
24
|
-
|
|
24
|
+
function mergeWindsurfHooksConfig(existing, template) {
|
|
25
25
|
return mergeConfigByEventType(existing, template, (existingCommands, templateCommands) => mergeArraysWithDedup(existingCommands, templateCommands, areHookCommandsEqual));
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
@@ -150,7 +150,7 @@ If a plan document path was provided in `$ARGUMENTS`:
|
|
|
150
150
|
Instead of writing the file directly, pipe your handoff content to the save script:
|
|
151
151
|
|
|
152
152
|
```bash
|
|
153
|
-
|
|
153
|
+
bun .aiwcli/_shared/scripts/save_handoff.ts "{context_id}" <<'EOF'
|
|
154
154
|
{Your complete handoff markdown content from Step 3}
|
|
155
155
|
EOF
|
|
156
156
|
```
|
|
@@ -150,7 +150,7 @@ If a plan document path was provided in `$ARGUMENTS`:
|
|
|
150
150
|
Instead of writing the file directly, pipe your handoff content to the save script:
|
|
151
151
|
|
|
152
152
|
```bash
|
|
153
|
-
|
|
153
|
+
bun .aiwcli/_shared/scripts/save_handoff.ts "{context_id}" <<'EOF'
|
|
154
154
|
{Your complete handoff markdown content from Step 3}
|
|
155
155
|
EOF
|
|
156
156
|
```
|
|
@@ -69,7 +69,10 @@ function main(): void {
|
|
|
69
69
|
state.plan_signature = content.slice(0, 200);
|
|
70
70
|
state.plan_id = generatePlanId();
|
|
71
71
|
state.plan_anchors = extractPlanAnchors(content);
|
|
72
|
-
|
|
72
|
+
// Preserve plan_consumed if already true (plan was implemented) —
|
|
73
|
+
// resetting it would re-stage the plan and block handoff staging.
|
|
74
|
+
// Only set to false when no prior consumption has occurred.
|
|
75
|
+
state.plan_consumed = state.plan_consumed || false;
|
|
73
76
|
|
|
74
77
|
logInfo("session_end", `Assigned plan fallback: hash=${planHash}, path=${latestPlanPath}`);
|
|
75
78
|
} catch (error) {
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
* SessionStart hook: Restore context after /clear (plan/handoff) or compaction.
|
|
4
4
|
* Routes by source field to appropriate handler.
|
|
5
5
|
*/
|
|
6
|
-
import { getProjectRoot } from "../lib-ts/base/constants.js";
|
|
7
6
|
import {
|
|
8
|
-
emitContext,
|
|
9
|
-
|
|
7
|
+
loadHookInput, emitContext, runHook, runHookAsync,
|
|
8
|
+
logDebug, logInfo, logError, logDiagnostic,
|
|
10
9
|
} from "../lib-ts/base/hook-utils.js";
|
|
10
|
+
import { getProjectRoot } from "../lib-ts/base/constants.js";
|
|
11
|
+
import {
|
|
12
|
+
getContextBySessionId, getAllContexts, bindSession, updateMode,
|
|
13
|
+
} from "../lib-ts/context/context-store.js";
|
|
11
14
|
import {
|
|
12
15
|
buildRestoreSections, formatHandoffContinuation, getModeDisplay,
|
|
13
16
|
} from "../lib-ts/context/context-formatter.js";
|
|
14
|
-
import {
|
|
15
|
-
bindSession, getAllContexts, getContextBySessionId, updateMode,
|
|
16
|
-
} from "../lib-ts/context/context-store.js";
|
|
17
|
-
import type { ContextState as _ContextState } from "../lib-ts/types.js";
|
|
17
|
+
import type { ContextState } from "../lib-ts/types.js";
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Handle post-compaction restore: re-inject context that was lost during compaction.
|
|
@@ -53,7 +53,7 @@ function handleCompactRestore(sessionId: string, projectRoot: string): void {
|
|
|
53
53
|
* Handle post-clear restore: find staged has_plan or has_handoff context,
|
|
54
54
|
* bind session, transition to active, inject context.
|
|
55
55
|
*/
|
|
56
|
-
function handleClearRestore(sessionId: string, projectRoot: string): void {
|
|
56
|
+
async function handleClearRestore(sessionId: string, projectRoot: string): Promise<void> {
|
|
57
57
|
const allContexts = getAllContexts("active", projectRoot);
|
|
58
58
|
|
|
59
59
|
// Priority 1: has_plan contexts
|
|
@@ -108,7 +108,7 @@ function handleClearRestore(sessionId: string, projectRoot: string): void {
|
|
|
108
108
|
logDebug("session_start", "No has_plan or has_handoff contexts found");
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
function main(): void {
|
|
111
|
+
async function main(): Promise<void> {
|
|
112
112
|
const payload = loadHookInput();
|
|
113
113
|
if (!payload) return;
|
|
114
114
|
|
|
@@ -124,21 +124,16 @@ function main(): void {
|
|
|
124
124
|
logDiagnostic("session_start", "entry", `source=${source}, session=${sessionId}`);
|
|
125
125
|
|
|
126
126
|
switch (source) {
|
|
127
|
-
case "
|
|
128
|
-
handleClearRestore(sessionId, projectRoot);
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
case "compact": {
|
|
127
|
+
case "compact":
|
|
133
128
|
handleCompactRestore(sessionId, projectRoot);
|
|
134
129
|
break;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
130
|
+
case "clear":
|
|
131
|
+
await handleClearRestore(sessionId, projectRoot);
|
|
132
|
+
break;
|
|
133
|
+
default:
|
|
138
134
|
logDebug("session_start", `Unhandled source: ${source}`);
|
|
139
135
|
break;
|
|
140
|
-
}
|
|
141
136
|
}
|
|
142
137
|
}
|
|
143
138
|
|
|
144
|
-
|
|
139
|
+
runHookAsync(main, "session_start");
|
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
* to a tracked context. The most complex shared hook.
|
|
5
5
|
*
|
|
6
6
|
* Uses emitContext() for output — context text is passed via hookSpecificOutput JSON.
|
|
7
|
-
* Catches BlockRequest and
|
|
7
|
+
* Catches BlockRequest and uses emitBlock() to block the prompt.
|
|
8
8
|
*/
|
|
9
|
-
import { getProjectRoot } from "../lib-ts/base/constants.js";
|
|
10
9
|
import {
|
|
11
|
-
|
|
10
|
+
loadHookInput, runHookAsync, logDebug, logInfo, logWarn, logBlocking, logDiagnostic, hookLog, emitContext, emitBlock,
|
|
12
11
|
} from "../lib-ts/base/hook-utils.js";
|
|
13
|
-
import {
|
|
12
|
+
import { getProjectRoot } from "../lib-ts/base/constants.js";
|
|
14
13
|
import {
|
|
15
|
-
|
|
14
|
+
getContextBySessionId, bindSession, maybeActivate, saveState,
|
|
16
15
|
} from "../lib-ts/context/context-store.js";
|
|
16
|
+
import { determineContext, BlockRequest } from "../lib-ts/context/context-selector.js";
|
|
17
17
|
|
|
18
18
|
async function asyncMain(): Promise<void> {
|
|
19
19
|
const payload = loadHookInput();
|
|
@@ -38,10 +38,9 @@ async function asyncMain(): Promise<void> {
|
|
|
38
38
|
// Returning user — context already bound (stderr: false to avoid "hook error" display)
|
|
39
39
|
try {
|
|
40
40
|
maybeActivate(existingCtx.id, permissionMode, projectRoot, "user_prompt_submit");
|
|
41
|
-
} catch (
|
|
42
|
-
hookLog("warn", "user_prompt_submit", `maybeActivate failed (non-critical): ${
|
|
41
|
+
} catch (e) {
|
|
42
|
+
hookLog("warn", "user_prompt_submit", `maybeActivate failed (non-critical): ${e}`, { stderr: false });
|
|
43
43
|
}
|
|
44
|
-
|
|
45
44
|
hookLog("debug", "user_prompt_submit", `Session bound to ${existingCtx.id}`, { stderr: false });
|
|
46
45
|
} else if (prompt) {
|
|
47
46
|
// First prompt — need to determine context
|
|
@@ -65,13 +64,12 @@ async function asyncMain(): Promise<void> {
|
|
|
65
64
|
if (outputText) {
|
|
66
65
|
outputs.push(outputText);
|
|
67
66
|
}
|
|
68
|
-
} catch (
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
} catch (e) {
|
|
68
|
+
if (e instanceof BlockRequest) {
|
|
69
|
+
emitBlock((e as Error).message);
|
|
70
|
+
return;
|
|
72
71
|
}
|
|
73
|
-
|
|
74
|
-
throw error; // Re-throw unexpected errors
|
|
72
|
+
throw e; // Re-throw unexpected errors
|
|
75
73
|
}
|
|
76
74
|
}
|
|
77
75
|
|
|
@@ -83,18 +83,38 @@ logBlocking("my_hook", "Critical: state corrupt"); // shows in UI
|
|
|
83
83
|
|
|
84
84
|
---
|
|
85
85
|
|
|
86
|
-
## Hook Output —
|
|
86
|
+
## Hook Output — Communication Channels
|
|
87
87
|
|
|
88
|
-
Hooks have
|
|
88
|
+
Hooks have multiple channels back to the session. Pick the right one:
|
|
89
89
|
|
|
90
90
|
| Want to... | Function | Who sees it |
|
|
91
91
|
|------------|----------|-------------|
|
|
92
|
-
| Block
|
|
92
|
+
| **Block (any event)** | `emitBlock(reason, context?)` | Claude + user — auto-dispatches to correct mechanism |
|
|
93
|
+
| Block tool (PreToolUse only) | `emitContextAndBlock(context, reason)` | Claude + user (denial reason prominent) |
|
|
93
94
|
| Return message, don't block | `emitContext(context)` | Claude + user (in transcript) |
|
|
94
95
|
| Log only (diagnostics) | `logInfo()` / `logWarn()` / etc. | Nobody in session — file only |
|
|
95
96
|
|
|
96
97
|
**There is no way to show something to the user but hide it from Claude, or vice versa.** Both `emitContext()` and `emitContextAndBlock()` produce output visible to both.
|
|
97
98
|
|
|
99
|
+
### Universal Blocking: `emitBlock()` (RECOMMENDED)
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
emitBlock("Reason for blocking", "Optional detailed context");
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Auto-detects the correct blocking mechanism from the current hook event:
|
|
106
|
+
|
|
107
|
+
| Event | Mechanism | Dispatches to |
|
|
108
|
+
|-------|-----------|---------------|
|
|
109
|
+
| **PreToolUse** | `permissionDecision: "deny"` in hookSpecificOutput | `emitContextAndBlock()` |
|
|
110
|
+
| **UserPromptSubmit** | Top-level `{ decision: "block", reason }` | `emitBlockPrompt()` |
|
|
111
|
+
| **PostToolUse / PostToolUseFailure** | Exit code 2 + stderr | `emitBlockViaExit()` |
|
|
112
|
+
| **Stop / SubagentStop** | Top-level `{ decision: "block", reason }` | `emitBlockTopLevel()` |
|
|
113
|
+
| **PermissionRequest** | `{ decision: { behavior: "deny" } }` | `emitPermissionDecision()` |
|
|
114
|
+
| **SessionStart / Notification / SubagentStart / SessionEnd** | No blocking mechanism — warns and no-ops | — |
|
|
115
|
+
|
|
116
|
+
**Use `emitBlock()` for all new hooks.** The per-pattern functions below exist for advanced use cases only.
|
|
117
|
+
|
|
98
118
|
### Channel 1: Block + Context (PreToolUse only)
|
|
99
119
|
|
|
100
120
|
```typescript
|
|
@@ -103,12 +123,41 @@ emitContextAndBlock(
|
|
|
103
123
|
"Short reason for the block" // permissionDecisionReason
|
|
104
124
|
);
|
|
105
125
|
// No SystemExit needed — permissionDecision:"deny" with exit 0 is sufficient.
|
|
106
|
-
//
|
|
126
|
+
// Warns if called from non-PreToolUse events (output will be silently rejected by Claude Code).
|
|
107
127
|
```
|
|
108
128
|
|
|
109
129
|
The tool call is **prevented from executing**. Only works for PreToolUse hooks.
|
|
110
130
|
|
|
111
|
-
### Channel 2:
|
|
131
|
+
### Channel 2: Block Prompt Submission (UserPromptSubmit only)
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
emitBlockPrompt("Reason the prompt was blocked", "Optional context for Claude");
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Emits top-level `{ decision: "block", reason }`. The prompt is rejected before Claude processes it.
|
|
138
|
+
|
|
139
|
+
### Channel 3: Block via Exit Code (PostToolUse / PostToolUseFailure)
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
emitBlockViaExit("Reason for blocking", "Optional context prepended to stderr");
|
|
143
|
+
// Throws SystemExit:2 — handled by runHook/runHookAsync
|
|
144
|
+
// NOTE: Exit 2 causes Claude Code to ignore all JSON stdout — only stderr matters
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Channel 4: Block Stop/SubagentStop
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
emitBlockTopLevel("Reason to prevent stopping");
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Channel 5: PermissionRequest Decision
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
emitPermissionDecision("deny", { message: "Why denied" });
|
|
157
|
+
emitPermissionDecision("allow", { updatedInput: { /* modified input */ } });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Channel 6: Non-blocking Context (any hook event)
|
|
112
161
|
|
|
113
162
|
```typescript
|
|
114
163
|
emitContext("Information added to Claude's context");
|
|
@@ -116,7 +165,7 @@ emitContext("Information added to Claude's context");
|
|
|
116
165
|
|
|
117
166
|
The tool call / session continues normally. Works for PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Notification, SubagentStart.
|
|
118
167
|
|
|
119
|
-
### Channel
|
|
168
|
+
### Channel 7: Log-only (diagnostics)
|
|
120
169
|
|
|
121
170
|
```typescript
|
|
122
171
|
logInfo("my_hook", "Processing started"); // File only
|
|
@@ -252,7 +301,7 @@ Use this table to find the right file. Read the source for full API details.
|
|
|
252
301
|
|
|
253
302
|
| File | Purpose | Key Exports |
|
|
254
303
|
|------|---------|-------------|
|
|
255
|
-
| `hook-utils.ts` | Hook lifecycle, stdin parsing, output emit, re-exports | `runHook`, `runHookAsync`, `loadHookInput`, `emitContext`, `emitContextAndBlock`, `logDebug`...`logBlocking` |
|
|
304
|
+
| `hook-utils.ts` | Hook lifecycle, stdin parsing, output emit, re-exports | `runHook`, `runHookAsync`, `loadHookInput`, `emitContext`, `emitContextAndBlock`, `emitBlock`, `emitBlockPrompt`, `emitBlockViaExit`, `emitBlockTopLevel`, `emitPermissionDecision`, `logDebug`...`logBlocking` |
|
|
256
305
|
| `logger.ts` | JSONL logging engine | `hookLog`, `logDebug`, `logInfo`, `logWarn`, `logError`, `logBlocking`, `logHookError`, `logDiagnostic` |
|
|
257
306
|
| `constants.ts` | Path resolution, limits | `getProjectRoot()`, `getContextDir()`, `MAX_FILE_SIZE` |
|
|
258
307
|
| `atomic-write.ts` | Crash-safe file writes | `atomicWriteFileSync()` |
|
|
@@ -23,7 +23,7 @@ export function getGitState(projectRoot: string): Record<string, any> {
|
|
|
23
23
|
try {
|
|
24
24
|
const status = execFileSync("git", ["status", "--short"], opts);
|
|
25
25
|
if (status) {
|
|
26
|
-
const files = status.trim().split(
|
|
26
|
+
const files = status.trim().split(/\r?\n/)
|
|
27
27
|
.filter(Boolean)
|
|
28
28
|
.slice(0, 10)
|
|
29
29
|
.map(line => line.trimStart().split(/\s+/).slice(1).join(" "));
|