okstra 0.110.0 → 0.112.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 (74) hide show
  1. package/README.kr.md +3 -2
  2. package/README.md +3 -2
  3. package/bin/okstra +7 -1
  4. package/docs/for-ai/README.md +2 -2
  5. package/docs/for-ai/skills/okstra-brief.md +2 -3
  6. package/docs/for-ai/skills/okstra-container-build.md +2 -3
  7. package/docs/for-ai/skills/okstra-inspect.md +5 -12
  8. package/docs/for-ai/skills/okstra-rollup.md +3 -4
  9. package/docs/for-ai/skills/okstra-run.md +10 -10
  10. package/docs/for-ai/skills/okstra-schedule.md +2 -7
  11. package/docs/for-ai/skills/okstra-setup.md +1 -1
  12. package/docs/kr/architecture/storage-model.md +1 -2
  13. package/docs/kr/architecture.md +11 -12
  14. package/docs/kr/cli.md +6 -4
  15. package/docs/project-structure-overview.md +14 -12
  16. package/docs/task-process/README.md +4 -4
  17. package/docs/task-process/common-flow.md +8 -5
  18. package/docs/task-process/implementation.md +4 -5
  19. package/docs/task-process/release-handoff.md +3 -3
  20. package/package.json +1 -1
  21. package/runtime/BUILD.json +2 -2
  22. package/runtime/agents/workers/antigravity-worker.md +1 -0
  23. package/runtime/agents/workers/codex-worker.md +1 -0
  24. package/runtime/prompts/coding-preflight/overview.md +3 -1
  25. package/runtime/prompts/launch.template.md +1 -5
  26. package/runtime/prompts/lead/context-loader.md +2 -4
  27. package/runtime/prompts/lead/convergence.md +11 -240
  28. package/runtime/prompts/lead/okstra-lead-contract.md +70 -34
  29. package/runtime/prompts/lead/plan-body-verification.md +240 -0
  30. package/runtime/prompts/lead/report-writer.md +7 -7
  31. package/runtime/prompts/lead/team-contract.md +15 -17
  32. package/runtime/prompts/profiles/_common-contract.md +2 -38
  33. package/runtime/prompts/profiles/_implementation-diff-review.md +43 -0
  34. package/runtime/prompts/profiles/_implementation-executor.md +9 -11
  35. package/runtime/prompts/profiles/_implementation-self-check.md +11 -5
  36. package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
  37. package/runtime/prompts/profiles/final-verification.md +1 -1
  38. package/runtime/prompts/profiles/implementation-planning.md +2 -2
  39. package/runtime/prompts/profiles/implementation.md +1 -1
  40. package/runtime/python/okstra_ctl/codex_dispatch.py +3 -0
  41. package/runtime/python/okstra_ctl/consumers.py +4 -0
  42. package/runtime/python/okstra_ctl/handoff.py +5 -23
  43. package/runtime/python/okstra_ctl/implementation_stage.py +11 -11
  44. package/runtime/python/okstra_ctl/path_hints.py +1 -0
  45. package/runtime/python/okstra_ctl/paths.py +3 -0
  46. package/runtime/python/okstra_ctl/render.py +8 -0
  47. package/runtime/python/okstra_ctl/report_views.py +76 -26
  48. package/runtime/python/okstra_ctl/set_work_status.py +147 -0
  49. package/runtime/python/okstra_ctl/stage_targets.py +152 -0
  50. package/runtime/python/okstra_ctl/team_reconcile.py +1 -1
  51. package/runtime/python/okstra_ctl/wizard.py +47 -0
  52. package/runtime/python/okstra_project/__init__.py +2 -0
  53. package/runtime/python/okstra_project/state.py +44 -2
  54. package/runtime/skills/okstra-brief/SKILL.md +32 -176
  55. package/runtime/skills/okstra-brief/references/reporter-confirmations.md +71 -0
  56. package/runtime/skills/okstra-brief/references/tracker-recursion.md +90 -0
  57. package/runtime/skills/okstra-container-build/SKILL.md +7 -20
  58. package/runtime/skills/okstra-graphify/SKILL.md +8 -8
  59. package/runtime/skills/okstra-inspect/SKILL.md +27 -43
  60. package/runtime/skills/okstra-rollup/SKILL.md +6 -6
  61. package/runtime/skills/okstra-run/SKILL.md +27 -32
  62. package/runtime/skills/okstra-schedule/SKILL.md +64 -419
  63. package/runtime/skills/okstra-setup/SKILL.md +25 -223
  64. package/runtime/skills/okstra-setup/references/project-config.md +188 -0
  65. package/runtime/templates/reports/schedule.template.md +2 -2
  66. package/runtime/templates/worker-prompt-preamble.md +1 -1
  67. package/runtime/validators/validate-run.py +7 -7
  68. package/src/cli-registry.mjs +14 -0
  69. package/src/commands/execute/wizard.mjs +3 -1
  70. package/src/commands/inspect/set-work-status.mjs +31 -0
  71. package/src/commands/inspect/task-show.mjs +11 -31
  72. package/src/commands/lifecycle/check-project.mjs +68 -56
  73. package/src/commands/lifecycle/install.mjs +1 -0
  74. package/src/commands/lifecycle/preflight.mjs +82 -0
