bigpowers 2.87.7 → 2.87.8
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/.gemini/extensions/bigpowers/gemini-extension.json +1 -1
- package/.pi/package.json +1 -1
- package/CHANGELOG.md +7 -0
- package/README.md +19 -0
- package/bin/bigpowers.js +8 -0
- package/bin/init.js +39 -0
- package/package.json +1 -1
- package/scripts/lib/__pycache__/link_utils.cpython-312.pyc +0 -0
- package/scripts/lib/__pycache__/srp_engine_links.cpython-312.pyc +0 -0
- package/scripts/lib/install-helpers.js +53 -1
- package/scripts/test-install-helpers.js +88 -0
package/.pi/package.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [2.87.8](https://github.com/danielvm-git/bigpowers/compare/v2.87.7...v2.87.8) (2026-09-01)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **cli:** bigpowers init provisions scripts/ + specs/ into consumer projects ([#117](https://github.com/danielvm-git/bigpowers/issues/117)) ([37bc0bf](https://github.com/danielvm-git/bigpowers/commit/37bc0bf72875cd66898d35138870982c8af2d310)), closes [#116](https://github.com/danielvm-git/bigpowers/issues/116) [#116](https://github.com/danielvm-git/bigpowers/issues/116) [#114](https://github.com/danielvm-git/bigpowers/issues/114) [#116](https://github.com/danielvm-git/bigpowers/issues/116) [#114](https://github.com/danielvm-git/bigpowers/issues/114)
|
|
7
|
+
|
|
1
8
|
## [2.87.7](https://github.com/danielvm-git/bigpowers/compare/v2.87.6...v2.87.7) (2026-09-01)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -216,6 +216,25 @@ pi install .
|
|
|
216
216
|
pi install npm:bigpowers
|
|
217
217
|
```
|
|
218
218
|
|
|
219
|
+
### Provision a consumer project (`bigpowers init`)
|
|
220
|
+
|
|
221
|
+
pi's package contract registers **skills and prompts only** — it has no resource
|
|
222
|
+
type for arbitrary project files, and no lifecycle script runs on install. Skills
|
|
223
|
+
reference the package's `scripts/*.sh` tooling project-relative (e.g.
|
|
224
|
+
`bash scripts/run-skill-verify.sh`, verify gates like `test -f scripts/... &&
|
|
225
|
+
test -d specs/bugs`), so after installing the package, run **one command per
|
|
226
|
+
consumer project** to provision what the skills expect:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
cd <your-project>
|
|
230
|
+
npx bigpowers init # or: bigpowers init
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
This symlinks `<project>/scripts` → the installed package's `scripts/` tree and
|
|
234
|
+
scaffolds `specs/bugs/` + `specs/verifications/`. It refuses (never clobbers) a
|
|
235
|
+
`scripts/` you already own; `bigpowers init --remove` undoes it. The same step
|
|
236
|
+
applies to `npm i -g bigpowers` / `npx bigpowers setup` consumers.
|
|
237
|
+
|
|
219
238
|
**What you get:**
|
|
220
239
|
- **pi skills** in `.pi/skills/` (one per SKILL.md) — loaded automatically into pi's system prompt as `<available_skills>`
|
|
221
240
|
- **pi prompt templates** in `.pi/prompts/` — slash commands like `/survey-context`, `/plan-work`
|
package/bin/bigpowers.js
CHANGED
|
@@ -75,6 +75,13 @@ if (cmd === 'setup' || cmd === 'install') {
|
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
if (cmd === 'init') {
|
|
79
|
+
// Provision the CURRENT project (cwd): scripts/ link + specs/ scaffolding (#116)
|
|
80
|
+
const initScript = path.join(ROOT, 'bin', 'init.js');
|
|
81
|
+
require(initScript);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
78
85
|
if (cmd === 'update') {
|
|
79
86
|
// Pull the newest release first (global installs), then re-sync + refresh symlinks.
|
|
80
87
|
selfUpdateGlobalPackage();
|
|
@@ -106,6 +113,7 @@ bigpowers — agent skills for spec-driven, test-first development
|
|
|
106
113
|
|
|
107
114
|
Commands:
|
|
108
115
|
bigpowers setup Install skills into ~/.claude/skills/
|
|
116
|
+
bigpowers init Provision the CURRENT project: scripts/ link + specs/ scaffolding (run per project)
|
|
109
117
|
bigpowers update Fetch the latest release (global installs) + re-sync and refresh symlinks
|
|
110
118
|
bigpowers status Show installed version and skill count (warns if a newer release exists)
|
|
111
119
|
bigpowers help This message
|
package/bin/init.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bigpowers init — provision the CURRENT project (cwd) for skill tooling.
|
|
3
|
+
// story: BUG-2026-08-31-pi-scripts-provisioning (#116)
|
|
4
|
+
//
|
|
5
|
+
// Package installs (pi install npm:bigpowers, npm i -g, npx) register skills
|
|
6
|
+
// but never provision a project-local scripts/ tree — pi's package contract
|
|
7
|
+
// has no resource type for project files and no lifecycle script runs — so
|
|
8
|
+
// SKILL.md commands (`bash scripts/*.sh`) and `→ verify:` gates fail in every
|
|
9
|
+
// consumer project. `bigpowers init` (or `npx bigpowers init`) links the
|
|
10
|
+
// package's scripts/ into the project and scaffolds specs/{bugs,verifications}.
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const ROOT = path.dirname(path.dirname(__filename));
|
|
17
|
+
const { initProject, initProjectRemove } = require('../scripts/lib/install-helpers.js');
|
|
18
|
+
|
|
19
|
+
if (process.argv.includes('--remove')) {
|
|
20
|
+
initProjectRemove(ROOT);
|
|
21
|
+
console.log('bigpowers init --remove: removed the managed scripts/ link');
|
|
22
|
+
console.log(' (and specs/ scaffolding dirs only where untouched).');
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const result = initProject(ROOT);
|
|
28
|
+
if (result.skipped === 'cwd-is-package') {
|
|
29
|
+
console.log('bigpowers init: already inside the bigpowers package — nothing to provision.');
|
|
30
|
+
} else {
|
|
31
|
+
console.log('bigpowers init: linked scripts/ → package scripts tree.');
|
|
32
|
+
console.log('bigpowers init: ensured specs/bugs/ + specs/verifications/ scaffolding.');
|
|
33
|
+
console.log('Skill commands and verify gates (fix-bug, verify-work, wire-ci, …) now resolve here.');
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
// e.g. Refusing to replace a user-owned scripts/ — surface it, don't stack-trace.
|
|
37
|
+
console.error(`bigpowers init: ${e.message}`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -36,7 +36,11 @@ function assertReplaceable(dst) {
|
|
|
36
36
|
}
|
|
37
37
|
if (st.isSymbolicLink()) {
|
|
38
38
|
const target = fs.readlinkSync(dst);
|
|
39
|
-
|
|
39
|
+
// Path-separator boundary: a string-prefix sibling of the package root
|
|
40
|
+
// (e.g. <ROOT>-evil) is NOT managed by this package.
|
|
41
|
+
if (target === REPO_ROOT || target.startsWith(REPO_ROOT + path.sep)) {
|
|
42
|
+
return; // managed by bigpowers — safe to re-link
|
|
43
|
+
}
|
|
40
44
|
}
|
|
41
45
|
throw new Error(
|
|
42
46
|
`Refusing to replace ${dst}: it exists and is not a bigpowers-managed symlink. ` +
|
|
@@ -416,6 +420,52 @@ function installLocal(tool, repoRoot) {
|
|
|
416
420
|
}
|
|
417
421
|
}
|
|
418
422
|
|
|
423
|
+
// ── Project provisioning (bigpowers init) ────────────────────────────────────
|
|
424
|
+
|
|
425
|
+
// Provision the CURRENT project (cwd) with the package's scripts/ tree and the
|
|
426
|
+
// specs/ scaffolding skill bodies assume. Package installs (pi, npm -g, npx)
|
|
427
|
+
// register skills but pi's package contract has no resource type for project
|
|
428
|
+
// files and no lifecycle script runs — so `bash scripts/*.sh` invocations and
|
|
429
|
+
// `→ verify:` gates in SKILL.md files fail outside a bigpowers checkout. (#116)
|
|
430
|
+
function initProject(repoRoot) {
|
|
431
|
+
const cwd = process.cwd();
|
|
432
|
+
if (path.resolve(cwd) === path.resolve(repoRoot)) {
|
|
433
|
+
return { skipped: 'cwd-is-package' };
|
|
434
|
+
}
|
|
435
|
+
linkDir(path.join(repoRoot, 'scripts'), path.join(cwd, 'scripts'));
|
|
436
|
+
const created = [];
|
|
437
|
+
for (const rel of [path.join('specs', 'bugs'), path.join('specs', 'verifications')]) {
|
|
438
|
+
const dir = path.join(cwd, rel);
|
|
439
|
+
if (!fs.existsSync(dir)) {
|
|
440
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
441
|
+
created.push(rel);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return { skipped: null, scriptsLinked: true, created };
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Inverse of initProject: remove ONLY the managed scripts symlink and specs/
|
|
448
|
+
// scaffolding dirs we created (and only while untouched — user content survives).
|
|
449
|
+
function initProjectRemove(repoRoot) {
|
|
450
|
+
const cwd = process.cwd();
|
|
451
|
+
const scriptsLink = path.join(cwd, 'scripts');
|
|
452
|
+
try {
|
|
453
|
+
if (fs.lstatSync(scriptsLink).isSymbolicLink()
|
|
454
|
+
&& fs.readlinkSync(scriptsLink) === path.join(repoRoot, 'scripts')) {
|
|
455
|
+
fs.unlinkSync(scriptsLink);
|
|
456
|
+
}
|
|
457
|
+
} catch {}
|
|
458
|
+
for (const rel of [path.join('specs', 'bugs'), path.join('specs', 'verifications'), 'specs']) {
|
|
459
|
+
const dir = path.join(cwd, rel);
|
|
460
|
+
try {
|
|
461
|
+
if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()
|
|
462
|
+
&& fs.readdirSync(dir).length === 0) {
|
|
463
|
+
fs.rmdirSync(dir);
|
|
464
|
+
}
|
|
465
|
+
} catch {}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
419
469
|
// ── Uninstall per tool ───────────────────────────────────────────────────────
|
|
420
470
|
|
|
421
471
|
function uninstallTool(toolId, repoRoot) {
|
|
@@ -694,6 +744,8 @@ module.exports = {
|
|
|
694
744
|
linkHook,
|
|
695
745
|
installGlobal,
|
|
696
746
|
installLocal,
|
|
747
|
+
initProject,
|
|
748
|
+
initProjectRemove,
|
|
697
749
|
uninstallTool,
|
|
698
750
|
removeSymlink,
|
|
699
751
|
detectExistingInstall,
|
|
@@ -269,6 +269,94 @@ try {
|
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
// story: BUG-2026-08-31-pi-scripts-provisioning (#116)
|
|
273
|
+
// A package consumer project needs bigpowers' scripts/ + specs/ scaffolding
|
|
274
|
+
// so SKILL.md bodies ("bash scripts/run-skill-verify.sh") and verify gates
|
|
275
|
+
// ("test -f scripts/... && test -d specs/bugs") resolve outside a checkout.
|
|
276
|
+
{
|
|
277
|
+
const { initProject, initProjectRemove } = require('../scripts/lib/install-helpers.js');
|
|
278
|
+
const savedInitCwd = process.cwd();
|
|
279
|
+
const consumerDir = mkdtempSync(path.join(os.tmpdir(), 'bp-init-project-'));
|
|
280
|
+
try {
|
|
281
|
+
process.chdir(consumerDir);
|
|
282
|
+
|
|
283
|
+
// (a) init provisions scripts/ + specs/ scaffolding
|
|
284
|
+
initProject(ROOT);
|
|
285
|
+
assert.ok(
|
|
286
|
+
fs.lstatSync(path.join(consumerDir, 'scripts')).isSymbolicLink(),
|
|
287
|
+
'init must symlink scripts/ into the consumer project'
|
|
288
|
+
);
|
|
289
|
+
assert.strictEqual(
|
|
290
|
+
fs.readlinkSync(path.join(consumerDir, 'scripts')),
|
|
291
|
+
path.join(ROOT, 'scripts'),
|
|
292
|
+
'scripts symlink must point at the package scripts tree'
|
|
293
|
+
);
|
|
294
|
+
assert.ok(
|
|
295
|
+
fs.existsSync(path.join(consumerDir, 'scripts', 'run-skill-verify.sh')),
|
|
296
|
+
'scripts/run-skill-verify.sh must resolve through the link (skill verify gate)'
|
|
297
|
+
);
|
|
298
|
+
assert.ok(
|
|
299
|
+
fs.existsSync(path.join(consumerDir, 'specs', 'bugs')),
|
|
300
|
+
'init must scaffold specs/bugs (fix-bug verify gate probes it)'
|
|
301
|
+
);
|
|
302
|
+
assert.ok(
|
|
303
|
+
fs.existsSync(path.join(consumerDir, 'specs', 'verifications')),
|
|
304
|
+
'init must scaffold specs/verifications'
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
// (b) init --remove cleans managed artifacts only
|
|
308
|
+
initProjectRemove(ROOT);
|
|
309
|
+
assert.ok(
|
|
310
|
+
!fs.existsSync(path.join(consumerDir, 'scripts')),
|
|
311
|
+
'init --remove must remove the managed scripts symlink'
|
|
312
|
+
);
|
|
313
|
+
assert.ok(
|
|
314
|
+
!fs.existsSync(path.join(consumerDir, 'specs')),
|
|
315
|
+
'init --remove must remove the untouched scaffolded specs tree'
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
// (c) refuses a pre-existing non-managed scripts/ (never clobbers)
|
|
319
|
+
fs.mkdirSync(path.join(consumerDir, 'scripts'));
|
|
320
|
+
fs.writeFileSync(path.join(consumerDir, 'scripts', 'mine.sh'), 'echo mine\n');
|
|
321
|
+
assert.throws(
|
|
322
|
+
() => initProject(ROOT),
|
|
323
|
+
/Refusing to replace/,
|
|
324
|
+
'init must refuse a user-owned scripts/ dir'
|
|
325
|
+
);
|
|
326
|
+
assert.ok(
|
|
327
|
+
fs.existsSync(path.join(consumerDir, 'scripts', 'mine.sh')),
|
|
328
|
+
'user-owned scripts/ content must survive the refusal'
|
|
329
|
+
);
|
|
330
|
+
} finally {
|
|
331
|
+
process.chdir(savedInitCwd);
|
|
332
|
+
rmSync(consumerDir, { recursive: true, force: true });
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// (d) CLI dispatch drift guard: `bigpowers init` must stay wired
|
|
336
|
+
const bigpowersSrc = fs.readFileSync(path.join(ROOT, 'bin', 'bigpowers.js'), 'utf8');
|
|
337
|
+
assert.ok(
|
|
338
|
+
/'init'/.test(bigpowersSrc),
|
|
339
|
+
"bin/bigpowers.js must dispatch the 'init' command"
|
|
340
|
+
);
|
|
341
|
+
assert.ok(
|
|
342
|
+
fs.existsSync(path.join(ROOT, 'bin', 'init.js')),
|
|
343
|
+
'bin/init.js must exist'
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// story: BUG-2026-08-31-pi-scripts-provisioning (follow-up from PR #114 review)
|
|
348
|
+
// The managed-symlink check must not treat a string-prefix sibling of the
|
|
349
|
+
// package root (e.g. <ROOT>-evil) as managed — needs a path-separator boundary.
|
|
350
|
+
{
|
|
351
|
+
const dst = path.join(tmpHome, 'prefix-boundary-hook.sh');
|
|
352
|
+
fs.symlinkSync(path.join(`${ROOT}-evil`, 'x.sh'), dst); // dangling foreign link
|
|
353
|
+
assert.throws(
|
|
354
|
+
() => linkHook(rtkSrc, dst),
|
|
355
|
+
/Refusing to replace/,
|
|
356
|
+
'managed-symlink check must require a path-separator boundary (prefix sibling is NOT managed)'
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
272
360
|
console.log('test-install-helpers: ALL PASS');
|
|
273
361
|
} finally {
|
|
274
362
|
rmSync(tmpHome, { recursive: true, force: true });
|