okstra 0.36.2 → 0.37.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 (64) hide show
  1. package/README.kr.md +6 -6
  2. package/README.md +6 -6
  3. package/bin/okstra +4 -2
  4. package/docs/kr/architecture.md +29 -29
  5. package/docs/kr/cli.md +7 -6
  6. package/docs/pr-template-usage.md +2 -2
  7. package/docs/project-structure-overview.md +4 -4
  8. package/docs/superpowers/plans/2026-05-25-okstra-project-root-rename.md +159 -0
  9. package/docs/superpowers/plans/2026-05-26-wizard-3-option-picker.md +860 -0
  10. package/docs/task-process/common-flow.md +2 -2
  11. package/package.json +1 -1
  12. package/runtime/BUILD.json +2 -2
  13. package/runtime/agents/SKILL.md +2 -2
  14. package/runtime/agents/workers/claude-worker.md +1 -1
  15. package/runtime/prompts/profiles/_common-contract.md +6 -6
  16. package/runtime/prompts/profiles/_implementation-executor.md +2 -2
  17. package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
  18. package/runtime/prompts/profiles/final-verification.md +1 -1
  19. package/runtime/prompts/profiles/implementation-planning.md +5 -5
  20. package/runtime/prompts/profiles/release-handoff.md +1 -1
  21. package/runtime/prompts/profiles/requirements-discovery.md +3 -3
  22. package/runtime/prompts/wizard/prompts.ko.json +80 -6
  23. package/runtime/python/lib/okstra/interactive.sh +2 -2
  24. package/runtime/python/lib/okstra/project-resolver.sh +1 -1
  25. package/runtime/python/lib/okstra/usage.sh +5 -5
  26. package/runtime/python/lib/okstra-ctl/cmd-rerun.sh +1 -1
  27. package/runtime/python/okstra_ctl/backfill.py +5 -3
  28. package/runtime/python/okstra_ctl/migrate.py +408 -0
  29. package/runtime/python/okstra_ctl/paths.py +12 -3
  30. package/runtime/python/okstra_ctl/pr_template.py +4 -2
  31. package/runtime/python/okstra_ctl/render.py +8 -6
  32. package/runtime/python/okstra_ctl/run.py +5 -5
  33. package/runtime/python/okstra_ctl/seeding.py +12 -6
  34. package/runtime/python/okstra_ctl/sequence.py +3 -1
  35. package/runtime/python/okstra_ctl/wizard.py +412 -77
  36. package/runtime/python/okstra_ctl/worktree.py +8 -6
  37. package/runtime/python/okstra_project/__init__.py +35 -5
  38. package/runtime/python/okstra_project/dirs.py +67 -0
  39. package/runtime/python/okstra_project/resolver.py +8 -8
  40. package/runtime/python/okstra_project/state.py +11 -9
  41. package/runtime/python/okstra_token_usage/collect.py +3 -1
  42. package/runtime/skills/okstra-brief/SKILL.md +30 -30
  43. package/runtime/skills/okstra-context-loader/SKILL.md +7 -7
  44. package/runtime/skills/okstra-inspect/SKILL.md +25 -25
  45. package/runtime/skills/okstra-run/templates/pr-body.template.md +1 -1
  46. package/runtime/skills/okstra-schedule/SKILL.md +7 -7
  47. package/runtime/skills/okstra-setup/SKILL.md +8 -8
  48. package/runtime/templates/okstra.CLAUDE.md +4 -4
  49. package/runtime/templates/reports/brief.template.md +5 -5
  50. package/runtime/templates/reports/task-brief.template.md +1 -1
  51. package/runtime/validators/lib/fixtures.sh +2 -2
  52. package/runtime/validators/lib/paths.sh +9 -3
  53. package/runtime/validators/validate-brief.py +2 -2
  54. package/runtime/validators/validate-brief.sh +1 -1
  55. package/runtime/validators/validate-run.py +3 -1
  56. package/runtime/validators/validate-workflow.sh +2 -2
  57. package/src/check-project.mjs +3 -3
  58. package/src/config.mjs +6 -5
  59. package/src/install.mjs +5 -5
  60. package/src/migrate.mjs +163 -0
  61. package/src/okstra-dirs.mjs +37 -0
  62. package/src/paths.mjs +17 -0
  63. package/src/setup.mjs +8 -4
  64. package/src/uninstall.mjs +3 -3
@@ -45,7 +45,7 @@ Trigger phrases: "okstra status", "task status", "current phase", "next phase",
45
45
 
46
46
  ### status.1 — Overall project status
47
47
 
48
- Read `.project-docs/okstra/discovery/task-catalog.json`. The catalog is the authoritative source — every field listed below (including `workStatus`, `workStatusUpdatedAt`, `workStatusNote`) is projected directly from each `task-manifest.json` by `scripts/okstra_ctl/render.py :: render_task_catalog_discovery`. Do NOT re-open individual manifests for the overview.
48
+ Read `.okstra/discovery/task-catalog.json`. The catalog is the authoritative source — every field listed below (including `workStatus`, `workStatusUpdatedAt`, `workStatusNote`) is projected directly from each `task-manifest.json` by `scripts/okstra_ctl/render.py :: render_task_catalog_discovery`. Do NOT re-open individual manifests for the overview.
49
49
 
50
50
  | Field | Description |
51
51
  |------|------|
@@ -86,7 +86,7 @@ If `awaitingApproval` is true OR `routingStatus == "pending"`, append a `*` to t
86
86
  Given a specific `task-key` or `task-group + task-id`:
87
87
 
88
88
  1. If possible, quickly look it up in `task-catalog.json`.
89
- 2. If necessary, read `.project-docs/okstra/tasks/<task-group>/<task-id>/task-manifest.json` directly.
89
+ 2. If necessary, read `.okstra/tasks/<task-group>/<task-id>/task-manifest.json` directly.
90
90
  3. If you need the latest run information, read `history/timeline.json` along with the latest run manifest.
91
91
 
92
92
  Required fields: `taskKey`, `taskType`, `workCategory`, `currentStatus`, `latestRunStatus`, `workflow.{currentPhase, currentPhaseState, phaseStates, lastCompletedPhase, nextRecommendedPhase, awaitingApproval, routingStatus, lastSafeCheckpoint}`, `workStatus`, `workStatusUpdatedAt`, `workStatusNote`, `latestReportPath`, `latestResumeCommandPath`, `historyTimelinePath`.
@@ -155,10 +155,10 @@ Accepted `<status>` values: `todo`, `in-progress`, `blocked`, `done`.
155
155
  **Procedure:**
156
156
 
