@loneexile/omp-superpowers 7.0.1-omp.3 → 7.0.1-omp.4
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/.pi/extensions/superpowers.ts +2 -114
- package/CHANGELOG.md +8 -1
- package/README.md +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
@@ -10,15 +10,8 @@ const extensionDir = dirname(fileURLToPath(import.meta.url));
|
|
|
10
10
|
const packageRoot = resolve(extensionDir, "../..");
|
|
11
11
|
const skillsDir = resolve(packageRoot, "skills");
|
|
12
12
|
const bootstrapSkillPath = resolve(skillsDir, "using-superpowers", "SKILL.md");
|
|
13
|
-
const SKILL_URL = /skill:\/\/([a-z0-9][a-z0-9-]*)/g;
|
|
14
|
-
// An SDD workspace directory, as sdd-workspace prints it or as part of a path
|
|
15
|
-
// into it; the ledger is its progress.md. Dot-names (`.gitignore`) and the old
|
|
16
|
-
// flat `.superpowers/sdd/progress.md`, which SDD's own text mentions, are not plans.
|
|
17
|
-
const SDD_WORKSPACE = /[^\s"'`()<>[\]\\]*\.superpowers\/sdd\/[A-Za-z0-9_-]+(?![\w.-])/g;
|
|
18
|
-
const MAX_SKILLS_NAMED = 4;
|
|
19
13
|
|
|
20
14
|
let cachedBootstrap: string | null | undefined;
|
|
21
|
-
let cachedSkillNames: Set<string> | undefined;
|
|
22
15
|
|
|
23
16
|
export default function superpowersPiExtension(pi: ExtensionAPI) {
|
|
24
17
|
let injectBootstrap = true;
|
|
@@ -35,13 +28,6 @@ export default function superpowersPiExtension(pi: ExtensionAPI) {
|
|
|
35
28
|
injectBootstrap = true;
|
|
36
29
|
});
|
|
37
30
|
|
|
38
|
-
// The compaction summary does not carry skill text. Name the superpowers
|
|
39
|
-
// skills read before it and the plan's ledger, so the agent reads them again.
|
|
40
|
-
pi.on("session.compacting", async (event) => {
|
|
41
|
-
const context = compactionContext(event?.messages ?? []);
|
|
42
|
-
return context.length > 0 ? { context } : undefined;
|
|
43
|
-
});
|
|
44
|
-
|
|
45
31
|
pi.on("session_switch", async () => {
|
|
46
32
|
injectBootstrap = true;
|
|
47
33
|
});
|
|
@@ -59,17 +45,9 @@ export default function superpowersPiExtension(pi: ExtensionAPI) {
|
|
|
59
45
|
const bootstrap = getBootstrapContent();
|
|
60
46
|
if (!bootstrap) return;
|
|
61
47
|
|
|
62
|
-
const content: Array<{ type: "text"; text: string }> = [{ type: "text", text: bootstrap }];
|
|
63
|
-
// After a compaction, name the superpowers skills and ledger used before it.
|
|
64
|
-
// Read from the session branch, so it holds whatever the compaction method
|
|
65
|
-
// and also after a restart or `omp -c`.
|
|
66
|
-
if (event.messages.some((message) => field(message, "role") === "compactionSummary")) {
|
|
67
|
-
const note = compactionContext(messagesBeforeLatestCompaction(ctx));
|
|
68
|
-
if (note.length > 0) content.push({ type: "text", text: note.join("\n") });
|
|
69
|
-
}
|
|
70
48
|
const bootstrapMessage = {
|
|
71
49
|
role: "user" as const,
|
|
72
|
-
content,
|
|
50
|
+
content: [{ type: "text" as const, text: bootstrap }],
|
|
73
51
|
timestamp: Date.now(),
|
|
74
52
|
};
|
|
75
53
|
|
|
@@ -133,96 +111,6 @@ From omp 18.4.12 every subagent is told to leave builds, tests and smoke runs to
|
|
|
133
111
|
If your tools include \`vibe_spawn\` but not \`task\`, omp's vibe mode is on and you are its director: you have none of the \`task\`, \`bash\`, \`edit\` or \`write\` tools named above. Read the "Vibe mode" section of \`skill://using-superpowers/references/pi-tools.md\` before acting on them.`;
|
|
134
112
|
}
|
|
135
113
|
|
|
136
|
-
// Reads one property of an unknown value; undefined unless it is an object holding it.
|
|
137
|
-
function field(value: unknown, key: string): unknown {
|
|
138
|
-
return value !== null && typeof value === "object" && key in value ? Reflect.get(value, key) : undefined;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function messagesBeforeLatestCompaction(ctx: unknown): unknown[] {
|
|
142
|
-
const sessionManager = field(ctx, "sessionManager");
|
|
143
|
-
const getBranch = field(sessionManager, "getBranch");
|
|
144
|
-
if (typeof getBranch !== "function") return [];
|
|
145
|
-
let entries: unknown;
|
|
146
|
-
try {
|
|
147
|
-
entries = getBranch.call(sessionManager);
|
|
148
|
-
} catch {
|
|
149
|
-
return [];
|
|
150
|
-
}
|
|
151
|
-
if (!Array.isArray(entries)) return [];
|
|
152
|
-
let end = -1;
|
|
153
|
-
entries.forEach((entry, index) => {
|
|
154
|
-
if (field(entry, "type") === "compaction") end = index;
|
|
155
|
-
});
|
|
156
|
-
return entries
|
|
157
|
-
.slice(0, Math.max(end, 0))
|
|
158
|
-
.filter((entry) => field(entry, "type") === "message")
|
|
159
|
-
.map((entry) => field(entry, "message"));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function compactionContext(messages: unknown[]): string[] {
|
|
163
|
-
const ours = superpowersSkillNames();
|
|
164
|
-
const skills: string[] = [];
|
|
165
|
-
let ledger: string | undefined;
|
|
166
|
-
|
|
167
|
-
for (const message of messages) {
|
|
168
|
-
for (const part of contentParts(message)) {
|
|
169
|
-
if (part.type === "toolCall") {
|
|
170
|
-
const args = JSON.stringify(part.arguments ?? {});
|
|
171
|
-
for (const match of args.matchAll(SKILL_URL)) {
|
|
172
|
-
const name = match[1];
|
|
173
|
-
if (!ours.has(name)) continue;
|
|
174
|
-
const seen = skills.indexOf(name);
|
|
175
|
-
if (seen !== -1) skills.splice(seen, 1);
|
|
176
|
-
skills.push(name);
|
|
177
|
-
}
|
|
178
|
-
ledger = lastLedgerPath(args) ?? ledger;
|
|
179
|
-
} else if (part.type === "text" && typeof part.text === "string") {
|
|
180
|
-
ledger = lastLedgerPath(part.text) ?? ledger;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (skills.length === 0) return [];
|
|
186
|
-
const named = skills.slice(-MAX_SKILLS_NAMED).map((name) => `skill://${name}`).join(", ");
|
|
187
|
-
const lines = [
|
|
188
|
-
`Superpowers skills in use before this compaction (most recent last): ${named}. This summary does not carry their text: before your next step, read again the ones you are still following.`,
|
|
189
|
-
];
|
|
190
|
-
if (ledger) {
|
|
191
|
-
lines.push(`Superpowers ledger: ${ledger}. Read it before you continue; it records what is done and what is next.`);
|
|
192
|
-
}
|
|
193
|
-
return lines;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function superpowersSkillNames(): Set<string> {
|
|
197
|
-
if (cachedSkillNames) return cachedSkillNames;
|
|
198
|
-
try {
|
|
199
|
-
cachedSkillNames = new Set(
|
|
200
|
-
readdirSync(skillsDir, { withFileTypes: true })
|
|
201
|
-
.filter((entry) => entry.isDirectory() && existsSync(resolve(skillsDir, entry.name, "SKILL.md")))
|
|
202
|
-
.map((entry) => entry.name),
|
|
203
|
-
);
|
|
204
|
-
} catch {
|
|
205
|
-
cachedSkillNames = new Set();
|
|
206
|
-
}
|
|
207
|
-
// The bootstrap re-injects using-superpowers after every compaction already.
|
|
208
|
-
cachedSkillNames.delete("using-superpowers");
|
|
209
|
-
return cachedSkillNames;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function contentParts(message: unknown): Array<{ type?: unknown; text?: unknown; arguments?: unknown }> {
|
|
213
|
-
const content = field(message, "content");
|
|
214
|
-
if (typeof content === "string") return [{ type: "text", text: content }];
|
|
215
|
-
if (!Array.isArray(content)) return [];
|
|
216
|
-
return content
|
|
217
|
-
.filter((part) => part !== null && typeof part === "object")
|
|
218
|
-
.map((part) => ({ type: field(part, "type"), text: field(part, "text"), arguments: field(part, "arguments") }));
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function lastLedgerPath(text: string): string | undefined {
|
|
222
|
-
const matches = text.match(SDD_WORKSPACE);
|
|
223
|
-
return matches ? `${matches[matches.length - 1]}/progress.md` : undefined;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
114
|
function messageContainsBootstrap(message: unknown): boolean {
|
|
227
115
|
const content = (message as { content?: unknown }).content;
|
|
228
116
|
if (typeof content === "string") return content.includes(BOOTSTRAP_MARKER);
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,12 @@ A version is `<upstream version>-omp.<n>`: the upstream release this fork is syn
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [7.0.1-omp.4] - 2026-10-24
|
|
10
|
+
|
|
11
|
+
### Removed
|
|
12
|
+
|
|
13
|
+
- **The post-compaction note from 7.0.1-omp.3.** After a compaction, the bootstrap no longer names the superpowers skills read before it and the SDD ledger, and `session.compacting` no longer hands those lines to the summarizer; the extension is back to 7.0.1-omp.2's. The note never changed behavior in testing on omp 18.12.6, with memory and the advisor off. With and without it, a brainstorm-then-build run compacted mid-conversation re-read the brainstorming skill in 5 of 5 runs. A six-task subagent-driven-development run compacted after its first wave re-read the SDD skill and the ledger before its next dispatch, redid no finished task and finished the plan in 2 of 2 runs, both with the usual summary and with a summary told to leave out skills, plans and ledgers plus a 1,000-token kept window. The note's only visible effect was an extra read of every skill it listed: both of those last runs also re-read `using-git-worktrees`. It has little to add on omp: each compaction method tried kept the list of files the session wrote, which in an SDD run includes the ledger under `.superpowers/sdd/`. With a plain-text summary that named the skill nowhere, the model still named the SDD skill and the ledger path from memory, without tools.
|
|
14
|
+
|
|
9
15
|
## [7.0.1-omp.3] - 2026-10-24
|
|
10
16
|
|
|
11
17
|
### Added
|
|
@@ -143,7 +149,8 @@ First release, synced to upstream [v6.4.2](https://github.com/obra/superpowers/b
|
|
|
143
149
|
- The fork's rewritten subagent-driven-development text invokes its helper scripts through `bash` as well, so a package extractor that drops exec bits cannot break them.
|
|
144
150
|
- `sdd-final-reviewer` carries upstream's new review rules from `code-reviewer.md`, the template it replaces in SDD's final review: the spec is a vision document (behavior it is silent on is graded by what a reasonable person expects), and a "Declined to judge" section that the controller rules on and ledgers like a plan conflict.
|
|
145
151
|
|
|
146
|
-
[Unreleased]: https://github.com/LoneExile/omp-superpowers/compare/v7.0.1-omp.
|
|
152
|
+
[Unreleased]: https://github.com/LoneExile/omp-superpowers/compare/v7.0.1-omp.4...HEAD
|
|
153
|
+
[7.0.1-omp.4]: https://github.com/LoneExile/omp-superpowers/releases/tag/v7.0.1-omp.4
|
|
147
154
|
[7.0.1-omp.3]: https://github.com/LoneExile/omp-superpowers/releases/tag/v7.0.1-omp.3
|
|
148
155
|
[7.0.1-omp.2]: https://github.com/LoneExile/omp-superpowers/releases/tag/v7.0.1-omp.2
|
|
149
156
|
[7.0.1-omp.1]: https://github.com/LoneExile/omp-superpowers/releases/tag/v7.0.1-omp.1
|
package/README.md
CHANGED
|
@@ -221,7 +221,7 @@ In short: the omp extension and `pi-tools.md` describe omp's real tools; five `s
|
|
|
221
221
|
<details>
|
|
222
222
|
<summary><b>File by file</b></summary>
|
|
223
223
|
|
|
224
|
-
- `.pi/extensions/superpowers.ts` — the injected mapping names `task`, `todo`, the `sdd-*` roster, and the no-`model:`-field rule; it also re-arms on `session_switch`, skips subagent sessions, and ignores an `agent_end` that will auto-continue; it tells a dispatching session to name the checks each subagent must run, since omp 18.4.12 tells subagents to leave builds, tests and smoke runs to the main agent unless their assignment explicitly instructs them; its last paragraph points a vibe-mode director (tools include `vibe_spawn` but not `task`) at the *Vibe mode* section of `pi-tools.md
|
|
224
|
+
- `.pi/extensions/superpowers.ts` — the injected mapping names `task`, `todo`, the `sdd-*` roster, and the no-`model:`-field rule; it also re-arms on `session_switch`, skips subagent sessions, and ignores an `agent_end` that will auto-continue; it tells a dispatching session to name the checks each subagent must run, since omp 18.4.12 tells subagents to leave builds, tests and smoke runs to the main agent unless their assignment explicitly instructs them; its last paragraph points a vibe-mode director (tools include `vibe_spawn` but not `task`) at the *Vibe mode* section of `pi-tools.md`
|
|
225
225
|
- `.omp-plugin/plugin.json` — new. Marks the repo root as an omp-native plugin so a marketplace or `--plugin-dir` install keeps each agent's `model:` line
|
|
226
226
|
- `skills/using-superpowers/references/pi-tools.md` — same, as the reference doc; it also gains a *Checks* subsection (the 18.4.12 subagent hand-off rule, and why every dispatch names its checks) and a *Vibe mode* section: what a director keeps and lacks, how skill actions map onto `vibe_*` and workers, and the worktree and request-budget limits
|
|
227
227
|
- `skills/brainstorming/` — a defined small-change path: after the in-chat approval the session builds it itself on a branch: the full suite before changing anything, test first, the covering tests and the suite again, then one commit of only its own files and hunks, with no design document, plan or subagents (a new behavior such as a `--quiet` flag can be small; security, auth, concurrency, money, migrations, changes one revert cannot undo, new config files or stored formats, changes to existing behavior, new interfaces other code builds on and new dependencies never are, and none of them takes the quick-task route however complete the request); a request is a quick task only if doing it leaves no choice the person would see (a format, a default, wording, bad input) for the agent to make; each question asks one thing, with any reason before it; a playback rides along with the next question instead of waiting for its own answer; for software, the plan is written right after the builder check so the design and the plan get one approval. Of upstream v7.0.1's retune it keeps reading the project first, the new first message and the sharper questions; the approaches ride with the last playback chunk, whose one question is the pick, instead of going out in a message of their own; upstream's short design doc and plan for every small change are not taken
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loneexile/omp-superpowers",
|
|
3
|
-
"version": "7.0.1-omp.
|
|
3
|
+
"version": "7.0.1-omp.4",
|
|
4
4
|
"description": "obra/superpowers adapted for Oh My Pi (omp): a truthful task/todo tool mapping and five subagent-driven-development agents, each with its own model, thinking level, and tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|