claude-devkit-cli 1.2.4 → 1.3.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.
@@ -66,7 +66,11 @@ function main() {
66
66
 
67
67
  // Skip files outside the project directory (e.g. ~/.claude/plans/)
68
68
  const filePath = payload.tool_input?.file_path;
69
- if (filePath && !path.resolve(filePath).startsWith(process.cwd())) process.exit(0);
69
+ if (filePath) {
70
+ const projectDir = process.cwd() + path.sep;
71
+ const resolved = path.resolve(filePath);
72
+ if (!resolved.startsWith(projectDir) && resolved !== process.cwd()) process.exit(0);
73
+ }
70
74
 
71
75
  const oldStr = payload.tool_input?.old_string;
72
76
  const newStr = payload.tool_input?.new_string;
@@ -60,8 +60,9 @@ function main() {
60
60
  if (!filePath) process.exit(0);
61
61
 
62
62
  // Skip files outside the project directory (e.g. ~/.claude/plans/)
63
- const projectDir = process.cwd();
64
- if (!path.resolve(filePath).startsWith(projectDir)) process.exit(0);
63
+ const projectDir = process.cwd() + path.sep;
64
+ const resolvedFile = path.resolve(filePath);
65
+ if (!resolvedFile.startsWith(projectDir) && resolvedFile !== process.cwd()) process.exit(0);
65
66
 
66
67
  // Skip excluded patterns
67
68
  if (matchesExclude(filePath)) process.exit(0);
@@ -80,7 +81,6 @@ function main() {
80
81
  try {
81
82
  const stat = fs.statSync(filePath);
82
83
  if (stat.size > MAX_BYTES) {
83
- // >1MB = definitely over threshold, warn without exact count
84
84
  const rel = path.relative(process.cwd(), filePath);
85
85
  process.stdout.write(JSON.stringify({
86
86
  continue: true,
@@ -88,7 +88,7 @@ function main() {
88
88
  hookEventName: "PostToolUse",
89
89
  additionalContext: `Warning: ${rel} is ${Math.round(stat.size / 1024)}KB. Consider splitting into smaller modules.`,
90
90
  },
91
- }));
91
+ }) + "\n");
92
92
  process.exit(0);
93
93
  }
94
94
  const buf = fs.readFileSync(filePath);
@@ -112,7 +112,7 @@ function main() {
112
112
  hookEventName: "PostToolUse",
113
113
  additionalContext: warning,
114
114
  },
115
- })
115
+ }) + "\n"
116
116
  );
117
117
  }
118
118
 
@@ -19,42 +19,44 @@ set -euo pipefail
19
19
  INPUT=$(cat)
20
20
  [[ -z "$INPUT" ]] && exit 0
21
21
 
22
- # Check Node.js availability
23
- if ! command -v node &>/dev/null; then
24
- echo "WARNING: path-guard disabled Node.js not found." >&2
25
- exit 0
26
- fi
22
+ # Extract command from JSON — try node first, fall back to grep/sed
23
+ extract_command() {
24
+ if command -v node &>/dev/null; then
25
+ printf '%s' "$1" | node -e "
26
+ try {
27
+ const d = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
28
+ const cmd = d.tool_input?.command;
29
+ if (typeof cmd === 'string') process.stdout.write(cmd);
30
+ } catch {}
31
+ " 2>/dev/null
32
+ else
33
+ # Lightweight fallback: extract "command":"..." from JSON
34
+ printf '%s' "$1" | grep -o '"command":"[^"]*"' | head -1 | sed 's/^"command":"//;s/"$//' 2>/dev/null
35
+ fi
36
+ }
27
37
 
