okstra 0.82.1 → 0.84.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 (77) hide show
  1. package/README.kr.md +7 -6
  2. package/README.md +6 -6
  3. package/docs/kr/architecture.md +8 -7
  4. package/docs/kr/cli.md +2 -2
  5. package/docs/kr/performance-improvement-plan-v2.md +14 -14
  6. package/docs/project-structure-overview.md +7 -8
  7. package/docs/superpowers/plans/2026-06-15-coding-preflight-pack-dispatch-path.md +504 -0
  8. package/docs/superpowers/plans/2026-06-15-internal-skill-migration-final-fixups.md +342 -0
  9. package/docs/superpowers/plans/2026-06-15-internal-skill-migration-fixups.md +258 -0
  10. package/docs/superpowers/plans/2026-06-15-internal-skill-migration-remaining-fixups.md +387 -0
  11. package/docs/superpowers/plans/2026-06-15-internal-skill-resource-migration.md +749 -0
  12. package/docs/superpowers/plans/2026-06-15-worker-prompt-anchor-final-fixups.md +828 -0
  13. package/docs/superpowers/plans/2026-06-15-worker-prompt-header-error-contract.md +490 -0
  14. package/docs/task-process/README.md +1 -1
  15. package/docs/task-process/error-analysis.md +1 -1
  16. package/docs/task-process/final-verification.md +1 -1
  17. package/docs/task-process/implementation-planning.md +1 -1
  18. package/docs/task-process/implementation.md +1 -1
  19. package/docs/task-process/release-handoff.md +1 -1
  20. package/docs/task-process/requirements-discovery.md +2 -2
  21. package/package.json +1 -1
  22. package/runtime/BUILD.json +2 -2
  23. package/runtime/agents/TODO.md +2 -0
  24. package/runtime/agents/workers/claude-worker.md +8 -8
  25. package/runtime/agents/workers/codex-worker.md +8 -8
  26. package/runtime/agents/workers/gemini-worker.md +8 -8
  27. package/runtime/agents/workers/report-writer-worker.md +2 -2
  28. package/runtime/bin/lib/okstra/globals.sh +0 -1
  29. package/runtime/bin/okstra-wrapper-status.py +1 -1
  30. package/runtime/prompts/launch.template.md +5 -3
  31. package/runtime/{skills/okstra-context-loader/SKILL.md → prompts/lead/context-loader.md} +7 -14
  32. package/runtime/{skills/okstra-convergence/SKILL.md → prompts/lead/convergence.md} +12 -19
  33. package/runtime/{agents/SKILL.md → prompts/lead/okstra-lead-contract.md} +53 -59
  34. package/runtime/{skills/okstra-report-writer/SKILL.md → prompts/lead/report-writer.md} +12 -19
  35. package/runtime/{skills/okstra-team-contract/SKILL.md → prompts/lead/team-contract.md} +13 -19
  36. package/runtime/{skills → prompts}/okstra-coding-preflight/languages/python.md +2 -2
  37. package/runtime/{skills → prompts}/okstra-coding-preflight/languages/rust.md +1 -1
  38. package/runtime/{skills/okstra-coding-preflight/SKILL.md → prompts/okstra-coding-preflight/overview.md} +27 -38
  39. package/runtime/prompts/okstra-coding-preflight/scripts/preedit-check.sh +79 -0
  40. package/runtime/prompts/profiles/_coding-conventions-preflight.md +2 -2
  41. package/runtime/prompts/profiles/_common-contract.md +2 -2
  42. package/runtime/prompts/profiles/_implementation-executor.md +2 -2
  43. package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
  44. package/runtime/prompts/profiles/error-analysis.md +2 -2
  45. package/runtime/prompts/profiles/final-verification.md +1 -1
  46. package/runtime/prompts/profiles/implementation-planning.md +4 -4
  47. package/runtime/prompts/profiles/requirements-discovery.md +2 -2
  48. package/runtime/python/okstra_ctl/codex_dispatch.py +12 -61
  49. package/runtime/python/okstra_ctl/context_cost.py +14 -11
  50. package/runtime/python/okstra_ctl/dispatch_core.py +36 -13
  51. package/runtime/python/okstra_ctl/paths.py +27 -1
  52. package/runtime/python/okstra_ctl/render.py +62 -8
  53. package/runtime/python/okstra_ctl/run.py +8 -6
  54. package/runtime/python/okstra_ctl/worker_prompt_headers.py +126 -0
  55. package/runtime/python/okstra_token_usage/claude.py +1 -1
  56. package/runtime/python/okstra_token_usage/collect.py +1 -1
  57. package/runtime/skills/okstra-brief/SKILL.md +65 -11
  58. package/runtime/skills/okstra-inspect/SKILL.md +1 -1
  59. package/runtime/skills/okstra-schedule/SKILL.md +4 -4
  60. package/runtime/skills/okstra-setup/SKILL.md +2 -2
  61. package/runtime/templates/reports/settings.template.json +14 -123
  62. package/runtime/templates/reports/task-brief.template.md +2 -2
  63. package/runtime/templates/worker-prompt-preamble.md +2 -2
  64. package/runtime/validators/lib/validate-assets.sh +12 -4
  65. package/runtime/validators/validate-brief.py +99 -16
  66. package/runtime/validators/validate-run.py +3 -3
  67. package/runtime/validators/validate_session_conformance.py +11 -11
  68. package/src/install.mjs +95 -81
  69. package/src/skill-catalog.mjs +35 -0
  70. package/src/uninstall.mjs +5 -0
  71. /package/runtime/{skills/okstra-coding-preflight/architecture → prompts/okstra-coding-preflight/architectures}/hexagonal.md +0 -0
  72. /package/runtime/{skills → prompts}/okstra-coding-preflight/clean-code.md +0 -0
  73. /package/runtime/{skills/okstra-coding-preflight/languages/nodejs.md → prompts/okstra-coding-preflight/frameworks/node-server.md} +0 -0
  74. /package/runtime/{skills → prompts}/okstra-coding-preflight/languages/java.md +0 -0
  75. /package/runtime/{skills → prompts}/okstra-coding-preflight/languages/javascript-typescript.md +0 -0
  76. /package/runtime/{skills → prompts}/okstra-coding-preflight/languages/kotlin.md +0 -0
  77. /package/runtime/{skills → prompts}/okstra-coding-preflight/languages/sql.md +0 -0