157
157
  1. **Validate status value.** If not in the enum, output an error and the allowed values list. Do not modify any file.
158
- 2. **Look up the task** in `.project-docs/okstra/discovery/task-catalog.json` by `task-id` (case-insensitive on both `task-id` and `task-group`).
158
+ 2. **Look up the task** in `.okstra/discovery/task-catalog.json` by `task-id` (case-insensitive on both `task-id` and `task-group`).
159
159
  - If the user provided `<task-group>` explicitly, scope the lookup to that group (case-insensitive).
160
160
  - Single match → proceed. Multiple matches across different groups → list and ask user to retry with explicit form (`okstra status set <task-group> <TASK-ID> <status>`). No match → `<TASK-ID>를 찾을 수 없습니다.` and stop.
161
- 3. **Open the matching `task-manifest.json`** at `.project-docs/okstra/tasks/<task-group-segment>/<task-id-segment>/task-manifest.json`. Assemble the path from the catalog entry's `taskGroupPathSegment` and `taskIdPathSegment` fields (filesystem-safe segments emitted by the renderer) — NOT from the raw user-supplied strings, which may differ in case or contain characters normalized when the manifest was created. Prefer the `taskManifestPath` field directly when present.
161
+ 3. **Open the matching `task-manifest.json`** at `.okstra/tasks/<task-group-segment>/<task-id-segment>/task-manifest.json`. Assemble the path from the catalog entry's `taskGroupPathSegment` and `taskIdPathSegment` fields (filesystem-safe segments emitted by the renderer) — NOT from the raw user-supplied strings, which may differ in case or contain characters normalized when the manifest was created. Prefer the `taskManifestPath` field directly when present.
162
162
  4. **Update fields at the manifest root:**
163
163
  - `workStatus` ← new status value
164
164
  - `workStatusUpdatedAt` ← current ISO-8601 UTC timestamp (`2026-05-01T10:23:45Z`)
@@ -199,7 +199,7 @@ Trigger phrases: "okstra history", "past runs", "run history", "re-run", "list t
199
199
 
200
200
  ### history.1 — Read the task catalog
201
201
 
202
- 1. Read `.project-docs/okstra/discovery/task-catalog.json`.
202
+ 1. Read `.okstra/discovery/task-catalog.json`.
203
203
  2. Apply filters from user input (all optional, AND-combined):
204
204
  - `--task-type <type>` → keep entries whose `taskType` matches.
205
205
  - `--latest-run-status <status>` → keep entries whose `latestRunStatus` matches (`completed`, `contract-violated`, `error`).
@@ -219,7 +219,7 @@ Trigger phrases: "okstra history", "past runs", "run history", "re-run", "list t
219
219
 
220
220
  **Catalog absent — fallback.** Do NOT bail out. Manifests on disk are the source of truth.
221
221
 
222
- 1. Glob `<projectRoot>/.project-docs/okstra/tasks/*/*/task-manifest.json`.
222
+ 1. Glob `<projectRoot>/.okstra/tasks/*/*/task-manifest.json`.
223
223
  2. For each manifest, read the same fields as above.
224
224
  3. Apply the same filters/sort/limit and print the same table, prefixed with: `note: task-catalog.json missing; reconstructed from task manifests on disk.`
225
225
  4. Only if the glob yields zero manifests: `There is no okstra execution history yet.`
@@ -292,13 +292,13 @@ task-key format: `<project-id>:<task-group>:<task-id>`.
292
292
 
293
293
  Lookup methods (in priority order):
294
294
 
295
- A. **`task-catalog.json` (fast):** read `.project-docs/okstra/discovery/task-catalog.json`, match `taskKey` lowercase. `latestReportPath` is task-type-agnostic "most recent report".
295
+ A. **`task-catalog.json` (fast):** read `.okstra/discovery/task-catalog.json`, match `taskKey` lowercase. `latestReportPath` is task-type-agnostic "most recent report".
296
296
 
297
- B. **`task-manifest.json` (direct):** if catalog missing, slugify task-group / task-id, read `.project-docs/okstra/tasks/<group-segment>/<id-segment>/task-manifest.json`, use `latestReportPath` (task-type-agnostic).
297
+ B. **`task-manifest.json` (direct):** if catalog missing, slugify task-group / task-id, read `.okstra/tasks/<group-segment>/<id-segment>/task-manifest.json`, use `latestReportPath` (task-type-agnostic).
298
298
 
299
- C. **`timeline.json` (specific run):** for a specific date or run, read `.project-docs/okstra/tasks/<group-segment>/<id-segment>/history/timeline.json`, filter `runs[]` by `runTimestamp` / `status` / `taskType`, use `runs[].reportPath`.
299
+ C. **`timeline.json` (specific run):** for a specific date or run, read `.okstra/tasks/<group-segment>/<id-segment>/history/timeline.json`, filter `runs[]` by `runTimestamp` / `status` / `taskType`, use `runs[].reportPath`.
300
300
 
301
- D. **Specific task-type (fallback):** `latestReportPath` is task-type-agnostic. For a specific task-type's latest report, look under `.project-docs/okstra/tasks/<group-segment>/<id-segment>/runs/<task-type-segment>/reports/`, filename pattern `final-report-<task-type-segment>-<NNN>.md` per `scripts/okstra_ctl/sequence.py:31`. Highest seq is latest. Cross-verify with `timeline.json`'s `runs[].taskType` filter.
301
+ D. **Specific task-type (fallback):** `latestReportPath` is task-type-agnostic. For a specific task-type's latest report, look under `.okstra/tasks/<group-segment>/<id-segment>/runs/<task-type-segment>/reports/`, filename pattern `final-report-<task-type-segment>-<NNN>.md` per `scripts/okstra_ctl/sequence.py:31`. Highest seq is latest. Cross-verify with `timeline.json`'s `runs[].taskType` filter.
302
302
 
303
303
  ### report.2 — Confirm existence
304
304
 
@@ -347,7 +347,7 @@ Aggregate elapsed work time for a given task, grouped by **task type** and broke
347
347
 
348
348
  **Data sources** (both collected by okstra):
349
349
 
350
- 1. `.project-docs/okstra/tasks/<task-group>/<task-id>/history/timeline.json` — `runs` array with `runTimestamp`, `taskType`, `status`, `teamStatePath`, `taskRootPath`. Both path fields may be either project-root-relative or task-root-relative depending on which `render.py` version wrote the manifest.
350
+ 1. `.okstra/tasks/<task-group>/<task-id>/history/timeline.json` — `runs` array with `runTimestamp`, `taskType`, `status`, `teamStatePath`, `taskRootPath`. Both path fields may be either project-root-relative or task-root-relative depending on which `render.py` version wrote the manifest.
351
351
  2. Each run's `.../runs/<task-type>/state/team-state-<suffix>.json` — populated by `scripts/okstra-token-usage.py` at Phase 7. Contains `leadUsage.{startedAt, endedAt, durationMs}` and `workers[].{workerId, agent, usage.{startedAt, endedAt, durationMs}}`.
