okstra 0.73.0 → 0.75.0
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/docs/project-structure-overview.md +1 -0
- package/docs/superpowers/plans/2026-06-12-html-plan-approval.md +1000 -0
- package/docs/superpowers/specs/2026-06-12-html-plan-approval-design.md +85 -0
- package/docs/superpowers/specs/2026-06-12-stage-discipline-rule-design.md +108 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/SKILL.md +1 -1
- package/runtime/agents/workers/codex-worker.md +5 -5
- package/runtime/agents/workers/gemini-worker.md +5 -5
- package/runtime/prompts/profiles/_implementation-executor.md +2 -0
- package/runtime/prompts/profiles/_implementation-verifier.md +1 -0
- package/runtime/prompts/profiles/_stage-discipline.md +36 -0
- package/runtime/prompts/profiles/implementation-planning.md +1 -0
- package/runtime/prompts/profiles/implementation.md +1 -0
- package/runtime/prompts/wizard/prompts.ko.json +11 -2
- package/runtime/python/okstra_ctl/clarification_items.py +6 -22
- package/runtime/python/okstra_ctl/md_table.py +56 -0
- package/runtime/python/okstra_ctl/render_final_report.py +8 -1
- package/runtime/python/okstra_ctl/report_views.py +155 -20
- package/runtime/python/okstra_ctl/user_response.py +55 -0
- package/runtime/python/okstra_ctl/wizard.py +116 -11
- package/runtime/python/okstra_token_usage/collect.py +5 -3
- package/runtime/skills/okstra-report-writer/SKILL.md +3 -2
- package/runtime/skills/okstra-run/SKILL.md +1 -1
- package/runtime/skills/okstra-team-contract/SKILL.md +1 -1
- package/runtime/templates/reports/final-report.template.md +55 -50
- package/runtime/templates/reports/report.css +21 -7
- package/runtime/templates/reports/report.js +34 -7
- package/runtime/templates/reports/user-response.template.md +15 -0
- package/runtime/validators/validate-implementation-plan-stages.py +9 -2
- package/runtime/validators/validate-report-views.py +2 -1
- package/runtime/validators/validate-run.py +6 -17
- package/runtime/validators/validate-schedule.py +9 -2
- package/runtime/validators/validate_improvement_report.py +2 -1
- package/src/install.mjs +73 -2
- package/src/uninstall.mjs +1 -0
|
@@ -23,6 +23,7 @@ from okstra_ctl.improvement_lenses import (
|
|
|
23
23
|
ABSOLUTE_CANDIDATE_CAP,
|
|
24
24
|
SOURCE_WORKERS,
|
|
25
25
|
)
|
|
26
|
+
from okstra_ctl.md_table import split_pipe_row
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
_VERDICT_TOKENS = ("candidates-ready", "no-candidates", "blocked")
|
|
@@ -57,7 +58,7 @@ def _read_section_table(body: str, heading: str) -> list[list[str]]:
|
|
|
57
58
|
s = line.strip()
|
|
58
59
|
if not s.startswith("|") or not s.endswith("|"):
|
|
59
60
|
continue
|
|
60
|
-
cells = [_CELL_ANCHOR_RE.sub("", c).strip() for c in s
|
|
61
|
+
cells = [_CELL_ANCHOR_RE.sub("", c).strip() for c in split_pipe_row(s)]
|
|
61
62
|
if all(set(c) <= set("-: ") for c in cells):
|
|
62
63
|
continue
|
|
63
64
|
rows.append(cells)
|
package/src/install.mjs
CHANGED
|
@@ -167,6 +167,34 @@ async function copyTreeIfChanged(srcRoot, dstRoot, opts) {
|
|
|
167
167
|
return { copied, skipped, missingSource };
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
// Single-file variant of copyTreeIfChanged, used for the lead `okstra` skill
|
|
171
|
+
// whose source is one file (agents/SKILL.md) rather than a skill directory.
|
|
172
|
+
async function copyFileIfChanged(src, dst, opts) {
|
|
173
|
+
const { refresh = false, dryRun = false, mode } = opts ?? {};
|
|
174
|
+
let srcBuf;
|
|
175
|
+
try {
|
|
176
|
+
srcBuf = await fs.readFile(src);
|
|
177
|
+
} catch {
|
|
178
|
+
return { copied: 0, skipped: 0, missingSource: true };
|
|
179
|
+
}
|
|
180
|
+
let needsCopy = refresh;
|
|
181
|
+
if (!needsCopy) {
|
|
182
|
+
try {
|
|
183
|
+
const [srcHash, dstHash] = await Promise.all([hashFile(src), hashFile(dst)]);
|
|
184
|
+
needsCopy = srcHash !== dstHash;
|
|
185
|
+
} catch {
|
|
186
|
+
needsCopy = true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (!needsCopy) return { copied: 0, skipped: 1, missingSource: false };
|
|
190
|
+
if (dryRun) {
|
|
191
|
+
process.stdout.write(`[dry-run] copy ${src} -> ${dst}\n`);
|
|
192
|
+
return { copied: 1, skipped: 0, missingSource: false };
|
|
193
|
+
}
|
|
194
|
+
await writeFileAtomic(dst, srcBuf, mode);
|
|
195
|
+
return { copied: 1, skipped: 0, missingSource: false };
|
|
196
|
+
}
|
|
197
|
+
|
|
170
198
|
async function ensureSymlink(target, linkPath, opts) {
|
|
171
199
|
const { dryRun = false } = opts ?? {};
|
|
172
200
|
try {
|
|
@@ -244,7 +272,8 @@ async function installLinkMode(repoPath, paths, opts) {
|
|
|
244
272
|
}
|
|
245
273
|
|
|
246
274
|
const skillResult = await installSkillsLink(repoAbs, { dryRun, quiet });
|
|
247
|
-
await
|
|
275
|
+
const leadSkillResult = await installLeadSkillLink(repoAbs, { dryRun, quiet });
|
|
276
|
+
await writeSkillsManifest(paths.home, [...skillResult.installed, ...leadSkillResult], { dryRun });
|
|
248
277
|
|
|
249
278
|
const agentResult = await installAgentsLink(repoAbs, { dryRun, quiet });
|
|
250
279
|
await writeAgentsManifest(paths.home, agentResult.installed, { dryRun });
|
|
@@ -487,6 +516,43 @@ async function installSkillsLink(repoAbs, opts) {
|
|
|
487
516
|
return { installed: names };
|
|
488
517
|
}
|
|
489
518
|
|
|
519
|
+
// The lead `okstra` skill source is agents/SKILL.md (frontmatter `name: okstra`);
|
|
520
|
+
// it doubles as the lead agent contract, so it lives under agents/ rather than
|
|
521
|
+
// skills/. Claude Code only discovers skills at ~/.claude/skills/<name>/SKILL.md,
|
|
522
|
+
// so it must be installed there as its own skill dir — otherwise install ships
|
|
523
|
+
// every okstra-* helper skill but not the lead skill, and edits to agents/SKILL.md
|
|
524
|
+
// never reach the running lead.
|
|
525
|
+
const LEAD_SKILL_NAME = "okstra";
|
|
526
|
+
|
|
527
|
+
async function installLeadSkillCopy(runtimeRoot, opts) {
|
|
528
|
+
const { refresh, dryRun, quiet } = opts;
|
|
529
|
+
const src = join(runtimeRoot, "agents", "SKILL.md");
|
|
530
|
+
const dst = join(CLAUDE_SKILLS_DIR, LEAD_SKILL_NAME, "SKILL.md");
|
|
531
|
+
const r = await copyFileIfChanged(src, dst, { refresh, dryRun, mode: 0o644 });
|
|
532
|
+
if (r.missingSource) {
|
|
533
|
+
if (!quiet) process.stdout.write(" skills/okstra: runtime/agents/SKILL.md missing — skipped\n");
|
|
534
|
+
return [];
|
|
535
|
+
}
|
|
536
|
+
if (!quiet) {
|
|
537
|
+
process.stdout.write(` skills/okstra: ${r.copied ? "copied" : "unchanged"} -> ${dst}\n`);
|
|
538
|
+
}
|
|
539
|
+
return [LEAD_SKILL_NAME];
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async function installLeadSkillLink(repoAbs, opts) {
|
|
543
|
+
const { dryRun, quiet } = opts;
|
|
544
|
+
const src = join(repoAbs, "agents", "SKILL.md");
|
|
545
|
+
if (!(await fileExists(src))) {
|
|
546
|
+
if (!quiet) process.stdout.write(" skills/okstra: <repo>/agents/SKILL.md missing — skipped\n");
|
|
547
|
+
return [];
|
|
548
|
+
}
|
|
549
|
+
const dst = join(CLAUDE_SKILLS_DIR, LEAD_SKILL_NAME, "SKILL.md");
|
|
550
|
+
if (!dryRun) await fs.mkdir(join(CLAUDE_SKILLS_DIR, LEAD_SKILL_NAME), { recursive: true });
|
|
551
|
+
const action = await ensureSymlink(src, dst, { dryRun });
|
|
552
|
+
if (!quiet) process.stdout.write(` skills/okstra/SKILL.md: ${action}\n`);
|
|
553
|
+
return [LEAD_SKILL_NAME];
|
|
554
|
+
}
|
|
555
|
+
|
|
490
556
|
function parseInstallArgs(args) {
|
|
491
557
|
const result = {
|
|
492
558
|
dryRun: false,
|
|
@@ -623,7 +689,12 @@ export async function runInstall(args) {
|
|
|
623
689
|
}
|
|
624
690
|
|
|
625
691
|
const skillResult = await installSkillsCopy(runtimeRoot, opts);
|
|
626
|
-
await
|
|
692
|
+
const leadSkillResult = await installLeadSkillCopy(runtimeRoot, opts);
|
|
693
|
+
await writeSkillsManifest(
|
|
694
|
+
paths.home,
|
|
695
|
+
[...skillResult.installed, ...leadSkillResult],
|
|
696
|
+
{ dryRun: opts.dryRun },
|
|
697
|
+
);
|
|
627
698
|
|
|
628
699
|
const agentResult = await installAgentsCopy(runtimeRoot, opts);
|
|
629
700
|
await writeAgentsManifest(paths.home, agentResult.installed, { dryRun: opts.dryRun });
|