28
- # Parse JSON with inline Node.js (avoids jq dependency)
29
- COMMAND=$(printf '%s' "$INPUT" | node -e "
30
- try {
31
- const d = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
32
- const cmd = d.tool_input?.command;
33
- if (typeof cmd === 'string') process.stdout.write(cmd);
34
- else process.exit(0);
35
- } catch { process.exit(0); }
36
- " 2>/dev/null) || exit 0
38
+ COMMAND=$(extract_command "$INPUT") || exit 0
37
39
 
38
40
  [[ -z "$COMMAND" ]] && exit 0
39
41
 
40
42
  # ─── Blocked directory patterns ─────────────────────────────────────
41
43
 
42
- BLOCKED="node_modules"
43
- BLOCKED+="|__pycache__"
44
- BLOCKED+="|\.git/objects"
45
- BLOCKED+="|\.git/refs"
46
- BLOCKED+="|dist/"
47
- BLOCKED+="|build/"
44
+ # Use word boundaries (\b) and explicit path separators to avoid substring false positives
45
+ # e.g. "build/" should not match "rebuild/src" or "my-build-tool"
46
+ BLOCKED="(^|[ /])node_modules(/|$| )"
47
+ BLOCKED+="|(__pycache__)"
48
+ BLOCKED+="|\.git/(objects|refs)"
49
+ BLOCKED+="|(^|[ /])dist(/|$| )"
50
+ BLOCKED+="|(^|[ /])build(/|$| )"
48
51
  BLOCKED+="|\.next/"
49
- BLOCKED+="|vendor/"
50
- BLOCKED+="|Pods/"
52
+ BLOCKED+="|(^|[ /])vendor(/|$| )"
53
+ BLOCKED+="|(^|[ /])Pods(/|$| )"
51
54
  BLOCKED+="|\.build/"
52
55
  BLOCKED+="|DerivedData"
53
56
  BLOCKED+="|\.gradle/"
54
- BLOCKED+="|target/debug"
55
- BLOCKED+="|target/release"
57
+ BLOCKED+="|target/(debug|release)(/|$| )"
56
58
  BLOCKED+="|\.nuget"
57
- BLOCKED+="|\.cache"
59
+ BLOCKED+="|\.cache(/|$| )"
58
60
 
59
61
  # Append project-specific patterns from env
60
62
  if [[ -n "${PATH_GUARD_EXTRA:-}" ]]; then
@@ -7,7 +7,7 @@
7
7
  # Environment:
8
8
  # SELF_REVIEW_ENABLED — set to "false" to disable (default: true)
9
9
 
10
- set -euo pipefail
10
+ # No set -euo pipefail — this hook must NEVER fail
11
11
 
12
12
  # Check if disabled
13
13
  if [[ "${SELF_REVIEW_ENABLED:-true}" == "false" ]]; then
@@ -57,13 +57,9 @@ is_sensitive() {
57
57
  return 0 ;;
58
58
  serviceAccountKey.json|service-account*.json)
59
59
  return 0 ;;
60
- .mcp.json|config.json)
61
- # config.json only sensitive inside .docker/ — check full path
62
- if [[ "$basename" == "config.json" ]]; then
63
- [[ "$filepath" == *".docker/config.json"* ]] && return 0
64
- else
65
- return 0
66
- fi
60
+ config.json)
61
+ # config.json only sensitive inside .docker/
62
+ [[ "$filepath" == *".docker/config.json"* ]] && return 0
67
63
  ;;
68
64
  esac
69
65
 
@@ -151,11 +147,28 @@ warn_with_message() {
151
147
  exit 0
152
148
  }
153
149
 
150
+ # ─── Fast-path: skip obviously safe files ──────────────────────────
151
+
152
+ fast_path_safe() {
153
+ local ext="${1##*.}"
154
+ case "$ext" in
155
+ md|ts|tsx|js|jsx|css|scss|html|svg|json|yaml|yml|toml|xml|txt|sh|py|rb|rs|go|java|kt|swift|c|cpp|h|hpp|cs|vue|svelte|astro)
156
+ # But json could be sensitive — check name
157
+ if [[ "$ext" == "json" ]]; then
158
+ return 1 # not fast-path safe, need full check
159
+ fi
160
+ return 0 ;;
161
+ esac
162
+ return 1
163
+ }
164
+
154
165
  # ─── Check direct file access (Read/Write/Edit) → BLOCK ────────────
155
166
 
156
167
  if [[ -n "$FILE_PATH" ]]; then
157
- if is_sensitive "$FILE_PATH" || check_agentignore "$FILE_PATH"; then
158
- block_with_message "$FILE_PATH"
168
+ if ! fast_path_safe "$FILE_PATH"; then
169
+ if is_sensitive "$FILE_PATH" || check_agentignore "$FILE_PATH"; then
170
+ block_with_message "$FILE_PATH"
171
+ fi
159
172
  fi