@@ -40,8 +40,8 @@ const SCRIPT = `
40
40
  import json, sys
41
41
  from pathlib import Path
42
42
  from okstra_project import (
43
- resolve_project_root, find_task_root, read_task_manifest,
44
- ResolverError,
43
+ resolve_project_root, task_read_side_snapshot,
44
+ ResolverError, StateError,
45
45
  )
46
46
 
47
47
  explicit = sys.argv[1]
@@ -55,37 +55,17 @@ except ResolverError as e:
55
55
  sys.exit(2)
56
56
 
57
57
  pr_path = Path(pr)
58
- task_root = find_task_root(pr_path, task_key)
59
- if task_root is None:
60
- print(json.dumps({"ok": False, "stage": "task_root_missing", "reason": f"no task root for {task_key}"}))
61
- sys.exit(1)
62
-
63
- manifest = read_task_manifest(task_root)
64
- if manifest is None:
65
- print(json.dumps({"ok": False, "stage": "manifest_missing", "reason": f"task-manifest.json missing in {task_root}"}))
58
+ try:
59
+ snapshot = task_read_side_snapshot(pr_path, task_key)
60
+ except StateError as e:
61
+ print(json.dumps({
62
+ "ok": False,
63
+ "stage": getattr(e, "stage", None) or "task_snapshot",
64
+ "reason": str(e),
65
+ }))
66
66
  sys.exit(1)
67
67
 
68
- wf = manifest.get("workflow", {}) or {}
69
- out = {
70
- "ok": True,
71
- "projectRoot": str(pr_path),
72
- "taskKey": manifest.get("taskKey"),
73
- "taskType": manifest.get("taskType"),
74
- "taskRoot": str(task_root),
75
- "taskBriefPath": manifest.get("taskBriefPath"),
76
- "workflow": {
77
- "currentPhase": wf.get("currentPhase"),
78
- "currentPhaseState": wf.get("currentPhaseState"),
79
- "lastCompletedPhase": wf.get("lastCompletedPhase"),
80
- "nextRecommendedPhase": wf.get("nextRecommendedPhase"),
81
- "routingStatus": wf.get("routingStatus"),
82
- "phaseStates": wf.get("phaseStates"),
83
- },
84
- "resultContract": manifest.get("resultContract"),
85
- "artifacts": manifest.get("artifacts"),
86
- "modelAssignments": manifest.get("modelAssignments"),
87
- "latestRunPath": manifest.get("latestRunPath"),
88
- }
68
+ out = {"ok": True, **snapshot}
89
69
  print(json.dumps(out, ensure_ascii=False, default=str, indent=2))
90
70
  `;
91
71
 
@@ -63,6 +63,14 @@ export async function run(args) {
63
63
  process.stderr.write(`error: ${err.message}\n\n${USAGE}`);
64
64
  return 2;
65
65
  }
66
+ const { code, payload } = await checkProject({ cwd: opts.cwd });
67
+ emit(opts, payload);
68
+ return code;
69
+ }
70
+
71
+ // Core check shared with 'okstra preflight' — resolves PROJECT_ROOT from cwd
72
+ // and validates .okstra/project.json without writing to stdout.
73
+ export async function checkProject({ cwd = process.cwd() } = {}) {
66
74
  const paths = await resolvePaths();
67
75
 
68
76
  const probe = await runProcess(
@@ -79,23 +87,25 @@ export async function run(args) {
79
87
  "except ResolverError as e:",
80
88
  " print('RESOLVER_ERROR', e)",
81
89
  ].join("\n"),
82
- opts.cwd,
90
+ cwd,
83
91
  ],
84
92
  { PYTHONPATH: buildPythonpath(paths) },
85
93
  );
