clud-bug 0.6.32 → 0.6.34

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/README.md CHANGED
@@ -17,6 +17,9 @@ npx clud-bug init
17
17
  git add .claude .github/workflows/clud-bug-review.yml
18
18
  git commit -m "Add clud-bug PR review"
19
19
  git push
20
+
21
+ # OR — install the full SkDD toolchain (clud-bug + logmind) in one go:
22
+ npx clud-bug init --with-skdd # subprocesses to `pip install logmind && logmind init`
20
23
  ```
21
24
 
22
25
  Then in your repo on GitHub:
package/bin/clud-bug.js CHANGED
@@ -37,6 +37,11 @@ function parseArgs(argv) {
37
37
  // Defaults to true (artifact mode); `--no-artifacts` forces local
38
38
  // .clud-bug.json read (matches v0.6.28 behavior).
39
39
  artifacts: true,
40
+ // v0.6.33: unified-install mirror — `clud-bug init --with-skdd` also
41
+ // subprocesses to `pip install logmind && logmind init` so Node-first
42
+ // users get the same one-command bootstrap as Python-first users
43
+ // (logmind v0.6.8's --with-skdd is the symmetric counterpart).
44
+ withSkdd: false,
40
45
  };
41
46
  for (let i = 0; i < argv.length; i++) {
42
47
  const a = argv[i];
@@ -58,6 +63,7 @@ function parseArgs(argv) {
58
63
  else if (a === '--stdin') args.stdin = true;
59
64
  else if (a === '--health') args.health = true;
60
65
  else if (a === '--no-artifacts') args.artifacts = false;
66
+ else if (a === '--with-skdd') args.withSkdd = true;
61
67
  else args._.push(a);
62
68
  }
63
69
  return args;
@@ -70,6 +76,7 @@ Usage:
70
76
 
71
77
  Commands:
72
78
  init Open field season: survey the repo, pin baseline specimens, write the workflows.
79
+ Pass \`--with-skdd\` to also install logmind in one go (requires Python + pip).
73
80
  list Show your collection (baseline / from skills.sh / custom).
74
81
  add <source/name> Pin one new specimen from skills.sh (e.g. vercel-labs/skills/next-best-practices).
75
82
  remove <slug> Unpin a clud-bug-managed specimen (refuses to touch your custom ones).
@@ -496,11 +503,95 @@ async function runInit(args) {
496
503
  log(' • Add `clud-bug-review` to your branch protection required checks for full enforcement.');
497
504
  log(' • Opt out by setting "strictMode": false in .claude/skills/.clud-bug.json.');
498
505
 
506
+ // v0.6.33 — opt-in unified install (mirror of logmind v0.6.8). When
507
+ // --with-skdd is passed, subprocess to `pip install logmind` + `logmind init`
508
+ // so Node-first users get the same one-command bootstrap as Python-first
509
+ // users do via `logmind init --with-skdd`.
510
+ // ANTI-LOOP: invoke `logmind init` (NOT `logmind init --with-skdd`).
511
+ // Each opt-in flag only goes one level — no mutual recursion possible.
512
+ if (args.withSkdd) {
513
+ await installLogmindViaPip();
514
+ }
515
+
499
516
  // Final agent-friendly summary line (always emitted, even with --quiet).
500
517
  const version = await readPkgVersion();
501
518
  ok(`initialized: .claude/skills/ ${chosen.length} specimens, workflow @v${version}`);
502
519
  }
503
520
 
521
+ async function installLogmindViaPip() {
522
+ // `spawn` is already imported at module top (line 5). No dynamic
523
+ // re-import needed.
524
+ //
525
+ // Warnings use process.stderr.write directly (always emitted, even
526
+ // under CLUD_BUG_QUIET=1) — recovery hints MUST surface to the user.
527
+ // The standard `log()` is for progress chatter which quiet suppresses.
528
+
529
+ // Find pip via fallback chain (pip → pip3 → python -m pip).
530
+ const pipCmd = await findPipCommand();
531
+ if (!pipCmd) {
532
+ process.stderr.write(
533
+ '\nWarning: --with-skdd requested but no `pip`/`pip3`/`python` found on PATH.\n' +
534
+ ' Install Python 3.10+ (https://python.org), then run:\n' +
535
+ ' pip install logmind && logmind init\n' +
536
+ ' Or skip this flag if you only want clud-bug standalone.\n'
537
+ );
538
+ return;
539
+ }
540
+
541
+ log('');
542
+ log(`→ --with-skdd: installing logmind (${pipCmd.join(' ')} install logmind)`);
543
+
544
+ const installCode = await new Promise((resolve) => {
545
+ const child = spawn(pipCmd[0], [...pipCmd.slice(1), 'install', 'logmind'], { stdio: 'inherit' });
546
+ child.on('error', () => resolve(127));
547
+ child.on('close', (code) => resolve(code ?? 1));
548
+ });
549
+ if (installCode !== 0) {
550
+ process.stderr.write(
551
+ `Warning: \`pip install logmind\` exited ${installCode}.\n` +
552
+ ' clud-bug side succeeded; logmind install is incomplete.\n' +
553
+ ' Inspect output above and re-run manually if needed.\n'
554
+ );
555
+ return;
556
+ }
557
+
558
+ log(`→ --with-skdd: running \`logmind init\` to scaffold the logmind side`);
559
+ const initCode = await new Promise((resolve) => {
560
+ const child = spawn('logmind', ['init'], { stdio: 'inherit' });
561
+ child.on('error', () => resolve(127));
562
+ child.on('close', (code) => resolve(code ?? 1));
563
+ });
564
+ if (initCode !== 0) {
565
+ process.stderr.write(
566
+ `Warning: \`logmind init\` exited ${initCode}. logmind install completed `
567
+ + `but init scaffolding is incomplete. Re-run manually to finish.\n`
568
+ );
569
+ return;
570
+ }
571
+ log('✓ logmind installed via --with-skdd');
572
+ }
573
+
574
+ async function findPipCommand() {
575
+ // Try pip → pip3 → python -m pip → python3 -m pip in order. First one
576
+ // that responds to --version wins. Returns array form for spawn().
577
+ // `spawn` is already imported at module top.
578
+ const candidates = [
579
+ ['pip'],
580
+ ['pip3'],
581
+ ['python', '-m', 'pip'],
582
+ ['python3', '-m', 'pip'],
583
+ ];
584
+ for (const cmd of candidates) {
585
+ const ok = await new Promise((resolve) => {
586
+ const child = spawn(cmd[0], [...cmd.slice(1), '--version'], { stdio: ['ignore', 'ignore', 'ignore'] });
587
+ child.on('error', () => resolve(false));
588
+ child.on('close', (code) => resolve(code === 0));
589
+ });
590
+ if (ok) return cmd;
591
+ }
592
+ return null;
593
+ }
594
+
504
595
  async function promptForSkills(recommended) {
505
596
  const rl = createInterface({ input, output });
506
597
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clud-bug",
3
- "version": "0.6.32",
3
+ "version": "0.6.34",
4
4
  "description": "Skill-driven Claude PR review. Ship a brand-voice skill, get brand reviews. Each finding cites the skill that motivated it. CLI installs the workflow + a baseline kit; add more from skills.sh.",
5
5
  "homepage": "https://cludbug.dev",
6
6
  "bugs": "https://github.com/thrillmade/clud-bug/issues",
@@ -366,7 +366,7 @@ jobs:
366
366
  # Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
367
367
  - name: Strict mode — fail check on critical findings
368
368
  if: success()
369
- uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.32
369
+ uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.34
370
370
  with:
371
371
  github-token: ${{ secrets.GITHUB_TOKEN }}
372
372
  # v0.6.22 / 0.0.O: summary now posted by github-actions[bot].
@@ -366,7 +366,7 @@ jobs:
366
366
  # Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
367
367
  - name: Strict mode — fail check on critical findings
368
368
  if: success()
369
- uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.32
369
+ uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.34
370
370
  with:
371
371
  github-token: ${{ secrets.GITHUB_TOKEN }}
372
372
  # v0.6.22 / 0.0.O: summary now posted by github-actions[bot].
@@ -632,7 +632,7 @@ jobs:
632
632
  # Letting the action's own failure fail the check is louder and right.
633
633
  - name: Strict mode — fail check on critical findings
634
634
  if: success()
635
- uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.32
635
+ uses: thrillmade/clud-bug/.github/actions/strict-mode-gate@v0.6.34
636
636
  with:
637
637
  github-token: ${{ secrets.GITHUB_TOKEN }}
638
638
  # v0.6.22 / 0.0.O: the summary is now posted by the workflow