160
173
  fi
161
174
 
@@ -1,6 +1,6 @@
1
1
  # Development Workflow Reference
2
2
 
3
- > Spec-first development: every change follows SPEC TEST PLAN → CODE + TESTS → BUILD PASS.
3
+ > Spec-first development: every change follows SPEC (with acceptance scenarios) → CODE + TESTS → BUILD PASS.
4
4
 
5
5
  ---
6
6
 
@@ -12,12 +12,11 @@ When: Building something that doesn't exist yet (no code, no spec).
12
12
 
13
13
  ```
14
14
  Step 1 → /mf-plan "description of feature"
15
- Generates: docs/specs/<feature>.md (spec)
16
- docs/test-plans/<feature>.md (test plan)
15
+ Generates: docs/specs/<feature>/<feature>.md (spec with acceptance scenarios)
17
16
  Answers validation questions about assumptions.
18
- Review both before proceeding.
17
+ Review before proceeding.
19
18
 
20
- Step 2 → (Optional) /mf-challenge docs/test-plans/<feature>.md
19
+ Step 2 → (Optional) /mf-challenge docs/specs/<feature>/<feature>.md
21
20
  Adversarial review: spawns hostile reviewers to find flaws.
22
21
  Recommended for complex features, auth, data pipelines.
23
22
  Skip for simple CRUD or small features.
@@ -36,13 +35,12 @@ Step 5 → /mf-commit
36
35
  When: Changing behavior, adding options, refactoring logic.
37
36
 
38
37
  ```
39
- Step 1 → Update spec FIRST: docs/specs/<feature>.md
40
- Describe what's changing and why.
38
+ Step 1 → /mf-plan docs/specs/<feature>/<feature>.md "description of changes"
39
+ Mode C handles everything: snapshot → classification → change report → apply.
40
+ Do NOT manually edit the spec before running /mf-plan — it creates the
41
+ snapshot first, then applies changes. Manual edits bypass snapshot protection.
41
42
 
42
- Step 2 → /mf-plan docs/specs/<feature>.md
43
- Updates the test plan with new/modified/removed test cases.
44
-
45
- Step 3 → Implement code changes.
43
+ Step 2 → Implement code changes.
46
44
  /mf-test
47
45
  Fix until green.
48
46
 
@@ -67,8 +65,10 @@ Optional → If the bug reveals an undocumented edge case, update the spec.
67
65
  When: Deleting a feature, removing deprecated code.
68
66
 
69
67
  ```
70
- Step 1 → Mark spec sections as removed in docs/specs/<feature>.md
71
- (Or archive the entire file if the feature is fully removed.)
68
+ Step 1 → /mf-plan docs/specs/<feature>/<feature>.md "remove stories S-XXX"
69
+ Mode C creates a snapshot (removing stories = M2 = Major),
70
+ then marks stories and AS as removed in the spec.
71
+ (Or if removing the entire feature: archive the directory.)
72
72
 
73
73
  Step 2 → Delete production code and related test code.
74
74
 
@@ -96,7 +96,7 @@ Is this a brand new feature (no existing spec or code)?
96
96
  │ └─ No
97
97
  │ ├─ Are you removing/deprecating code?
98
98
  │ │ ├─ Yes → Remove Feature workflow.
99
- │ │ └─ No → Update Feature workflow. Start by editing the spec.
99
+ │ │ └─ No → Update Feature workflow. Start with /mf-plan.
100
100
  │ │
101
101
  │ └─ Is the change very small (< 5 lines, behavior unchanged)?
102
102
  │ └─ Yes → Skip spec update. Just /mf-test and /mf-commit.
@@ -115,8 +115,8 @@ I just implemented [brief description].
115
115
  Files changed: [list files]
116
116
 
117
117
  Based on:
118
- - Spec: docs/specs/<feature>.md (section §X)
119
- - Test plan: docs/test-plans/<feature>.md
118
+ - Spec: docs/specs/<feature>/<feature>.md (section §X)
119
+ - Acceptance scenarios: docs/specs/<feature>/<feature>.md (section ## Stories)
120
120
 
121
121
  Write tests for the part I just implemented.
122
122
  Only tests related to this change — not the entire feature.
@@ -130,11 +130,11 @@ If the spec seems incomplete, note what's missing but don't change it.
130
130
  I'm about to change [description of change].
131
131
  Affected files: [list]
132
132
 
133
- 1. Update the spec: docs/specs/<feature>.md
134
- 2. Update the test plan: docs/test-plans/<feature>.md (only affected test cases)
135
- 3. Implement the code change
136
- 4. Update tests to match
137
- 5. Build and run → fix until green
133
+ 1. /mf-plan docs/specs/<feature>/<feature>.md "description of changes"
134
+ (handles snapshot + spec update + acceptance scenarios)
135
+ 2. Implement the code change
136
+ 3. Update tests to match
137
+ 4. Build and run → fix until green
138
138
  ```
