cap-pro 1.0.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/.claude-plugin/README.md +26 -0
- package/.claude-plugin/marketplace.json +24 -0
- package/.claude-plugin/plugin.json +24 -0
- package/LICENSE +21 -0
- package/README.ja-JP.md +834 -0
- package/README.ko-KR.md +823 -0
- package/README.md +806 -0
- package/README.pt-BR.md +452 -0
- package/README.zh-CN.md +800 -0
- package/agents/cap-architect.md +269 -0
- package/agents/cap-brainstormer.md +207 -0
- package/agents/cap-curator.md +276 -0
- package/agents/cap-debugger.md +365 -0
- package/agents/cap-designer.md +246 -0
- package/agents/cap-historian.md +464 -0
- package/agents/cap-migrator.md +291 -0
- package/agents/cap-prototyper.md +197 -0
- package/agents/cap-validator.md +308 -0
- package/bin/install.js +5433 -0
- package/cap/bin/cap-tools.cjs +853 -0
- package/cap/bin/lib/arc-scanner.cjs +344 -0
- package/cap/bin/lib/cap-affinity-engine.cjs +862 -0
- package/cap/bin/lib/cap-anchor.cjs +228 -0
- package/cap/bin/lib/cap-annotation-writer.cjs +340 -0
- package/cap/bin/lib/cap-checkpoint.cjs +434 -0
- package/cap/bin/lib/cap-cluster-detect.cjs +945 -0
- package/cap/bin/lib/cap-cluster-display.cjs +52 -0
- package/cap/bin/lib/cap-cluster-format.cjs +245 -0
- package/cap/bin/lib/cap-cluster-helpers.cjs +295 -0
- package/cap/bin/lib/cap-cluster-io.cjs +212 -0
- package/cap/bin/lib/cap-completeness.cjs +540 -0
- package/cap/bin/lib/cap-deps.cjs +583 -0
- package/cap/bin/lib/cap-design-families.cjs +332 -0
- package/cap/bin/lib/cap-design.cjs +966 -0
- package/cap/bin/lib/cap-divergence-detector.cjs +400 -0
- package/cap/bin/lib/cap-doctor.cjs +752 -0
- package/cap/bin/lib/cap-feature-map-internals.cjs +19 -0
- package/cap/bin/lib/cap-feature-map-migrate.cjs +335 -0
- package/cap/bin/lib/cap-feature-map-monorepo.cjs +885 -0
- package/cap/bin/lib/cap-feature-map-shard.cjs +315 -0
- package/cap/bin/lib/cap-feature-map.cjs +1943 -0
- package/cap/bin/lib/cap-fitness-score.cjs +1075 -0
- package/cap/bin/lib/cap-impact-analysis.cjs +652 -0
- package/cap/bin/lib/cap-learn-review.cjs +1072 -0
- package/cap/bin/lib/cap-learning-signals.cjs +627 -0
- package/cap/bin/lib/cap-loader.cjs +227 -0
- package/cap/bin/lib/cap-logger.cjs +57 -0
- package/cap/bin/lib/cap-memory-bridge.cjs +764 -0
- package/cap/bin/lib/cap-memory-confidence.cjs +452 -0
- package/cap/bin/lib/cap-memory-dir.cjs +987 -0
- package/cap/bin/lib/cap-memory-engine.cjs +698 -0
- package/cap/bin/lib/cap-memory-extends.cjs +398 -0
- package/cap/bin/lib/cap-memory-graph.cjs +790 -0
- package/cap/bin/lib/cap-memory-migrate.cjs +2015 -0
- package/cap/bin/lib/cap-memory-pin.cjs +183 -0
- package/cap/bin/lib/cap-memory-platform.cjs +490 -0
- package/cap/bin/lib/cap-memory-prune.cjs +707 -0
- package/cap/bin/lib/cap-memory-schema.cjs +812 -0
- package/cap/bin/lib/cap-migrate-tags.cjs +309 -0
- package/cap/bin/lib/cap-migrate.cjs +540 -0
- package/cap/bin/lib/cap-pattern-apply.cjs +1203 -0
- package/cap/bin/lib/cap-pattern-pipeline.cjs +1034 -0
- package/cap/bin/lib/cap-plugin-manifest.cjs +80 -0
- package/cap/bin/lib/cap-realtime-affinity.cjs +399 -0
- package/cap/bin/lib/cap-reconcile.cjs +570 -0
- package/cap/bin/lib/cap-research-gate.cjs +218 -0
- package/cap/bin/lib/cap-scope-filter.cjs +402 -0
- package/cap/bin/lib/cap-semantic-pipeline.cjs +1038 -0
- package/cap/bin/lib/cap-session-extract.cjs +987 -0
- package/cap/bin/lib/cap-session.cjs +445 -0
- package/cap/bin/lib/cap-snapshot-linkage.cjs +963 -0
- package/cap/bin/lib/cap-stack-docs.cjs +646 -0
- package/cap/bin/lib/cap-tag-observer.cjs +371 -0
- package/cap/bin/lib/cap-tag-scanner.cjs +1766 -0
- package/cap/bin/lib/cap-telemetry.cjs +466 -0
- package/cap/bin/lib/cap-test-audit.cjs +1438 -0
- package/cap/bin/lib/cap-thread-migrator.cjs +307 -0
- package/cap/bin/lib/cap-thread-synthesis.cjs +545 -0
- package/cap/bin/lib/cap-thread-tracker.cjs +519 -0
- package/cap/bin/lib/cap-trace.cjs +399 -0
- package/cap/bin/lib/cap-trust-mode.cjs +336 -0
- package/cap/bin/lib/cap-ui-design-editor.cjs +642 -0
- package/cap/bin/lib/cap-ui-mind-map.cjs +712 -0
- package/cap/bin/lib/cap-ui-thread-nav.cjs +693 -0
- package/cap/bin/lib/cap-ui.cjs +1245 -0
- package/cap/bin/lib/cap-upgrade.cjs +1028 -0
- package/cap/bin/lib/cli/arg-helpers.cjs +49 -0
- package/cap/bin/lib/cli/frontmatter-router.cjs +31 -0
- package/cap/bin/lib/cli/init-router.cjs +68 -0
- package/cap/bin/lib/cli/phase-router.cjs +102 -0
- package/cap/bin/lib/cli/state-router.cjs +61 -0
- package/cap/bin/lib/cli/template-router.cjs +37 -0
- package/cap/bin/lib/cli/uat-router.cjs +29 -0
- package/cap/bin/lib/cli/validation-router.cjs +26 -0
- package/cap/bin/lib/cli/verification-router.cjs +31 -0
- package/cap/bin/lib/cli/workstream-router.cjs +39 -0
- package/cap/bin/lib/commands.cjs +961 -0
- package/cap/bin/lib/config.cjs +467 -0
- package/cap/bin/lib/convention-reader.cjs +258 -0
- package/cap/bin/lib/core.cjs +1241 -0
- package/cap/bin/lib/feature-aggregator.cjs +423 -0
- package/cap/bin/lib/frontmatter.cjs +337 -0
- package/cap/bin/lib/init.cjs +1443 -0
- package/cap/bin/lib/manifest-generator.cjs +383 -0
- package/cap/bin/lib/milestone.cjs +253 -0
- package/cap/bin/lib/model-profiles.cjs +69 -0
- package/cap/bin/lib/monorepo-context.cjs +226 -0
- package/cap/bin/lib/monorepo-migrator.cjs +509 -0
- package/cap/bin/lib/phase.cjs +889 -0
- package/cap/bin/lib/profile-output.cjs +989 -0
- package/cap/bin/lib/profile-pipeline.cjs +540 -0
- package/cap/bin/lib/roadmap.cjs +330 -0
- package/cap/bin/lib/security.cjs +394 -0
- package/cap/bin/lib/session-manager.cjs +292 -0
- package/cap/bin/lib/skeleton-generator.cjs +179 -0
- package/cap/bin/lib/state.cjs +1032 -0
- package/cap/bin/lib/template.cjs +231 -0
- package/cap/bin/lib/test-detector.cjs +62 -0
- package/cap/bin/lib/uat.cjs +283 -0
- package/cap/bin/lib/verify.cjs +889 -0
- package/cap/bin/lib/workspace-detector.cjs +371 -0
- package/cap/bin/lib/workstream.cjs +492 -0
- package/cap/commands/gsd/workstreams.md +63 -0
- package/cap/references/arc-standard.md +315 -0
- package/cap/references/cap-agent-architecture.md +101 -0
- package/cap/references/cap-gitignore-template +9 -0
- package/cap/references/cap-zero-deps.md +158 -0
- package/cap/references/checkpoints.md +778 -0
- package/cap/references/continuation-format.md +249 -0
- package/cap/references/contract-test-templates.md +312 -0
- package/cap/references/feature-map-template.md +25 -0
- package/cap/references/git-integration.md +295 -0
- package/cap/references/git-planning-commit.md +38 -0
- package/cap/references/model-profiles.md +174 -0
- package/cap/references/phase-numbering.md +126 -0
- package/cap/references/planning-config.md +202 -0
- package/cap/references/property-test-templates.md +316 -0
- package/cap/references/security-test-templates.md +347 -0
- package/cap/references/session-template.json +8 -0
- package/cap/references/tdd.md +263 -0
- package/cap/references/user-profiling.md +681 -0
- package/cap/references/verification-patterns.md +612 -0
- package/cap/templates/UAT.md +265 -0
- package/cap/templates/claude-md.md +175 -0
- package/cap/templates/codebase/architecture.md +255 -0
- package/cap/templates/codebase/concerns.md +310 -0
- package/cap/templates/codebase/conventions.md +307 -0
- package/cap/templates/codebase/integrations.md +280 -0
- package/cap/templates/codebase/stack.md +186 -0
- package/cap/templates/codebase/structure.md +285 -0
- package/cap/templates/codebase/testing.md +480 -0
- package/cap/templates/config.json +44 -0
- package/cap/templates/context.md +352 -0
- package/cap/templates/continue-here.md +78 -0
- package/cap/templates/copilot-instructions.md +7 -0
- package/cap/templates/debug-subagent-prompt.md +91 -0
- package/cap/templates/discussion-log.md +63 -0
- package/cap/templates/milestone-archive.md +123 -0
- package/cap/templates/milestone.md +115 -0
- package/cap/templates/phase-prompt.md +610 -0
- package/cap/templates/planner-subagent-prompt.md +117 -0
- package/cap/templates/project.md +186 -0
- package/cap/templates/requirements.md +231 -0
- package/cap/templates/research-project/ARCHITECTURE.md +204 -0
- package/cap/templates/research-project/FEATURES.md +147 -0
- package/cap/templates/research-project/PITFALLS.md +200 -0
- package/cap/templates/research-project/STACK.md +120 -0
- package/cap/templates/research-project/SUMMARY.md +170 -0
- package/cap/templates/research.md +552 -0
- package/cap/templates/roadmap.md +202 -0
- package/cap/templates/state.md +176 -0
- package/cap/templates/summary.md +364 -0
- package/cap/templates/user-preferences.md +498 -0
- package/cap/templates/verification-report.md +322 -0
- package/cap/workflows/add-phase.md +112 -0
- package/cap/workflows/add-tests.md +351 -0
- package/cap/workflows/add-todo.md +158 -0
- package/cap/workflows/audit-milestone.md +340 -0
- package/cap/workflows/audit-uat.md +109 -0
- package/cap/workflows/autonomous.md +891 -0
- package/cap/workflows/check-todos.md +177 -0
- package/cap/workflows/cleanup.md +152 -0
- package/cap/workflows/complete-milestone.md +767 -0
- package/cap/workflows/diagnose-issues.md +231 -0
- package/cap/workflows/discovery-phase.md +289 -0
- package/cap/workflows/discuss-phase-assumptions.md +653 -0
- package/cap/workflows/discuss-phase.md +1049 -0
- package/cap/workflows/do.md +104 -0
- package/cap/workflows/execute-phase.md +846 -0
- package/cap/workflows/execute-plan.md +514 -0
- package/cap/workflows/fast.md +105 -0
- package/cap/workflows/forensics.md +265 -0
- package/cap/workflows/health.md +181 -0
- package/cap/workflows/help.md +660 -0
- package/cap/workflows/insert-phase.md +130 -0
- package/cap/workflows/list-phase-assumptions.md +178 -0
- package/cap/workflows/list-workspaces.md +56 -0
- package/cap/workflows/manager.md +362 -0
- package/cap/workflows/map-codebase.md +377 -0
- package/cap/workflows/milestone-summary.md +223 -0
- package/cap/workflows/new-milestone.md +486 -0
- package/cap/workflows/new-project.md +1250 -0
- package/cap/workflows/new-workspace.md +237 -0
- package/cap/workflows/next.md +97 -0
- package/cap/workflows/node-repair.md +92 -0
- package/cap/workflows/note.md +156 -0
- package/cap/workflows/pause-work.md +176 -0
- package/cap/workflows/plan-milestone-gaps.md +273 -0
- package/cap/workflows/plan-phase.md +857 -0
- package/cap/workflows/plant-seed.md +169 -0
- package/cap/workflows/pr-branch.md +129 -0
- package/cap/workflows/profile-user.md +449 -0
- package/cap/workflows/progress.md +507 -0
- package/cap/workflows/quick.md +757 -0
- package/cap/workflows/remove-phase.md +155 -0
- package/cap/workflows/remove-workspace.md +90 -0
- package/cap/workflows/research-phase.md +82 -0
- package/cap/workflows/resume-project.md +326 -0
- package/cap/workflows/review.md +228 -0
- package/cap/workflows/session-report.md +146 -0
- package/cap/workflows/settings.md +283 -0
- package/cap/workflows/ship.md +228 -0
- package/cap/workflows/stats.md +60 -0
- package/cap/workflows/transition.md +671 -0
- package/cap/workflows/ui-phase.md +298 -0
- package/cap/workflows/ui-review.md +161 -0
- package/cap/workflows/update.md +323 -0
- package/cap/workflows/validate-phase.md +170 -0
- package/cap/workflows/verify-phase.md +254 -0
- package/cap/workflows/verify-work.md +637 -0
- package/commands/cap/annotate.md +165 -0
- package/commands/cap/brainstorm.md +393 -0
- package/commands/cap/checkpoint.md +106 -0
- package/commands/cap/completeness.md +94 -0
- package/commands/cap/continue.md +72 -0
- package/commands/cap/debug.md +588 -0
- package/commands/cap/deps.md +169 -0
- package/commands/cap/design.md +479 -0
- package/commands/cap/init.md +354 -0
- package/commands/cap/iterate.md +249 -0
- package/commands/cap/learn.md +459 -0
- package/commands/cap/memory.md +275 -0
- package/commands/cap/migrate-feature-map.md +91 -0
- package/commands/cap/migrate-memory.md +108 -0
- package/commands/cap/migrate-tags.md +91 -0
- package/commands/cap/migrate.md +131 -0
- package/commands/cap/prototype.md +510 -0
- package/commands/cap/reconcile.md +121 -0
- package/commands/cap/review.md +360 -0
- package/commands/cap/save.md +72 -0
- package/commands/cap/scan.md +404 -0
- package/commands/cap/start.md +356 -0
- package/commands/cap/status.md +118 -0
- package/commands/cap/test-audit.md +262 -0
- package/commands/cap/test.md +394 -0
- package/commands/cap/trace.md +133 -0
- package/commands/cap/ui.md +167 -0
- package/hooks/dist/cap-check-update.js +115 -0
- package/hooks/dist/cap-context-monitor.js +185 -0
- package/hooks/dist/cap-learn-review-hook.js +114 -0
- package/hooks/dist/cap-learning-hook.js +192 -0
- package/hooks/dist/cap-memory.js +299 -0
- package/hooks/dist/cap-prompt-guard.js +97 -0
- package/hooks/dist/cap-statusline.js +157 -0
- package/hooks/dist/cap-tag-observer.js +115 -0
- package/hooks/dist/cap-version-check.js +112 -0
- package/hooks/dist/cap-workflow-guard.js +175 -0
- package/hooks/hooks.json +55 -0
- package/package.json +58 -0
- package/scripts/base64-scan.sh +262 -0
- package/scripts/build-hooks.js +93 -0
- package/scripts/cap-removal-checklist.md +202 -0
- package/scripts/prompt-injection-scan.sh +199 -0
- package/scripts/run-tests.cjs +181 -0
- package/scripts/secret-scan.sh +227 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cap:trace
|
|
3
|
+
description: "Print the call graph for a single acceptance criterion across all contributing files. Resolves primary file via @cap-feature(...primary:true) or tag-density heuristic."
|
|
4
|
+
argument-hint: "<AC-N | F-NNN/AC-N> [--depth N] [--restrict] [--json]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Bash
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<!-- @cap-context CAP v2.0 trace command -- shows where an AC is implemented across files and what it depends on. Read-only. -->
|
|
11
|
+
<!-- @cap-decision Trace is read-only -- no Feature Map writes, no session updates. Safe to run repeatedly. -->
|
|
12
|
+
<!-- @cap-feature(feature:F-045) /cap:trace renders TraceResult from cap-trace.cjs as a tree. -->
|
|
13
|
+
<!-- @cap-todo(ac:F-045/AC-4) /cap:trace AC-N shall print the call graph from the primary file across referenced files for a given acceptance criterion. -->
|
|
14
|
+
|
|
15
|
+
<objective>
|
|
16
|
+
Print a structured trace for a single acceptance criterion:
|
|
17
|
+
|
|
18
|
+
- Which file is the primary implementation (designated via `@cap-feature(...primary:true)` or inferred via tag density)
|
|
19
|
+
- Which other files contribute to the same AC
|
|
20
|
+
- Static call graph (require/import edges) walked outward from the primary file up to N hops
|
|
21
|
+
|
|
22
|
+
This makes multi-file ACs navigable when a single AC spans more than one source file.
|
|
23
|
+
|
|
24
|
+
**Argument formats:**
|
|
25
|
+
- `F-NNN/AC-M` -- fully qualified
|
|
26
|
+
- `AC-M` -- short form, requires an active feature in `.cap/SESSION.json`
|
|
27
|
+
|
|
28
|
+
**Flags:**
|
|
29
|
+
- `--depth N` -- max BFS depth for the call graph (default 3)
|
|
30
|
+
- `--restrict` -- only follow edges into other AC-contributing files (suppresses non-AC neighbors)
|
|
31
|
+
- `--json` -- emit raw `TraceResult` JSON instead of formatted output
|
|
32
|
+
</objective>
|
|
33
|
+
|
|
34
|
+
<context>
|
|
35
|
+
$ARGUMENTS
|
|
36
|
+
|
|
37
|
+
@.cap/SESSION.json
|
|
38
|
+
</context>
|
|
39
|
+
|
|
40
|
+
<process>
|
|
41
|
+
|
|
42
|
+
## Step 0: Parse arguments
|
|
43
|
+
|
|
44
|
+
Read `$ARGUMENTS`. Extract:
|
|
45
|
+
- The first non-flag token as `ac_arg`
|
|
46
|
+
- `--depth N` -> `depth` (integer, default 3)
|
|
47
|
+
- `--restrict` -> `restrict_to_ac_files` (boolean)
|
|
48
|
+
- `--json` -> `json_output` (boolean)
|
|
49
|
+
|
|
50
|
+
If `ac_arg` is missing, print usage and stop:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
Usage: /cap:trace <AC-N | F-NNN/AC-N> [--depth N] [--restrict] [--json]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Step 1: Resolve AC reference
|
|
57
|
+
|
|
58
|
+
If `ac_arg` already contains `/` (e.g. `F-045/AC-4`), use it as-is.
|
|
59
|
+
|
|
60
|
+
Otherwise, read the active feature from SESSION.json and prefix it:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
node -e "
|
|
64
|
+
const session = require('./cap/bin/lib/cap-session.cjs');
|
|
65
|
+
const s = session.loadSession(process.cwd());
|
|
66
|
+
console.log(s.activeFeature || '');
|
|
67
|
+
"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
If the active feature is empty, print:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
No active feature in SESSION.json. Pass a fully-qualified AC reference like F-045/AC-1, or run /cap:start to set an active feature.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
and stop. Otherwise build `ac_ref = "<activeFeature>/<ac_arg>"`.
|
|
77
|
+
|
|
78
|
+
## Step 2: Run the trace
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
node -e "
|
|
82
|
+
const tr = require('./cap/bin/lib/cap-trace.cjs');
|
|
83
|
+
const acRef = process.argv[1];
|
|
84
|
+
const _parsedDepth = parseInt(process.argv[2], 10);
|
|
85
|
+
const depth = Number.isFinite(_parsedDepth) ? _parsedDepth : tr.DEFAULT_MAX_DEPTH;
|
|
86
|
+
const restrict = process.argv[3] === 'true';
|
|
87
|
+
const result = tr.traceAc(process.cwd(), acRef, { maxDepth: depth, restrictToAcFiles: restrict });
|
|
88
|
+
const json = process.argv[4] === 'true';
|
|
89
|
+
if (json) {
|
|
90
|
+
console.log(JSON.stringify(result, null, 2));
|
|
91
|
+
} else {
|
|
92
|
+
console.log(tr.formatTraceResult(result));
|
|
93
|
+
}
|
|
94
|
+
" '<AC_REF>' '<DEPTH>' '<RESTRICT>' '<JSON>'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Display the output verbatim.
|
|
98
|
+
|
|
99
|
+
## Step 2b: Append Design-Usage for the AC's feature (F-063)
|
|
100
|
+
|
|
101
|
+
<!-- @cap-todo(ac:F-063/AC-5) /cap:trace shall emit a Design-Usage line per feature whose usesDesign is non-empty. -->
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
node -e "
|
|
105
|
+
const fm = require('./cap/bin/lib/cap-feature-map.cjs');
|
|
106
|
+
const d = require('./cap/bin/lib/cap-design.cjs');
|
|
107
|
+
const trace = require('./cap/bin/lib/cap-trace.cjs');
|
|
108
|
+
const featureId = process.argv[1];
|
|
109
|
+
// @cap-todo(ac:F-081/AC-4 iter:2) Migrated to {safe: true} opt-in to preserve CLI on duplicate-ID FEATURE-MAP.
|
|
110
|
+
// @cap-decision(F-081/iter2) Warn on parseError; continue with partial map for read-only display.
|
|
111
|
+
const map = fm.readFeatureMap(process.cwd(), undefined, { safe: true });
|
|
112
|
+
if (map && map.parseError) {
|
|
113
|
+
console.warn('cap: trace — duplicate feature ID detected, lookup uses partial map: ' + String(map.parseError.message).trim());
|
|
114
|
+
}
|
|
115
|
+
const feature = map.features.find(f => f.id === featureId);
|
|
116
|
+
if (!feature) process.exit(0);
|
|
117
|
+
const design = d.readDesignMd(process.cwd());
|
|
118
|
+
const designIdx = design ? d.parseDesignIds(design) : { byToken: {}, byComponent: {} };
|
|
119
|
+
const line = trace.formatDesignUsage(feature, designIdx);
|
|
120
|
+
if (line) console.log('Design-Usage: ' + line);
|
|
121
|
+
" '<FEATURE_ID>'
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Step 3: Suggest next action (only when not in --json mode)
|
|
125
|
+
|
|
126
|
+
Inspect the trace result and suggest one of:
|
|
127
|
+
|
|
128
|
+
- If `primary.role === 'inferred'` AND `allFiles.length > 1` -> "Designate the canonical file with `@cap-feature(feature:<id>, primary:true)` to suppress the heuristic warning."
|
|
129
|
+
- If `allFiles.length === 0` -> "No tags reference this AC. Add `@cap-todo(ac:<ref>)` at the implementation site, then re-run /cap:scan."
|
|
130
|
+
- If `callGraph.length === 0` AND `primary.file` is set -> "Primary file has no resolvable internal imports. The AC may be self-contained, or imports use dynamic require() / TS path aliases that the static walker cannot resolve."
|
|
131
|
+
- Otherwise -> "Trace looks complete."
|
|
132
|
+
|
|
133
|
+
</process>
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cap:ui
|
|
3
|
+
description: CAP-UI local read-only server (--serve, default) or standalone HTML snapshot (--share) of Feature Map, Memory, Threads and DESIGN.md. Optional --editable unlocks DESIGN.md write endpoints (F-068). Zero deps. Node-builtin http + SSE only.
|
|
4
|
+
argument-hint: "[--serve | --share] [--port N] [--editable]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Bash
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<!-- @cap-context CAP F-065 /cap:ui command — orchestrates cap-ui.cjs. No agent is spawned: this is deterministic infrastructure with no wizard. -->
|
|
13
|
+
<!-- @cap-context CAP F-068 adds a --editable flag to --serve. It unlocks a DESIGN.md-only write surface (color picker, spacing sliders, component variants). FEATURE-MAP.md and Memory stay read-only even in edit mode. -->
|
|
14
|
+
<!-- @cap-decision Command layer parses flags and calls cap-ui.cjs directly. No Task()/agent spawn — there is nothing conversational here. -->
|
|
15
|
+
<!-- @cap-decision --serve is the default because developers running /cap:ui interactively expect a browser view, not a snapshot-only export. -->
|
|
16
|
+
<!-- @cap-decision(F-068) --editable is opt-in. Default --serve stays fully read-only to match F-065/AC-5. --share never embeds the edit UI (no server to write to). -->
|
|
17
|
+
<!-- @cap-constraint Zero external deps. Do not introduce npm packages at any point in this command. -->
|
|
18
|
+
<!-- @cap-constraint SECURITY: --editable exposes DESIGN.md PUT/DELETE endpoints on localhost. Do NOT use on shared hosts, tunnels (ngrok), or anywhere the loopback is reachable by other users. -->
|
|
19
|
+
|
|
20
|
+
<!-- @cap-feature(feature:F-065) CAP-UI Core — Local Server + Static Export -->
|
|
21
|
+
<!-- @cap-feature(feature:F-068) CAP-UI Visual Design Editor — DESIGN.md-only edit surface -->
|
|
22
|
+
|
|
23
|
+
<objective>
|
|
24
|
+
<!-- @cap-todo(ac:F-065/AC-1) /cap:ui --serve starts local Node http server on configurable port (default 4747), zero deps, node builtins only. -->
|
|
25
|
+
<!-- @cap-todo(ac:F-065/AC-2) The served UI renders Feature-Map + Memory + Threads + DESIGN.md as a readable HTML view. -->
|
|
26
|
+
<!-- @cap-todo(ac:F-065/AC-3) File-watcher observes FEATURE-MAP.md, DESIGN.md, .cap/memory/, .cap/SESSION.json → UI refreshes via SSE. -->
|
|
27
|
+
<!-- @cap-todo(ac:F-065/AC-4) /cap:ui --share generates standalone HTML snapshot at .cap/ui/snapshot.html with inline CSS/JS. -->
|
|
28
|
+
<!-- @cap-todo(ac:F-065/AC-5) UI is read-only for Feature-Map and Memory; DESIGN.md edit capability is introduced in F-068. -->
|
|
29
|
+
<!-- @cap-todo(ac:F-065/AC-6) Server logs all events (start, SSE connect, file changes) on stdout with ISO timestamps. -->
|
|
30
|
+
|
|
31
|
+
Runs one of two flows:
|
|
32
|
+
- `--serve` (default) — starts a local HTTP server on port 4747 (or `--port N`) that renders the Feature Map, Memory, Threads, and DESIGN.md. Broadcasts file changes to the browser via Server-Sent Events.
|
|
33
|
+
- `--share` — writes a standalone HTML snapshot to `.cap/ui/snapshot.html` with inline CSS/JS, safe to share via PR/Slack.
|
|
34
|
+
|
|
35
|
+
Optional flags:
|
|
36
|
+
- `--editable` (F-068, only valid with `--serve`) — unlocks the DESIGN.md Visual Editor:
|
|
37
|
+
- Color-pickers for each DT-NNN color token
|
|
38
|
+
- Range sliders for spacing + typography scales
|
|
39
|
+
- Variant add/remove per DC-NNN component
|
|
40
|
+
- Edits are atomic (temp+rename), Git-friendly (single-line diffs), and restricted to DESIGN.md.
|
|
41
|
+
- FEATURE-MAP.md and Memory remain read-only even in edit mode.
|
|
42
|
+
- **Security**: write endpoints are exposed on localhost. Do not use on shared hosts, tunnels, or multi-user machines.
|
|
43
|
+
|
|
44
|
+
**Key guarantees:**
|
|
45
|
+
- Zero runtime dependencies. Only `node:` builtins (`http`, `fs`, `path`, `url`, `os`).
|
|
46
|
+
- Read-only by default: no POST/PUT/DELETE routes, no forms, no edit endpoints without `--editable` (F-065/AC-5).
|
|
47
|
+
- `--editable` only unlocks `/api/design/*` PUT/DELETE; FEATURE-MAP + Memory always 405 on writes (F-068/AC-6).
|
|
48
|
+
- SSE-only for live updates (no WebSockets).
|
|
49
|
+
- Port auto-increments up to +10 if the default is taken; fails loudly otherwise.
|
|
50
|
+
- `--share` snapshot contains no external URLs (no Google Fonts, no CDN, no fetch calls) — safe for offline sharing.
|
|
51
|
+
- `--share` NEVER embeds the edit UI, even when run in a session where `--editable` was set — snapshots are static and have no server to write against.
|
|
52
|
+
|
|
53
|
+
</objective>
|
|
54
|
+
|
|
55
|
+
<context>
|
|
56
|
+
$ARGUMENTS
|
|
57
|
+
|
|
58
|
+
@FEATURE-MAP.md
|
|
59
|
+
</context>
|
|
60
|
+
|
|
61
|
+
<process>
|
|
62
|
+
|
|
63
|
+
## Step 0: Parse flags
|
|
64
|
+
|
|
65
|
+
<!-- @cap-todo(ac:F-065/AC-1) Parse --serve (default), --share, --port N from $ARGUMENTS. -->
|
|
66
|
+
<!-- @cap-todo(ac:F-068/AC-1) Parse --editable flag. Only valid with --serve; --share ignores it. -->
|
|
67
|
+
|
|
68
|
+
Inspect `$ARGUMENTS`:
|
|
69
|
+
- `--share` — set `mode = "share"`
|
|
70
|
+
- `--serve` — set `mode = "serve"`
|
|
71
|
+
- `--port N` — capture `port` (integer)
|
|
72
|
+
- `--editable` — set `editable = true` (ignored with `--share`)
|
|
73
|
+
- If no mode flag, default to `mode = "serve"`
|
|
74
|
+
|
|
75
|
+
Log: `cap:ui | mode: {mode}`{ " | port: " + port if mode === 'serve' && port }{ " | editable" if editable }
|
|
76
|
+
|
|
77
|
+
## Step 1: Dispatch by mode
|
|
78
|
+
|
|
79
|
+
### Step 1a: --share (snapshot flow)
|
|
80
|
+
|
|
81
|
+
<!-- @cap-todo(ac:F-065/AC-4) --share writes .cap/ui/snapshot.html via createSnapshot(). -->
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
node -e "
|
|
85
|
+
const ui = require('./cap/bin/lib/cap-ui.cjs');
|
|
86
|
+
const out = ui.createSnapshot({ projectRoot: process.cwd() });
|
|
87
|
+
console.log(JSON.stringify(out));
|
|
88
|
+
"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Report:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
cap:ui --share complete.
|
|
95
|
+
|
|
96
|
+
Snapshot: .cap/ui/snapshot.html
|
|
97
|
+
Bytes: {bytes}
|
|
98
|
+
|
|
99
|
+
Next steps:
|
|
100
|
+
- Open .cap/ui/snapshot.html in any browser (no server required).
|
|
101
|
+
- Share via PR comment / Slack DM — inline CSS/JS, no external fetch.
|
|
102
|
+
- Re-run /cap:ui --share to refresh.
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Stop here. Do not run Step 1b.
|
|
106
|
+
|
|
107
|
+
### Step 1b: --serve (live server flow)
|
|
108
|
+
|
|
109
|
+
<!-- @cap-todo(ac:F-065/AC-1) --serve starts the local HTTP server. -->
|
|
110
|
+
<!-- @cap-todo(ac:F-065/AC-3) File watcher attached, SSE broadcasts change events to browser clients. -->
|
|
111
|
+
<!-- @cap-todo(ac:F-065/AC-6) Every server event is logged to stdout via cap-ui.logEvent. -->
|
|
112
|
+
|
|
113
|
+
Start the server in the foreground and print the URL. The server blocks until the user hits Ctrl+C (SIGINT).
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
node -e "
|
|
117
|
+
const ui = require('./cap/bin/lib/cap-ui.cjs');
|
|
118
|
+
const port = {PORT_OR_DEFAULT};
|
|
119
|
+
const editable = {EDITABLE};
|
|
120
|
+
(async function(){
|
|
121
|
+
const { url, port: actual, stop } = await ui.startServer({ projectRoot: process.cwd(), port, editable });
|
|
122
|
+
console.log('cap:ui listening on ' + url + ' (port ' + actual + ')' + (editable ? ' [EDITABLE — DESIGN.md writes enabled]' : ''));
|
|
123
|
+
console.log('Ctrl+C to stop.');
|
|
124
|
+
process.on('SIGINT', async function(){
|
|
125
|
+
console.log('\\ncap:ui stopping…');
|
|
126
|
+
await stop();
|
|
127
|
+
process.exit(0);
|
|
128
|
+
});
|
|
129
|
+
})().catch(function(err){ console.error('cap:ui server failed:', err.message); process.exit(1); });
|
|
130
|
+
"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Final report appears only after the user stops the server (Ctrl+C):
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
cap:ui --serve stopped.
|
|
137
|
+
Port: {actual_port}
|
|
138
|
+
Session saved: .cap/SESSION.json (lastCommand updated)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Step 2: Update session state (both modes, fire-and-forget)
|
|
142
|
+
|
|
143
|
+
<!-- @cap-decision Session write happens after the main action so a failing --serve still reports the attempt. -->
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
node -e "
|
|
147
|
+
const session = require('./cap/bin/lib/cap-session.cjs');
|
|
148
|
+
session.updateSession(process.cwd(), {
|
|
149
|
+
lastCommand: '/cap:ui',
|
|
150
|
+
lastCommandTimestamp: new Date().toISOString(),
|
|
151
|
+
step: 'ui-{mode}'
|
|
152
|
+
});
|
|
153
|
+
"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
</process>
|
|
157
|
+
|
|
158
|
+
## Notes
|
|
159
|
+
|
|
160
|
+
- **No agent is spawned.** cap:ui is pure infrastructure — no LLM call is required.
|
|
161
|
+
- **Mind-Map (F-066), Thread Navigator (F-067), DESIGN.md editor (F-068)** are all shipped.
|
|
162
|
+
- **Port conflicts**: if 4747 is taken, the server auto-tries 4748, 4749, …, up to 4757; then fails with a clear error.
|
|
163
|
+
- **File watcher caveats**: `fs.watch` is platform-specific. Linux fires multiple events per write (inotify); macOS coalesces via FSEvents. A 100ms debounce smooths the difference.
|
|
164
|
+
- **Read-only guarantee (default)**: the HTML has no `<form>`, no POST targets, and the server rejects non-GET/HEAD methods with 405. Only `--editable` unlocks PUT/DELETE on `/api/design/*`.
|
|
165
|
+
- **Edit scope (F-068/AC-6)**: `--editable` is strictly DESIGN.md. FEATURE-MAP.md and Memory endpoints always respond 405 on writes — collaboration there stays Git-based.
|
|
166
|
+
- **Atomic writes (F-068/AC-5)**: each edit writes a temp file and renames it over DESIGN.md, so a crashed process never leaves a truncated file.
|
|
167
|
+
- **Path-traversal guard (F-068)**: `createSnapshot(outputPath)` now refuses paths that escape the project root; the same guard gates DESIGN.md edits.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @cap-feature(feature:F-009) Hooks System — background update checker (SessionStart hook)
|
|
3
|
+
// cap-hook-version: {{CAP_VERSION}}
|
|
4
|
+
// Check for CAP updates in background, write result to cache
|
|
5
|
+
// Called by SessionStart hook - runs once per session
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const { spawn } = require('child_process');
|
|
11
|
+
|
|
12
|
+
const homeDir = os.homedir();
|
|
13
|
+
const cwd = process.cwd();
|
|
14
|
+
|
|
15
|
+
// Detect runtime config directory (supports Claude, OpenCode, Gemini)
|
|
16
|
+
// Respects CLAUDE_CONFIG_DIR for custom config directory setups
|
|
17
|
+
function detectConfigDir(baseDir) {
|
|
18
|
+
// Check env override first (supports multi-account setups)
|
|
19
|
+
const envDir = process.env.CLAUDE_CONFIG_DIR;
|
|
20
|
+
if (envDir && fs.existsSync(path.join(envDir, 'cap', 'VERSION'))) {
|
|
21
|
+
return envDir;
|
|
22
|
+
}
|
|
23
|
+
for (const dir of ['.config/opencode', '.opencode', '.gemini', '.claude']) {
|
|
24
|
+
if (fs.existsSync(path.join(baseDir, dir, 'cap', 'VERSION'))) {
|
|
25
|
+
return path.join(baseDir, dir);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return envDir || path.join(baseDir, '.claude');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const globalConfigDir = detectConfigDir(homeDir);
|
|
32
|
+
const projectConfigDir = detectConfigDir(cwd);
|
|
33
|
+
const cacheDir = path.join(globalConfigDir, 'cache');
|
|
34
|
+
const cacheFile = path.join(cacheDir, 'cap-update-check.json');
|
|
35
|
+
|
|
36
|
+
// VERSION file locations (check project first, then global)
|
|
37
|
+
const projectVersionFile = path.join(projectConfigDir, 'cap', 'VERSION');
|
|
38
|
+
const globalVersionFile = path.join(globalConfigDir, 'cap', 'VERSION');
|
|
39
|
+
|
|
40
|
+
// Ensure cache directory exists
|
|
41
|
+
if (!fs.existsSync(cacheDir)) {
|
|
42
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Run check in background (spawn background process, windowsHide prevents console flash)
|
|
46
|
+
const child = spawn(process.execPath, ['-e', `
|
|
47
|
+
const fs = require('fs');
|
|
48
|
+
const path = require('path');
|
|
49
|
+
const { execSync } = require('child_process');
|
|
50
|
+
|
|
51
|
+
const cacheFile = ${JSON.stringify(cacheFile)};
|
|
52
|
+
const projectVersionFile = ${JSON.stringify(projectVersionFile)};
|
|
53
|
+
const globalVersionFile = ${JSON.stringify(globalVersionFile)};
|
|
54
|
+
|
|
55
|
+
// Check project directory first (local install), then global
|
|
56
|
+
let installed = '0.0.0';
|
|
57
|
+
let configDir = '';
|
|
58
|
+
try {
|
|
59
|
+
if (fs.existsSync(projectVersionFile)) {
|
|
60
|
+
installed = fs.readFileSync(projectVersionFile, 'utf8').trim();
|
|
61
|
+
configDir = path.dirname(path.dirname(projectVersionFile));
|
|
62
|
+
} else if (fs.existsSync(globalVersionFile)) {
|
|
63
|
+
installed = fs.readFileSync(globalVersionFile, 'utf8').trim();
|
|
64
|
+
configDir = path.dirname(path.dirname(globalVersionFile));
|
|
65
|
+
}
|
|
66
|
+
} catch (e) {}
|
|
67
|
+
|
|
68
|
+
// Check for stale hooks — compare hook version headers against installed VERSION
|
|
69
|
+
// Hooks live inside cap/hooks/, not configDir/hooks/
|
|
70
|
+
let staleHooks = [];
|
|
71
|
+
if (configDir) {
|
|
72
|
+
const hooksDir = path.join(configDir, 'cap', 'hooks');
|
|
73
|
+
try {
|
|
74
|
+
if (fs.existsSync(hooksDir)) {
|
|
75
|
+
const hookFiles = fs.readdirSync(hooksDir).filter(f => (f.startsWith('gsd-') || f.startsWith('cap-')) && f.endsWith('.js'));
|
|
76
|
+
for (const hookFile of hookFiles) {
|
|
77
|
+
try {
|
|
78
|
+
const content = fs.readFileSync(path.join(hooksDir, hookFile), 'utf8');
|
|
79
|
+
const versionMatch = content.match(/\\/\\/ (?:gsd|cap)-hook-version:\\s*(.+)/);
|
|
80
|
+
if (versionMatch) {
|
|
81
|
+
const hookVersion = versionMatch[1].trim();
|
|
82
|
+
if (hookVersion !== installed && !hookVersion.includes('{{')) {
|
|
83
|
+
staleHooks.push({ file: hookFile, hookVersion, installedVersion: installed });
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
// No version header at all — definitely stale (pre-version-tracking)
|
|
87
|
+
staleHooks.push({ file: hookFile, hookVersion: 'unknown', installedVersion: installed });
|
|
88
|
+
}
|
|
89
|
+
} catch (e) {}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} catch (e) {}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let latest = null;
|
|
96
|
+
try {
|
|
97
|
+
latest = execSync('npm view code-as-plan version', { encoding: 'utf8', timeout: 10000, windowsHide: true }).trim();
|
|
98
|
+
} catch (e) {}
|
|
99
|
+
|
|
100
|
+
const result = {
|
|
101
|
+
update_available: latest && installed !== latest,
|
|
102
|
+
installed,
|
|
103
|
+
latest: latest || 'unknown',
|
|
104
|
+
checked: Math.floor(Date.now() / 1000),
|
|
105
|
+
stale_hooks: staleHooks.length > 0 ? staleHooks : undefined
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
fs.writeFileSync(cacheFile, JSON.stringify(result));
|
|
109
|
+
`], {
|
|
110
|
+
stdio: 'ignore',
|
|
111
|
+
windowsHide: true,
|
|
112
|
+
detached: true // Required on Windows for proper process detachment
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
child.unref();
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @cap-feature(feature:F-009) Hooks System — context window monitor (PostToolUse hook)
|
|
3
|
+
// cap-hook-version: {{CAP_VERSION}}
|
|
4
|
+
/**
|
|
5
|
+
* Context Monitor - PostToolUse/AfterTool hook (Gemini uses AfterTool)
|
|
6
|
+
*
|
|
7
|
+
* Reads context metrics from the statusline bridge file and injects
|
|
8
|
+
* advisory hints when context usage is high. This makes the AGENT aware of
|
|
9
|
+
* context limits (the statusline only shows the user).
|
|
10
|
+
*
|
|
11
|
+
* How it works:
|
|
12
|
+
* 1. The statusline hook writes metrics to /tmp/claude-ctx-{session_id}.json
|
|
13
|
+
* 2. This hook reads those metrics after each tool use
|
|
14
|
+
* 3. When remaining context drops below thresholds, it injects an advisory
|
|
15
|
+
* hint as additionalContext, which the agent sees in its conversation
|
|
16
|
+
*
|
|
17
|
+
* Behavior changes (vs. earlier revisions):
|
|
18
|
+
* - Tonality is **advisory**, not imperative. Messages no longer say
|
|
19
|
+
* "Inform the user…" — they say "Hinweis (vom CAP-Framework, vom
|
|
20
|
+
* User-Prompt unabhängig): … Der Agent kann den User informieren falls
|
|
21
|
+
* relevant für die Aufgabe." This avoids overriding user preferences
|
|
22
|
+
* such as "terse responses, no summaries".
|
|
23
|
+
* - Warning threshold lowered from 35% → 30% to reduce early warnings;
|
|
24
|
+
* focus is on the truly critical 25% escalation.
|
|
25
|
+
* - New ENV `CAP_DISABLE_CONTEXT_MONITOR=1` silences the hook entirely
|
|
26
|
+
* (for power users who don't want any advisory injection).
|
|
27
|
+
*
|
|
28
|
+
* Thresholds:
|
|
29
|
+
* WARNING (remaining <= 30%): advisory hint, agent decides whether to surface
|
|
30
|
+
* CRITICAL (remaining <= 25%): stronger advisory, agent decides whether to surface
|
|
31
|
+
*
|
|
32
|
+
* Debounce: 5 tool uses between warnings to avoid spam.
|
|
33
|
+
* Severity escalation bypasses debounce (WARNING -> CRITICAL fires immediately).
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const fs = require('fs');
|
|
37
|
+
const os = require('os');
|
|
38
|
+
const path = require('path');
|
|
39
|
+
|
|
40
|
+
const WARNING_THRESHOLD = 30; // remaining_percentage <= 30% (was 35%)
|
|
41
|
+
const CRITICAL_THRESHOLD = 25; // remaining_percentage <= 25%
|
|
42
|
+
const STALE_SECONDS = 60; // ignore metrics older than 60s
|
|
43
|
+
const DEBOUNCE_CALLS = 5; // min tool uses between warnings
|
|
44
|
+
|
|
45
|
+
// Power-user kill switch: if set, hook is fully silent regardless of context state.
|
|
46
|
+
if (process.env.CAP_DISABLE_CONTEXT_MONITOR === '1' ||
|
|
47
|
+
process.env.CAP_DISABLE_CONTEXT_MONITOR === 'true') {
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let input = '';
|
|
52
|
+
// Timeout guard: if stdin doesn't close within 10s (e.g. pipe issues on
|
|
53
|
+
// Windows/Git Bash, or slow Claude Code piping during large outputs),
|
|
54
|
+
// exit silently instead of hanging until Claude Code kills the process
|
|
55
|
+
// and reports "hook error". See #775, #1162.
|
|
56
|
+
const stdinTimeout = setTimeout(() => process.exit(0), 10000);
|
|
57
|
+
process.stdin.setEncoding('utf8');
|
|
58
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
59
|
+
process.stdin.on('end', () => {
|
|
60
|
+
clearTimeout(stdinTimeout);
|
|
61
|
+
try {
|
|
62
|
+
const data = JSON.parse(input);
|
|
63
|
+
const sessionId = data.session_id;
|
|
64
|
+
|
|
65
|
+
if (!sessionId) {
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check if context warnings are disabled via config
|
|
70
|
+
const cwd = data.cwd || process.cwd();
|
|
71
|
+
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
72
|
+
if (fs.existsSync(configPath)) {
|
|
73
|
+
try {
|
|
74
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
75
|
+
if (config.hooks?.context_warnings === false) {
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
} catch (e) {
|
|
79
|
+
// Ignore config parse errors
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const tmpDir = os.tmpdir();
|
|
84
|
+
const metricsPath = path.join(tmpDir, `claude-ctx-${sessionId}.json`);
|
|
85
|
+
|
|
86
|
+
// If no metrics file, this is a subagent or fresh session -- exit silently
|
|
87
|
+
if (!fs.existsSync(metricsPath)) {
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const metrics = JSON.parse(fs.readFileSync(metricsPath, 'utf8'));
|
|
92
|
+
const now = Math.floor(Date.now() / 1000);
|
|
93
|
+
|
|
94
|
+
// Ignore stale metrics
|
|
95
|
+
if (metrics.timestamp && (now - metrics.timestamp) > STALE_SECONDS) {
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const remaining = metrics.remaining_percentage;
|
|
100
|
+
const usedPct = metrics.used_pct;
|
|
101
|
+
|
|
102
|
+
// No warning needed
|
|
103
|
+
if (remaining > WARNING_THRESHOLD) {
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Debounce: check if we warned recently
|
|
108
|
+
const warnPath = path.join(tmpDir, `claude-ctx-${sessionId}-warned.json`);
|
|
109
|
+
let warnData = { callsSinceWarn: 0, lastLevel: null };
|
|
110
|
+
let firstWarn = true;
|
|
111
|
+
|
|
112
|
+
if (fs.existsSync(warnPath)) {
|
|
113
|
+
try {
|
|
114
|
+
warnData = JSON.parse(fs.readFileSync(warnPath, 'utf8'));
|
|
115
|
+
firstWarn = false;
|
|
116
|
+
} catch (e) {
|
|
117
|
+
// Corrupted file, reset
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
warnData.callsSinceWarn = (warnData.callsSinceWarn || 0) + 1;
|
|
122
|
+
|
|
123
|
+
const isCritical = remaining <= CRITICAL_THRESHOLD;
|
|
124
|
+
const currentLevel = isCritical ? 'critical' : 'warning';
|
|
125
|
+
|
|
126
|
+
// Emit immediately on first warning, then debounce subsequent ones
|
|
127
|
+
// Severity escalation (WARNING -> CRITICAL) bypasses debounce
|
|
128
|
+
const severityEscalated = currentLevel === 'critical' && warnData.lastLevel === 'warning';
|
|
129
|
+
if (!firstWarn && warnData.callsSinceWarn < DEBOUNCE_CALLS && !severityEscalated) {
|
|
130
|
+
// Update counter and exit without warning
|
|
131
|
+
fs.writeFileSync(warnPath, JSON.stringify(warnData));
|
|
132
|
+
process.exit(0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Reset debounce counter
|
|
136
|
+
warnData.callsSinceWarn = 0;
|
|
137
|
+
warnData.lastLevel = currentLevel;
|
|
138
|
+
fs.writeFileSync(warnPath, JSON.stringify(warnData));
|
|
139
|
+
|
|
140
|
+
// Detect if GSD is active (has .planning/STATE.md in working directory)
|
|
141
|
+
const isGsdActive = fs.existsSync(path.join(cwd, '.planning', 'STATE.md'));
|
|
142
|
+
|
|
143
|
+
// Build advisory message (no imperative commands — see #884).
|
|
144
|
+
// The framework-prefix makes it explicit that this is metadata
|
|
145
|
+
// independent of the user prompt, so user preferences like
|
|
146
|
+
// "terse responses" are not violated by the agent itself.
|
|
147
|
+
const PREFIX = 'Hinweis (vom CAP-Framework, vom User-Prompt unabhängig): ';
|
|
148
|
+
const SUFFIX = ' Der Agent kann den User informieren falls relevant für die Aufgabe.';
|
|
149
|
+
|
|
150
|
+
let message;
|
|
151
|
+
if (isCritical) {
|
|
152
|
+
message = isGsdActive
|
|
153
|
+
? `${PREFIX}CONTEXT CRITICAL — Auslastung bei ${usedPct}%, verbleibend ${remaining}%. ` +
|
|
154
|
+
'Context ist nahezu erschöpft. GSD-State ist bereits in STATE.md getrackt; ' +
|
|
155
|
+
'es ist nicht nötig, autonom Handoff-Dateien zu schreiben oder neue komplexe Arbeit zu starten.' +
|
|
156
|
+
SUFFIX
|
|
157
|
+
: `${PREFIX}CONTEXT CRITICAL — Auslastung bei ${usedPct}%, verbleibend ${remaining}%. ` +
|
|
158
|
+
'Context ist nahezu erschöpft. Autonomes Speichern von State oder Handoff-Dateien ' +
|
|
159
|
+
'ist nicht erforderlich, sofern der User nicht ausdrücklich danach fragt.' +
|
|
160
|
+
SUFFIX;
|
|
161
|
+
} else {
|
|
162
|
+
message = isGsdActive
|
|
163
|
+
? `${PREFIX}CONTEXT WARNING — Auslastung bei ${usedPct}%, verbleibend ${remaining}%. ` +
|
|
164
|
+
'Context-Budget wird knapp. Größere neue Arbeit zwischen definierten Plan-Schritten ' +
|
|
165
|
+
'ist günstig zu vermeiden.' +
|
|
166
|
+
SUFFIX
|
|
167
|
+
: `${PREFIX}CONTEXT WARNING — Auslastung bei ${usedPct}%, verbleibend ${remaining}%. ` +
|
|
168
|
+
'Context-Budget wird knapp. Unnötige Exploration oder das Anstoßen neuer komplexer ' +
|
|
169
|
+
'Arbeit ist günstig zu vermeiden.' +
|
|
170
|
+
SUFFIX;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const output = {
|
|
174
|
+
hookSpecificOutput: {
|
|
175
|
+
hookEventName: process.env.GEMINI_API_KEY ? "AfterTool" : "PostToolUse",
|
|
176
|
+
additionalContext: message
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
process.stdout.write(JSON.stringify(output));
|
|
181
|
+
} catch (e) {
|
|
182
|
+
// Silent fail -- never block tool execution
|
|
183
|
+
process.exit(0);
|
|
184
|
+
}
|
|
185
|
+
});
|