352
352
 
353
353
  If a run never reached Phase 7, its `team-state` lacks `durationMs`. Mark such runs as `unavailable` rather than guessing.
@@ -355,7 +355,7 @@ If a run never reached Phase 7, its `team-state` lacks `durationMs`. Mark such r
355
355
  ### time.1 — Resolve task-id → timeline path
356
356
 
357
357
  1. If the user gave a full `task-key`, use it directly.
358
- 2. Otherwise read `.project-docs/okstra/discovery/task-catalog.json` and find the entry whose `taskId` matches.
358
+ 2. Otherwise read `.okstra/discovery/task-catalog.json` and find the entry whose `taskId` matches.
359
359
  3. Multiple matches → list candidates (`taskKey`, `taskType`, `updatedAt`) and ask the user to pick.
360
360
  4. Read `historyTimelinePath` from the chosen entry.
361
361
 
@@ -476,7 +476,7 @@ Read-only inventory of codex/gemini wrapper log files written next to each promp
476
476
  **Background:** codex/gemini wrappers (`okstra-codex-exec.sh`, `okstra-gemini-exec.sh`) write a sidecar log next to each prompt history file:
477
477
 
478
478
  ```
479
- .project-docs/okstra/tasks/<task-group>/<task-id>/runs/<phase>/prompts/
479
+ .okstra/tasks/<task-group>/<task-id>/runs/<phase>/prompts/
480
480
  <worker>-prompt-<phase>-<seq>.md <-- prompt (git-tracked)
481
481
  <worker>-prompt-<phase>-<seq>.log <-- live stdout+stderr mirror
482
482
  ```
@@ -485,7 +485,7 @@ The log is truncated at each dispatch (`: > "$log_path"`) — only the latest ru
485
485
 
486
486
  ### logs.1 — Inventory
487
487
 
488
- Construct the logs root by appending `/.project-docs/okstra/tasks` to the literal `projectRoot` value parsed in Step 0; paste as a literal absolute path in place of `<LOGS_ROOT>` below (no shell variables, no `$(...)`):
488
+ Construct the logs root by appending `/.okstra/tasks` to the literal `projectRoot` value parsed in Step 0; paste as a literal absolute path in place of `<LOGS_ROOT>` below (no shell variables, no `$(...)`):
489
489
 
490
490
  ```bash
491
491
  find <LOGS_ROOT> -type f -path '*/runs/*/prompts/*.log' \
@@ -529,33 +529,33 @@ Emit a fenced bash block the user can copy-paste. Do NOT execute. Each block pai
529
529
  ## Cleanup options (manual)
530
530
 
531
531
  # 7일 이상 된 로그만 삭제
532
- find <PROJECT_ROOT>/.project-docs/okstra/tasks \
532
+ find <PROJECT_ROOT>/.okstra/tasks \
533
533
  -type f -path '*/runs/*/prompts/*.log' -mtime +7 -print # dry-run
534
- find <PROJECT_ROOT>/.project-docs/okstra/tasks \
534
+ find <PROJECT_ROOT>/.okstra/tasks \
535
535
  -type f -path '*/runs/*/prompts/*.log' -mtime +7 -delete
536
536
 
537
537
  # 30일 이상 된 로그만 삭제
538
- find <PROJECT_ROOT>/.project-docs/okstra/tasks \
538
+ find <PROJECT_ROOT>/.okstra/tasks \
539
539
  -type f -path '*/runs/*/prompts/*.log' -mtime +30 -print # dry-run
540
- find <PROJECT_ROOT>/.project-docs/okstra/tasks \
540
+ find <PROJECT_ROOT>/.okstra/tasks \
541
541
  -type f -path '*/runs/*/prompts/*.log' -mtime +30 -delete
542
542
 
543
543
  # 특정 task-group 의 로그 일괄 삭제 (예: dev-9388)
544
- find <PROJECT_ROOT>/.project-docs/okstra/tasks/dev-9388 \
544
+ find <PROJECT_ROOT>/.okstra/tasks/dev-9388 \
545
545
  -type f -name '*.log' -print # dry-run
546
- find <PROJECT_ROOT>/.project-docs/okstra/tasks/dev-9388 \
546
+ find <PROJECT_ROOT>/.okstra/tasks/dev-9388 \
547
547
  -type f -name '*.log' -delete
548
548
 
549
549
  # 특정 task-id 의 로그 일괄 삭제 (예: dev-9428)
550
- find <PROJECT_ROOT>/.project-docs/okstra/tasks/*/dev-9428 \
550
+ find <PROJECT_ROOT>/.okstra/tasks/*/dev-9428 \
551
551
  -type f -name '*.log' -print # dry-run
552
- find <PROJECT_ROOT>/.project-docs/okstra/tasks/*/dev-9428 \
552
+ find <PROJECT_ROOT>/.okstra/tasks/*/dev-9428 \
553
553
  -type f -name '*.log' -delete
554
554
 
555
555
  # 전체 일괄 삭제 (주의)
556
- find <PROJECT_ROOT>/.project-docs/okstra/tasks \
556
+ find <PROJECT_ROOT>/.okstra/tasks \
557
557
  -type f -path '*/runs/*/prompts/*.log' -print # dry-run
558
- find <PROJECT_ROOT>/.project-docs/okstra/tasks \
558
+ find <PROJECT_ROOT>/.okstra/tasks \
559
559
  -type f -path '*/runs/*/prompts/*.log' -delete
560
560
  ```
561
561
 
@@ -566,7 +566,7 @@ Substitute the literal `<PROJECT_ROOT>` with the resolved absolute path so the c
566
566
  - Logs are truncated on each re-dispatch of the same `seq`, so deleting an in-flight run's log will cause the wrapper to recreate an empty file on the next dispatch — no data loss beyond the current trace.
567
567
  - **If a dispatch is currently running, check the `status` sub-command first** and avoid deleting logs for tasks in `in-progress` state — you will lose the live trace for the active run.
568
568
  - Prompt history files (`.md`) are separate and are NOT touched by these commands — only `.log` sidecars.