139
139
 
140
140
  ### Template C — Test-First Bug Fix
@@ -157,11 +157,11 @@ Actual: [broken behavior]
157
157
  Removing: [feature name]
158
158
  Files to delete: [list]
159
159
 
160
- 1. Mark relevant spec sections as removed
161
- 2. Mark related test plan entries as removed
162
- 3. Delete production code
163
- 4. Delete test code
164
- 5. Run full test suite → fix cascading breaks
160
+ 1. /mf-plan docs/specs/<feature>/<feature>.md "remove stories S-XXX, S-YYY"
161
+ (handles snapshot + marks stories and AS as removed)
162
+ 2. Delete production code
163
+ 3. Delete test code
164
+ 4. Run full test suite → fix cascading breaks
165
165
  ```
166
166
 
167
167
  ---
@@ -178,7 +178,7 @@ Files to delete: [list]
178
178
  | `/mf-challenge` (adversarial) | 15–30k | After /mf-plan, for complex features |
179
179
  | Full audit (manual) | 100k+ | Before release, quarterly |
180
180
 
181
- **Rule of thumb:** Daily work uses templates + `/test` → low token cost.
181
+ **Rule of thumb:** Daily work uses templates + `/mf-test` → low token cost.
182
182
  Save `/mf-plan` and full audits for significant milestones.
183
183
 
184
184
  ---
@@ -187,8 +187,8 @@ Save `/mf-plan` and full audits for significant milestones.
187
187
 
188
188
  Use this as a PR review checklist (enforce manually or via CI):
189
189
 
190
- - [ ] **Spec updated?** If production behavior changed, `docs/specs/` should have changes.
191
- - [ ] **Test plan updated?** If spec changed, `docs/test-plans/` should have changes.
190
+ - [ ] **Spec updated?** If production behavior changed, `docs/specs/<feature>/` should have changes.
191
+ - [ ] **Acceptance scenarios updated?** If spec behavior changed, AS in spec should reflect it.
192
192
  - [ ] **Tests pass?** `bash scripts/build-test.sh` exits 0.
193
193
  - [ ] **No dead tests?** Removed production code → removed corresponding tests.
194
194
  - [ ] **Coverage not decreased?** (Optional, per-team decision.)
@@ -201,16 +201,15 @@ Use this as a PR review checklist (enforce manually or via CI):
201
201
 
202
202
  | Change | Must Also Update |
203
203
  |--------|-----------------|
204
- | Production code behavior changed | Spec + test plan + tests |
205
- | Spec updated | Test plan + tests (if behavior changed) |
206
- | Test plan updated | Tests (implement new/modified test cases) |
207
- | Code removed | Remove related tests. Mark spec as removed. |
204
+ | Production code behavior changed | Spec (including acceptance scenarios) + tests |
205
+ | Spec updated | Acceptance scenarios + tests (if behavior changed) |
206
+ | Code removed | Remove related tests. Mark spec and AS as removed. |
208
207
  | Bug fix | Add test. Update spec if edge case was undocumented. |
209
208
 
210
209
  **Never acceptable:**
211
210
  - Code changed, spec not updated (spec drift)
212
211
  - Code changed, tests not updated (untested code)
213
- - Spec changed, tests not updated (plan drift)
212
+ - Spec changed, acceptance scenarios or tests not updated (AS drift)
214
213
  - Code removed, dead tests remain (orphaned tests)
215
214
 
216
215
  **Acceptable shortcut** for changes under 5 lines with no behavior change: