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.
Files changed (36) hide show
  1. package/docs/project-structure-overview.md +1 -0
  2. package/docs/superpowers/plans/2026-06-12-html-plan-approval.md +1000 -0
  3. package/docs/superpowers/specs/2026-06-12-html-plan-approval-design.md +85 -0
  4. package/docs/superpowers/specs/2026-06-12-stage-discipline-rule-design.md +108 -0
  5. package/package.json +1 -1
  6. package/runtime/BUILD.json +2 -2
  7. package/runtime/agents/SKILL.md +1 -1
  8. package/runtime/agents/workers/codex-worker.md +5 -5
  9. package/runtime/agents/workers/gemini-worker.md +5 -5
  10. package/runtime/prompts/profiles/_implementation-executor.md +2 -0
  11. package/runtime/prompts/profiles/_implementation-verifier.md +1 -0
  12. package/runtime/prompts/profiles/_stage-discipline.md +36 -0
  13. package/runtime/prompts/profiles/implementation-planning.md +1 -0
  14. package/runtime/prompts/profiles/implementation.md +1 -0
  15. package/runtime/prompts/wizard/prompts.ko.json +11 -2
  16. package/runtime/python/okstra_ctl/clarification_items.py +6 -22
  17. package/runtime/python/okstra_ctl/md_table.py +56 -0
  18. package/runtime/python/okstra_ctl/render_final_report.py +8 -1
  19. package/runtime/python/okstra_ctl/report_views.py +155 -20
  20. package/runtime/python/okstra_ctl/user_response.py +55 -0
  21. package/runtime/python/okstra_ctl/wizard.py +116 -11
  22. package/runtime/python/okstra_token_usage/collect.py +5 -3
  23. package/runtime/skills/okstra-report-writer/SKILL.md +3 -2
  24. package/runtime/skills/okstra-run/SKILL.md +1 -1
  25. package/runtime/skills/okstra-team-contract/SKILL.md +1 -1
  26. package/runtime/templates/reports/final-report.template.md +55 -50
  27. package/runtime/templates/reports/report.css +21 -7
  28. package/runtime/templates/reports/report.js +34 -7
  29. package/runtime/templates/reports/user-response.template.md +15 -0
  30. package/runtime/validators/validate-implementation-plan-stages.py +9 -2
  31. package/runtime/validators/validate-report-views.py +2 -1
  32. package/runtime/validators/validate-run.py +6 -17
  33. package/runtime/validators/validate-schedule.py +9 -2
  34. package/runtime/validators/validate_improvement_report.py +2 -1
  35. package/src/install.mjs +73 -2
  36. 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.strip("|").split("|")]
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 writeSkillsManifest(paths.home, skillResult.installed, { dryRun });
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 writeSkillsManifest(paths.home, skillResult.installed, { dryRun: opts.dryRun });
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 });
package/src/uninstall.mjs CHANGED
@@ -18,6 +18,7 @@ const BIN_ENTRYPOINTS = [
18
18
  ];
19
19
 
20
20
  const FALLBACK_SKILL_NAMES = [
21
+ "okstra",
21
22
  "okstra-setup",
22
23
  "okstra-brief",
23
24
  "okstra-run",