569
- - This sub-command **does not modify any external files itself**, including `.gitignore`. If the project commits `.project-docs/okstra/`, the user may want to add `.project-docs/okstra/tasks/**/runs/**/prompts/*.log` to `.gitignore` manually to keep large logs out of git.
569
+ - This sub-command **does not modify any external files itself**, including `.gitignore`. If the project commits `.okstra/`, the user may want to add `.okstra/tasks/**/runs/**/prompts/*.log` to `.gitignore` manually to keep large logs out of git.
570
570
 
571
571
  ---
572
572
 
@@ -3,7 +3,7 @@ okstra release-handoff 기본 PR 본문 템플릿.
3
3
 
4
4
  이 파일은 사용자 정의 PR 템플릿이 없을 때 사용됩니다. 우선순위:
5
5
  1. okstra-run Step 6 에서 입력한 per-run override 경로
6
- 2. <project-root>/.project-docs/okstra/project.json 의 `prTemplatePath`
6
+ 2. <project-root>/.okstra/project.json 의 `prTemplatePath`
7
7
  3. ~/.okstra/config.json 의 `prTemplatePath`
8
8
  4. 이 디폴트 파일
9
9
 
@@ -8,7 +8,7 @@ model: opus
8
8
 
9
9
  ## Overview
10
10
 
11
- Generate a consolidated work schedule for all non-done tasks in a given `task-group`. The skill reads each task's `task-manifest.json` and `latestReport`, classifies tasks into phases by priority and risk, and writes a single Markdown plan file under `.project-docs/okstra/tasks/<task-group>/schedule/`.
11
+ Generate a consolidated work schedule for all non-done tasks in a given `task-group`. The skill reads each task's `task-manifest.json` and `latestReport`, classifies tasks into phases by priority and risk, and writes a single Markdown plan file under `.okstra/tasks/<task-group>/schedule/`.
12
12
 
13
13
  The skill runs as a single Claude lead synthesis (lightweight mode). A `--cross-verify` multi-agent variant was previously sketched here but never specified end-to-end; it has been dropped pre-1.0 and is tracked as a follow-up if multi-agent schedule verification is needed later.
14
14
 
@@ -16,7 +16,7 @@ The skill runs as a single Claude lead synthesis (lightweight mode). A `--cross-
16
16
 
17
17
  - User asks to generate a work schedule / plan / "일정" for an entire `task-group`
18
18
  - User wants a single document that summarizes all non-done tasks with effort, risk, and dependencies
19
- - A `task-group` exists in `.project-docs/okstra/discovery/task-catalog.json` with at least one task whose `workStatus` is not `done`
19
+ - A `task-group` exists in `.okstra/discovery/task-catalog.json` with at least one task whose `workStatus` is not `done`
20
20
 
21
21
  **Do NOT use** for single-task analysis (use `okstra-inspect status` instead) or when the user wants to actually execute one task (use the parent `okstra` skill).
22
22
 
@@ -46,7 +46,7 @@ Before anything else in this skill, run each of the following commands as a **se
46
46
  Reads the project from the current working directory. Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, projectId}`.
47
47
 
48
48
  - `ok: false` → tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
49
- - `ok: true` → carry `projectRoot` as a literal string and use it to locate `.project-docs/okstra/discovery/task-catalog.json` and the task-group directory.
49
+ - `ok: true` → carry `projectRoot` as a literal string and use it to locate `.okstra/discovery/task-catalog.json` and the task-group directory.
50
50
 
51
51
  Subsequent `okstra <subcmd>` calls self-bootstrap their Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
52
52
 
@@ -68,10 +68,10 @@ This skill performs cross-task synthesis (multi-task classification, dependency
68
68
 
69
69
  ### Step 1: Resolve task-group and collect tasks
70
70
 
71
- 1. Read `.project-docs/okstra/discovery/task-catalog.json`.
71
+ 1. Read `.okstra/discovery/task-catalog.json`.
72
72
  2. **Normalise the user-supplied `<task-group>` argument:** lowercase it, then strip every character that is not `[a-z0-9]` (drop spaces, hyphens, underscores, dots, etc.). Apply the same transform to each entry's `taskGroupPathSegment`. Match when the two normalised forms are equal. This is the single comparison rule — do NOT also fall back to the raw `taskGroup` field.
73
73
  3. If no tasks found, output `해당 task-group을 찾을 수 없습니다.` and stop.
74
- 4. For each matched task, read `.project-docs/okstra/tasks/<task-group-segment>/<task-id-segment>/task-manifest.json` directly. Catalog data may be stale; the manifest is authoritative.
74
+ 4. For each matched task, read `.okstra/tasks/<task-group-segment>/<task-id-segment>/task-manifest.json` directly. Catalog data may be stale; the manifest is authoritative.
75
75
  5. **Derive `<project-id>`** for the schedule header: prefer `task-catalog.json`'s top-level `projectId` field if present, otherwise use the first matched manifest's `projectId` field. Do not invent a value.
76
76
 
77
77
  ### Step 2: Filter by workStatus
@@ -157,7 +157,7 @@ When the classification is genuinely ambiguous after applying the table + priori
157
157
 
158
158
  Output path:
159
159
  ```
160
- .project-docs/okstra/tasks/<task-group-segment>/schedule/<task-group-segment>-plan-<YYYY-MM-DD_HH-MM-SS>.md
160
+ .okstra/tasks/<task-group-segment>/schedule/<task-group-segment>-plan-<YYYY-MM-DD_HH-MM-SS>.md
161
161
  ```
162
162
 
163
163
  - `<task-group-segment>`: from manifest's `taskGroupPathSegment` (lowercase, hyphenated).
@@ -322,7 +322,7 @@ Before applying the heuristics above, **check the schedule-level directive sourc
322
322
  **Resolution order (first hit wins):**
323
323
 
324
324
  1. Absolute path passed via the `--directive-file <abs-path>` argument when invoking the skill.
325
- 2. `<PROJECT_ROOT>/.project-docs/okstra/tasks/<task-group-segment>/schedule/instruction-set/analysis-material.md` (the canonical schedule-level instruction-set location — okstra writes here when a schedule run is dispatched with `--directive`).
325
+ 2. `<PROJECT_ROOT>/.okstra/tasks/<task-group-segment>/schedule/instruction-set/analysis-material.md` (the canonical schedule-level instruction-set location — okstra writes here when a schedule run is dispatched with `--directive`).
326
326
  3. **No directive file** — fall through and apply the default heuristic without any override. This is the normal case; do not warn, do not block.
327
327
 
328
328
  If a directive source is found and contains a `## Directive` section that affects Gantt rendering — e.g. "render a Gantt even with single XL task", "no Gantt needed" — that directive **overrides the heuristic and the skip-reason rule** for the affected section. When following a Directive override that contradicts the default heuristic, append a one-line note inside the rendered (or skipped) section: `> _Per Directive directive: <verbatim short excerpt>._` so the reader can trace why the section appeared/disappeared against the default. The Directive may also pre-supply day allocations, phase weights, or sub-task decompositions — use those verbatim as bar lengths in the Gantt.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: okstra-setup
3
- description: One-time bootstrap for okstra in a new project or on a new machine — installs the okstra runtime via npx and creates the project's .project-docs/okstra/project.json. Trigger words include "okstra setup", "setup okstra", "initialize okstra", "okstra init", "first time okstra setup", "configure okstra here".
3
+ description: One-time bootstrap for okstra in a new project or on a new machine — installs the okstra runtime via npx and creates the project's .okstra/project.json. Trigger words include "okstra setup", "setup okstra", "initialize okstra", "okstra init", "first time okstra setup", "configure okstra here".
4
4
  ---
5
5
 
6
6
  # okstra-setup
@@ -11,7 +11,7 @@ machine, or when adopting okstra in a new project.
11
11
  ## When to use
12
12
 
13
13
  - `~/.okstra/version` is missing or stale → okstra runtime is not installed yet.
14
- - The current project has no `.project-docs/okstra/project.json` yet.
14
+ - The current project has no `.okstra/project.json` yet.
15
15
  - The user says "set up okstra here", "first time", "okstra init", etc.
16
16
 
17
17
  ## When NOT to use
@@ -76,7 +76,7 @@ Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, pro
76
76
 
77
77
  ## Step 4: Inspect or create `project.json`
78
78
 
79
- Use the `Read` tool on the literal absolute path `<projectRoot>/.project-docs/okstra/project.json` (substitute the literal `projectRoot` value parsed in Step 3). If `Read` errors with "file does not exist", treat that as the "create" branch below; otherwise the file exists and you can inspect its contents inline.
79
+ Use the `Read` tool on the literal absolute path `<projectRoot>/.okstra/project.json` (substitute the literal `projectRoot` value parsed in Step 3). If `Read` errors with "file does not exist", treat that as the "create" branch below; otherwise the file exists and you can inspect its contents inline.
80
80
 
81
81
  If the file exists, surface its `projectId`/`projectRoot` and ask whether to
82
82
  keep or overwrite. Default is to keep — okstra refuses to change `projectId`
@@ -112,7 +112,7 @@ directories is symlinked from the main checkout into that worktree so
112
112
  every task sees the shared state. The built-in default is
113
113
  `.project-docs`, `.scratch`, `graphify-out`, `.claude`.
114
114
  Syncing a directory does not make it okstra memory; okstra-owned context
115
- and writes still stay under `<PROJECT_ROOT>/.project-docs/okstra/**`
115
+ and writes still stay under `<PROJECT_ROOT>/.okstra/**`
116
116
  unless the task brief explicitly authorizes a non-okstra path.
117
117
 
118
118
  To override per-project, add a `worktreeSyncDirs` array to
@@ -231,7 +231,7 @@ okstra-run prompt convention):
231
231
 