86
94
 
87
95
  if (probe.code !== 0) {
88
96
  const raw = probe.stderr.trim() || probe.stdout.trim();
89
- emit(opts, {
90
- ok: false,
91
- stage: "python",
92
- reason:
93
- `python invocation failed: ${raw}. ` +
94
- "This usually means the okstra runtime is stale or missing — " +
95
- "run 'okstra doctor' to diagnose, then 'okstra ensure-installed' " +
96
- "to repair.",
97
- });
98
- return 1;
97
+ return {
98
+ code: 1,
99
+ payload: {
100
+ ok: false,
101
+ stage: "python",
102
+ reason:
103
+ `python invocation failed: ${raw}. ` +
104
+ "This usually means the okstra runtime is stale or missing — " +
105
+ "run 'okstra doctor' to diagnose, then 'okstra ensure-installed' " +
106
+ "to repair.",
107
+ },
108
+ };
99
109
  }
100
110
 
101
111
  const lines = probe.stdout.trim().split("\n");
@@ -107,36 +117,37 @@ export async function run(args) {
107
117
 
108
118
  const resolverError = tagOf("RESOLVER_ERROR");
109
119
  if (resolverError) {
110
- emit(opts, {
111
- ok: false,
112
- stage: "resolve",
113
- reason: resolverError,
114
- cwd: opts.cwd,
115
- });
116
- return 2;
120
+ return {
121
+ code: 2,
122
+ payload: { ok: false, stage: "resolve", reason: resolverError, cwd },
123
+ };
117
124
  }
118
125
 
119
126
  const projectRoot = tagOf("PROJECT_ROOT");
120
127
  const projectJsonPath = tagOf("PROJECT_JSON");
121
128
  if (!projectRoot || !projectJsonPath) {
122
- emit(opts, {
123
- ok: false,
124
- stage: "parse",
125
- reason: "could not parse python output",
126
- raw: probe.stdout,
127
- });
128
- return 1;
129
+ return {
130
+ code: 1,
131
+ payload: {
132
+ ok: false,
133
+ stage: "parse",
134
+ reason: "could not parse python output",
135
+ raw: probe.stdout,
136
+ },
137
+ };
129
138
  }
130
139
 
131
140
  if (!(await fileExists(projectJsonPath))) {
132
- emit(opts, {
133
- ok: false,
134
- stage: "project_json_missing",
135
- reason: `${projectJsonPath} not found — run /okstra-setup in this project first`,
136
- projectRoot,
137
- projectJsonPath,
138
- });
139
- return 1;
141
+ return {
142
+ code: 1,
143
+ payload: {
144
+ ok: false,
145
+ stage: "project_json_missing",
146
+ reason: `${projectJsonPath} not found — run /okstra-setup in this project first`,
147
+ projectRoot,
148
+ projectJsonPath,
149
+ },
150
+ };
140
151
  }
141
152
 
142
153
  let projectId = null;
@@ -144,32 +155,33 @@ export async function run(args) {
144
155
  const data = JSON.parse(await fs.readFile(projectJsonPath, "utf8"));
145
156
  projectId = typeof data?.projectId === "string" ? data.projectId : null;
146
157
  } catch (err) {
147
- emit(opts, {
148
- ok: false,
149
- stage: "project_json_invalid",
150
- reason: `failed to parse ${projectJsonPath}: ${err.message}`,
151
- projectRoot,
152
- projectJsonPath,
153
- });
154
- return 1;
158
+ return {
159
+ code: 1,
160
+ payload: {
161
+ ok: false,
162
+ stage: "project_json_invalid",
163
+ reason: `failed to parse ${projectJsonPath}: ${err.message}`,
164
+ projectRoot,
165
+ projectJsonPath,
166
+ },
167
+ };
155
168
  }
156
169
 
157
170
  if (!projectId) {
158
- emit(opts, {
159
- ok: false,
160
- stage: "project_json_invalid",
161
- reason: `${projectJsonPath} missing projectId field`,
162
- projectRoot,
163
- projectJsonPath,
164
- });
165
- return 1;
171
+ return {
172
+ code: 1,
173
+ payload: {
174
+ ok: false,
175
+ stage: "project_json_invalid",
176
+ reason: `${projectJsonPath} missing projectId field`,
177
+ projectRoot,
178
+ projectJsonPath,
179
+ },
180
+ };
166
181
  }
167
182
 
168
- emit(opts, {
169
- ok: true,
170
- projectRoot,
171
- projectJsonPath,
172
- projectId,
173
- });
174
- return 0;
183
+ return {
184
+ code: 0,
185
+ payload: { ok: true, projectRoot, projectJsonPath, projectId },
186
+ };
175
187
  }
