bigpowers 2.88.0 → 2.88.2
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 +14 -0
- package/extensions/omp-hooks.ts +26 -16
- package/package.json +6 -7
- package/scripts/generate-epics-wiki.sh +6 -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/omp-smoke.ts +50 -16
- package/scripts/validate-omp-extension.sh +29 -6
package/.pi/package.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [2.88.2](https://github.com/danielvm-git/bigpowers/compare/v2.88.1...v2.88.2) (2026-09-06)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **ci:** run npm ci before verification gates so omp-extension smoke resolves typebox ([#120](https://github.com/danielvm-git/bigpowers/issues/120)) ([9a457e6](https://github.com/danielvm-git/bigpowers/commit/9a457e62959dd0d5cdf5139502adfbf1031179d4))
|
|
7
|
+
|
|
8
|
+
## [2.88.1](https://github.com/danielvm-git/bigpowers/compare/v2.88.0...v2.88.1) (2026-09-05)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **extensions:** prevent omp-hooks from crashing pi at startup ([#119](https://github.com/danielvm-git/bigpowers/issues/119)) ([a487565](https://github.com/danielvm-git/bigpowers/commit/a487565160cbb20fae7ee8d85504898186fbe167))
|
|
14
|
+
|
|
1
15
|
# [2.88.0](https://github.com/danielvm-git/bigpowers/compare/v2.87.8...v2.88.0) (2026-09-01)
|
|
2
16
|
|
|
3
17
|
|
package/extensions/omp-hooks.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// extensions/omp-hooks.ts
|
|
3
3
|
// bigpowers — Single OMP extension entry exposing every source skill as a
|
|
4
4
|
// slash command, a bigpowers_skill LLM tool, and safety policy guards.
|
|
5
|
-
import type { ExtensionAPI } from "@
|
|
5
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { Type } from "typebox";
|
|
6
7
|
import { execSync } from "node:child_process";
|
|
7
8
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
8
9
|
import { dirname, join } from "node:path";
|
|
@@ -236,9 +237,12 @@ async function injectSkillPrompt(
|
|
|
236
237
|
// ---------------------------------------------------------------------------
|
|
237
238
|
|
|
238
239
|
export default function bigpowers(pi: ExtensionAPI) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
// NOTE: Do not call action methods (setLabel, sendUserMessage, setModel, …)
|
|
241
|
+
// in the factory body. pi installs throwing stubs for them during extension
|
|
242
|
+
// loading and only binds real implementations afterward — a load-time call
|
|
243
|
+
// aborts the whole pi startup (BUG-2026-09-05, #119). Only registration
|
|
244
|
+
// methods (registerCommand/registerTool/on/registerFlag) are valid here;
|
|
245
|
+
// perform actions inside event/command handlers via the runtime-bound `pi`.
|
|
242
246
|
|
|
243
247
|
// Discover skills from skills/ in the installed plugin package root.
|
|
244
248
|
const skills = discoverSkills(join(pluginRoot(), "skills"));
|
|
@@ -262,18 +266,24 @@ export default function bigpowers(pi: ExtensionAPI) {
|
|
|
262
266
|
label: "Bigpowers Skill",
|
|
263
267
|
description:
|
|
264
268
|
"Interact with bigpowers source skills. Use 'list' to enumerate available skills, 'get' to retrieve a skill's full markdown, and 'run' to activate a skill with arguments.",
|
|
265
|
-
parameters:
|
|
266
|
-
action:
|
|
267
|
-
.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
.
|
|
275
|
-
|
|
276
|
-
|
|
269
|
+
parameters: Type.Object({
|
|
270
|
+
action: Type.Union(
|
|
271
|
+
[Type.Literal("list"), Type.Literal("get"), Type.Literal("run")],
|
|
272
|
+
{
|
|
273
|
+
description:
|
|
274
|
+
"Operation: list all skills, get one skill, or run a skill with arguments",
|
|
275
|
+
},
|
|
276
|
+
),
|
|
277
|
+
skill: Type.Optional(
|
|
278
|
+
Type.String({
|
|
279
|
+
description: "Skill name — required for 'get' and 'run' actions",
|
|
280
|
+
}),
|
|
281
|
+
),
|
|
282
|
+
args: Type.Optional(
|
|
283
|
+
Type.String({
|
|
284
|
+
description: "User arguments passed to the skill — only used with 'run'",
|
|
285
|
+
}),
|
|
286
|
+
),
|
|
277
287
|
}),
|
|
278
288
|
async execute(_id, params, _signal, _onUpdate, _ctx) {
|
|
279
289
|
const { action, skill: skillName, args } = params;
|
package/package.json
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bigpowers",
|
|
3
|
-
"version": "2.88.
|
|
3
|
+
"version": "2.88.2",
|
|
4
4
|
"description": "73 agent skills synthesizing 17 years of software engineering discipline into a prescriptive methodology for solo developers",
|
|
5
5
|
"main": "index.js",
|
|
6
|
-
"omp": {
|
|
7
|
-
"extensions": [
|
|
8
|
-
"extensions/omp-hooks.ts"
|
|
9
|
-
]
|
|
10
|
-
},
|
|
11
6
|
"scripts": {
|
|
12
7
|
"setup": "node bin/setup.js",
|
|
13
8
|
"compliance": "bash scripts/audit-compliance.sh specs/verifications/features && bash scripts/validate-doctrine.sh && python3 scripts/check-skill-links.py",
|
|
@@ -34,6 +29,9 @@
|
|
|
34
29
|
"pi-package"
|
|
35
30
|
],
|
|
36
31
|
"pi": {
|
|
32
|
+
"extensions": [
|
|
33
|
+
"extensions/omp-hooks.ts"
|
|
34
|
+
],
|
|
37
35
|
"skills": [
|
|
38
36
|
"./.pi/skills"
|
|
39
37
|
],
|
|
@@ -69,6 +67,7 @@
|
|
|
69
67
|
"@semantic-release/git": "^10.0.1",
|
|
70
68
|
"@semantic-release/github": "^11.0.1",
|
|
71
69
|
"@semantic-release/npm": "^12.0.1",
|
|
72
|
-
"semantic-release": "^24.2.0"
|
|
70
|
+
"semantic-release": "^24.2.0",
|
|
71
|
+
"typebox": "^1.3.7"
|
|
73
72
|
}
|
|
74
73
|
}
|
|
@@ -30,6 +30,11 @@ epics_wiki_assign_tier() {
|
|
|
30
30
|
for epic_yaml in "$ROOT"/specs/epics/*/epic.yaml "$ROOT"/specs/epics/archive/*/epic.yaml; do
|
|
31
31
|
[ -f "$epic_yaml" ] || continue
|
|
32
32
|
|
|
33
|
+
# Emit a repo-relative path — an absolute $ROOT path bakes the generating
|
|
34
|
+
# machine/worktree into the committed wiki, producing spurious diffs whenever
|
|
35
|
+
# a different checkout regenerates it (BUG-2026-09-05 follow-up).
|
|
36
|
+
epic_yaml_rel="${epic_yaml#"$ROOT"/}"
|
|
37
|
+
|
|
33
38
|
epic_dir=$(dirname "$epic_yaml")
|
|
34
39
|
epic_slug=$(basename "$epic_dir")
|
|
35
40
|
|
|
@@ -82,7 +87,7 @@ ${refs}
|
|
|
82
87
|
**Epic:** ${epic_id} | **WSJF:** ${wsjf:-0} | **BCP:** ${bcps:-0} | **Status:** ${status:-proposed}
|
|
83
88
|
**Release:** ${release:-unknown} | **Tier:** ${tier}
|
|
84
89
|
|
|
85
|
-
${story_count} stories. See \`${
|
|
90
|
+
${story_count} stories. See \`${epic_yaml_rel}\` for full specifications.
|
|
86
91
|
OKFEOF
|
|
87
92
|
|
|
88
93
|
count=$((count + 1))
|
|
Binary file
|
|
Binary file
|
package/scripts/omp-smoke.ts
CHANGED
|
@@ -2,52 +2,86 @@
|
|
|
2
2
|
// Runtime smoke for extensions/omp-hooks.ts.
|
|
3
3
|
// Imports the extension, registers a fake ExtensionAPI, calls registered hooks,
|
|
4
4
|
// and asserts the slash-command handler awaits injection.
|
|
5
|
+
//
|
|
6
|
+
// Load-phase contract (BUG-2026-09-05, #119): pi installs throwing stubs for
|
|
7
|
+
// ACTION methods during extension loading and only binds real implementations
|
|
8
|
+
// after the factory returns. Registration methods are valid during load. This
|
|
9
|
+
// fake models that contract so a load-time action call (e.g. pi.setLabel())
|
|
10
|
+
// fails here exactly as it does in real pi, instead of silently passing.
|
|
11
|
+
// See pi 0.85.1 src/core/extensions/loader.ts (createExtensionRuntime).
|
|
5
12
|
|
|
6
13
|
import bigpowers from "../extensions/omp-hooks.ts";
|
|
7
14
|
|
|
8
15
|
type AnyObj = Record<string, unknown>;
|
|
9
16
|
type Listener = (...args: unknown[]) => unknown;
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
// pi's verbatim message when an action method is called during load.
|
|
19
|
+
const LOAD_PHASE_ERROR =
|
|
20
|
+
"Extension runtime not initialized. Action methods cannot be called during extension loading.";
|
|
21
|
+
|
|
22
|
+
// True while the factory body runs. Action methods throw while this is set.
|
|
23
|
+
let loading = true;
|
|
17
24
|
|
|
18
25
|
const commands = new Map<string, { description: string; handler: Listener }>();
|
|
19
26
|
const tools: AnyObj[] = [];
|
|
20
27
|
const events = new Map<string, Listener>();
|
|
21
28
|
const sendCalls: AnyObj[] = [];
|
|
22
29
|
|
|
30
|
+
// Action method: rejects if invoked during the loading phase, like real pi.
|
|
31
|
+
function action<T extends Listener>(fn: T): T {
|
|
32
|
+
return ((...args: unknown[]) => {
|
|
33
|
+
if (loading) throw new Error(LOAD_PHASE_ERROR);
|
|
34
|
+
return fn(...args);
|
|
35
|
+
}) as T;
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
const pi: AnyObj = {
|
|
24
|
-
|
|
25
|
-
zod: {
|
|
26
|
-
object: (_shape: AnyObj) => makeZod(),
|
|
27
|
-
string: () => makeZod(),
|
|
28
|
-
enum: () => makeZod(),
|
|
29
|
-
},
|
|
39
|
+
// ----- Registration methods: valid during load -----
|
|
30
40
|
registerCommand: (name: string, spec: { description: string; handler: Listener }) => {
|
|
31
41
|
commands.set(name, spec);
|
|
32
42
|
},
|
|
33
43
|
registerTool: (tool: AnyObj) => {
|
|
34
44
|
tools.push(tool);
|
|
35
45
|
},
|
|
46
|
+
registerShortcut: (_shortcut: string, _opts: AnyObj) => {},
|
|
47
|
+
registerFlag: (_name: string, _opts: AnyObj) => {},
|
|
36
48
|
on: (event: string, handler: Listener) => {
|
|
37
49
|
events.set(event, handler);
|
|
38
50
|
},
|
|
39
|
-
|
|
51
|
+
// registerProvider() calls made in the factory are queued by real pi, not
|
|
52
|
+
// thrown — model that as a permissive no-op.
|
|
53
|
+
registerProvider: (..._args: unknown[]) => {},
|
|
54
|
+
|
|
55
|
+
// ----- Action methods: throw if called during load -----
|
|
56
|
+
setLabel: action((_entryId: string, _label?: string) => {}),
|
|
57
|
+
setSessionName: action((_name: string) => {}),
|
|
58
|
+
getSessionName: action(() => undefined),
|
|
59
|
+
appendEntry: action((_customType: string, _data?: unknown) => {}),
|
|
60
|
+
setModel: action(() => Promise.resolve(true)),
|
|
61
|
+
getActiveTools: action(() => [] as string[]),
|
|
62
|
+
getAllTools: action(() => [] as AnyObj[]),
|
|
63
|
+
setActiveTools: action((_names: string[]) => {}),
|
|
64
|
+
getCommands: action(() => [] as AnyObj[]),
|
|
65
|
+
getThinkingLevel: action(() => "off"),
|
|
66
|
+
setThinkingLevel: action((_level: string) => {}),
|
|
67
|
+
sendUserMessage: action(async (content: string, opts?: { deliverAs?: string }) => {
|
|
40
68
|
sendCalls.push({ kind: "sendUserMessage", content, opts });
|
|
41
69
|
return undefined;
|
|
42
|
-
},
|
|
43
|
-
sendMessage: async (msg: AnyObj, opts?: { triggerTurn?: boolean; deliverAs?: string }) => {
|
|
70
|
+
}),
|
|
71
|
+
sendMessage: action(async (msg: AnyObj, opts?: { triggerTurn?: boolean; deliverAs?: string }) => {
|
|
44
72
|
sendCalls.push({ kind: "sendMessage", msg, opts });
|
|
45
73
|
return undefined;
|
|
46
|
-
},
|
|
74
|
+
}),
|
|
47
75
|
};
|
|
48
76
|
|
|
77
|
+
// Load the extension under the load-phase contract. A load-time action call
|
|
78
|
+
// throws here and fails the smoke test (RED), matching pi's startup abort.
|
|
49
79
|
await bigpowers(pi as unknown as never);
|
|
50
80
|
|
|
81
|
+
// Factory returned successfully: the runtime is now "bound". Action methods
|
|
82
|
+
// are callable from here on (handlers, tool executions).
|
|
83
|
+
loading = false;
|
|
84
|
+
|
|
51
85
|
console.log(JSON.stringify({
|
|
52
86
|
commands: commands.size,
|
|
53
87
|
tool: tools[0]?.name,
|
|
@@ -19,20 +19,22 @@ pass() {
|
|
|
19
19
|
echo "ok : $*"
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
# ── 1. Package has exactly one
|
|
22
|
+
# ── 1. Package has exactly one pi.extensions entry ─────────────────────────
|
|
23
|
+
# pi reads extension paths from the `pi` manifest key (readPiManifest). The
|
|
24
|
+
# legacy `omp.extensions` key was never consumed by pi — see BUG-2026-09-05.
|
|
23
25
|
echo "--- [OMP] manifest: single extension entry ---"
|
|
24
|
-
EXT_COUNT=$(node -e "const p = require('./package.json'); const exts = p.
|
|
26
|
+
EXT_COUNT=$(node -e "const p = require('./package.json'); const exts = p.pi?.extensions; if (!exts) { console.log(0); } else { console.log(exts.length); }")
|
|
25
27
|
if [[ "$EXT_COUNT" -eq 1 ]]; then
|
|
26
|
-
pass "
|
|
28
|
+
pass "pi.extensions has exactly 1 entry"
|
|
27
29
|
else
|
|
28
|
-
fail "
|
|
30
|
+
fail "pi.extensions has $EXT_COUNT entries (expected 1)"
|
|
29
31
|
fi
|
|
30
32
|
|
|
31
33
|
# ── 2. Extension file exists ───────────────────────────────────────────────
|
|
32
34
|
echo "--- [OMP] extension file exists ---"
|
|
33
|
-
EXT_PATH=$(node -e "const p = require('./package.json'); console.log(p.
|
|
35
|
+
EXT_PATH=$(node -e "const p = require('./package.json'); console.log(p.pi?.extensions?.[0] || '')")
|
|
34
36
|
if [[ -z "$EXT_PATH" ]]; then
|
|
35
|
-
fail "No
|
|
37
|
+
fail "No pi.extensions entry found"
|
|
36
38
|
else
|
|
37
39
|
if [[ -f "$EXT_PATH" ]]; then
|
|
38
40
|
pass "extension file '$EXT_PATH' exists"
|
|
@@ -93,6 +95,27 @@ if [[ -n "$EXT_PATH" && -f "$EXT_PATH" ]]; then
|
|
|
93
95
|
fi
|
|
94
96
|
fi
|
|
95
97
|
|
|
98
|
+
# ── 6. Runtime load smoke (load-phase contract, BUG-2026-09-05) ────────────
|
|
99
|
+
# Loads the extension through a fake ExtensionAPI that models pi's load-phase
|
|
100
|
+
# guard: action methods (setLabel, sendUserMessage, …) throw during the factory
|
|
101
|
+
# body, exactly as real pi does. Catches load-time action calls that abort pi
|
|
102
|
+
# startup (#119). Requires a Node with TypeScript type-stripping (>=22.6).
|
|
103
|
+
echo "--- [OMP] runtime load smoke (scripts/omp-smoke.ts) ---"
|
|
104
|
+
SMOKE="$REPO_ROOT/scripts/omp-smoke.ts"
|
|
105
|
+
if [[ ! -f "$SMOKE" ]]; then
|
|
106
|
+
fail "omp-smoke.ts not found at $SMOKE"
|
|
107
|
+
elif ! command -v node >/dev/null 2>&1; then
|
|
108
|
+
echo "warn: node unavailable — skipping runtime load smoke"
|
|
109
|
+
else
|
|
110
|
+
SMOKE_ERR=$(mktemp)
|
|
111
|
+
if node "$SMOKE" >/dev/null 2>"$SMOKE_ERR"; then
|
|
112
|
+
pass "extension loads without calling action methods during load"
|
|
113
|
+
else
|
|
114
|
+
fail "runtime load smoke failed — $(head -3 "$SMOKE_ERR" 2>/dev/null | tr '\n' ' ')"
|
|
115
|
+
fi
|
|
116
|
+
rm -f "$SMOKE_ERR"
|
|
117
|
+
fi
|
|
118
|
+
|
|
96
119
|
# ── Summary ────────────────────────────────────────────────────────────────
|
|
97
120
|
echo "---"
|
|
98
121
|
if [[ "$ERRORS" -eq 0 ]]; then
|