232
232
  - **Question**: `"이 프로젝트에서 release-handoff 가 사용할 PR 본문 템플릿을 등록할까요?"`
233
233
  - **Options**:
234
- 1. `이번 프로젝트만 (project scope)` — write to `<PROJECT_ROOT>/.project-docs/okstra/project.json` `prTemplatePath`.
234
+ 1. `이번 프로젝트만 (project scope)` — write to `<PROJECT_ROOT>/.okstra/project.json` `prTemplatePath`.
235
235
  2. `전역 (global scope)` — write to `~/.okstra/config.json` `prTemplatePath`.
236
236
  3. `나중에` — skip.
237
237
 
@@ -292,7 +292,7 @@ the output and let the user decide whether to re-run install or skip.
292
292
  Inform the user with a short summary:
293
293
 
294
294
  > okstra is ready. Runtime: `~/.okstra` (version stamp). Project metadata:
295
- > `<PROJECT_ROOT>/.project-docs/okstra/project.json` (`projectId`). Run
295
+ > `<PROJECT_ROOT>/.okstra/project.json` (`projectId`). Run
296
296
  > `/okstra-run` to start your first task.
297
297
 
298
298
  ## Failure modes
@@ -302,7 +302,7 @@ Inform the user with a short summary:
302
302
  | `command not found: npx` | Node missing | Install node 18+. |
303
303
  | `okstra ensure-installed` keeps reinstalling | `~/.okstra/version` write fails (permissions) | Check `~/.okstra` ownership and writability. |
304
304
  | `error: --project-id is required (no existing project.json, not a TTY)` | `okstra setup --yes` invoked without `--project-id`, or with empty answer to Step 4 prompt | Re-ask Step 4 and pass a non-empty id via `--project-id`. |
305
- | `projectId mismatch` / `projectId 불일치` | `project.json` already exists with a different id | Decide which id is canonical; manually delete `<PROJECT_ROOT>/.project-docs/okstra/project.json` to re-register, or re-run with the existing id. |
306
- | `EACCES` writing under `.project-docs/okstra/` | directory owned by another user (e.g. created by a previous root-shell run) | `chown -R "$USER" <PROJECT_ROOT>/.project-docs` or delete and let setup recreate. |
305
+ | `projectId mismatch` / `projectId 불일치` | `project.json` already exists with a different id | Decide which id is canonical; manually delete `<PROJECT_ROOT>/.okstra/project.json` to re-register, or re-run with the existing id. |
306
+ | `EACCES` writing under `.okstra/` | directory owned by another user (e.g. created by a previous root-shell run) | `chown -R "$USER" <PROJECT_ROOT>/.project-docs` or delete and let setup recreate. |
307
307
  | `warning: failed to provision .claude/settings.local.json symlink` | a non-symlink `.claude/settings.local.json` already exists and the backup-and-replace step failed | Inspect `<PROJECT_ROOT>/.claude/settings.local.json{,.bak.*}`; manually merge project-specific rules, then re-run setup. |
308
308
  | `npx okstra@latest install` succeeds but `doctor` shows FAIL | runtime/{python,bin,skills} sync not yet performed (pre-release package) | Use dev install: clone the repo and run `node bin/okstra install --link <repo>`. |
@@ -3,7 +3,7 @@
3
3
  This file is shipped by `okstra install` and surfaced into the active project
4
4
  through two channels by `okstra setup`:
5
5
 
6
- - **Claude Code**: `<PROJECT>/CLAUDE.md` gets an `@.project-docs/okstra/CLAUDE.md`
6
+ - **Claude Code**: `<PROJECT>/CLAUDE.md` gets an `@.okstra/CLAUDE.md`
7
7
  import block (per-project symlink to this template). Existing user content
