okstra 0.6.0 → 0.6.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.6.0",
3
- "builtAt": "2026-05-12T04:57:34.255Z",
2
+ "package": "0.6.1",
3
+ "builtAt": "2026-05-12T05:33:06.736Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -285,15 +285,27 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
285
285
  project_root = Path(inp.project_root)
286
286
 
287
287
  # ---- validate inputs ----
288
+ # Hint suffix added to every "okstra runtime asset missing" PrepareError below.
289
+ # These files ship with the package and live under runtime/ — if any are
290
+ # missing the install is incomplete or stale, not a user-content issue.
291
+ _INSTALL_HINT = (
292
+ " This file ships with the okstra package; its absence usually means a stale "
293
+ "or partial install. Run 'okstra ensure-installed' (or 'okstra install' again) "
294
+ "to repair the runtime. If the problem persists, run 'okstra doctor' for a "
295
+ "fuller diagnostic."
296
+ )
288
297
  profile_dir = workspace_root / "prompts" / "profiles"
289
298
  profile_file = profile_dir / f"{inp.task_type}.md"
290
299
  if not profile_file.is_file():
291
300
  raise PrepareError(
292
- f"analysis profile file not found for task-type {inp.task_type}: {profile_file}"
301
+ f"analysis profile file not found for task-type {inp.task_type}: "
302
+ f"{profile_file}.{_INSTALL_HINT}"
293
303
  )
294
304
  prompt_template = workspace_root / "prompts" / "launch.template.md"
295
305
  if not prompt_template.is_file():
296
- raise PrepareError(f"okstra prompt template not found: {prompt_template}")
306
+ raise PrepareError(
307
+ f"okstra prompt template not found: {prompt_template}.{_INSTALL_HINT}"
308
+ )
297
309
  task_index_template = (
298
310
  workspace_root / "templates" / "project-docs" / "task-index.template.md"
299
311
  )
@@ -304,7 +316,9 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
304
316
  run_validator = workspace_root / "validators" / "validate-run.py"
305
317
  for required in (task_index_template, final_report_template, run_validator, source_skill):
306
318
  if not required.is_file():
307
- raise PrepareError(f"required okstra template or source skill missing: {required}")
319
+ raise PrepareError(
320
+ f"required okstra template or source skill missing: {required}.{_INSTALL_HINT}"
321
+ )
308
322
  if not project_root.is_dir():
309
323
  raise PrepareError(f"project root not found: {project_root}")
310
324
  if not inp.brief_path.is_file():
@@ -329,7 +343,11 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
329
343
  try:
330
344
  upsert_project_json(project_root, inp.project_id)
331
345
  except ResolverError as exc:
332
- raise PrepareError(f"project.json upsert failed: {exc}") from exc
346
+ # Surface the project_root in the prefix so the user can tell which
347
+ # registration failed when multiple projects are in play. The full
348
+ # underlying ResolverError text (which carries remediation guidance)
349
+ # is preserved by the `: {exc}` suffix and the `raise ... from exc`.
350
+ raise PrepareError(f"project.json upsert failed for {project_root}: {exc}") from exc
333
351
 
334
352
  # ---- workers resolution ----
335
353
  profile_workers_csv = ",".join(resolve_profile_workers(profile_file))
@@ -75,9 +75,12 @@ def resolve_project_root(*, explicit_root: str = "",
75
75
  if git_top is not None:
76
76
  return git_top.resolve()
77
77
  raise ResolverError(
78
- "PROJECT_ROOT 해석할 없습니다. "
79
- "--project-root 명시하거나, 프로젝트 루트(또는 그 하위)에서 실행하거나, "
80
- "git 작업 트리 안에서 실행해 주십시오.")
78
+ "could not resolve PROJECT_ROOT from cwd. "
79
+ "Pass --project-root <abs-path>, "
80
+ "run from inside a project (a directory with .project-docs/okstra/project.json at or above cwd), "
81
+ "or run from inside a git working tree. "
82
+ "(PROJECT_ROOT 를 해석할 수 없습니다 — --project-root 를 명시하거나, "
83
+ "프로젝트 루트 또는 그 하위에서 실행하거나, git 작업 트리 안에서 실행해 주십시오.)")
81
84
 
82
85
 