@@ -48,6 +48,7 @@ const REQUIRED_PROMPT_RESOURCE_FILES = Object.freeze([
48
48
  ["prompts", "lead", "context-loader.md"],
49
49
  ["prompts", "lead", "team-contract.md"],
50
50
  ["prompts", "lead", "convergence.md"],
51
+ ["prompts", "lead", "plan-body-verification.md"],
51
52
  ["prompts", "lead", "report-writer.md"],
52
53
  ["prompts", "coding-preflight", "overview.md"],
53
54
  ["prompts", "coding-preflight", "clean-code.md"],
@@ -0,0 +1,82 @@
1
+ import { checkProject } from "./check-project.mjs";
2
+ import { runEnsureInstalled } from "./install.mjs";
3
+
4
+ const USAGE = `okstra preflight — one-call skill preflight (ensure-installed + check-project)
5
+
6
+ Usage:
7
+ okstra preflight [--runtime <name>] [--cwd <dir>] [--json]
8
+
9
+ Runs the runtime freshness check (quietly reinstalling when stale), then
10
+ verifies the target project has .okstra/project.json. Prints one JSON object:
11
+
12
+ ok: true {ok, projectRoot, projectJsonPath, projectId}
13
+ ok: false {ok, stage, reason, ...}
14
+ stage: install | resolve | project_json_missing |
15
+ project_json_invalid | python | parse
16
+
17
+ Options:
18
+ --runtime <name> Runtime whose assets must be present (e.g. claude-code);
19
+ forwarded to ensure-installed.
20
+ --cwd <dir> Resolve PROJECT_ROOT from <dir> instead of process cwd.
21
+ --json Default output format (kept for symmetry).
22
+
23
+ Exit codes:
24
+ 0 runtime fresh and project registered
25
+ 1 runtime install failed, or project setup missing/invalid
26
+ 2 PROJECT_ROOT could not be resolved from cwd (pass --cwd), or bad flags
27
+ `;
28
+
29
+ function parseArgs(args) {
30
+ const opts = { cwd: process.cwd(), runtime: null };
31
+ for (let i = 0; i < args.length; i++) {
32
+ const a = args[i];
33
+ if (a === "--json") continue;
34
+ if (a === "--cwd" || a === "--runtime") {
35
+ const next = args[i + 1];
36
+ if (!next || next.startsWith("--")) throw new Error(`${a} requires a value`);
37
+ if (a === "--cwd") opts.cwd = next;
38
+ else opts.runtime = next;
39
+ i++;
40
+ } else {
41
+ throw new Error(`unknown argument '${a}'`);
42
+ }
43
+ }
44
+ return opts;
45
+ }
46
+
47
+ function emit(payload) {
48
+ process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
49
+ }
50
+
51
+ export async function run(args) {
52
+ if (args.includes("--help") || args.includes("-h")) {
53
+ process.stdout.write(USAGE);
54
+ return 0;
55
+ }
56
+
57
+ let opts;
58
+ try {
59
+ opts = parseArgs(args);
60
+ } catch (err) {
61
+ process.stderr.write(`error: ${err.message}\n\n${USAGE}`);
62
+ return 2;
63
+ }
64
+
65
+ const ensureArgs = ["--quiet"];
66
+ if (opts.runtime) ensureArgs.push("--runtime", opts.runtime);
67
+ const installCode = await runEnsureInstalled(ensureArgs);
68
+ if (installCode !== 0) {
69
+ emit({
70
+ ok: false,
71
+ stage: "install",
72
+ reason:
73
+ "okstra runtime install failed — run 'okstra doctor' to diagnose, " +
74
+ "then 'okstra install' to repair.",
75
+ });
76
+ return installCode === 2 ? 2 : 1;
77
+ }
78
+
79
+ const { code, payload } = await checkProject({ cwd: opts.cwd });
80
+ emit(payload);
81
+ return code;
82
+ }