8
8
  in `CLAUDE.md` is preserved — the block lives between
9
9
  `<!-- okstra:claude-md:begin ... -->` markers.
@@ -14,7 +14,7 @@ through two channels by `okstra setup`:
14
14
  It is **owned by the okstra package**: every `okstra install` overwrites
15
15
  `~/.okstra/templates/okstra.CLAUDE.md` from the version currently on disk in
16
16
  the npm package. Edits made directly to this file (or to the per-project
17
- `<PROJECT>/.project-docs/okstra/CLAUDE.md` / `<PROJECT>/AGENTS.md` symlinks)
17
+ `<PROJECT>/.okstra/CLAUDE.md` / `<PROJECT>/AGENTS.md` symlinks)
18
18
  will be lost on the next install. Put project-specific overrides outside the
19
19
  managed block in `<PROJECT>/CLAUDE.md` instead (or replace `AGENTS.md` with
20
20
  your own file — okstra will respect it).
@@ -30,7 +30,7 @@ through slash commands inside a Claude Code session.
30
30
 
31
31
  | Command | When to use |
32
32
  | --- | --- |
33
- | `/okstra-setup` | First-time bootstrap in a new project — writes `.project-docs/okstra/project.json`, provisions `.claude/settings.local.json` and `.project-docs/okstra/CLAUDE.md`. |
33
+ | `/okstra-setup` | First-time bootstrap in a new project — writes `.okstra/project.json`, provisions `.claude/settings.local.json` and `.okstra/CLAUDE.md`. |
34
34
  | `/okstra-brief` | Turn a requirements doc / ticket / link / conversation into the markdown task brief consumed by `okstra-run`. |
35
35
  | `/okstra-run` | Start a cross-verification task in the current session. Drives the interactive wizard. |
36
36
  | `/okstra-status` | Inspect overall okstra task status, current phase, blockers, next recommended phase. Also flips a task's workStatus (todo / in-progress / blocked / done). |
@@ -55,7 +55,7 @@ Type the slash command — do not paraphrase the trigger words into prose.
55
55
  duplicate those permissions in the user's global `~/.claude/settings.json`.
56
56
  - Each okstra run provisions a task-scoped git worktree under
57
57
  `~/.okstra/worktrees/`. Files synced from the main checkout are governed by
