okstra 0.121.1 → 0.123.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.md +4 -2
- package/docs/architecture/storage-model.md +14 -0
- package/docs/architecture.md +38 -8
- package/docs/cli.md +45 -5
- package/docs/for-ai/README.md +2 -2
- package/docs/for-ai/skills/okstra-rollup.md +1 -1
- package/docs/for-ai/skills/{okstra-schedule.md → okstra-schedule-gen.md} +3 -3
- package/docs/project-structure-overview.md +4 -3
- package/docs/task-process/implementation-planning.md +33 -9
- package/docs/task-process/implementation.md +21 -2
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/bin/lib/okstra/usage.sh +3 -3
- package/runtime/prompts/launch.template.md +9 -6
- package/runtime/prompts/lead/adapters/claude-code.md +104 -0
- package/runtime/prompts/lead/adapters/codex.md +45 -0
- package/runtime/prompts/lead/adapters/external.md +46 -0
- package/runtime/prompts/lead/context-loader.md +5 -5
- package/runtime/prompts/lead/convergence.md +11 -28
- package/runtime/prompts/lead/okstra-lead-contract.md +44 -94
- package/runtime/prompts/lead/plan-body-verification.md +50 -5
- package/runtime/prompts/lead/report-writer.md +59 -54
- package/runtime/prompts/lead/team-contract.md +33 -106
- package/runtime/prompts/profiles/_common-contract.md +3 -3
- package/runtime/prompts/profiles/_implementation-deliverable.md +3 -1
- package/runtime/prompts/profiles/_implementation-executor.md +6 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
- package/runtime/prompts/profiles/final-verification.md +2 -0
- package/runtime/prompts/profiles/implementation-planning.md +14 -4
- package/runtime/prompts/profiles/requirements-discovery.md +1 -1
- package/runtime/prompts/wizard/prompts.ko.json +44 -0
- package/runtime/python/okstra_ctl/codex_dispatch.py +23 -1
- package/runtime/python/okstra_ctl/design_prep.py +1462 -0
- package/runtime/python/okstra_ctl/design_surfaces.py +243 -0
- package/runtime/python/okstra_ctl/final_report_schema.py +33 -1
- package/runtime/python/okstra_ctl/implementation_stage.py +35 -0
- package/runtime/python/okstra_ctl/incremental_carry.py +294 -21
- package/runtime/python/okstra_ctl/incremental_scope.py +51 -5
- package/runtime/python/okstra_ctl/lead_runtime.py +4 -0
- package/runtime/python/okstra_ctl/material.py +1 -1
- package/runtime/python/okstra_ctl/model_discovery.py +98 -0
- package/runtime/python/okstra_ctl/models.py +8 -3
- package/runtime/python/okstra_ctl/render.py +81 -194
- package/runtime/python/okstra_ctl/report_views.py +22 -7
- package/runtime/python/okstra_ctl/run.py +53 -5
- package/runtime/python/okstra_ctl/team_reconcile.py +3 -1
- package/runtime/python/okstra_ctl/user_response.py +67 -2
- package/runtime/python/okstra_ctl/wizard.py +283 -3
- package/runtime/python/okstra_token_usage/report.py +11 -0
- package/runtime/schemas/final-report-v1.0.schema.json +339 -0
- package/runtime/skills/okstra-inspect/SKILL.md +2 -2
- package/runtime/skills/okstra-rollup/SKILL.md +1 -1
- package/runtime/skills/{okstra-schedule → okstra-schedule-gen}/SKILL.md +62 -20
- package/runtime/skills/okstra-user-response/SKILL.md +8 -6
- package/runtime/templates/reports/final-report.template.md +71 -4
- package/runtime/templates/reports/i18n/en.json +32 -1
- package/runtime/templates/reports/i18n/ko.json +32 -1
- package/runtime/validators/validate-run.py +515 -5
- package/runtime/validators/validate-schedule.py +11 -7
- package/runtime/validators/validate_session_conformance.py +58 -33
- package/src/cli-registry.mjs +14 -0
- package/src/commands/inspect/design-prep.mjs +23 -0
- package/src/commands/inspect/stage-map.mjs +100 -0
- package/src/commands/lifecycle/install.mjs +3 -0
- package/src/commands/lifecycle/uninstall.mjs +8 -23
- package/src/lib/skill-catalog.mjs +10 -1
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
|
6
|
-
|
|
7
|
-
| 1. lead PROGRESS
|
|
8
|
-
| 2.
|
|
9
|
-
| 3. implementation sidecar entry guard | prompts/lead/okstra-lead-contract.md "Entry guard (BLOCKING)" |
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- `
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- validator 실행 시점(Phase 7) **이후** 에 출력되는 체크포인트
|
|
19
|
-
(`phase-7-teardown`, `complete`)는 구조적으로 검사 불가 — 요구하지 않는다.
|
|
1
|
+
"""Post-hoc checks for three BLOCKING neutral lead lifecycle contracts.
|
|
2
|
+
|
|
3
|
+
Design: docs/superpowers/specs/2026-06-10-blocking-contract-posthoc-conformance-design.md
|
|
4
|
+
|
|
5
|
+
| Check | Contract | Evidence |
|
|
6
|
+
|-------|----------|----------|
|
|
7
|
+
| 1. lead PROGRESS checkpoints | prompts/lead/okstra-lead-contract.md "Progress reporting (BLOCKING)" | selected adapter evidence source |
|
|
8
|
+
| 2. worker-provider heartbeat | worker provider contract | audit sidecar `- PROGRESS: <stage> <ISO>` lines |
|
|
9
|
+
| 3. implementation sidecar entry guard | prompts/lead/okstra-lead-contract.md "Entry guard (BLOCKING)" | selected adapter evidence source |
|
|
10
|
+
|
|
11
|
+
Evidence rules prevent false passes:
|
|
12
|
+
- `claude-jsonl` accepts only assistant text and Read tool-use records, excluding
|
|
13
|
+
injected skill text and sidechain records.
|
|
14
|
+
- Session evidence is scoped to the current run window so a previous run cannot
|
|
15
|
+
satisfy the neutral lead lifecycle contract.
|
|
16
|
+
- Checkpoints emitted after validation begins (`phase-7-teardown`, `complete`)
|
|
17
|
+
cannot be observed and are not required.
|
|
20
18
|
"""
|
|
21
19
|
from __future__ import annotations
|
|
22
20
|
|
|
@@ -202,7 +200,8 @@ def _collect_lead_evidence(
|
|
|
202
200
|
if not evidence.scanned_files:
|
|
203
201
|
return None, (
|
|
204
202
|
f"lead session jsonl not found under {proj_dir} "
|
|
205
|
-
f"(lead.sessionId={lead_sid or '<empty>'}) —
|
|
203
|
+
f"(lead.sessionId={lead_sid or '<empty>'}) — selected adapter evidence "
|
|
204
|
+
"source `claude-jsonl` cannot verify the PROGRESS checkpoint / "
|
|
206
205
|
"implementation entry-guard conformance cannot be verified, which "
|
|
207
206
|
"fails the run (same principle as the token-usage accuracy contract)."
|
|
208
207
|
)
|
|
@@ -223,15 +222,16 @@ def _resolve_lead_events_path(
|
|
|
223
222
|
if not raw:
|
|
224
223
|
return None, (
|
|
225
224
|
"artifact lead event log path missing from team-state "
|
|
226
|
-
"(`leadEventsPath`) — artifact-only
|
|
225
|
+
"(`leadEventsPath`) — selected adapter evidence source `artifact-only` "
|
|
226
|
+
"cannot verify conformance."
|
|
227
227
|
)
|
|
228
228
|
path = Path(str(raw))
|
|
229
229
|
if not path.is_absolute():
|
|
230
230
|
path = project_root / path
|
|
231
231
|
if not path.is_file():
|
|
232
232
|
return None, (
|
|
233
|
-
f"artifact lead event log not found: {path} —
|
|
234
|
-
"
|
|
233
|
+
f"artifact lead event log not found: {path} — selected adapter evidence "
|
|
234
|
+
"source `artifact-only` cannot verify conformance."
|
|
235
235
|
)
|
|
236
236
|
return path, None
|
|
237
237
|
|
|
@@ -301,7 +301,10 @@ def _collect_artifact_lead_evidence(
|
|
|
301
301
|
try:
|
|
302
302
|
events = read_lead_events(events_path)
|
|
303
303
|
except LeadEventParseError as exc:
|
|
304
|
-
return None,
|
|
304
|
+
return None, (
|
|
305
|
+
"artifact lead event log is malformed — selected adapter evidence "
|
|
306
|
+
f"source `artifact-only` cannot verify conformance: {exc}"
|
|
307
|
+
)
|
|
305
308
|
|
|
306
309
|
run_seq = suffix.rsplit("-", 1)[1]
|
|
307
310
|
evidence = _LeadEvidence(scanned_files=[events_path])
|
|
@@ -324,6 +327,27 @@ def _collect_artifact_lead_evidence(
|
|
|
324
327
|
return evidence, None
|
|
325
328
|
|
|
326
329
|
|
|
330
|
+
def _conformance_evidence_source(
|
|
331
|
+
team_state: dict,
|
|
332
|
+
) -> tuple[str | None, str | None]:
|
|
333
|
+
dispatch_mode = str(team_state.get("dispatchMode", "")).strip()
|
|
334
|
+
if dispatch_mode in _WORKER_DISPATCH_MODES:
|
|
335
|
+
return "artifact-only", None
|
|
336
|
+
|
|
337
|
+
adapter = team_state.get("leadAdapter")
|
|
338
|
+
if isinstance(adapter, dict):
|
|
339
|
+
accounting = str(adapter.get("sessionAccounting", "")).strip()
|
|
340
|
+
if accounting in {"claude-jsonl", "artifact-only"}:
|
|
341
|
+
return accounting, None
|
|
342
|
+
if accounting:
|
|
343
|
+
return None, f"unsupported sessionAccounting: `{accounting}`"
|
|
344
|
+
|
|
345
|
+
legacy_runtime = str(team_state.get("leadRuntime", "") or "claude-code")
|
|
346
|
+
if legacy_runtime in {"codex", "external"}:
|
|
347
|
+
return "artifact-only", None
|
|
348
|
+
return "claude-jsonl", None
|
|
349
|
+
|
|
350
|
+
|
|
327
351
|
def _convergence_rounds_ran(run_dir: Path, suffix: str | None) -> bool:
|
|
328
352
|
"""이 run 의 convergence state artifact 가 실제 round 를 1회 이상 돌았는지.
|
|
329
353
|
auto-disable(`totalRounds: 0`)·artifact 부재는 phase-5.5 라인을 요구하지 않는다."""
|
|
@@ -569,7 +593,8 @@ def _check_implementation_sidecar_reads(evidence: _LeadEvidence, errors: list[st
|
|
|
569
593
|
if not ts_list:
|
|
570
594
|
errors.append(
|
|
571
595
|
f"implementation entry guard: no `Read` of `{basename}` found in "
|
|
572
|
-
|
|
596
|
+
"the selected adapter evidence source within this run's window — "
|
|
597
|
+
"the sidecar "
|
|
573
598
|
f"MUST be read fresh at {read_at} every implementation run "
|
|
574
599
|
"(prompts/lead/okstra-lead-contract.md 'Entry guard (BLOCKING)')."
|
|
575
600
|
)
|
|
@@ -593,10 +618,10 @@ def validate_session_conformance(
|
|
|
593
618
|
task_type: str,
|
|
594
619
|
claude_projects_dir: Path | None = None,
|
|
595
620
|
) -> SessionConformanceResult:
|
|
596
|
-
"""post-hoc
|
|
621
|
+
"""Run three post-hoc checks for the neutral lead lifecycle contract.
|
|
597
622
|
|
|
598
|
-
`claude_projects_dir`
|
|
599
|
-
|
|
623
|
+
`claude_projects_dir` injects the Claude project root for tests and diagnostics.
|
|
624
|
+
Heartbeat validation runs before selecting the adapter evidence source.
|
|
600
625
|
"""
|
|
601
626
|
result = SessionConformanceResult()
|
|
602
627
|
_ensure_token_usage_importable()
|
|
@@ -610,11 +635,11 @@ def validate_session_conformance(
|
|
|
610
635
|
suffix = run_artifact_suffix(team_state_path)
|
|
611
636
|
_check_heartbeat_sidecars(run_dir, task_type, suffix, result.errors)
|
|
612
637
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
638
|
+
evidence_source, source_error = _conformance_evidence_source(team_state)
|
|
639
|
+
if source_error:
|
|
640
|
+
result.errors.append(source_error)
|
|
641
|
+
return result
|
|
642
|
+
if evidence_source == "artifact-only":
|
|
618
643
|
evidence, error = _collect_artifact_lead_evidence(
|
|
619
644
|
team_state, project_root, task_type, suffix
|
|
620
645
|
)
|
package/src/cli-registry.mjs
CHANGED
|
@@ -110,6 +110,13 @@ export const COMMAND_REGISTRY = [
|
|
|
110
110
|
category: "introspection",
|
|
111
111
|
summary: ["Summarize a task's manifest + workflow phase state"],
|
|
112
112
|
},
|
|
113
|
+
{
|
|
114
|
+
name: "stage-map",
|
|
115
|
+
module: "./commands/inspect/stage-map.mjs",
|
|
116
|
+
export: "run",
|
|
117
|
+
category: "introspection",
|
|
118
|
+
summary: ["Dump a task's implementation-planning Stage Map + done stages"],
|
|
119
|
+
},
|
|
113
120
|
{
|
|
114
121
|
name: "context-cost",
|
|
115
122
|
module: "./commands/inspect/context-cost.mjs",
|
|
@@ -180,6 +187,13 @@ export const COMMAND_REGISTRY = [
|
|
|
180
187
|
category: "introspection",
|
|
181
188
|
summary: ["Answer a task's open clarification questions in-session and write the response sidecar"],
|
|
182
189
|
},
|
|
190
|
+
{
|
|
191
|
+
name: "design-prep",
|
|
192
|
+
module: "./commands/inspect/design-prep.mjs",
|
|
193
|
+
export: "run",
|
|
194
|
+
category: "introspection",
|
|
195
|
+
summary: ["Review and record AI-prepared implementation design inputs"],
|
|
196
|
+
},
|
|
183
197
|
{
|
|
184
198
|
name: "rollup",
|
|
185
199
|
module: "./commands/inspect/rollup.mjs",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { runPythonModule } from "../../lib/python-helper.mjs";
|
|
2
|
+
|
|
3
|
+
const USAGE = `okstra design-prep — review AI-prepared implementation design inputs
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
okstra design-prep list [--project-root <path>] [--report <path>]
|
|
7
|
+
okstra design-prep show --report <path> [--item PREP-001]
|
|
8
|
+
okstra design-prep write --report <path> --item PREP-001 \\
|
|
9
|
+
--decision <accept-draft|modify-draft|reject-draft|defer> \\
|
|
10
|
+
[--overrides <json-object>] [--notes <text>] --confirmed
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
export async function run(args) {
|
|
14
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
15
|
+
process.stdout.write(USAGE);
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
const { code } = await runPythonModule({
|
|
19
|
+
module: "okstra_ctl.design_prep",
|
|
20
|
+
args,
|
|
21
|
+
});
|
|
22
|
+
return code ?? 1;
|
|
23
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { runPythonSnippet, emitJsonError } from "../../lib/python-helper.mjs";
|
|
2
|
+
|
|
3
|
+
const USAGE = `okstra stage-map — dump a task's implementation-planning Stage Map + done stages
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
okstra stage-map <task-key> task-key is project-id:task-group:task-id
|
|
7
|
+
okstra stage-map <task-key> --cwd <dir> Resolve PROJECT_ROOT from <dir>
|
|
8
|
+
okstra stage-map <task-key> --project <dir> Use <dir> directly as PROJECT_ROOT
|
|
9
|
+
|
|
10
|
+
Output: JSON { ok, taskKey, taskRoot, stages:[{stage_number,title,depends_on,step_count}], doneStages:[int] }.
|
|
11
|
+
stages is [] when no implementation-planning Stage Map exists.
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
function parseArgs(args) {
|
|
15
|
+
const opts = { cwd: process.cwd(), projectRoot: "", taskKey: "" };
|
|
16
|
+
const positional = [];
|
|
17
|
+
for (let i = 0; i < args.length; i++) {
|
|
18
|
+
const a = args[i];
|
|
19
|
+
if (a === "--cwd") {
|
|
20
|
+
opts.cwd = args[++i];
|
|
21
|
+
if (!opts.cwd) throw new Error("--cwd requires a path");
|
|
22
|
+
} else if (a === "--project") {
|
|
23
|
+
opts.projectRoot = args[++i];
|
|
24
|
+
if (!opts.projectRoot) throw new Error("--project requires a path");
|
|
25
|
+
} else if (a === "--json") {
|
|
26
|
+
// JSON is always emitted; accept the flag for parity with sibling commands.
|
|
27
|
+
} else if (a.startsWith("--")) {
|
|
28
|
+
throw new Error(`unknown argument '${a}'`);
|
|
29
|
+
} else {
|
|
30
|
+
positional.push(a);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (positional.length !== 1) {
|
|
34
|
+
throw new Error("expected exactly one positional argument: <task-key>");
|
|
35
|
+
}
|
|
36
|
+
opts.taskKey = positional[0];
|
|
37
|
+
return opts;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const SCRIPT = `
|
|
41
|
+
import json, sys
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
from okstra_project import resolve_project_root, find_task_root, read_task_manifest, ResolverError
|
|
44
|
+
from okstra_ctl.implementation_outcome import _load_stage_map
|
|
45
|
+
from okstra_ctl.consumers import read_stage_consumer_state
|
|
46
|
+
|
|
47
|
+
explicit, cwd, task_key = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
pr = resolve_project_root(explicit_root=explicit, cwd=cwd)
|
|
51
|
+
except ResolverError as e:
|
|
52
|
+
print(json.dumps({"ok": False, "stage": "resolve", "reason": str(e)}))
|
|
53
|
+
sys.exit(2)
|
|
54
|
+
|
|
55
|
+
task_root = find_task_root(Path(pr), task_key)
|
|
56
|
+
if task_root is None:
|
|
57
|
+
print(json.dumps({"ok": False, "stage": "task_root", "reason": f"task root not found for {task_key}"}))
|
|
58
|
+
sys.exit(1)
|
|
59
|
+
|
|
60
|
+
manifest = read_task_manifest(task_root) or {}
|
|
61
|
+
stages = _load_stage_map(task_root, manifest)
|
|
62
|
+
|
|
63
|
+
plan_run_root = task_root / "runs" / "implementation-planning"
|
|
64
|
+
done = sorted(read_stage_consumer_state(plan_run_root, recover_from_carry=True).done_stages) if plan_run_root.is_dir() else []
|
|
65
|
+
|
|
66
|
+
print(json.dumps({
|
|
67
|
+
"ok": True,
|
|
68
|
+
"taskKey": task_key,
|
|
69
|
+
"taskRoot": str(task_root),
|
|
70
|
+
"stages": stages,
|
|
71
|
+
"doneStages": done,
|
|
72
|
+
}, ensure_ascii=False, indent=2))
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
export async function run(args) {
|
|
76
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
77
|
+
process.stdout.write(USAGE);
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
let opts;
|
|
81
|
+
try {
|
|
82
|
+
opts = parseArgs(args);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
process.stderr.write(`error: ${err.message}\n\n${USAGE}`);
|
|
85
|
+
return 2;
|
|
86
|
+
}
|
|
87
|
+
const result = await runPythonSnippet({
|
|
88
|
+
script: SCRIPT,
|
|
89
|
+
args: [opts.projectRoot, opts.cwd, opts.taskKey],
|
|
90
|
+
});
|
|
91
|
+
if (result.code !== 0 && !result.stdout.trim()) {
|
|
92
|
+
emitJsonError({
|
|
93
|
+
stage: "python",
|
|
94
|
+
reason: `python invocation failed: ${result.stderr.trim() || "no output"}`,
|
|
95
|
+
});
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
process.stdout.write(result.stdout);
|
|
99
|
+
return result.code === 0 ? 0 : result.code;
|
|
100
|
+
}
|
|
@@ -45,6 +45,9 @@ const BIN_ENTRYPOINTS = [
|
|
|
45
45
|
// depend on. Intentionally an exact allowlist, not a directory wildcard.
|
|
46
46
|
const REQUIRED_PROMPT_RESOURCE_FILES = Object.freeze([
|
|
47
47
|
["prompts", "lead", "okstra-lead-contract.md"],
|
|
48
|
+
["prompts", "lead", "adapters", "claude-code.md"],
|
|
49
|
+
["prompts", "lead", "adapters", "codex.md"],
|
|
50
|
+
["prompts", "lead", "adapters", "external.md"],
|
|
48
51
|
["prompts", "lead", "context-loader.md"],
|
|
49
52
|
["prompts", "lead", "team-contract.md"],
|
|
50
53
|
["prompts", "lead", "convergence.md"],
|
|
@@ -6,30 +6,15 @@ import {
|
|
|
6
6
|
RUNTIMES_MANIFEST_REL,
|
|
7
7
|
runtimeManifestIncludesClaudeAssets,
|
|
8
8
|
} from "../../lib/runtime-manifest.mjs";
|
|
9
|
+
import { OBSOLETE_SKILL_NAMES, USER_SKILL_NAMES } from "../../lib/skill-catalog.mjs";
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"okstra-schedule",
|
|
18
|
-
"okstra-container-build",
|
|
19
|
-
"okstra-container",
|
|
20
|
-
"okstra-brief",
|
|
21
|
-
"okstra-context-loader",
|
|
22
|
-
"okstra-team-contract",
|
|
23
|
-
"okstra-convergence",
|
|
24
|
-
"okstra-report-writer",
|
|
25
|
-
// Pre-merge names — kept so uninstall on an upgraded machine still cleans
|
|
26
|
-
// them out of ~/.claude/skills/ once the user runs `okstra uninstall`.
|
|
27
|
-
// Safe to drop after a few releases when nobody has them installed.
|
|
28
|
-
"okstra-status",
|
|
29
|
-
"okstra-history",
|
|
30
|
-
"okstra-logs",
|
|
31
|
-
"okstra-report-finder",
|
|
32
|
-
"okstra-time-summary",
|
|
11
|
+
// Manifest-less uninstall must clean out every okstra skill directory — the
|
|
12
|
+
// live names AND every former/renamed one. Both sets are the single source of
|
|
13
|
+
// truth in skill-catalog.mjs (install prunes OBSOLETE_SKILL_NAMES from each
|
|
14
|
+
// skill home), so deriving the fallback from their union keeps uninstall from
|
|
15
|
+
// drifting behind skill additions or renames.
|
|
16
|
+
export const FALLBACK_SKILL_NAMES = [
|
|
17
|
+
...new Set([...USER_SKILL_NAMES, ...OBSOLETE_SKILL_NAMES]),
|
|
33
18
|
];
|
|
34
19
|
|
|
35
20
|
const FALLBACK_AGENT_NAMES = [
|
|
@@ -13,7 +13,7 @@ export const USER_SKILL_NAMES = Object.freeze([
|
|
|
13
13
|
"okstra-inspect",
|
|
14
14
|
"okstra-rollup",
|
|
15
15
|
"okstra-usage",
|
|
16
|
-
"okstra-schedule",
|
|
16
|
+
"okstra-schedule-gen",
|
|
17
17
|
"okstra-container-build",
|
|
18
18
|
"okstra-graphify",
|
|
19
19
|
"okstra-pr-gen",
|
|
@@ -33,6 +33,15 @@ export const OBSOLETE_SKILL_NAMES = Object.freeze([
|
|
|
33
33
|
"okstra-coding-preflight",
|
|
34
34
|
"okstra-container",
|
|
35
35
|
"okstra-brief",
|
|
36
|
+
"okstra-schedule",
|
|
37
|
+
// Pre-merge status/history/logs/report/time skills, since folded into
|
|
38
|
+
// okstra-inspect. Absent here, upgrades left them stranded in agent skill
|
|
39
|
+
// homes and their pre-.okstra-migration bodies kept triggering.
|
|
40
|
+
"okstra-status",
|
|
41
|
+
"okstra-history",
|
|
42
|
+
"okstra-logs",
|
|
43
|
+
"okstra-report-finder",
|
|
44
|
+
"okstra-time-summary",
|
|
36
45
|
]);
|
|
37
46
|
|
|
38
47
|
export function userSkillNames() {
|