@haposoft/cafekit 0.7.24 → 0.7.26
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/package.json +1 -1
- package/src/claude/agents/code-auditor.md +8 -3
- package/src/claude/agents/docs-keeper.md +12 -1
- package/src/claude/agents/god-developer.md +3 -3
- package/src/claude/agents/spec-maker.md +14 -7
- package/src/claude/agents/test-runner.md +14 -0
- package/src/claude/hooks/privacy-block.cjs +133 -80
- package/src/claude/hooks/spec-state.cjs +21 -2
- package/src/claude/hooks/state.cjs +164 -122
- package/src/claude/rules/manage-docs.md +6 -5
- package/src/claude/rules/state-sync.md +7 -6
- package/src/claude/settings/settings.json +10 -1
- package/src/claude/skills/develop/SKILL.md +51 -9
- package/src/claude/skills/develop/references/quality-gate.md +13 -4
- package/src/claude/skills/specs/SKILL.md +33 -6
- package/src/claude/skills/specs/references/review.md +32 -3
- package/src/claude/skills/specs/references/task-hydration.md +18 -16
- package/src/claude/skills/specs/rules/tasks-generation.md +4 -0
- package/src/claude/skills/specs/templates/design.md +2 -1
- package/src/claude/skills/specs/templates/init.json +3 -1
- package/src/claude/skills/sync/SKILL.md +13 -10
- package/src/claude/skills/sync/references/sync-protocols.md +39 -13
- package/src/claude/skills/test/SKILL.md +2 -2
- package/src/claude/skills/test/references/execution-strategy.md +2 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haposoft/cafekit",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.26",
|
|
4
4
|
"description": "Claude Code-first spec-driven workflow for AI coding assistants. Bundles CafeKit hapo: skills, runtime hooks, agents, and installer scaffolding.",
|
|
5
5
|
"author": "Haposoft <nghialt@haposoft.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,9 +19,10 @@ If the prompt includes task file paths, requirement IDs, completion criteria, or
|
|
|
19
19
|
|
|
20
20
|
Extract and verify:
|
|
21
21
|
1. Declared deliverables (files, routes, entrypoints, UI surfaces, schemas, migrations)
|
|
22
|
-
2.
|
|
23
|
-
3.
|
|
24
|
-
4.
|
|
22
|
+
2. Declared task scope (`Related Files` and direct support files that are clearly justified)
|
|
23
|
+
3. Completion Criteria
|
|
24
|
+
4. Verification & Evidence expectations
|
|
25
|
+
5. Canonical Contracts & Invariants from the design
|
|
25
26
|
|
|
26
27
|
Any missing declared deliverable, placeholder-only wiring, or contract drift is a **Critical** issue even if tests/build pass.
|
|
27
28
|
|
|
@@ -58,6 +59,7 @@ Before reading any specific logic, you MUST run a Dependency Scope Check (Blast
|
|
|
58
59
|
- Hunt serious logic bugs (crashes, data loss, infinite loops).
|
|
59
60
|
- Hunt severe architecture violations (circular imports, cross-layer coupling).
|
|
60
61
|
- Hunt missing required artifacts/runtime entrypoints and spec contract mismatches.
|
|
62
|
+
- Hunt overscope edits: later-task deliverables, unjustified file additions, or edits outside the active task packet.
|
|
61
63
|
|
|
62
64
|
**Pass 2 — Quality Scan (Non-Blocking Issues):**
|
|
63
65
|
- Project conventions (`docs/code-standards.md` if available).
|
|
@@ -94,6 +96,7 @@ Classify each issue:
|
|
|
94
96
|
|
|
95
97
|
### Task / Spec Compliance
|
|
96
98
|
- [OK or issue] Required deliverables present?
|
|
99
|
+
- [OK or issue] Changes stayed within task scope?
|
|
97
100
|
- [OK or issue] Completion criteria actually satisfied?
|
|
98
101
|
- [OK or issue] Any contract drift vs design/task?
|
|
99
102
|
|
|
@@ -126,6 +129,8 @@ When called from `hapo:develop` Step 4 (Quality Gate Auto-Fix):
|
|
|
126
129
|
- Missing required entrypoint/artifact/runtime output named in the task/spec
|
|
127
130
|
- Placeholder scaffolding marked as complete when the task demanded real wiring
|
|
128
131
|
- Auth/session/transport/persistence behavior that contradicts the design contracts
|
|
132
|
+
- Files or features from later tasks delivered early without explicit scope-escape justification
|
|
133
|
+
- Task marked complete while required commands/evidence are still FAIL / UNVERIFIED
|
|
129
134
|
|
|
130
135
|
## Operating Guidelines
|
|
131
136
|
|
|
@@ -40,10 +40,21 @@ Before documenting ANY code reference, you MUST prove it exists:
|
|
|
40
40
|
|
|
41
41
|
### 4. Codebase Summary Engine
|
|
42
42
|
Generate the project's technical DNA map:
|
|
43
|
-
-
|
|
43
|
+
- Default to direct code + docs verification first.
|
|
44
|
+
- Run `repomix` only when macro-architecture context is truly required or `./docs/codebase-summary.md` is missing/stale enough that you cannot safely update docs from the local evidence alone.
|
|
45
|
+
- When `repomix` is used, compact the repo into `./repomix-output.xml`.
|
|
44
46
|
- Digest and synthesize into `./docs/codebase-summary.md` (Update the existing one).
|
|
45
47
|
- This file acts as the single source of truth for all other agents to quickly grasp the project landscape.
|
|
46
48
|
|
|
49
|
+
### 4b. Task Closeout Mode
|
|
50
|
+
When called from `hapo:develop` after a verified task is complete:
|
|
51
|
+
- Treat the job as a **lightweight task-closeout sync**
|
|
52
|
+
- Update only the existing docs affected by that task
|
|
53
|
+
- Start by classifying `Docs impact: none | minor | major`
|
|
54
|
+
- If impact is `none`, return a short report and do not force edits
|
|
55
|
+
- If impact is `minor` or `major`, prefer surgical edits to `docs/project-overview-pdr.md`, `docs/system-architecture.md`, `docs/code-standards.md`, changelog/roadmap files, or other already-existing docs
|
|
56
|
+
- Do NOT run `repomix` just because code changed; use it only if direct verification is insufficient
|
|
57
|
+
|
|
47
58
|
### 5. File Size Discipline
|
|
48
59
|
If any doc file exceeds **800 LOC**, enforce modularity:
|
|
49
60
|
1. Identify semantic boundaries (distinct topics that can stand alone).
|
|
@@ -35,7 +35,7 @@ Any logic gaps must be clarified BEFORE typing, not discovered after bugs ship.
|
|
|
35
35
|
### 1. Read & Understand Input
|
|
36
36
|
|
|
37
37
|
When activated, you will receive one of two input types:
|
|
38
|
-
- **Task file list** (`tasks/task-01-*.md`, `task-
|
|
38
|
+
- **Task file list** (`tasks/task-R0-01-*.md`, `task-R1-01-*.md`...) with `spec.json`.
|
|
39
39
|
- **Direct description** from the main agent or `hapo:develop` skill.
|
|
40
40
|
*(Always proactively leverage domain-specific best practices by invoking `hapo:frontend-development`, `hapo:backend-development`, `hapo:mobile-development`, or `hapo:react-best-practices` depending on the current task).*
|
|
41
41
|
|
|
@@ -77,8 +77,8 @@ Upon completion, output a concise report in this format:
|
|
|
77
77
|
- ...
|
|
78
78
|
|
|
79
79
|
### Tasks Completed
|
|
80
|
-
- [x]
|
|
81
|
-
- [x]
|
|
80
|
+
- [x] R0-01: ...
|
|
81
|
+
- [x] R0-02: ...
|
|
82
82
|
|
|
83
83
|
### Build Results
|
|
84
84
|
- Typecheck: [pass/fail]
|
|
@@ -85,6 +85,7 @@ Before writing `design.md`, select a discovery mode and record the reason:
|
|
|
85
85
|
- For light mode: Load `{{SKILLS_DIR}}/specs/rules/design-discovery-light.md`
|
|
86
86
|
- Include Mermaid diagrams for multi-step or cross-boundary flows
|
|
87
87
|
- For auth/session, transport/entrypoint, persistence/schema, generated-artifact, or runtime-sensitive work: fill the `Canonical Contracts & Invariants` section and keep those decisions stable across all task files.
|
|
88
|
+
- For privacy/delete-data work: the design MUST choose one canonical deletion policy and express it verbatim in `Canonical Contracts & Invariants` before tasks are generated.
|
|
88
89
|
- Record `discovery_mode` and `discovery_reason` in `spec.json.design_context`
|
|
89
90
|
|
|
90
91
|
### Requirements Traceability (MANDATORY)
|
|
@@ -107,6 +108,7 @@ Before writing `design.md`, select a discovery mode and record the reason:
|
|
|
107
108
|
- Apply `(P)` parallel markers when applicable (load `{{SKILLS_DIR}}/specs/rules/tasks-parallel-analysis.md`)
|
|
108
109
|
- Every task MUST include `Verification & Evidence` with exact commands, artifacts/runtime surfaces, and negative-path checks.
|
|
109
110
|
- Completion criteria MUST be objective enough that a downstream quality gate can prove them without guesswork.
|
|
111
|
+
- Validation decisions that affect implementation MUST be written into implementation-facing sections (`Objective`, `Constraints`, `Implementation Steps`, `Completion Criteria`, `Verification & Evidence`) rather than only `Risk Assessment`.
|
|
110
112
|
|
|
111
113
|
### Sub-Task Detail Requirements (MANDATORY)
|
|
112
114
|
Each task file MUST contain granular sub-tasks with the following structure:
|
|
@@ -137,17 +139,22 @@ Task(subagent_type="researcher", prompt="Research [feature topic]")
|
|
|
137
139
|
|
|
138
140
|
## Pre-Completion Checklist
|
|
139
141
|
|
|
140
|
-
Before finalizing any specification, assert
|
|
142
|
+
Before finalizing any specification, assert every point in the `Pre-Finalization Checklist` defined in `SKILL.md`. Do not exit or declare completion until verifiable.
|
|
141
143
|
|
|
142
144
|
### Finalization Audit (MANDATORY)
|
|
143
145
|
|
|
144
146
|
Before marking the spec ready:
|
|
145
147
|
1. Re-scan `tasks/` and write `spec.json.task_files` from the real filesystem (sorted, relative paths)
|
|
146
|
-
2.
|
|
147
|
-
3. Fail if any
|
|
148
|
-
4.
|
|
149
|
-
5.
|
|
150
|
-
6.
|
|
148
|
+
2. Build or refresh `spec.json.task_registry` from the same filesystem scan. Each registry entry MUST include `id`, `title`, `status`, `dependencies` (relative task paths), `blocker`, `started_at`, `completed_at`, and `last_updated_at`
|
|
149
|
+
3. Fail if any on-disk task file is missing from `task_files`
|
|
150
|
+
4. Fail if any path in `task_files` does not exist
|
|
151
|
+
5. Fail if any on-disk task file is missing from `task_registry` or any registry path does not exist
|
|
152
|
+
6. Infer `design_context.validation_recommended = true` for auth, privacy, delete-data, migration, schema-change, browser-extension-permission, external-provider, or 5+ task file specs
|
|
153
|
+
7. If the spec scope switched away from Claude/Anthropic, fail if `requirements.md`, `design.md`, or `tasks/*.md` still contain stale provider strings like `Claude API`, `Haiku`, or `haiku_reachable`. `research.md` may mention old providers only as historical comparison.
|
|
154
|
+
8. For delete/privacy specs, fail if requirements/design/tasks mix multiple deletion policies (for example `email_hash` in one place and `deleted-<uuid>` in another) without one canonical design decision.
|
|
155
|
+
9. If `validation_recommended = true` and validation has not completed (or the user did not explicitly accept risk), keep `ready_for_implementation = false`
|
|
156
|
+
10. Reject task files that use legacy non-numeric mappings like `NFR-1`
|
|
157
|
+
11. If validation decisions were accepted, fail unless they are reflected in implementation-facing sections of affected artifacts and `spec.json.updated_at` / review timestamps reflect the reviewed state
|
|
151
158
|
|
|
152
159
|
## Execution Workflow Summary
|
|
153
160
|
|
|
@@ -175,7 +182,7 @@ specs/<feature>/
|
|
|
175
182
|
|
|
176
183
|
### 4. Handoff
|
|
177
184
|
- Update `spec.json` with `"status": "in_progress"` and `"current_phase": "develop"`
|
|
178
|
-
- Ensure `task_files`
|
|
185
|
+
- Ensure `task_files` + `task_registry` are synchronized and `ready_for_implementation` reflects the finalization audit outcome
|
|
179
186
|
- Report the spec directory path to the orchestrator
|
|
180
187
|
- DO NOT begin implementation yourself
|
|
181
188
|
|
|
@@ -13,6 +13,15 @@ You are a battle-hardened QA engineer who has been burned by production incident
|
|
|
13
13
|
If the prompt includes task file paths, Completion Criteria, or Verification & Evidence instructions, treat them as authoritative.
|
|
14
14
|
Diff-aware test selection does NOT replace task-specific verification.
|
|
15
15
|
|
|
16
|
+
## Command Resolution Order
|
|
17
|
+
|
|
18
|
+
When the task file names exact commands, use this order:
|
|
19
|
+
1. Run every exact executable command from `Verification & Evidence` in declaration order.
|
|
20
|
+
2. Run repo-default typecheck/test/build commands only to fill gaps not already covered above.
|
|
21
|
+
3. Apply diff-aware test selection only after task-mandated commands are satisfied.
|
|
22
|
+
|
|
23
|
+
Never silently substitute a lighter command for a task-mandated one. Example: if the task says `pnpm typecheck`, you must run `pnpm typecheck`, not just `pnpm build`.
|
|
24
|
+
|
|
16
25
|
## Operating Modes
|
|
17
26
|
|
|
18
27
|
### Mode 1: Diff-Aware (Default)
|
|
@@ -73,6 +82,9 @@ Run the entire test suite without diff filtering. Use when: first run, major ref
|
|
|
73
82
|
- Typecheck/Lint: PASS | FAIL | N/A
|
|
74
83
|
- Build: PASS | FAIL | N/A
|
|
75
84
|
|
|
85
|
+
### Exact Commands Executed
|
|
86
|
+
- `command here` → PASS | FAIL | UNVERIFIED
|
|
87
|
+
|
|
76
88
|
### Coverage
|
|
77
89
|
- Lines: [X%] | Branches: [X%] | Functions: [X%]
|
|
78
90
|
- ⚠️ Below threshold: [list modules < 80%]
|
|
@@ -100,4 +112,6 @@ Run the entire test suite without diff filtering. Use when: first run, major ref
|
|
|
100
112
|
- **Flaky Tests:** If a test is flaky (passes/fails intermittently), flag it explicitly — do not retry silently.
|
|
101
113
|
- **No Evidence, No PASS:** If required artifact/runtime verification is missing, omitted, or blocked, you MUST NOT return PASS.
|
|
102
114
|
- **Placeholder Trap:** If build succeeds but the task-required entrypoint/artifact/runtime surface is missing (for example popup, content script, route, migration, auth flow), return FAIL or NEEDS_ATTENTION with evidence.
|
|
115
|
+
- **Required Command Missing = FAIL:** If the task explicitly names a command and it was not run successfully, you MUST NOT return PASS.
|
|
116
|
+
- **NO_TESTS Semantics:** If no tests exist, report `NO_TESTS` explicitly. `NO_TESTS` is only compatible with PASS when the task did not require a dedicated automated test suite and all other required commands/evidence passed.
|
|
103
117
|
- Report honestly. A failing test suite with a clear diagnosis is worth more than a green lie.
|
|
@@ -3,124 +3,177 @@
|
|
|
3
3
|
* Copyright (c) 2026 Haposoft. MIT License.
|
|
4
4
|
*
|
|
5
5
|
* PreToolUse Hook — privacy-block.cjs
|
|
6
|
-
* Implements: https://docs.anthropic.com/en/docs/claude-code/hooks
|
|
7
6
|
*
|
|
8
|
-
*
|
|
7
|
+
* Claude Code CLI privacy gate for sensitive files.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* Disable: set "privacyBlock": false in .claude/runtime.json
|
|
9
|
+
* Runtime contract:
|
|
10
|
+
* - Non-bash file access to sensitive files is blocked with a JSON marker
|
|
11
|
+
* - Assistant must use AskUserQuestion with that JSON payload
|
|
12
|
+
* - If user approves, assistant should read via `bash cat "file"`
|
|
13
|
+
* - Bash access is allowed with a warning to enable the approved follow-up path
|
|
17
14
|
*
|
|
18
15
|
* Exit: 0 = allow, 2 = block
|
|
19
16
|
*/
|
|
20
17
|
|
|
21
18
|
try {
|
|
22
|
-
const fs
|
|
19
|
+
const fs = require('fs');
|
|
23
20
|
const path = require('path');
|
|
24
21
|
|
|
25
|
-
// Sensitive file patterns — matched against basename and full path
|
|
26
22
|
const RESTRICTED_PATTERNS = [
|
|
27
|
-
/^\.env(\.|$)/i,
|
|
28
|
-
/^credentials/i,
|
|
29
|
-
/secrets?\.(ya?ml|json)$/i,
|
|
30
|
-
/\.pem$/i,
|
|
31
|
-
/\.key$/i,
|
|
32
|
-
/\.p12$/i,
|
|
33
|
-
/\.pfx$/i,
|
|
34
|
-
/^id_(rsa|ed25519|ecdsa|dsa)$/i,
|
|
35
|
-
/\.netrc$/i,
|
|
36
|
-
/\.pgpass$/i,
|
|
37
|
-
/kubeconfig/i,
|
|
38
|
-
/\.keystore$/i,
|
|
39
|
-
/\.jks$/i,
|
|
40
|
-
/auth\.json$/i,
|
|
41
|
-
/token(s)?\.json$/i
|
|
23
|
+
/^\.env(\.|$)/i,
|
|
24
|
+
/^credentials/i,
|
|
25
|
+
/secrets?\.(ya?ml|json)$/i,
|
|
26
|
+
/\.pem$/i,
|
|
27
|
+
/\.key$/i,
|
|
28
|
+
/\.p12$/i,
|
|
29
|
+
/\.pfx$/i,
|
|
30
|
+
/^id_(rsa|ed25519|ecdsa|dsa)$/i,
|
|
31
|
+
/\.netrc$/i,
|
|
32
|
+
/\.pgpass$/i,
|
|
33
|
+
/kubeconfig/i,
|
|
34
|
+
/\.keystore$/i,
|
|
35
|
+
/\.jks$/i,
|
|
36
|
+
/auth\.json$/i,
|
|
37
|
+
/token(s)?\.json$/i
|
|
42
38
|
];
|
|
43
39
|
|
|
44
|
-
// Safe exceptions — these always pass through (example / template files)
|
|
45
40
|
const ALLOWED_EXEMPTIONS = [
|
|
46
|
-
/\.env\.(example|sample|template|test)$/i
|
|
41
|
+
/\.env\.(example|sample|template|test)$/i
|
|
47
42
|
];
|
|
48
43
|
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (!input) return out;
|
|
56
|
-
if (input.file_path) out.push(input.file_path);
|
|
57
|
-
if (input.path) out.push(input.path);
|
|
58
|
-
// Bash: look for cat/less/head/tail etc.
|
|
59
|
-
if (typeof input.command === 'string') {
|
|
60
|
-
const m = input.command.match(/(?:cat|less|more|head|tail|source|\.)\s+(\S+)/g);
|
|
61
|
-
if (m) m.forEach(s => out.push(s.trim().split(/\s+/).pop()));
|
|
44
|
+
function readRuntime(cwd) {
|
|
45
|
+
try {
|
|
46
|
+
const file = path.join(cwd, '.claude', 'runtime.json');
|
|
47
|
+
return fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : {};
|
|
48
|
+
} catch {
|
|
49
|
+
return {};
|
|
62
50
|
}
|
|
63
|
-
return out.filter(Boolean);
|
|
64
51
|
}
|
|
65
52
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return
|
|
53
|
+
function isSafe(filePath) {
|
|
54
|
+
const base = path.basename(filePath);
|
|
55
|
+
return ALLOWED_EXEMPTIONS.some((rule) => rule.test(base) || rule.test(filePath));
|
|
69
56
|
}
|
|
70
57
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
58
|
+
function isSensitive(filePath) {
|
|
59
|
+
const base = path.basename(filePath);
|
|
60
|
+
return RESTRICTED_PATTERNS.some((rule) => rule.test(base) || rule.test(filePath));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function extractBashPaths(command) {
|
|
64
|
+
const paths = [];
|
|
65
|
+
const regex = /(?:cat|less|more|head|tail|source|\.)\s+(?:"([^"]+)"|'([^']+)'|([^\s]+))/g;
|
|
66
|
+
let match;
|
|
67
|
+
while ((match = regex.exec(command)) !== null) {
|
|
68
|
+
paths.push(match[1] || match[2] || match[3]);
|
|
69
|
+
}
|
|
70
|
+
return paths;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function extractPaths(toolName, input) {
|
|
74
|
+
const paths = [];
|
|
75
|
+
if (!input) return paths;
|
|
76
|
+
|
|
77
|
+
for (const key of ['file_path', 'path']) {
|
|
78
|
+
if (typeof input[key] === 'string' && input[key].trim()) {
|
|
79
|
+
paths.push(input[key].trim());
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (const key of ['paths', 'search_paths']) {
|
|
84
|
+
if (Array.isArray(input[key])) {
|
|
85
|
+
paths.push(...input[key].filter(Boolean));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (toolName === 'Bash' && typeof input.command === 'string') {
|
|
90
|
+
paths.push(...extractBashPaths(input.command));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return paths.filter(Boolean);
|
|
77
94
|
}
|
|
78
95
|
|
|
79
|
-
|
|
96
|
+
function formatBlockMessage(filePath) {
|
|
97
|
+
const basename = path.basename(filePath);
|
|
98
|
+
const promptData = {
|
|
99
|
+
type: 'PRIVACY_PROMPT',
|
|
100
|
+
file: filePath,
|
|
101
|
+
basename,
|
|
102
|
+
question: {
|
|
103
|
+
header: 'File Access',
|
|
104
|
+
text: `I need to read "${basename}" which may contain sensitive data (API keys, passwords, tokens). Do you approve?`,
|
|
105
|
+
options: [
|
|
106
|
+
{
|
|
107
|
+
label: 'Yes, approve access',
|
|
108
|
+
description: `Allow reading ${basename} this time`
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
label: 'No, skip this file',
|
|
112
|
+
description: 'Continue without accessing this file'
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return [
|
|
119
|
+
'NOTE: This is not an error. This block protects sensitive data.',
|
|
120
|
+
'',
|
|
121
|
+
`PRIVACY BLOCK: Sensitive file access requires user approval`,
|
|
122
|
+
`File: ${filePath}`,
|
|
123
|
+
'',
|
|
124
|
+
'@@PRIVACY_PROMPT_START@@',
|
|
125
|
+
JSON.stringify(promptData, null, 2),
|
|
126
|
+
'@@PRIVACY_PROMPT_END@@',
|
|
127
|
+
'',
|
|
128
|
+
'Claude Code follow-up:',
|
|
129
|
+
`- If approved: use bash to read: cat "${filePath}"`,
|
|
130
|
+
'- If denied: continue without this file'
|
|
131
|
+
].join('\n');
|
|
132
|
+
}
|
|
80
133
|
|
|
81
|
-
const stdin
|
|
134
|
+
const stdin = fs.readFileSync(0, 'utf8').trim();
|
|
82
135
|
if (!stdin) process.exit(0);
|
|
83
136
|
|
|
84
|
-
const data
|
|
85
|
-
const toolName
|
|
137
|
+
const data = JSON.parse(stdin);
|
|
138
|
+
const toolName = data.tool_name || '';
|
|
86
139
|
const toolInput = data.tool_input || {};
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const runtime = readRuntime(cwd);
|
|
140
|
+
const cwd = data.cwd || process.cwd();
|
|
141
|
+
const runtime = readRuntime(cwd);
|
|
90
142
|
|
|
91
|
-
// Disabled via config
|
|
92
143
|
if (runtime.privacyBlock === false) process.exit(0);
|
|
93
144
|
|
|
94
|
-
// Already approved by user
|
|
95
|
-
if (approved(prompt)) process.exit(0);
|
|
96
|
-
|
|
97
145
|
const paths = extractPaths(toolName, toolInput);
|
|
98
146
|
if (!paths.length) process.exit(0);
|
|
99
147
|
|
|
100
148
|
for (const filePath of paths) {
|
|
101
149
|
if (isSafe(filePath)) continue;
|
|
102
|
-
if (isSensitive(filePath))
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
`--- RESTRICTED_FILE_PROMPT_BEGIN ---\n` +
|
|
108
|
-
JSON.stringify({ type: 'RESTRICTED_PROMPT', file: filePath, tool: toolName }) + '\n' +
|
|
109
|
-
`--- RESTRICTED_FILE_PROMPT_END ---`
|
|
110
|
-
);
|
|
111
|
-
process.exit(2);
|
|
150
|
+
if (!isSensitive(filePath)) continue;
|
|
151
|
+
|
|
152
|
+
if (toolName === 'Bash') {
|
|
153
|
+
console.error(`WARN: Privacy-sensitive file access via bash allowed for approved follow-up: ${path.basename(filePath)}`);
|
|
154
|
+
process.exit(0);
|
|
112
155
|
}
|
|
156
|
+
|
|
157
|
+
console.error(formatBlockMessage(filePath));
|
|
158
|
+
process.exit(2);
|
|
113
159
|
}
|
|
114
160
|
|
|
115
161
|
process.exit(0);
|
|
116
|
-
|
|
117
|
-
} catch (e) {
|
|
162
|
+
} catch (error) {
|
|
118
163
|
try {
|
|
119
|
-
const fs = require('fs')
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
fs.
|
|
123
|
-
|
|
164
|
+
const fs = require('fs');
|
|
165
|
+
const path = require('path');
|
|
166
|
+
const logDir = path.join(__dirname, '.logs');
|
|
167
|
+
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
|
|
168
|
+
fs.appendFileSync(
|
|
169
|
+
path.join(logDir, 'hook-log.jsonl'),
|
|
170
|
+
JSON.stringify({
|
|
171
|
+
ts: new Date().toISOString(),
|
|
172
|
+
hook: 'privacy-block',
|
|
173
|
+
status: 'crash',
|
|
174
|
+
error: error.message
|
|
175
|
+
}) + '\n'
|
|
176
|
+
);
|
|
124
177
|
} catch (_) {}
|
|
125
|
-
process.exit(0);
|
|
178
|
+
process.exit(0);
|
|
126
179
|
}
|
|
@@ -63,17 +63,36 @@ try {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
const phase = activeSpec.current_phase || activeSpec.phase || 'unknown';
|
|
66
|
-
|
|
66
|
+
const taskRegistry = activeSpec.task_registry || {};
|
|
67
|
+
const taskEntries = Object.entries(taskRegistry);
|
|
68
|
+
const taskCounts = taskEntries.reduce((acc, [, task]) => {
|
|
69
|
+
const status = task?.status || 'pending';
|
|
70
|
+
acc[status] = (acc[status] || 0) + 1;
|
|
71
|
+
return acc;
|
|
72
|
+
}, {});
|
|
73
|
+
const taskStatusByPath = new Map(taskEntries.map(([taskPath, task]) => [taskPath, task?.status || 'pending']));
|
|
74
|
+
const nextUnblocked = taskEntries.find(([, task]) => {
|
|
75
|
+
const status = task?.status || 'pending';
|
|
76
|
+
const deps = Array.isArray(task?.dependencies) ? task.dependencies : [];
|
|
77
|
+
return status === 'pending' && deps.every((dep) => taskStatusByPath.get(dep) === 'done');
|
|
78
|
+
});
|
|
79
|
+
|
|
67
80
|
// Format the output
|
|
68
81
|
const lines = [];
|
|
69
82
|
lines.push('');
|
|
70
83
|
lines.push('### 🔴 URGENT SYSTEM TOLLGATE (STATE SYNC) 🔴');
|
|
71
84
|
lines.push(`- **Active Feature:** \`${featureName}\``);
|
|
72
85
|
lines.push(`- **Current Phase:** \`${phase}\``);
|
|
86
|
+
if (taskEntries.length > 0) {
|
|
87
|
+
lines.push(`- **Task Registry:** \`${taskEntries.length} total | ${(taskCounts.done || 0)} done | ${(taskCounts.in_progress || 0)} in_progress | ${(taskCounts.blocked || 0)} blocked | ${(taskCounts.pending || 0)} pending\``);
|
|
88
|
+
if (nextUnblocked) {
|
|
89
|
+
lines.push(`- **Next Unblocked Task:** \`${nextUnblocked[0]}\``);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
73
92
|
lines.push('');
|
|
74
93
|
lines.push(`> BẮT BUỘC (MANDATORY): Nếu bạn vừa hoàn thành một bước, bạn KHÔNG ĐƯỢC báo cáo "Đã xong" ngay.`);
|
|
75
94
|
lines.push(`> Bạn PHẢI sử dụng công cụ Edit để cập nhật trạng thái vật lý sau khi đã có bằng chứng verify thật (build/test/runtime/artifact), không phải chỉ vì code đã viết xong.`);
|
|
76
|
-
lines.push(`> 1. Sửa file \`spec.json\` (status, phase/current_phase, timestamps, \`task_files\`, validation state nếu có thay đổi).`);
|
|
95
|
+
lines.push(`> 1. Sửa file \`spec.json\` (status, phase/current_phase, timestamps, \`task_files\`, \`task_registry\`, validation state nếu có thay đổi).`);
|
|
77
96
|
lines.push(`> 2. Chỉ khi verify xong mới sửa file \`tasks/task-*.md\` (status + tick '[x]' các sub-task và completion criteria liên quan).`);
|
|
78
97
|
lines.push(`> 3. NẾU VỪA HOÀN THÀNH 1 TASK CÓ SỬA SOURCE CODE, BẮT BUỘC cập nhật ngay tài liệu trong \`docs/\` (\`system-architecture.md\` hoặc Changelog) cho đồng bộ.`);
|
|
79
98
|
lines.push(`> CẤM VI PHẠM LUẬT TOLLGATE NÀY NHẰM ĐẢM BẢO TÍNH ĐỒNG BỘ CỦA HỆ THỐNG.`);
|