58
- `worktreeSyncDirs` in `.project-docs/okstra/project.json` (default:
58
+ `worktreeSyncDirs` in `.okstra/project.json` (default:
59
59
  `.project-docs`, `.scratch`, `graphify-out`, `.claude`).
60
60
  - QA gating is configured via `qaCommands` in the same `project.json`.
61
61
  Verifiers reject mutation-style commands (`--fix`, `--write`, `cargo update`,
@@ -105,14 +105,14 @@ handle it. Free-form rows are allowed only as `general:`.
105
105
  Allowed signals:
106
106
  - `general: <unresolved question the user flagged>`
107
107
  - `terminology: <reporter word> — needs canonical resolution against
108
- <PROJECT_ROOT>/.project-docs/okstra/glossary.md`
108
+ <PROJECT_ROOT>/.okstra/glossary.md`
109
109
  - `intent-check: <restated inference> — confirm with reporter`
110
110
  (auto-paired with every `intent-inference` augmentation)
111
111
  - `conversion-block: <reporter statement> — could not be mapped to project
112
112
  vocabulary; reporter query required`
113
113
  - `adr-candidate: <topic>` — signal only; `implementation-planning`
114
114
  evaluates and, if accepted, drafts a decision file at
115
- `<PROJECT_ROOT>/.project-docs/okstra/decisions/<NNNN>-<slug>.md`.
115
+ `<PROJECT_ROOT>/.okstra/decisions/<NNNN>-<slug>.md`.
116
116
 
117
117
  Use `_(none)_` only if every signal is empty. `intent-check:` and
118
118
  `conversion-block:` rows that are answered in Step 6.5 are NOT removed
@@ -156,17 +156,17 @@ Every entry below must start with one of the four labels:
156
156
  <!-- author guidance — strip out at fill-in time:
157
157
  Observations from Step 3b and the outcome of Step 4.5 (glossary applied
158
158
  vs. skipped). The actual glossary edits live in
159
- `<PROJECT_ROOT>/.project-docs/okstra/glossary.md` when applied; this
159
+ `<PROJECT_ROOT>/.okstra/glossary.md` when applied; this
160
160
  section records what happened. Decision candidates are NOT recorded here —
161
161
  they flow through `Open Questions` as `adr-candidate:` rows for
162
162
  `implementation-planning` to evaluate (and, if accepted, draft into
163
- `<PROJECT_ROOT>/.project-docs/okstra/decisions/`).
163
+ `<PROJECT_ROOT>/.okstra/decisions/`).
164
164
 
165
165
  Allowed entry shapes:
166
166
  - `terminology-mapping: <reporter word> → <okstra glossary canonical>` —
167
167
  routine glossary alignment, paired with `terminology:` in Open Questions
168
168
  when unresolved.
169
- - `terminology-mapping: applied glossary: <term> → <PROJECT_ROOT>/.project-docs/okstra/glossary.md`
169
+ - `terminology-mapping: applied glossary: <term> → <PROJECT_ROOT>/.okstra/glossary.md`
170
170
  - `terminology-mapping: skipped glossary: <term> = <definition>` —
171
171
  Step 4.5 outcomes.
172
172
  Use `_(none)_` if every alignment entry is empty.
@@ -145,7 +145,7 @@ taskType: "{{FM_TASK_TYPE}}"
145
145
 
146
146
  ## Available MCP Servers
147
147
 
148
- The MCP servers available to this run are declared in `.project-docs/okstra/project.json`'s `mcpServers` array and rendered into the Claude lead's launch prompt under `## Available MCP Servers`. They may be invoked **as needed** by Claude lead, Claude worker, and Report writer worker. The lead is responsible for forwarding the rendered list verbatim into the worker prompts (Phase 2) so workers know which tools they are allowed to call.
148
+ The MCP servers available to this run are declared in `.okstra/project.json`'s `mcpServers` array and rendered into the Claude lead's launch prompt under `## Available MCP Servers`. They may be invoked **as needed** by Claude lead, Claude worker, and Report writer worker. The lead is responsible for forwarding the rendered list verbatim into the worker prompts (Phase 2) so workers know which tools they are allowed to call.
149
149
 
150
150
  To declare servers, add entries shaped `{ "name": "<server>", "description": "...", "tools": ["..."], "notes": "..." }` to that array. If the array is empty or absent, treat MCP as unavailable for this run.
151
151
 
@@ -52,11 +52,11 @@ write_validation_brief() {
52
52
  - Config file: \`.claude/settings.json\`
53
53
  - Expected values:
54
54
  - installed okstra Claude assets must remain discoverable under \`~/.claude/skills/\` and \`~/.claude/agents/\` (managed by \`okstra install\`)
55
- - Config file: \`.project-docs/okstra/discovery/latest-task.json\`
55
+ - Config file: \`.okstra/discovery/latest-task.json\`
56
56
  - Expected values:
57
57
  - latest prepared task pointer must include the current task key
58
58
  - task catalog path must be present
59
- - Config file: \`.project-docs/okstra/discovery/task-catalog.json\`
59
+ - Config file: \`.okstra/discovery/task-catalog.json\`
60
60
  - Expected values:
61
61
  - task catalog must preserve prepared task bundles by task key
62
62
  - task catalog must allow task-group and task-id level distinction
@@ -1,5 +1,11 @@
1
1
  # shellcheck shell=bash
2
2
 
3
+ # Single source of truth for the okstra-relative directory name in shell code.
4
+ # Python (okstra_project/dirs.py) 와 Node (src/okstra-dirs.mjs) 와 같은 값을
5
+ # 유지해야 한다. 디렉토리 이름을 바꾸려면 세 곳의 한 줄씩만 수정.
6
+ OKSTRA_DIR="${OKSTRA_DIR:-.okstra}"
7
+ LEGACY_OKSTRA_DIR=".project-docs/okstra"
8
+
3
9
  validate_project_root_safety() {
4
10
  if [[ -z "${PROJECT_ROOT:-}" ]]; then
5
11
  fail "PROJECT_ROOT is not defined"
@@ -26,19 +32,19 @@ task_root() {
26
32
  local task_group="$1"
27
33
  local task_id="$2"
28
34
 
29
- printf '%s/.project-docs/okstra/tasks/%s/%s\n' "$PROJECT_ROOT" "$task_group" "$task_id"
35
+ printf '%s/%s/tasks/%s/%s\n' "$PROJECT_ROOT" "$OKSTRA_DIR" "$task_group" "$task_id"
30
36
  }
31
37
 
32
38
  task_manifest_relative_path() {
33
39
  local task_group="$1"
34
40
  local task_id="$2"
35
41
 
36
- printf '.project-docs/okstra/tasks/%s/%s/task-manifest.json\n' "$task_group" "$task_id"
42
+ printf '%s/tasks/%s/%s/task-manifest.json\n' "$OKSTRA_DIR" "$task_group" "$task_id"
37
43
  }
38
44
 
39
45
  reference_expectations_relative_path() {
40
46
  local task_group="$1"
41
47
  local task_id="$2"
42
48
 
43
- printf '.project-docs/okstra/tasks/%s/%s/instruction-set/reference-expectations.md\n' "$task_group" "$task_id"
49
+ printf '%s/tasks/%s/%s/instruction-set/reference-expectations.md\n' "$OKSTRA_DIR" "$task_group" "$task_id"
44
50
  }
@@ -10,7 +10,7 @@ Checks performed per brief file:
10
10
  4. Every Open Questions row starts with one of the five signal prefixes
11
11
  (general | terminology | intent-check | conversion-block | adr-candidate).
12
12
  `adr-candidate:` targets okstra-internal
13
- `<PROJECT_ROOT>/.project-docs/okstra/decisions/`, not external `docs/adr/`.
13
+ `<PROJECT_ROOT>/.okstra/decisions/`, not external `docs/adr/`.
14
14
  5. Every Augmentation entry (inline `> augmented: <label>` blockquotes and
15
15
  `Augmentation` section bullets) carries one of the four labels
16
16
  (evidence-link | format-conversion | terminology-mapping | intent-inference).
@@ -342,7 +342,7 @@ def main(argv: list[str] | None = None) -> int:
342
342
  default=None,
343
343
  help=(
344
344
  "Root used for depth computation (defaults to briefs_dir). "
345
- "Usually `<PROJECT_ROOT>/.project-docs/okstra/briefs`."
345
+ "Usually `<PROJECT_ROOT>/.okstra/briefs`."
346
346
  ),
347
347
  )
348
348
  args = parser.parse_args(argv)
@@ -6,7 +6,7 @@
6
6
  # validators/validate-brief.sh <briefs-dir> [--briefs-root <dir>]
7
7
  #
8
8
  # Typical invocation (inside a project that has run okstra-setup):
9
- # validators/validate-brief.sh "$PROJECT_ROOT/.project-docs/okstra/briefs"
9
+ # validators/validate-brief.sh "$PROJECT_ROOT/.okstra/briefs"
10
10
  #
11
11
  # Thin bash entrypoint — delegates to validate-brief.py for content checks.
12
12
 
@@ -29,6 +29,8 @@ except ImportError: # pragma: no cover — runtime guarantees this import
29
29
  load_schema = None # type: ignore[assignment]
30
30
  schema_validate = None # type: ignore[assignment]
31
31
 
32
+ from okstra_project.dirs import tasks_root as _okstra_tasks_root # noqa: E402
33
+
32
34
  TERMINAL_STATUSES = {"completed", "timeout", "error", "not-run"}
33
35
  ATTEMPTED_STATUSES = {"completed", "timeout", "error"}
34
36
 
@@ -1317,7 +1319,7 @@ def _refresh_task_catalog(project_root: Path, task_manifest: dict) -> tuple[bool
1317
1319
  except Exception as exc: # noqa: BLE001
1318
1320
  return False, f"okstra_ctl import failed: {exc}"
1319
1321
 
1320
- tasks_root = (project_root / ".project-docs" / "okstra" / "tasks").resolve()
1322
+ tasks_root = _okstra_tasks_root(project_root).resolve()
1321
1323
  catalog_path = (project_root / catalog_relative).resolve()
1322
1324
  ctx = {
1323
1325
  "PROJECT_ROOT": str(project_root),
@@ -24,13 +24,13 @@ PRIMARY_BRIEF_FILENAME="validation-brief-primary.md"
24
24
  SECONDARY_TASK_GROUP="discovery"
25
25
  SECONDARY_TASK_ID="task-catalog"
26
26
  SECONDARY_BRIEF_FILENAME="validation-brief-secondary.md"
27
- LATEST_TASK_RELATIVE_PATH=".project-docs/okstra/discovery/latest-task.json"
28
- TASK_CATALOG_RELATIVE_PATH=".project-docs/okstra/discovery/task-catalog.json"
29
27
 
30
28
  # shellcheck source=lib/common.sh
31
29
  source "$SCRIPT_DIR/lib/common.sh"
32
30
  # shellcheck source=lib/paths.sh
33
31
  source "$SCRIPT_DIR/lib/paths.sh"
32
+ LATEST_TASK_RELATIVE_PATH="$OKSTRA_DIR/discovery/latest-task.json"
33
+ TASK_CATALOG_RELATIVE_PATH="$OKSTRA_DIR/discovery/task-catalog.json"
34
34
  # shellcheck source=lib/fixtures.sh
35
35
  source "$SCRIPT_DIR/lib/fixtures.sh"
36
36
  # shellcheck source=lib/runners.sh
@@ -1,13 +1,13 @@
1
1
  import { promises as fs } from "node:fs";
2
2
  import { spawn } from "node:child_process";
3
3
  import { join } from "node:path";
4
- import { resolvePaths } from "./paths.mjs";
4
+ import { buildPythonpath, resolvePaths } from "./paths.mjs";
5
5
 
6
6
  const USAGE = `okstra check-project — verify that the current project has okstra setup
7
7
 
8
8
  Usage:
9
9
  okstra check-project Resolve PROJECT_ROOT from cwd, look for
10
- .project-docs/okstra/project.json,
10
+ .okstra/project.json,
11
11
  print JSON status to stdout.
12
12
  okstra check-project --cwd <dir> Use <dir> as the search starting point
13
13
  instead of process cwd.
@@ -99,7 +99,7 @@ export async function run(args) {
99
99
  ].join("\n"),
100
100
  opts.cwd,
101
101
  ],
102
- { PYTHONPATH: paths.pythonpath },
102
+ { PYTHONPATH: buildPythonpath(paths) },
103
103
  );
104
104
 
105
105
  if (probe.code !== 0) {
package/src/config.mjs CHANGED
@@ -2,12 +2,13 @@ import { promises as fs } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, join, resolve, isAbsolute } from "node:path";
4
4
  import { spawn } from "node:child_process";
5
- import { resolvePaths } from "./paths.mjs";
5
+ import { buildPythonpath, resolvePaths } from "./paths.mjs";
6
+ import { OKSTRA_DIR, projectJsonPath } from "./okstra-dirs.mjs";
6
7
 
7
8
  const USAGE = `okstra config — read / write okstra settings (project + global scopes)
8
9
 
9
10
  Settings are persisted as JSON keys in one of two files:
10
- project: <project-root>/.project-docs/okstra/project.json
11
+ project: <project-root>/${OKSTRA_DIR}/project.json
11
12
  global: ~/.okstra/config.json
12
13
 
13
14
  Supported keys (CLI alias -> JSON field):
@@ -132,7 +133,7 @@ function globalConfigPath() {
132
133
  }
133
134
 
134
135
  function projectConfigPath(projectRoot) {
135
- return join(projectRoot, ".project-docs", "okstra", "project.json");
136
+ return projectJsonPath(projectRoot);
136
137
  }
137
138
 
138
139
  async function fileExists(p) {
@@ -193,7 +194,7 @@ async function resolveProjectRoot(cwd) {
193
194
  ].join("\n"),
194
195
  cwd,
195
196
  ],
196
- { PYTHONPATH: paths.pythonpath },
197
+ { PYTHONPATH: buildPythonpath(paths) },
197
198
  );
198
199
  if (result.code === 0) return result.stdout.trim();
199
200
  return null;
@@ -229,7 +230,7 @@ async function effectivePrTemplatePath(projectRoot) {
229
230
  ].join("\n"),
230
231
  projectRoot ?? process.cwd(),
231
232
  ],
232
- { PYTHONPATH: paths.pythonpath },
233
+ { PYTHONPATH: buildPythonpath(paths) },
233
234
  );
234
235
  if (result.code !== 0) return null;
235
236
  try {
package/src/install.mjs CHANGED
@@ -15,8 +15,8 @@ const SETTINGS_TEMPLATE_SRC_REL = ["templates", "reports", "settings.template.js
15
15
  // Destination under ~/.okstra/. Project-local .claude/settings.local.json symlinks here.
16
16
  const SETTINGS_TEMPLATE_DST_REL = ["templates", "settings.local.json"];
17
17
 
18
- // okstra-managed CLAUDE.md template. Per-project <PROJECT>/.project-docs/okstra/CLAUDE.md
19
- // symlinks here; <PROJECT>/CLAUDE.md gets an `@.project-docs/okstra/CLAUDE.md` import line.
18
+ // okstra-managed CLAUDE.md template. Per-project <PROJECT>/.okstra/CLAUDE.md
19
+ // symlinks here; <PROJECT>/CLAUDE.md gets an `@.okstra/CLAUDE.md` import line.
20
20
  const CLAUDE_MD_TEMPLATE_REL = ["templates", "okstra.CLAUDE.md"];
21
21
 
22
22
  const PYTHON_PACKAGES = ["okstra_project", "okstra_ctl", "okstra_token_usage", "okstra_vendor", "lib"];
@@ -70,8 +70,8 @@ provisions, granting per-project Claude Code permissions for okstra
70
70
  worker wrapper scripts without modifying the user's global settings.
71
71
 
72
72
  The okstra.CLAUDE.md file is the symlink target referenced by every
73
- project-local <project>/.project-docs/okstra/CLAUDE.md that okstra-setup provisions;
74
- <project>/CLAUDE.md gets an "@.project-docs/okstra/CLAUDE.md" import block so Claude
73
+ project-local <project>/.okstra/CLAUDE.md that okstra-setup provisions;
74
+ <project>/CLAUDE.md gets an "@.okstra/CLAUDE.md" import block so Claude
75
75
  Code automatically picks up the okstra runtime guidance.
76
76
 
77
77
  Worker agent definitions are installed into ${"$HOME"}/.claude/agents/ so
@@ -579,7 +579,7 @@ export async function runInstall(args) {
579
579
  );
580
580
  // templates/ tree — report.css / report.js / *.template.md / okstra.CLAUDE.md
581
581
  // are consumed at runtime by okstra-render-report-views.py, final-report
582
- // assembly, and the per-project .project-docs/okstra/CLAUDE.md symlink provisioned by
582
+ // assembly, and the per-project .okstra/CLAUDE.md symlink provisioned by
583
583
  // setup. They are NOT covered by installNamedTemplate (which only handles
584
584
  // the renamed settings.local.json sidecar), so without this step copy-mode
585
585
  // installs miss every asset other than that single file. See