mustard-claude 3.1.23 → 3.1.25

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mustard-claude",
3
- "version": "3.1.23",
3
+ "version": "3.1.25",
4
4
  "description": "Framework-agnostic CLI for Claude Code project setup",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,7 +4,16 @@
4
4
 
5
5
  ## Trigger
6
6
 
7
- `/scan` or `/scan <subproject>`
7
+ `/scan`, `/scan <subproject>`, `/scan --force`, `/scan <subproject> --force`
8
+
9
+ ## Flags
10
+
11
+ - `--force` — descarta tudo o que é `<!-- mustard:generated -->` e regera do zero. Semântica:
12
+ - Ignora o incremental skip do Step 1/C: **todos** os subprojetos são reprocessados, independente de hash match ou `gitDirty`.
13
+ - Bypassa o fast-path de §2.6 (Bootstrap): sempre regenera `.claude/CLAUDE.md` e afins.
14
+ - Repassa "FORCE MODE" aos Task agents do Step 3 (eles apagam `{subproject}/.claude/skills/*/` com header `mustard:generated` antes de regerar).
15
+ - Roda sempre os dois comandos de §4.7 (`sync-registry.js --force` + `skill-generator.js --force`), mesmo com registry já v4.0.
16
+ - Skills sem o header `mustard:generated` (user-authored) são **preservadas**.
8
17
 
9
18
  ## Execution Model
10
19
 
@@ -49,6 +58,7 @@ Parse JSON output → list of `{ name, path, role, agent, stackSummary, gitDirty
49
58
  4. **Hash mismatch OR gitDirty** → include in agent launch list (dirty files indicate changes the previous scan may not have captured)
50
59
  5. If ALL subprojects can be skipped → skip to step 4 (Update CLAUDE.md) + step 5 (Compile) directly
51
60
  6. **No old cache** → scan ALL subprojects (first run)
61
+ 7. **`--force` ativo** → ignore tudo acima: marque **todos** os subprojetos como `needs-rescan`, não compare hashes, não pule nada.
52
62
 
53
63
  **Module-level incremental** (when subproject hash changed):
54
64
  - Compare `moduleHashes[subproject][module]` with cached values
@@ -98,7 +108,7 @@ If `.claude/docs/` exists and contains `.md` files:
98
108
 
99
109
  ### 2.6. Bootstrap (if needed)
100
110
 
101
- **Fast-path**: If root `CLAUDE.md` exists AND `.claude/entity-registry.json` exists → skip to step 3 (Launch Agents).
111
+ **Fast-path**: If root `CLAUDE.md` exists AND `.claude/entity-registry.json` exists AND `--force` is **not** active → skip to step 3 (Launch Agents).
102
112
  Bootstrap only runs on first scan or when foundational files are missing.
103
113
 
104
114
  Otherwise create foundational files:
@@ -202,6 +212,12 @@ Path: {path}
202
212
  Role: {role}
203
213
  Stack: {stackSummary}
204
214
 
215
+ FORCE MODE (only when /scan was invoked with --force):
216
+ - Before generating skills, scan {path}/.claude/skills/ and delete every subdirectory
217
+ whose SKILL.md contains "<!-- mustard:generated" (preserve user-authored skills that
218
+ lack that marker).
219
+ - Also delete any pre-existing _backup/ under {path}/.claude/commands/ to avoid stacking stale backups.
220
+
205
221
  Tasks:
206
222
  1. Read existing knowledge from {path}/.claude/commands/ and {path}/CLAUDE.md
207
223
  2. Backup generated files to {path}/.claude/commands/_backup/
@@ -330,6 +346,8 @@ Mark all with `<!-- mustard:generated -->`. Overwrite on next scan.
330
346
 
331
347
  After agent-generated skills (4.6), run the registry-based skill generator to create structural pattern skills from `_patterns`:
332
348
 
349
+ > With `--force`, **always** run both commands — even if `entity-registry.json` already exists at v4.0.
350
+
333
351
  ```bash
334
352
  node .claude/scripts/sync-registry.js --force
335
353
  node .claude/scripts/skill-generator.js --force
@@ -349,7 +367,7 @@ These skills are derived from **detected patterns** (not hardcoded). They comple
349
367
  **Skip conditions:**
350
368
  - `entity-registry.json` version < 4.0 → skip (registry not populated)
351
369
  - `skill-generator.js` not present → skip
352
- - Pattern skill already exists and was NOT generated by mustard → skip (user-edited)
370
+ - Pattern skill already exists and was NOT generated by mustard → skip (user-edited, unless `--force` — but `skill-generator.js` already preserves user-authored skills by checking the `mustard:generated` header)
353
371
 
354
372
  ### 4. Update CLAUDE.md files
355
373
 
@@ -518,26 +518,44 @@ function getSubmodulePaths() {
518
518
  }
519
519
 
520
520
  /**
521
- * Fallback: scan root directory for folders that contain a CLAUDE.md file.
521
+ * Fallback: scan recursively (BFS, max depth 3) for folders that contain a
522
+ * CLAUDE.md file. Stops descending into a branch as soon as a CLAUDE.md is
523
+ * found, so nested workspaces (e.g. subproject/.claude/CLAUDE.md) don't get
524
+ * double-registered.
525
+ *
526
+ * Depth 3 covers common monorepo shapes: apps/X/, services/api/v1/.
527
+ * Fully agnostic — no hardcoded directory names.
522
528
  */
523
529
  function scanForSubprojects() {
524
- const paths = [];
525
- try {
526
- const entries = fs.readdirSync(ROOT, { withFileTypes: true });
530
+ const IGNORE = new Set([
531
+ "node_modules", "bin", "obj", "dist", ".next",
532
+ "_backup", "migrations", ".claude", ".git",
533
+ ]);
534
+ const MAX_DEPTH = 3;
535
+ const results = [];
536
+
537
+ function walk(absDir, relDir, depth) {
538
+ if (depth > MAX_DEPTH) return;
539
+ if (depth > 0 && fs.existsSync(path.join(absDir, "CLAUDE.md"))) {
540
+ results.push(relDir.split(path.sep).join("/"));
541
+ return;
542
+ }
543
+ let entries;
544
+ try {
545
+ entries = fs.readdirSync(absDir, { withFileTypes: true });
546
+ } catch {
547
+ return;
548
+ }
527
549
  for (const entry of entries) {
528
550
  if (!entry.isDirectory()) continue;
529
551
  if (entry.name.startsWith(".")) continue;
530
- if (entry.name === "node_modules") continue;
531
-
532
- const claudePath = path.join(ROOT, entry.name, "CLAUDE.md");
533
- if (fs.existsSync(claudePath)) {
534
- paths.push(entry.name);
535
- }
552
+ if (IGNORE.has(entry.name)) continue;
553
+ walk(path.join(absDir, entry.name), path.join(relDir, entry.name), depth + 1);
536
554
  }
537
- } catch {
538
- // ignore
539
555
  }
540
- return paths;
556
+
557
+ walk(ROOT, "", 0);
558
+ return results;
541
559
  }
542
560
 
543
561
  // ---------------------------------------------------------------------------