package/src/install.mjs CHANGED
@@ -7,6 +7,7 @@ import { resolvePaths } from "./paths.mjs";
7
7
  import { fileExists } from "./_proc.mjs";
8
8
  import { buildRuntimeManifest, RUNTIMES_MANIFEST_REL } from "./runtime-manifest.mjs";
9
9
  import { normalizeRuntimeRequest, resolveRuntime } from "./runtime-resolver.mjs";
10
+ import { OBSOLETE_INTERNAL_SKILL_NAMES, USER_SKILL_NAMES } from "./skill-catalog.mjs";
10
11
 
11
12
  const SKILLS_MANIFEST_REL = "installed-skills.json";
12
13
  const AGENTS_MANIFEST_REL = "installed-agents.json";
@@ -41,6 +42,26 @@ const BIN_ENTRYPOINTS = [
41
42
  "okstra-spawn-followups.py",
42
43
  ];
43
44
 
45
+ // Runtime prompt resources the launch prompt and routed coding-preflight pack
46
+ // depend on. Intentionally an exact allowlist, not a directory wildcard.
47
+ const REQUIRED_PROMPT_RESOURCE_FILES = Object.freeze([
48
+ ["prompts", "lead", "okstra-lead-contract.md"],
49
+ ["prompts", "lead", "context-loader.md"],
50
+ ["prompts", "lead", "team-contract.md"],
51
+ ["prompts", "lead", "convergence.md"],
52
+ ["prompts", "lead", "report-writer.md"],
53
+ ["prompts", "coding-preflight", "overview.md"],
54
+ ["prompts", "coding-preflight", "clean-code.md"],
55
+ ["prompts", "coding-preflight", "languages", "javascript-typescript.md"],
56
+ ["prompts", "coding-preflight", "languages", "python.md"],
57
+ ["prompts", "coding-preflight", "languages", "rust.md"],
58
+ ["prompts", "coding-preflight", "languages", "java.md"],
59
+ ["prompts", "coding-preflight", "languages", "kotlin.md"],
60
+ ["prompts", "coding-preflight", "languages", "sql.md"],
61
+ ["prompts", "coding-preflight", "frameworks", "node-server.md"],
62
+ ["prompts", "coding-preflight", "architectures", "hexagonal.md"],
63
+ ]);
64
+
44
65
  const INSTALL_USAGE = `okstra install — install runtime into ~/.okstra
45
66
 
46
67
  Usage:
@@ -57,6 +78,7 @@ Effect (copy mode):
57
78
  ${"$HOME"}/.okstra/lib/validators <- runtime/validators (stage/report structure validators)
58
79
  ${"$HOME"}/.okstra/bin <- runtime/bin
59
80
  ${"$HOME"}/.okstra/templates <- runtime/templates (report.css / report.js / *.template.md)
81
+ ${"$HOME"}/.okstra/prompts <- runtime/prompts (lead contracts + coding-preflight pack)
60
82
  ${"$HOME"}/.okstra/templates/settings.local.json <- runtime/templates/reports/settings.template.json
61
83
  ${"$HOME"}/.claude/skills/<name> <- skills when ${"$HOME"}/.claude exists
62
84
  ${"$HOME"}/.codex/skills/<name> <- skills when ${"$HOME"}/.codex exists
@@ -70,6 +92,7 @@ Effect (copy mode):
70
92
  Effect (link mode):
71
93
  ${"$HOME"}/.okstra/lib/python/<pkg> -> <repo>/scripts/<pkg> (symlink)
72
94
  ${"$HOME"}/.okstra/bin/<name>.sh -> <repo>/scripts/<name>.sh
95
+ ${"$HOME"}/.okstra/prompts -> <repo>/prompts
73
96
  ${"$HOME"}/.okstra/templates/settings.local.json -> <repo>/templates/reports/settings.template.json
74
97
  ${"$HOME"}/.claude/skills/<name> -> <repo>/skills/<name> when ${"$HOME"}/.claude exists
75
98
  ${"$HOME"}/.codex/skills/<name> -> <repo>/skills/<name> when ${"$HOME"}/.codex exists
@@ -167,6 +190,16 @@ async function dirExists(path) {
167
190
  }
168
191
  }
169
192
 
193
+ async function pathEntryExists(path) {
194
+ try {
195
+ await fs.lstat(path);
196
+ return true;
197
+ } catch (err) {
198
+ if (err.code === "ENOENT") return false;
199
+ throw err;
200
+ }
201
+ }
202
+
170
203
  async function* walkFiles(root) {
171
204
  let entries;
172
205
  try {
@@ -237,34 +270,6 @@ async function copyTreeIfChanged(srcRoot, dstRoot, opts) {
237
270
  return { copied, skipped, missingSource };
238
271
  }
239
272
 
240
- // Single-file variant of copyTreeIfChanged, used for the lead `okstra` skill
241
- // whose source is one file (agents/SKILL.md) rather than a skill directory.
242
- async function copyFileIfChanged(src, dst, opts) {
243
- const { refresh = false, dryRun = false, mode } = opts ?? {};
244
- let srcBuf;
245
- try {
246
- srcBuf = await fs.readFile(src);
247
- } catch {
248
- return { copied: 0, skipped: 0, missingSource: true };
249
- }
250
- let needsCopy = refresh;
251
- if (!needsCopy) {
252
- try {
253
- const [srcHash, dstHash] = await Promise.all([hashFile(src), hashFile(dst)]);
254
- needsCopy = srcHash !== dstHash;
255
- } catch {
256
- needsCopy = true;
257
- }
258
- }
259
- if (!needsCopy) return { copied: 0, skipped: 1, missingSource: false };
260
- if (dryRun) {
261
- process.stdout.write(`[dry-run] copy ${src} -> ${dst}\n`);
262
- return { copied: 1, skipped: 0, missingSource: false };
263
- }
264
- await writeFileAtomic(dst, srcBuf, mode);
265
- return { copied: 1, skipped: 0, missingSource: false };
266
- }
267
-
268
273
  async function ensureSymlink(target, linkPath, opts) {
269
274
  const { dryRun = false } = opts ?? {};
270
275
  try {
@@ -341,6 +346,15 @@ async function installLinkMode(repoPath, paths, opts) {
341
346
  const action = await ensureSymlink(src, dst, { dryRun });
342
347
  if (!quiet) process.stdout.write(` bin/${name}: ${action}\n`);
343
348
  }
349
+ const promptsSrc = join(repoAbs, "prompts");
350
+ const promptsDst = join(paths.home, "prompts");
351
+ if (await dirExists(promptsSrc)) {
352
+ const action = await ensureSymlink(promptsSrc, promptsDst, { dryRun });
353
+ if (!quiet) process.stdout.write(` prompts: ${action}\n`);
354
+ } else if (!quiet) {
355
+ process.stdout.write(" prompts: missing in repo — skipped\n");
356
+ }
357
+
344
358
 
345
359
  const skillTargets = await detectSkillTargets();
346
360
  const installedSkillTargets = await installSkillTargetsLink(repoAbs, skillTargets, { dryRun, quiet });
@@ -376,16 +390,7 @@ async function installLinkMode(repoPath, paths, opts) {
376
390
  );
377
391
  }
378
392
  return 0;
379
- }
380
393
 
381
- async function listSkillDirs(skillsRoot) {
382
- try {
383
- const entries = await fs.readdir(skillsRoot, { withFileTypes: true });
384
- return entries.filter((e) => e.isDirectory()).map((e) => e.name);
385
- } catch (err) {
386
- if (err.code === "ENOENT") return [];
387
- throw err;
388
- }
389
394
  }
390
395
 
391
396
  async function detectSkillTargets() {
@@ -612,15 +617,12 @@ const SETTINGS_TEMPLATE_DESCRIPTOR = {
612
617
  label: "settings template",
613
618
  };
614
619
 
615
- const LEAD_SKILL_NAME = "okstra";
616
-
617
620
  async function installSkillsCopy(runtimeRoot, target, opts) {
618
621
  const { refresh, dryRun, quiet } = opts;
619
622
  const srcRoot = join(runtimeRoot, "skills");
620
- const names = await listSkillDirs(srcRoot);
621
623
  let copied = 0;
622
624
  let skipped = 0;
623
- for (const name of names) {
625
+ for (const name of USER_SKILL_NAMES) {
624
626
  const r = await copyTreeIfChanged(
625
627
  join(srcRoot, name),
626
628
  join(target.skillsDir, name),
@@ -629,65 +631,48 @@ async function installSkillsCopy(runtimeRoot, target, opts) {
629
631
  copied += r.copied;
630
632
  skipped += r.skipped;
631
633
  }
632
- const lead = await installLeadSkillCopy(runtimeRoot, target, opts);
634
+ const pruned = await pruneObsoleteInternalSkills(target, opts);
633
635
  if (!quiet) {
634
- const totalCopied = copied + lead.copied;
635
- const totalSkipped = skipped + lead.skipped;
636
- const totalSkills = names.length + lead.installed.length;
637
636
  process.stdout.write(
638
- ` ${target.provider} skills: copied=${totalCopied} skipped=${totalSkipped} -> ${target.skillsDir}/ (${totalSkills} skills)\n`,
637
+ ` ${target.provider} skills: copied=${copied} skipped=${skipped} pruned=${pruned} -> ${target.skillsDir}/ (${USER_SKILL_NAMES.length} skills)\n`,
639
638
  );
640
639
  }
641
- return { ...target, installed: [...names, ...lead.installed] };
640
+ return { ...target, installed: [...USER_SKILL_NAMES] };
642
641
  }
643
642
 
644
643
  async function installSkillsLink(repoAbs, target, opts) {
645
644
  const { dryRun, quiet } = opts;
646
645
  const srcRoot = join(repoAbs, "skills");
647
- const names = await listSkillDirs(srcRoot);
648
646
  if (!dryRun) await fs.mkdir(target.skillsDir, { recursive: true });
649
- for (const name of names) {
647
+ for (const name of USER_SKILL_NAMES) {
650
648
  const src = join(srcRoot, name);
651
649
  const dst = join(target.skillsDir, name);
652
650
  const action = await ensureSymlink(src, dst, { dryRun });
653
651
  if (!quiet) process.stdout.write(` ${target.provider} skills/${name}: ${action}\n`);
654
652
  }
655
- const lead = await installLeadSkillLink(repoAbs, target, opts);
656
- return { ...target, installed: [...names, ...lead] };
657
- }
658
-
659
- async function installLeadSkillCopy(runtimeRoot, target, opts) {
660
- const { refresh, dryRun, quiet } = opts;
661
- const src = join(runtimeRoot, "agents", "SKILL.md");
662
- const dst = join(target.skillsDir, LEAD_SKILL_NAME, "SKILL.md");
663
- const r = await copyFileIfChanged(src, dst, { refresh, dryRun, mode: 0o644 });
664
- if (r.missingSource) {
665
- if (!quiet) {
666
- process.stdout.write(
667
- ` ${target.provider} skills/okstra: runtime/agents/SKILL.md missing — skipped\n`,
668
- );
669
- }
670
- return { installed: [], copied: 0, skipped: 0 };
653
+ const pruned = await pruneObsoleteInternalSkills(target, opts);
654
+ if (!quiet && pruned > 0) {
655
+ process.stdout.write(` ${target.provider} obsolete internal skills: pruned=${pruned}\n`);
671
656
  }
672
- return { installed: [LEAD_SKILL_NAME], copied: r.copied, skipped: r.skipped };
657
+ return { ...target, installed: [...USER_SKILL_NAMES] };
673
658
  }
674
659
 
675
- async function installLeadSkillLink(repoAbs, target, opts) {
676
- const { dryRun, quiet } = opts;
677
- const src = join(repoAbs, "agents", "SKILL.md");
678
- if (!(await fileExists(src))) {
679
- if (!quiet) {
680
- process.stdout.write(
681
- ` ${target.provider} skills/okstra: <repo>/agents/SKILL.md missing — skipped\n`,
682
- );
660
+ // Remove former internal skill directories left by older installs. Exact-name
661
+ // only (OBSOLETE_INTERNAL_SKILL_NAMES) those contracts now ship as runtime
662
+ // resources under ~/.okstra/prompts/, not as agent skills.
663
+ async function pruneObsoleteInternalSkills(target, opts) {
664
+ let pruned = 0;
665
+ for (const name of OBSOLETE_INTERNAL_SKILL_NAMES) {
666
+ const path = join(target.skillsDir, name);
667
+ if (!(await pathEntryExists(path))) continue;
668
+ pruned += 1;
669
+ if (opts.dryRun) {
670
+ process.stdout.write(`[dry-run] rm ${path}\n`);
671
+ continue;
683
672
  }
684
- return [];
673
+ await fs.rm(path, { recursive: true, force: true });
685
674
  }
686
- const dst = join(target.skillsDir, LEAD_SKILL_NAME, "SKILL.md");
687
- if (!dryRun) await fs.mkdir(join(target.skillsDir, LEAD_SKILL_NAME), { recursive: true });
688
- const action = await ensureSymlink(src, dst, { dryRun });
689
- if (!quiet) process.stdout.write(` ${target.provider} skills/okstra/SKILL.md: ${action}\n`);
690
- return [LEAD_SKILL_NAME];
675
+ return pruned;
691
676
  }
692
677
 
693
678
  async function installSkillTargetsCopy(runtimeRoot, targets, opts) {
@@ -828,6 +813,17 @@ export async function runInstall(args) {
828
813
  join(paths.home, "lib", "validators"),
829
814
  { refresh: opts.refresh, dryRun: opts.dryRun, mode: 0o755 },
830
815
  );
816
+ // prompts/ tree — lead/internal operating contracts (prompts/lead/*.md) and
817
+ // the coding-preflight resource pack (prompts/coding-preflight/*) are read at
818
+ // runtime by the lead and executor via absolute ~/.okstra/prompts/ paths
819
+ // (paths.py OKSTRA_LEAD_CONTRACT_PATH / OKSTRA_CODING_PREFLIGHT_DIR). Run-prep
820
+ // reads prompts/profiles + launch.template from the package runtime dir, but
821
+ // the lead's runtime Read needs them under the home tree like templates.
822
+ const promptsResult = await copyTreeIfChanged(
823
+ join(runtimeRoot, "prompts"),
824
+ join(paths.home, "prompts"),
825
+ { refresh: opts.refresh, dryRun: opts.dryRun, mode: 0o644 },
826
+ );
831
827
 
832
828
  if (!opts.quiet) {
833
829
  summarise("python", pythonResult, paths.pythonpath);
@@ -835,6 +831,7 @@ export async function runInstall(args) {
835
831
  summarise("templates", templatesResult, join(paths.home, "templates"));
836
832
  summarise("schemas", schemasResult, join(paths.home, "schemas"));
837
833
  summarise("validators", validatorsResult, join(paths.home, "lib", "validators"));
834
+ summarise("prompts", promptsResult, join(paths.home, "prompts"));
838
835
  }
839
836
 
840
837
  if (pythonResult.missingSource && binResult.missingSource) {
@@ -857,6 +854,11 @@ export async function runInstall(args) {
857
854
  "warning: runtime/validators is empty. stage-structure / report validation will use stale or missing validators — re-run the build step.\n",
858
855
  );
859
856
  }
857
+ if (promptsResult.missingSource) {
858
+ process.stderr.write(
859
+ "warning: runtime/prompts is empty. lead contracts + coding-preflight resources will be missing — re-run the build step.\n",
860
+ );
861
+ }
860
862
 
861
863
  const skillTargets = await detectSkillTargets();
862
864
  const installedSkillTargets = await installSkillTargetsCopy(runtimeRoot, skillTargets, opts);
@@ -895,7 +897,7 @@ export async function runInstall(args) {
895
897
  async function skillTargetDriftReasons(targets) {
896
898
  const reasons = [];
897
899
  for (const target of targets) {
898
- for (const name of ["okstra-run", LEAD_SKILL_NAME]) {
900
+ for (const name of USER_SKILL_NAMES) {
899
901
  const skillPath = join(target.skillsDir, name, "SKILL.md");
900
902
  if (!(await fileExists(skillPath))) {
901
903
  reasons.push(`missing ${skillPath}`);
@@ -905,6 +907,15 @@ async function skillTargetDriftReasons(targets) {
905
907
  return reasons;
906
908
  }
907
909
 
910
+ async function promptResourceDriftReasons(paths) {
911
+ const reasons = [];
912
+ for (const parts of REQUIRED_PROMPT_RESOURCE_FILES) {
913
+ const path = join(paths.home, ...parts);
914
+ if (!(await fileExists(path))) reasons.push(`missing ${path}`);
915
+ }
916
+ return reasons;
917
+ }
918
+
908
919
  async function agentDriftReasons(paths) {
909
920
  const reasons = [];
910
921
  const workersDir = join(paths.agents, "workers");
@@ -987,6 +998,9 @@ export async function runEnsureInstalled(args) {
987
998
  if (!(await fileExists(join(paths.home, "schemas", "final-report-v1.0.schema.json")))) {
988
999
  reasons.push(`missing ${join(paths.home, "schemas", "final-report-v1.0.schema.json")}`);
989
1000
  }
1001
+ for (const reason of await promptResourceDriftReasons(paths)) {
1002
+ reasons.push(reason);
1003
+ }
990
1004
  const skillTargets = await detectSkillTargets();
991
1005
  for (const reason of await skillTargetDriftReasons(skillTargets)) {
992
1006
  reasons.push(reason);
@@ -0,0 +1,35 @@
1
+ // Single source of truth for which okstra skills are user-facing and which
2
+ // former internal skill directories must be pruned from agent skill homes on
3
+ // (re)install. The Claude plugin manifest (.claude-plugin/plugin.json) and the
4
+ // installer both derive their skill list from here, so the public surface
5
+ // cannot drift between discovery and install.
6
+
7
+ export const USER_SKILL_NAMES = Object.freeze([
8
+ "okstra-setup",
9
+ "okstra-brief",
10
+ "okstra-run",
11
+ "okstra-memory",
12
+ "okstra-inspect",
13
+ "okstra-schedule",
14
+ ]);
15
+
16
+ // Names okstra used to install as skills before lead/internal contracts moved
17
+ // into the runtime resource tree (~/.okstra/prompts/). Install prunes these
18
+ // exact directories from each target skill home so upgrades do not leave the
19
+ // old internal skills behind. Exact-name only — never wildcard.
20
+ export const OBSOLETE_INTERNAL_SKILL_NAMES = Object.freeze([
21
+ "okstra",
22
+ "okstra-context-loader",
23
+ "okstra-team-contract",
24
+ "okstra-convergence",
25
+ "okstra-report-writer",
26
+ "okstra-coding-preflight",
27
+ ]);
28
+
29
+ export function userSkillNames() {
30
+ return [...USER_SKILL_NAMES];
31
+ }
32
+
33
+ export function obsoleteInternalSkillNames() {
34
+ return [...OBSOLETE_INTERNAL_SKILL_NAMES];
35
+ }
package/src/uninstall.mjs CHANGED
@@ -206,6 +206,11 @@ export async function runUninstall(args) {
206
206
  // never removed it, leaving a stale final-report schema behind.
207
207
  await removePath(join(paths.home, "schemas"), opts);
208
208
 
209
+ // prompts/ tree — installed by copy mode (runtime/prompts): lead contracts
210
+ // + coding-preflight resource pack. Remove so an upgrade never serves stale
211
+ // operating contracts.
212
+ await removePath(join(paths.home, "prompts"), opts);
213
+
209
214
  // Remove the skills we own. Manifest v2 records every target root; legacy
210
215
  // manifests fall back to Claude-only names when the runtime manifest says
211
216
  // Claude assets may have been installed.