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.
- package/README.kr.md +7 -6
- package/README.md +6 -6
- package/docs/kr/architecture.md +8 -7
- package/docs/kr/cli.md +2 -2
- package/docs/kr/performance-improvement-plan-v2.md +14 -14
- package/docs/project-structure-overview.md +7 -8
- package/docs/superpowers/plans/2026-06-15-coding-preflight-pack-dispatch-path.md +504 -0
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-final-fixups.md +342 -0
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-fixups.md +258 -0
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-remaining-fixups.md +387 -0
- package/docs/superpowers/plans/2026-06-15-internal-skill-resource-migration.md +749 -0
- package/docs/superpowers/plans/2026-06-15-worker-prompt-anchor-final-fixups.md +828 -0
- package/docs/superpowers/plans/2026-06-15-worker-prompt-header-error-contract.md +490 -0
- package/docs/task-process/README.md +1 -1
- package/docs/task-process/error-analysis.md +1 -1
- package/docs/task-process/final-verification.md +1 -1
- package/docs/task-process/implementation-planning.md +1 -1
- package/docs/task-process/implementation.md +1 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/docs/task-process/requirements-discovery.md +2 -2
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/TODO.md +2 -0
- package/runtime/agents/workers/claude-worker.md +8 -8
- package/runtime/agents/workers/codex-worker.md +8 -8
- package/runtime/agents/workers/gemini-worker.md +8 -8
- package/runtime/agents/workers/report-writer-worker.md +2 -2
- package/runtime/bin/lib/okstra/globals.sh +0 -1
- package/runtime/bin/okstra-wrapper-status.py +1 -1
- package/runtime/prompts/launch.template.md +5 -3
- package/runtime/{skills/okstra-context-loader/SKILL.md → prompts/lead/context-loader.md} +7 -14
- package/runtime/{skills/okstra-convergence/SKILL.md → prompts/lead/convergence.md} +12 -19
- package/runtime/{agents/SKILL.md → prompts/lead/okstra-lead-contract.md} +53 -59
- package/runtime/{skills/okstra-report-writer/SKILL.md → prompts/lead/report-writer.md} +12 -19
- package/runtime/{skills/okstra-team-contract/SKILL.md → prompts/lead/team-contract.md} +13 -19
- package/runtime/{skills → prompts}/okstra-coding-preflight/languages/python.md +2 -2
- package/runtime/{skills → prompts}/okstra-coding-preflight/languages/rust.md +1 -1
- package/runtime/{skills/okstra-coding-preflight/SKILL.md → prompts/okstra-coding-preflight/overview.md} +27 -38
- package/runtime/prompts/okstra-coding-preflight/scripts/preedit-check.sh +79 -0
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +2 -2
- package/runtime/prompts/profiles/_common-contract.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +2 -2
- package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
- package/runtime/prompts/profiles/error-analysis.md +2 -2
- package/runtime/prompts/profiles/final-verification.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +4 -4
- package/runtime/prompts/profiles/requirements-discovery.md +2 -2
- package/runtime/python/okstra_ctl/codex_dispatch.py +12 -61
- package/runtime/python/okstra_ctl/context_cost.py +14 -11
- package/runtime/python/okstra_ctl/dispatch_core.py +36 -13
- package/runtime/python/okstra_ctl/paths.py +27 -1
- package/runtime/python/okstra_ctl/render.py +62 -8
- package/runtime/python/okstra_ctl/run.py +8 -6
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +126 -0
- package/runtime/python/okstra_token_usage/claude.py +1 -1
- package/runtime/python/okstra_token_usage/collect.py +1 -1
- package/runtime/skills/okstra-brief/SKILL.md +65 -11
- package/runtime/skills/okstra-inspect/SKILL.md +1 -1
- package/runtime/skills/okstra-schedule/SKILL.md +4 -4
- package/runtime/skills/okstra-setup/SKILL.md +2 -2
- package/runtime/templates/reports/settings.template.json +14 -123
- package/runtime/templates/reports/task-brief.template.md +2 -2
- package/runtime/templates/worker-prompt-preamble.md +2 -2
- package/runtime/validators/lib/validate-assets.sh +12 -4
- package/runtime/validators/validate-brief.py +99 -16
- package/runtime/validators/validate-run.py +3 -3
- package/runtime/validators/validate_session_conformance.py +11 -11
- package/src/install.mjs +95 -81
- package/src/skill-catalog.mjs +35 -0
- package/src/uninstall.mjs +5 -0
- /package/runtime/{skills/okstra-coding-preflight/architecture → prompts/okstra-coding-preflight/architectures}/hexagonal.md +0 -0
- /package/runtime/{skills → prompts}/okstra-coding-preflight/clean-code.md +0 -0
- /package/runtime/{skills/okstra-coding-preflight/languages/nodejs.md → prompts/okstra-coding-preflight/frameworks/node-server.md} +0 -0
- /package/runtime/{skills → prompts}/okstra-coding-preflight/languages/java.md +0 -0
- /package/runtime/{skills → prompts}/okstra-coding-preflight/languages/javascript-typescript.md +0 -0
- /package/runtime/{skills → prompts}/okstra-coding-preflight/languages/kotlin.md +0 -0
- /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
|
|
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
|
|
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=${
|
|
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: [...
|
|
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
|
|
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
|
|
656
|
-
|
|
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: [
|
|
657
|
+
return { ...target, installed: [...USER_SKILL_NAMES] };
|
|
673
658
|
}
|
|
674
659
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
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
|
-
|
|
673
|
+
await fs.rm(path, { recursive: true, force: true });
|
|
685
674
|
}
|
|
686
|
-
|
|
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
|
|
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.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/runtime/{skills → prompts}/okstra-coding-preflight/languages/javascript-typescript.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|