aiwcli 0.10.3 → 0.11.1
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/bin/run.js +1 -1
- package/dist/commands/clear.js +28 -131
- package/dist/commands/init/index.js +3 -3
- package/dist/lib/gitignore-manager.d.ts +32 -0
- package/dist/lib/gitignore-manager.js +141 -2
- package/dist/templates/CLAUDE.md +8 -8
- package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
- package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
- package/dist/templates/_shared/.claude/settings.json +7 -7
- package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
- package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
- package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
- package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
- package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
- package/dist/templates/_shared/hooks-ts/session_end.ts +107 -0
- package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
- package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
- package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
- package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
- package/dist/templates/_shared/lib-ts/base/atomic-write.ts +12 -12
- package/dist/templates/_shared/lib-ts/base/constants.ts +22 -15
- package/dist/templates/_shared/lib-ts/base/git-state.ts +1 -1
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +129 -50
- package/dist/templates/_shared/lib-ts/base/inference.ts +28 -21
- package/dist/templates/_shared/lib-ts/base/logger.ts +15 -2
- package/dist/templates/_shared/lib-ts/base/state-io.ts +9 -7
- package/dist/templates/_shared/lib-ts/base/stop-words.ts +131 -131
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +142 -0
- package/dist/templates/_shared/lib-ts/base/utils.ts +69 -69
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +30 -24
- package/dist/templates/_shared/lib-ts/context/context-selector.ts +50 -32
- package/dist/templates/_shared/lib-ts/context/context-store.ts +76 -48
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +43 -23
- package/dist/templates/_shared/lib-ts/context/task-tracker.ts +10 -6
- package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +11 -10
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +158 -0
- package/dist/templates/_shared/lib-ts/templates/formatters.ts +6 -4
- package/dist/templates/_shared/lib-ts/types.ts +68 -55
- package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
- package/dist/templates/_shared/scripts/resume_handoff.ts +345 -0
- package/dist/templates/_shared/scripts/save_handoff.ts +3 -3
- package/dist/templates/_shared/scripts/status_line.ts +687 -0
- package/dist/templates/cc-native/.claude/settings.json +175 -185
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +0 -2
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +1027 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +156 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +792 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +144 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +115 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +120 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +168 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +250 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +275 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +107 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +240 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +385 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +14 -1
- package/oclif.manifest.json +1 -1
- package/package.json +2 -2
- package/dist/templates/_shared/hooks/__init__.py +0 -16
- package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/archive_plan.py +0 -177
- package/dist/templates/_shared/hooks/context_monitor.py +0 -270
- package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
- package/dist/templates/_shared/hooks/pre_compact.py +0 -104
- package/dist/templates/_shared/hooks/session_end.py +0 -173
- package/dist/templates/_shared/hooks/session_start.py +0 -206
- package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
- package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
- package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
- package/dist/templates/_shared/lib/__init__.py +0 -1
- package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__init__.py +0 -65
- package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
- package/dist/templates/_shared/lib/base/constants.py +0 -358
- package/dist/templates/_shared/lib/base/hook_utils.py +0 -339
- package/dist/templates/_shared/lib/base/inference.py +0 -307
- package/dist/templates/_shared/lib/base/logger.py +0 -305
- package/dist/templates/_shared/lib/base/stop_words.py +0 -221
- package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
- package/dist/templates/_shared/lib/base/utils.py +0 -263
- package/dist/templates/_shared/lib/context/__init__.py +0 -102
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
- package/dist/templates/_shared/lib/context/context_selector.py +0 -508
- package/dist/templates/_shared/lib/context/context_store.py +0 -653
- package/dist/templates/_shared/lib/context/plan_manager.py +0 -303
- package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
- package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
- package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
- package/dist/templates/_shared/lib/templates/README.md +0 -206
- package/dist/templates/_shared/lib/templates/__init__.py +0 -36
- package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/formatters.py +0 -146
- package/dist/templates/_shared/lib/templates/plan_context.py +0 -73
- package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/save_handoff.py +0 -357
- package/dist/templates/_shared/scripts/status_line.py +0 -716
- package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/MIGRATION.md +0 -86
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -954
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
- package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
- package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
- package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
- package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
- package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1071
- package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
- package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure verdict aggregation logic.
|
|
3
|
+
* See cc-native-plan-review-spec.md §4.2
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ReviewDecisionResult, Verdict } from "./types.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Return the worst verdict from a list.
|
|
10
|
+
* Order: pass < warn < fail. skip→pass, error→warn.
|
|
11
|
+
*/
|
|
12
|
+
export function worstVerdict(verdicts: Verdict[]): Verdict {
|
|
13
|
+
const order: Record<Verdict, number> = {
|
|
14
|
+
pass: 0,
|
|
15
|
+
warn: 1,
|
|
16
|
+
fail: 2,
|
|
17
|
+
skip: 0,
|
|
18
|
+
error: 1,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
let worst: Verdict = "pass";
|
|
22
|
+
for (const v of verdicts) {
|
|
23
|
+
if ((order[v] ?? 1) > (order[worst] ?? 0)) {
|
|
24
|
+
worst = v;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Normalize error → warn
|
|
29
|
+
if (worst === "error") return "warn";
|
|
30
|
+
return worst;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Verdict aggregation: fail veto triggers a block.
|
|
35
|
+
*
|
|
36
|
+
* Priority order:
|
|
37
|
+
* 1. Fail Veto: Any fail → deny (ISO 61508 zero-tolerance)
|
|
38
|
+
* 2. Acceptable: warns are informational only
|
|
39
|
+
*
|
|
40
|
+
* Error exclusion: Detectors that produce no signal (error/skip) are excluded
|
|
41
|
+
* from the denominator.
|
|
42
|
+
*
|
|
43
|
+
* @param allVerdicts - List of verdict strings from all reviewers
|
|
44
|
+
* @returns ReviewDecisionResult with should_deny, reason, and score
|
|
45
|
+
*/
|
|
46
|
+
export function computeReviewDecision(
|
|
47
|
+
allVerdicts: Verdict[],
|
|
48
|
+
): ReviewDecisionResult {
|
|
49
|
+
// Exclude non-signal verdicts
|
|
50
|
+
const signalVerdicts = allVerdicts.filter(
|
|
51
|
+
(v) => v === "pass" || v === "warn" || v === "fail",
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
if (signalVerdicts.length === 0) {
|
|
55
|
+
return { should_deny: false, reason: "no_signal", score: 0.0 };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Fail blocks unconditionally
|
|
59
|
+
const failCount = signalVerdicts.filter((v) => v === "fail").length;
|
|
60
|
+
if (failCount > 0) {
|
|
61
|
+
return { should_deny: true, reason: "fail_veto", score: 1.0 };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Warn also blocks — reviewers flagged concerns worth addressing
|
|
65
|
+
const warnCount = signalVerdicts.filter((v) => v === "warn").length;
|
|
66
|
+
const warnRatio = warnCount / signalVerdicts.length;
|
|
67
|
+
if (warnCount > 0) {
|
|
68
|
+
return { should_deny: true, reason: "warn_block", score: warnRatio };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { should_deny: false, reason: "acceptable", score: 0.0 };
|
|
72
|
+
}
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
{
|
|
2
|
+
"models": {
|
|
3
|
+
"providers": {
|
|
4
|
+
"claude": {
|
|
5
|
+
"enabled": true,
|
|
6
|
+
"models": ["sonnet"]
|
|
7
|
+
},
|
|
8
|
+
"codex": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"models": ["gpt-5.1-codex-mini"]
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
2
14
|
"planReview": {
|
|
3
15
|
"enabled": true,
|
|
4
16
|
"reviewers": {
|
|
5
17
|
"codex": {
|
|
6
18
|
"enabled": true,
|
|
7
|
-
"model": "
|
|
19
|
+
"model": "gpt-5.1-codex-mini",
|
|
8
20
|
"timeout": 120
|
|
9
21
|
},
|
|
10
22
|
"gemini": {
|
|
@@ -23,6 +35,7 @@
|
|
|
23
35
|
"enabled": true,
|
|
24
36
|
"timeout": 180,
|
|
25
37
|
"warnThreshold": 0.01,
|
|
38
|
+
"maxIssuesPerAgent": 3,
|
|
26
39
|
"orchestrator": {
|
|
27
40
|
"enabled": true,
|
|
28
41
|
"model": "opus",
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiwcli",
|
|
3
3
|
"description": "AI Workflow CLI - Command-line interface for AI-powered workflows",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.1",
|
|
5
5
|
"author": "jofu-tofu",
|
|
6
6
|
"bin": {
|
|
7
|
-
"aiw": "
|
|
7
|
+
"aiw": "bin/run.js"
|
|
8
8
|
},
|
|
9
9
|
"bugs": {
|
|
10
10
|
"url": "https://github.com/jofu-tofu/AI-Workflow-CLI/issues"
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"""Shared hooks for AIW CLI templates.
|
|
2
|
-
|
|
3
|
-
These hooks integrate with Claude Code's hook system to provide:
|
|
4
|
-
- UserPromptSubmit: Context enforcement (ensures work happens in tracked context)
|
|
5
|
-
- PostToolUse: Context monitoring and handoff warnings
|
|
6
|
-
- ExitPlanMode: Plan archival to context
|
|
7
|
-
|
|
8
|
-
Hooks read JSON input from stdin and output instructions to stdout.
|
|
9
|
-
|
|
10
|
-
Available hooks:
|
|
11
|
-
- user_prompt_submit.py: Runs on user prompt, enforces context tracking
|
|
12
|
-
- context_monitor.py: Runs on PostToolUse, monitors context and warns when low
|
|
13
|
-
- archive_plan.py: Runs on ExitPlanMode, archives plan to context
|
|
14
|
-
- task_create_capture.py: Runs on TaskCreate, captures task events
|
|
15
|
-
- task_update_capture.py: Runs on TaskUpdate, captures task updates
|
|
16
|
-
"""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Plan archival hook for ExitPlanMode PermissionRequest event.
|
|
3
|
-
|
|
4
|
-
This hook runs when ExitPlanMode is requested (BEFORE user accepts/rejects),
|
|
5
|
-
extracting the plan path from the tool input and archiving it to the
|
|
6
|
-
context's plans/ folder. It does NOT modify state.json plan fields or mode.
|
|
7
|
-
|
|
8
|
-
Separation of concerns:
|
|
9
|
-
- archive_plan.py (PermissionRequest) -> archives file only, no state.json changes
|
|
10
|
-
- plan_accepted.py (PostToolUse) -> assigns plan fields (hash/signature/path) to state.json
|
|
11
|
-
- session_end.py (SessionEnd) -> transitions active -> has_plan when plan is assigned
|
|
12
|
-
- context_selector.py -> matches plan content, transitions has_plan -> active
|
|
13
|
-
|
|
14
|
-
Usage in .claude/settings.json:
|
|
15
|
-
{
|
|
16
|
-
"hooks": {
|
|
17
|
-
"PermissionRequest": [{
|
|
18
|
-
"matcher": "ExitPlanMode",
|
|
19
|
-
"hooks": [{
|
|
20
|
-
"type": "command",
|
|
21
|
-
"command": "python .aiwcli/_shared/hooks/archive_plan.py",
|
|
22
|
-
"timeout": 5000
|
|
23
|
-
}]
|
|
24
|
-
}]
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
"""
|
|
28
|
-
import re
|
|
29
|
-
import sys
|
|
30
|
-
from pathlib import Path
|
|
31
|
-
from typing import Optional
|
|
32
|
-
|
|
33
|
-
# Add parent directories to path for imports
|
|
34
|
-
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
35
|
-
SHARED_LIB = SCRIPT_DIR.parent / "lib"
|
|
36
|
-
sys.path.insert(0, str(SHARED_LIB.parent))
|
|
37
|
-
|
|
38
|
-
from lib.base.hook_utils import load_hook_input, log_debug, log_info, log_warn, log_error
|
|
39
|
-
from lib.base.utils import project_dir
|
|
40
|
-
from lib.base.constants import get_context_dir
|
|
41
|
-
from lib.context.context_store import get_context_by_session_id
|
|
42
|
-
from lib.context.plan_manager import archive_plan, extract_plan_path_from_result, find_plan_path_in_transcript
|
|
43
|
-
|
|
44
|
-
# Import debug cleanup function from cc-native lib
|
|
45
|
-
_cc_native_lib = SCRIPT_DIR.parent / "_cc-native" / "lib"
|
|
46
|
-
sys.path.insert(0, str(_cc_native_lib))
|
|
47
|
-
try:
|
|
48
|
-
from debug import cleanup_debug_folder
|
|
49
|
-
except ImportError:
|
|
50
|
-
def cleanup_debug_folder(context_path):
|
|
51
|
-
pass
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _find_plan_path(hook_input: dict, project_root: Path) -> Optional[str]:
|
|
55
|
-
"""Find the plan file path from hook input or standard locations."""
|
|
56
|
-
tool_input = hook_input.get("tool_input", {})
|
|
57
|
-
tool_result = hook_input.get("tool_result", "")
|
|
58
|
-
hook_event = hook_input.get("hook_event_name", "")
|
|
59
|
-
tool_name = hook_input.get("tool_name", "")
|
|
60
|
-
|
|
61
|
-
plan_path = None
|
|
62
|
-
|
|
63
|
-
# For ExitPlanMode, extract from tool result
|
|
64
|
-
if tool_name == "ExitPlanMode" and tool_result:
|
|
65
|
-
plan_path = extract_plan_path_from_result(tool_result)
|
|
66
|
-
if plan_path:
|
|
67
|
-
log_info("archive_plan", f"Extracted plan path from result: {plan_path}")
|
|
68
|
-
|
|
69
|
-
# Check tool_input for plan path
|
|
70
|
-
if not plan_path:
|
|
71
|
-
plan_path = tool_input.get("plan_path") or tool_input.get("planPath")
|
|
72
|
-
|
|
73
|
-
# Parse transcript for most recent Write to .claude/plans/
|
|
74
|
-
if not plan_path:
|
|
75
|
-
transcript_path = hook_input.get("transcript_path")
|
|
76
|
-
if transcript_path:
|
|
77
|
-
plan_path = find_plan_path_in_transcript(transcript_path)
|
|
78
|
-
if plan_path:
|
|
79
|
-
log_info("archive_plan", f"Found plan path via transcript: {plan_path}")
|
|
80
|
-
|
|
81
|
-
# Search standard locations (mtime-based fallback)
|
|
82
|
-
if not plan_path:
|
|
83
|
-
log_debug("archive_plan", "No plan_path found, searching standard locations...")
|
|
84
|
-
claude_plans_dir = Path.home() / ".claude" / "plans"
|
|
85
|
-
if claude_plans_dir.exists():
|
|
86
|
-
claude_plans = sorted(
|
|
87
|
-
claude_plans_dir.glob("*.md"),
|
|
88
|
-
key=lambda p: p.stat().st_mtime,
|
|
89
|
-
reverse=True,
|
|
90
|
-
)
|
|
91
|
-
if claude_plans:
|
|
92
|
-
plan_path = str(claude_plans[0])
|
|
93
|
-
|
|
94
|
-
if not plan_path:
|
|
95
|
-
for fallback in [
|
|
96
|
-
project_root / "_output" / "cc-native" / "plans" / "current-plan.md",
|
|
97
|
-
project_root / "_output" / "plans" / "current-plan.md",
|
|
98
|
-
project_root / "plan.md",
|
|
99
|
-
]:
|
|
100
|
-
if fallback.exists():
|
|
101
|
-
plan_path = str(fallback)
|
|
102
|
-
break
|
|
103
|
-
|
|
104
|
-
return plan_path
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def on_plan_archive():
|
|
108
|
-
"""Archive plan on PermissionRequest:ExitPlanMode — file archival only, no state.json changes."""
|
|
109
|
-
hook_input = load_hook_input()
|
|
110
|
-
if not hook_input:
|
|
111
|
-
log_warn("archive_plan", "No valid JSON input")
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
hook_event = hook_input.get("hook_event_name", "unknown")
|
|
115
|
-
tool_name = hook_input.get("tool_name", "")
|
|
116
|
-
|
|
117
|
-
log_info("archive_plan", f"Hook triggered: {hook_event}, tool: {tool_name}")
|
|
118
|
-
|
|
119
|
-
# Only handle PermissionRequest for ExitPlanMode
|
|
120
|
-
if not (hook_event == "PermissionRequest" and tool_name == "ExitPlanMode"):
|
|
121
|
-
log_debug("archive_plan", "Skipping: not PermissionRequest:ExitPlanMode")
|
|
122
|
-
return
|
|
123
|
-
|
|
124
|
-
if hook_input.get("stop_hook_active", False):
|
|
125
|
-
log_debug("archive_plan", "Stop hook active, skipping")
|
|
126
|
-
return
|
|
127
|
-
|
|
128
|
-
project_root = project_dir(hook_input)
|
|
129
|
-
plan_path = _find_plan_path(hook_input, project_root)
|
|
130
|
-
|
|
131
|
-
if not plan_path:
|
|
132
|
-
log_warn("archive_plan", "Could not find plan file, skipping archival")
|
|
133
|
-
return
|
|
134
|
-
|
|
135
|
-
# Resolve plan path
|
|
136
|
-
plan_file = Path(plan_path)
|
|
137
|
-
if not plan_file.is_absolute():
|
|
138
|
-
plan_file = project_root / plan_path
|
|
139
|
-
|
|
140
|
-
log_debug("archive_plan", f"Resolved plan file: {plan_file}")
|
|
141
|
-
|
|
142
|
-
if not plan_file.exists():
|
|
143
|
-
log_error("archive_plan", f"Plan file not found: {plan_file}")
|
|
144
|
-
return
|
|
145
|
-
|
|
146
|
-
# Find context by session ID
|
|
147
|
-
session_id = hook_input.get("session_id", "unknown")
|
|
148
|
-
state = get_context_by_session_id(session_id, project_root)
|
|
149
|
-
|
|
150
|
-
if not state:
|
|
151
|
-
log_warn("archive_plan", "Could not determine context for session")
|
|
152
|
-
return
|
|
153
|
-
|
|
154
|
-
context_id = state.id
|
|
155
|
-
|
|
156
|
-
# Archive the plan file (returns path, hash, signature)
|
|
157
|
-
archived_path, plan_hash, plan_signature = archive_plan(
|
|
158
|
-
str(plan_file), context_id, project_root
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
if archived_path:
|
|
162
|
-
# Clean up debug logs
|
|
163
|
-
try:
|
|
164
|
-
context_path = get_context_dir(context_id, project_root)
|
|
165
|
-
cleanup_debug_folder(context_path)
|
|
166
|
-
except Exception as e:
|
|
167
|
-
log_warn("archive_plan", f"could not clean debug folder: {e}")
|
|
168
|
-
|
|
169
|
-
log_info("archive_plan", f"SUCCESS: archived plan for {context_id}")
|
|
170
|
-
log_debug("archive_plan", f"Path: {archived_path}, hash: {plan_hash}")
|
|
171
|
-
else:
|
|
172
|
-
log_error("archive_plan", f"Could not archive plan for '{context_id}'")
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if __name__ == "__main__":
|
|
176
|
-
from lib.base.hook_utils import run_hook
|
|
177
|
-
run_hook(on_plan_archive, "archive_plan")
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Context monitor hook for proactive handoff warnings.
|
|
3
|
-
|
|
4
|
-
This hook runs on PostToolUse for context-heavy tools and monitors
|
|
5
|
-
context window usage. When context drops below a threshold, it injects
|
|
6
|
-
a system reminder instructing Claude to wrap up and create a handoff document.
|
|
7
|
-
|
|
8
|
-
Unlike UserPromptSubmit hooks, this fires DURING Claude's work,
|
|
9
|
-
allowing proactive intervention without waiting for user input.
|
|
10
|
-
|
|
11
|
-
Monitored tools (configured via settings.json matcher):
|
|
12
|
-
- Task: Subagent responses can be huge
|
|
13
|
-
- Read: File content loads into context
|
|
14
|
-
- Bash: Command output can be large
|
|
15
|
-
- WebFetch: Web content loads into context
|
|
16
|
-
|
|
17
|
-
Hook input (from Claude Code):
|
|
18
|
-
{
|
|
19
|
-
"hook_event_name": "PostToolUse",
|
|
20
|
-
"tool_name": "Task",
|
|
21
|
-
"tool_input": {...},
|
|
22
|
-
"tool_result": {...},
|
|
23
|
-
"transcript_path": "/path/to/transcript.jsonl",
|
|
24
|
-
"session_id": "abc123",
|
|
25
|
-
"context_window": {
|
|
26
|
-
"current_usage": {
|
|
27
|
-
"cache_read_input_tokens": 0,
|
|
28
|
-
"input_tokens": 12345,
|
|
29
|
-
"cache_creation_input_tokens": 0,
|
|
30
|
-
"output_tokens": 6789
|
|
31
|
-
},
|
|
32
|
-
"context_window_size": 200000
|
|
33
|
-
},
|
|
34
|
-
...
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
Hook output:
|
|
38
|
-
- Outputs JSON with additionalContext if context is low
|
|
39
|
-
- This injects a system reminder into Claude's context
|
|
40
|
-
- Plain stdout from PostToolUse only goes to verbose mode, not Claude
|
|
41
|
-
- Using additionalContext ensures Claude sees and responds to the warning
|
|
42
|
-
|
|
43
|
-
KNOWN LIMITATION: Context percentage won't match /context exactly.
|
|
44
|
-
Hook JSON excludes system prompt, tools, MCP tokens. We add a baseline
|
|
45
|
-
to compensate (~22.6k tokens typical). See:
|
|
46
|
-
https://github.com/anthropics/claude-code/issues/13783
|
|
47
|
-
"""
|
|
48
|
-
import sys
|
|
49
|
-
from pathlib import Path
|
|
50
|
-
from typing import Optional
|
|
51
|
-
|
|
52
|
-
# Add parent directories to path for imports
|
|
53
|
-
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
54
|
-
SHARED_LIB = SCRIPT_DIR.parent / "lib"
|
|
55
|
-
sys.path.insert(0, str(SHARED_LIB.parent))
|
|
56
|
-
|
|
57
|
-
from lib.base.hook_utils import emit_context, load_hook_input, get_context_percent_remaining, log_debug, log_info, log_warn, log_error, log_diagnostic
|
|
58
|
-
from lib.base.utils import now_iso, project_dir
|
|
59
|
-
from lib.context.context_store import (
|
|
60
|
-
get_all_contexts,
|
|
61
|
-
get_context_by_session_id,
|
|
62
|
-
maybe_activate,
|
|
63
|
-
save_state,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
# Module-level flag: only save auto-state once per process lifetime
|
|
67
|
-
_PROGRESSIVE_SAVE_MARKER = ".progressive-save-done"
|
|
68
|
-
|
|
69
|
-
# Configuration
|
|
70
|
-
SAVE_STATE_THRESHOLD = 60 # Silently save auto-state at 60% remaining
|
|
71
|
-
HANDOFF_SUGGEST_THRESHOLD = 30 # Gentle nudge at 30% remaining (70% used)
|
|
72
|
-
HANDOFF_PREPARE_THRESHOLD = 20 # Stronger warning at 20% remaining (80% used)
|
|
73
|
-
CRITICAL_CONTEXT_THRESHOLD = 10 # Urgent warning at 10% remaining (90% used)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def get_current_context_id(project_root: Path = None) -> Optional[str]:
|
|
77
|
-
"""Determine the current active context (most recently active)."""
|
|
78
|
-
contexts = get_all_contexts(status="active", project_root=project_root)
|
|
79
|
-
if contexts:
|
|
80
|
-
return contexts[0].id
|
|
81
|
-
return None
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def get_context_warning(
|
|
85
|
-
percent_remaining: int,
|
|
86
|
-
tokens_used: Optional[int],
|
|
87
|
-
max_tokens: Optional[int],
|
|
88
|
-
context_id: Optional[str],
|
|
89
|
-
tool_name: str
|
|
90
|
-
) -> str:
|
|
91
|
-
"""Generate appropriate warning based on context level."""
|
|
92
|
-
if tokens_used is not None and max_tokens is not None:
|
|
93
|
-
tokens_used_k = tokens_used // 1000
|
|
94
|
-
max_tokens_k = max_tokens // 1000
|
|
95
|
-
usage_line = f"**Estimated usage**: ~{tokens_used_k}k / {max_tokens_k}k tokens"
|
|
96
|
-
else:
|
|
97
|
-
usage_line = f"**Estimated usage**: ~{percent_remaining}% remaining"
|
|
98
|
-
|
|
99
|
-
context_line = f"\nContext ID: `{context_id}`" if context_id else ""
|
|
100
|
-
|
|
101
|
-
if percent_remaining <= CRITICAL_CONTEXT_THRESHOLD:
|
|
102
|
-
return f"""<system-reminder>
|
|
103
|
-
## CRITICAL CONTEXT WARNING ({percent_remaining}% remaining)
|
|
104
|
-
|
|
105
|
-
{usage_line}
|
|
106
|
-
**Triggered by**: {tool_name} tool completion
|
|
107
|
-
|
|
108
|
-
**CRITICAL: Run `/handoff` now before context is compacted.**
|
|
109
|
-
{context_line}
|
|
110
|
-
|
|
111
|
-
You are about to lose context. Stop all other work and run `/handoff` immediately.
|
|
112
|
-
</system-reminder>"""
|
|
113
|
-
|
|
114
|
-
elif percent_remaining <= HANDOFF_PREPARE_THRESHOLD:
|
|
115
|
-
return f"""<system-reminder>
|
|
116
|
-
## LOW CONTEXT WARNING ({percent_remaining}% remaining)
|
|
117
|
-
|
|
118
|
-
{usage_line}
|
|
119
|
-
**Triggered by**: {tool_name} tool completion
|
|
120
|
-
|
|
121
|
-
**Context is getting low. Please finish your current task and run `/handoff`.**
|
|
122
|
-
{context_line}
|
|
123
|
-
|
|
124
|
-
**Actions:**
|
|
125
|
-
1. Complete your current atomic task (if 1-2 steps away)
|
|
126
|
-
2. Do NOT start new multi-step work
|
|
127
|
-
3. Run `/handoff` to generate a handoff document
|
|
128
|
-
</system-reminder>"""
|
|
129
|
-
|
|
130
|
-
else:
|
|
131
|
-
return f"""<system-reminder>
|
|
132
|
-
## CONTEXT NOTICE ({percent_remaining}% remaining)
|
|
133
|
-
|
|
134
|
-
{usage_line}
|
|
135
|
-
**Triggered by**: {tool_name} tool completion
|
|
136
|
-
|
|
137
|
-
**Consider preparing a handoff soon. When ready, run `/handoff` to generate a handoff document.**
|
|
138
|
-
{context_line}
|
|
139
|
-
|
|
140
|
-
Continue your current work, but avoid starting large new tasks.
|
|
141
|
-
</system-reminder>"""
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def check_and_transition_mode(hook_input: dict) -> None:
|
|
145
|
-
"""
|
|
146
|
-
Check if context mode needs to transition based on tool usage.
|
|
147
|
-
|
|
148
|
-
Handles:
|
|
149
|
-
- has_plan + implementation tool -> active (started implementing)
|
|
150
|
-
- idle + implementation tool -> active
|
|
151
|
-
"""
|
|
152
|
-
project_root = project_dir(hook_input)
|
|
153
|
-
session_id = hook_input.get("session_id")
|
|
154
|
-
|
|
155
|
-
if not session_id:
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
state = get_context_by_session_id(session_id, project_root)
|
|
159
|
-
if not state:
|
|
160
|
-
return
|
|
161
|
-
|
|
162
|
-
# Implementation transitions only trigger on implementation tools
|
|
163
|
-
implementation_tools = {"Edit", "Write", "Bash", "NotebookEdit"}
|
|
164
|
-
tool_name = hook_input.get("tool_name", "")
|
|
165
|
-
|
|
166
|
-
if tool_name not in implementation_tools:
|
|
167
|
-
return
|
|
168
|
-
|
|
169
|
-
permission_mode = hook_input.get("permission_mode", "default")
|
|
170
|
-
maybe_activate(state.id, permission_mode, project_root=project_root, caller="context_monitor")
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def _try_progressive_save(hook_input: dict, percent_remaining: int) -> None:
|
|
174
|
-
"""Silently save state at SAVE_STATE_THRESHOLD (60%)."""
|
|
175
|
-
try:
|
|
176
|
-
session_id = hook_input.get("session_id", "")
|
|
177
|
-
if not session_id:
|
|
178
|
-
return
|
|
179
|
-
|
|
180
|
-
project_root = project_dir(hook_input)
|
|
181
|
-
state = get_context_by_session_id(session_id, project_root)
|
|
182
|
-
if not state:
|
|
183
|
-
return
|
|
184
|
-
|
|
185
|
-
from lib.base.constants import get_context_dir
|
|
186
|
-
marker_path = get_context_dir(state.id, project_root) / _PROGRESSIVE_SAVE_MARKER
|
|
187
|
-
if marker_path.exists():
|
|
188
|
-
try:
|
|
189
|
-
saved_session = marker_path.read_text(encoding="utf-8").strip()
|
|
190
|
-
if saved_session == session_id:
|
|
191
|
-
return
|
|
192
|
-
except OSError:
|
|
193
|
-
pass
|
|
194
|
-
|
|
195
|
-
log_info("context_monitor", f"Progressive save at {percent_remaining}% remaining")
|
|
196
|
-
|
|
197
|
-
# Just update last_active and save state
|
|
198
|
-
state.last_active = now_iso()
|
|
199
|
-
save_state(state, project_root)
|
|
200
|
-
|
|
201
|
-
try:
|
|
202
|
-
marker_path.write_text(session_id, encoding="utf-8")
|
|
203
|
-
except OSError:
|
|
204
|
-
pass
|
|
205
|
-
|
|
206
|
-
except Exception as e:
|
|
207
|
-
log_warn("context_monitor", f"Progressive save error (non-fatal): {e}")
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
def check_context_level(hook_input: dict) -> Optional[str]:
|
|
211
|
-
"""Check context level and return warning if low."""
|
|
212
|
-
tool_name = hook_input.get("tool_name", "Unknown")
|
|
213
|
-
percent_remaining, tokens_used, max_tokens = get_context_percent_remaining(hook_input)
|
|
214
|
-
|
|
215
|
-
log_diagnostic("context_monitor", "receive", f"tool={tool_name}, pct_remaining={percent_remaining}",
|
|
216
|
-
inputs={"tool_name": tool_name, "percent_remaining": percent_remaining,
|
|
217
|
-
"tokens_used": tokens_used, "max_tokens": max_tokens})
|
|
218
|
-
|
|
219
|
-
if percent_remaining is None:
|
|
220
|
-
return None
|
|
221
|
-
|
|
222
|
-
if percent_remaining > SAVE_STATE_THRESHOLD:
|
|
223
|
-
return None
|
|
224
|
-
|
|
225
|
-
if percent_remaining > HANDOFF_SUGGEST_THRESHOLD:
|
|
226
|
-
_try_progressive_save(hook_input, percent_remaining)
|
|
227
|
-
return None
|
|
228
|
-
|
|
229
|
-
if tokens_used is not None and max_tokens is not None:
|
|
230
|
-
log_info("context_monitor", f"Context: {percent_remaining}% remaining "
|
|
231
|
-
f"(~{tokens_used//1000}k/{max_tokens//1000}k tokens)")
|
|
232
|
-
else:
|
|
233
|
-
log_info("context_monitor", f"Context: ~{percent_remaining}% remaining (from context.json)")
|
|
234
|
-
|
|
235
|
-
project_root = project_dir(hook_input)
|
|
236
|
-
context_id = get_current_context_id(project_root)
|
|
237
|
-
|
|
238
|
-
threshold = ("critical" if percent_remaining <= CRITICAL_CONTEXT_THRESHOLD
|
|
239
|
-
else "prepare" if percent_remaining <= HANDOFF_PREPARE_THRESHOLD
|
|
240
|
-
else "suggest")
|
|
241
|
-
log_diagnostic("context_monitor", "decide", f"Threshold={threshold} at {percent_remaining}%",
|
|
242
|
-
decision=threshold, reasoning=f"{percent_remaining}% remaining",
|
|
243
|
-
inputs={"context_id": context_id, "percent_remaining": percent_remaining})
|
|
244
|
-
|
|
245
|
-
return get_context_warning(percent_remaining, tokens_used, max_tokens, context_id, tool_name)
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
def main():
|
|
249
|
-
"""Main entry point for PostToolUse hook."""
|
|
250
|
-
try:
|
|
251
|
-
hook_input = load_hook_input()
|
|
252
|
-
if not hook_input:
|
|
253
|
-
return
|
|
254
|
-
|
|
255
|
-
check_and_transition_mode(hook_input)
|
|
256
|
-
|
|
257
|
-
warning = check_context_level(hook_input)
|
|
258
|
-
if warning:
|
|
259
|
-
emit_context(warning)
|
|
260
|
-
|
|
261
|
-
except Exception as e:
|
|
262
|
-
import traceback
|
|
263
|
-
tb = traceback.format_exc()
|
|
264
|
-
from lib.base.hook_utils import log_hook_error
|
|
265
|
-
log_hook_error("context_monitor", e, "PostToolUse", traceback_str=tb)
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if __name__ == "__main__":
|
|
269
|
-
from lib.base.hook_utils import run_hook
|
|
270
|
-
run_hook(main, "context_monitor")
|