83
86
  def upsert_project_json(project_root: Path, project_id: str, *,
@@ -100,13 +103,20 @@ def upsert_project_json(project_root: Path, project_id: str, *,
100
103
  data = json.loads(target.read_text())
101
104
  except (OSError, json.JSONDecodeError) as exc:
102
105
  raise ResolverError(
103
- f"project.json 읽을 수 없습니다: {target} ({exc})") from exc
106
+ f"failed to read project.json at {target}: {exc} "
107
+ f"(project.json 을 읽을 수 없습니다.) "
108
+ f"If the file is corrupted, delete it and re-run 'okstra setup --project-id <id>'."
109
+ ) from exc
104
110
  existing_id = str(data.get("projectId") or "")
105
111
  if existing_id and existing_id != project_id:
106
112
  raise ResolverError(
107
- f"projectId 불일치: project.json={existing_id!r}, "
108
- f"인자={project_id!r}. "
109
- f"동일 PROJECT_ROOT 에서는 하나의 projectId 사용할 수 있습니다.")
113
+ f"projectId mismatch: existing project.json has {existing_id!r} "
114
+ f"but the supplied argument is {project_id!r}. "
115
+ f"okstra allows only one projectId per PROJECT_ROOT. "
116
+ f"To fix: re-run with --project-id {existing_id!r} to keep the existing registration, "
117
+ f"or manually delete {target} if you intend to re-register this directory "
118
+ f"under a different id. "
119
+ f"(projectId 불일치: 한 PROJECT_ROOT 에는 하나의 projectId 만 허용됩니다.)")
110
120
  result = {
111
121
  "projectId": project_id,
112
122
  "projectRoot": abs_root,
@@ -103,10 +103,15 @@ export async function run(args) {
103
103
  );
104
104
 
105
105
  if (probe.code !== 0) {
106
+ const raw = probe.stderr.trim() || probe.stdout.trim();
106
107
  emit(opts, {
107
108
  ok: false,
108
109
  stage: "python",
109
- reason: `python invocation failed: ${probe.stderr.trim() || probe.stdout.trim()}`,
110
+ reason:
111
+ `python invocation failed: ${raw}. ` +
112
+ "This usually means the okstra runtime is stale or missing — " +
113
+ "run 'okstra doctor' to diagnose, then 'okstra ensure-installed' " +
114
+ "to repair.",
110
115
  });
111
116
  return 1;
112
117
  }
package/src/setup.mjs CHANGED
@@ -1,6 +1,8 @@
1
1
  import { promises as fs } from "node:fs";
2
2
  import { spawn } from "node:child_process";
3
3
  import { createInterface } from "node:readline";
4
+ import { homedir } from "node:os";
5
+ import { resolve as resolvePath } from "node:path";
4
6
  import { resolvePaths } from "./paths.mjs";
5
7
 
6
8
  const USAGE = `okstra setup — register the current project with okstra
@@ -192,8 +194,51 @@ export async function run(args) {
192
194
  try {
193
195
  resolved = await resolveProjectRoot(paths, opts.projectRoot);
194
196
  } catch (err) {
197
+ if (err.code === "RESOLVER") {
198
+ const cwd = resolvePath(process.cwd());
199
+ const home = resolvePath(homedir());
200
+ const inHome = cwd === home;
201
+ process.stderr.write(
202
+ "error: 'okstra setup' registers the CURRENT directory as an okstra project,\n" +
203
+ " but no project root could be resolved from here.\n" +
204
+ ` cwd: ${cwd}\n` +
205
+ (inHome
206
+ ? " (this looks like your home directory — setup is project-level, not machine-level;\n" +
207
+ " machine-level install is 'okstra install', which you've likely already done.)\n"
208
+ : "") +
209
+ "\nFix one of:\n" +
210
+ " 1. cd into the project you want to register, then re-run:\n" +
211
+ " cd <your project> && okstra setup --project-id <id>\n" +
212
+ " 2. pass an explicit path:\n" +
213
+ " okstra setup --project-root <abs-path> --project-id <id>\n" +
214
+ " 3. run from inside any git working tree (the toplevel becomes PROJECT_ROOT).\n" +
215
+ `\n(underlying error: ${err.message})\n`,
216
+ );
217
+ return 2;
218
+ }
219
+ // Non-RESOLVER path: typically python invocation failure (PYTHONPATH wrong,
220
+ // ~/.okstra missing, python3 not on PATH, okstra_project module missing).
221
+ // The user can't fix this by adjusting setup arguments — it's a runtime
222
+ // health issue, not a PROJECT_ROOT issue. Reword accordingly.
223
+ const looksLikePythonFailure = /python invocation failed|ModuleNotFoundError|No module named/.test(
224
+ err.message,
225
+ );
226
+ if (looksLikePythonFailure) {
227
+ process.stderr.write(
228
+ "error: 'okstra setup' could not start because the okstra runtime is not\n" +
229
+ " reachable from this process. This is almost always a stale or\n" +
230
+ " incomplete install, not a problem with your setup arguments.\n" +
231
+ "\nFix:\n" +
232
+ " 1. run 'okstra doctor' to see exactly what is missing\n" +
233
+ " 2. run 'okstra ensure-installed' (or 'npx -y okstra@latest install')\n" +
234
+ " to repair the runtime\n" +
235
+ " 3. retry: okstra setup --project-id <id>\n" +
236
+ `\n(underlying error: ${err.message})\n`,
237
+ );
238
+ return 1;
239
+ }
195
240
  process.stderr.write(`error: could not resolve PROJECT_ROOT: ${err.message}\n`);
196
- return err.code === "RESOLVER" ? 2 : 1;
241
+ return 1;
197
242
  }
198
243
  const { projectRoot, projectJsonPath } = resolved;
199
244