okstra 0.105.0 → 0.106.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/README.kr.md +1 -0
- package/README.md +2 -1
- package/docs/for-ai/README.md +3 -1
- package/docs/for-ai/skills/okstra-rollup.md +113 -0
- package/docs/kr/architecture.md +2 -2
- package/docs/kr/cli.md +1 -0
- package/docs/pr-template-usage.md +1 -1
- package/docs/project-structure-overview.md +7 -3
- package/docs/task-process/release-handoff.md +1 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/python/okstra_ctl/rollup.py +113 -0
- package/runtime/skills/okstra-inspect/SKILL.md +22 -10
- package/runtime/skills/okstra-rollup/SKILL.md +73 -0
- package/src/cli-registry.mjs +7 -0
- package/src/commands/inspect/rollup.mjs +27 -0
- package/src/lib/skill-catalog.mjs +1 -0
package/README.kr.md
CHANGED
|
@@ -161,6 +161,7 @@ Claude Code 세션 안에서 사용하는 슬래시 커맨드:
|
|
|
161
161
|
| `/okstra-run` | 새 task 시작 (또는 기존 task 의 다음 phase 이어가기) |
|
|
162
162
|
| `/okstra-memory` | `~/.okstra/memory-book` 전역 대화 메모리 저장·검색·보관 |
|
|
163
163
|
| `/okstra-inspect` | 통합 read-side 스킬. sub-command: `status` (phase / 상태, workStatus 설정), `history` (과거 task / re-run / resume), `report` (final-report 조회·읽기), `time` (소요 시간 breakdown), `logs` (wrapper log sidecar 조회·정리 제안), `cost` (task bundle 컨텍스트/읽기 비용), `errors` (run 에러 로그를 리포트로 집계), `error-zip` (cross-project 에러 로그를 익명화 zip 으로 수집·클러스터 요약), `recap` (run 간 전/후 요약 + task 의 `.okstra` 산출물 위 자유 Q&A) |
|
|
164
|
+
| `/okstra-rollup` | task-group(또는 프로젝트 전체)의 모든 task run 결과를 모아 task별 run수/소요시간/에러와 그룹 합계를 집계하고, report 들을 묶어 task 횡단 종합 요약 작성 |
|
|
164
165
|
| `/okstra-schedule` | task-group 전체에 대한 작업 계획표 생성 |
|
|
165
166
|
| `/okstra-container-build` | 검증 완료된 task 의 코드를 로컬 docker compose 그룹으로 배포하고 컨테이너별 로그를 감시 (sub-command: `up` / `status` / `logs` / `stop-watcher` / `down`) |
|
|
166
167
|
| `/okstra-manager` | 여러 프로젝트에 걸친 okstra task 를 manager-owned plan, assignment, 단방향 project sync snapshot, status, child launch context packet 으로 조정 |
|
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ okstra/ npm package = repo root
|
|
|
43
43
|
├── tools/build.mjs runtime/ sync script (invoked by prepack)
|
|
44
44
|
├── runtime/ gitignored install payload copied to ~/.okstra
|
|
45
45
|
├── scripts/ python + bash runtime sources
|
|
46
|
-
├── skills/ public skill markdown sources (
|
|
46
|
+
├── skills/ public skill markdown sources (9 user-facing skills)
|
|
47
47
|
├── agents/ worker agent markdown sources
|
|
48
48
|
├── prompts/, schemas/, templates/, validators/
|
|
49
49
|
├── tests/, tests-e2e/
|
|
@@ -159,6 +159,7 @@ User-facing slash commands inside a Claude Code session:
|
|
|
159
159
|
| `/okstra-run` | Start a new task (or resume the next phase of an existing one) |
|
|
160
160
|
| `/okstra-memory` | Store/search/archive global conversation memory in `~/.okstra/memory-book` |
|
|
161
161
|
| `/okstra-inspect` | Unified read-side. Sub-commands: `status` (phase / state, workStatus update), `history` (past runs, re-run, resume), `report` (find/read final-report), `time` (elapsed-time breakdown), `logs` (wrapper log sidecar inventory + cleanup), `cost` (task bundle context/read cost), `errors` (aggregate run error logs into a report), `error-zip` (collect cross-project error logs into an anonymized zip and summarize clusters), `recap` (run-to-run before/after summary + free-form Q&A over a task's `.okstra` artifacts) |
|
|
162
|
+
| `/okstra-rollup` | Roll up run results across every task in a task-group (or the whole project) — per-task runs/time/errors plus group totals — and synthesize a cross-task digest from the report files |
|
|
162
163
|
| `/okstra-schedule` | Generate a work schedule for an entire task-group |
|
|
163
164
|
| `/okstra-container-build` | Deploy a verified task's code as a local docker compose group and watch each container's logs (sub-commands: `up` / `status` / `logs` / `stop-watcher` / `down`) |
|
|
164
165
|
| `/okstra-manager` | Coordinate cross-project okstra tasks through manager-owned plans, assignments, one-way project sync snapshots, status, and child launch context packets |
|
package/docs/for-ai/README.md
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
| 현재 Claude Code 세션에서 okstra run 시작 또는 다음 phase 실행 | `okstra-run` | [`skills/okstra-run.md`](skills/okstra-run.md) |
|
|
22
22
|
| 여러 프로젝트에 걸친 okstra task 묶음, 할당, sync snapshot, child launch packet 관리 | `okstra-manager` | [`skills/okstra-manager.md`](skills/okstra-manager.md) |
|
|
23
23
|
| 상태, history, report, time, logs, cost, errors, error-zip, recap 확인 | `okstra-inspect` | [`skills/okstra-inspect.md`](skills/okstra-inspect.md) |
|
|
24
|
+
| task-group(또는 프로젝트 전체)의 여러 task run 결과를 모아 집계·종합 요약 | `okstra-rollup` | [`skills/okstra-rollup.md`](skills/okstra-rollup.md) |
|
|
24
25
|
| task-group 전체의 클라이언트용 작업 일정 생성 | `okstra-schedule` | [`skills/okstra-schedule.md`](skills/okstra-schedule.md) |
|
|
25
26
|
| 대화/결정/선호/요구사항을 전역 Memory Book에 저장 또는 검색 | `okstra-memory` | [`skills/okstra-memory.md`](skills/okstra-memory.md) |
|
|
26
27
|
| implementation task worktree 기반 docker compose 사용자 테스트 환경 관리 | `okstra-container-build` | [`skills/okstra-container-build.md`](skills/okstra-container-build.md) |
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
|
|
45
46
|
## 공개 스킬 목록
|
|
46
47
|
|
|
47
|
-
`src/lib/skill-catalog.mjs` 기준 공개 스킬은 다음
|
|
48
|
+
`src/lib/skill-catalog.mjs` 기준 공개 스킬은 다음 9개다.
|
|
48
49
|
|
|
49
50
|
- `okstra-setup`
|
|
50
51
|
- `okstra-brief`
|
|
@@ -52,5 +53,6 @@
|
|
|
52
53
|
- `okstra-manager`
|
|
53
54
|
- `okstra-memory`
|
|
54
55
|
- `okstra-inspect`
|
|
56
|
+
- `okstra-rollup`
|
|
55
57
|
- `okstra-schedule`
|
|
56
58
|
- `okstra-container-build`
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# okstra-rollup AI Manual
|
|
2
|
+
|
|
3
|
+
## 원천
|
|
4
|
+
|
|
5
|
+
- 스킬 원문: [`skills/okstra-rollup/SKILL.md`](../../../skills/okstra-rollup/SKILL.md)
|
|
6
|
+
- 집계 코어(CLI): [`scripts/okstra_ctl/rollup.py`](../../../scripts/okstra_ctl/rollup.py)
|
|
7
|
+
- Node 래퍼: [`src/commands/inspect/rollup.mjs`](../../../src/commands/inspect/rollup.mjs)
|
|
8
|
+
- 재사용하는 단일-task 집계기: [`scripts/okstra_ctl/time_report.py`](../../../scripts/okstra_ctl/time_report.py), [`scripts/okstra_ctl/error_log_core.py`](../../../scripts/okstra_ctl/error_log_core.py)
|
|
9
|
+
- catalog 열거 헬퍼: [`scripts/okstra_project/state.py`](../../../scripts/okstra_project/state.py) (`list_project_tasks`)
|
|
10
|
+
- 단위 테스트: [`tests/inspect/test_okstra_rollup.py`](../../../tests/inspect/test_okstra_rollup.py)
|
|
11
|
+
|
|
12
|
+
## 목적
|
|
13
|
+
|
|
14
|
+
`okstra-rollup`은 **여러 task의 run 결과를 한꺼번에 모아 요약**한다. 단일 task를 보는 `okstra-inspect`와 대비되는 task 횡단(cross-task) read-side 레이어다.
|
|
15
|
+
|
|
16
|
+
- 입력 범위: 한 task-group, 또는 `--task-group`을 생략하면 프로젝트 전체 catalog.
|
|
17
|
+
- deterministic 집계(개수·시간 합·에러 합·상태/카테고리/phase 분포)는 `okstra rollup` CLI가 전담한다. 스킬은 그 표를 렌더하고, 각 task의 report 본문을 읽어 **task 횡단 종합 요약(digest)**을 작성한다.
|
|
18
|
+
- 설계 원칙: 집계 손계산은 LLM이 틀리기 쉬우므로 CLI(SSOT)로 내리고, 자연어 종합만 LLM이 맡는다. `okstra-inspect time`이 "절대 손으로 시간을 재합산하지 말 것"이라 못 박는 것과 같은 분업이다.
|
|
19
|
+
|
|
20
|
+
이 스킬은 read-only다. task 산출물을 mutate 하지 않는다.
|
|
21
|
+
|
|
22
|
+
## 사용 조건
|
|
23
|
+
|
|
24
|
+
사용한다:
|
|
25
|
+
|
|
26
|
+
- 사용자가 "롤업", "task-group 요약", "그룹 단위 리포트", "여러 task 결과 모아", "전체 task 현황 요약", "run 결과 한꺼번에 정리"를 요청한다.
|
|
27
|
+
- 한 task가 아니라 **여러 task**를 가로질러 보고 싶을 때.
|
|
28
|
+
|
|
29
|
+
사용하지 않는다:
|
|
30
|
+
|
|
31
|
+
- 단일 task의 report/시간/에러/recap → `okstra-inspect` (report / time / errors / recap facet).
|
|
32
|
+
- 미래 작업 계획표(non-done task의 클라이언트용 일정) → `okstra-schedule`. rollup은 과거 run 결과를 모으는 **회고형**이고, schedule은 앞으로의 계획을 짜는 **전망형**이다.
|
|
33
|
+
- 실제 phase 실행 → `okstra-run`.
|
|
34
|
+
|
|
35
|
+
## Preflight
|
|
36
|
+
|
|
37
|
+
각각 별도 Bash 호출. 모두 리터럴 `okstra` 토큰으로 시작해 `Bash(okstra:*)` 자동 허용을 받는다. `if`/`eval`/`$(...)`/`VAR=`/`||`/`&&`/`npx` fallback으로 감싸지 않는다.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
okstra ensure-installed --runtime claude-code
|
|
41
|
+
okstra check-project --json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`ensure-installed` 비정상 종료 → `/okstra-setup` 안내 후 멈춘다. `check-project`의 `ok:false` → `/okstra-setup` 안내 후 멈춘다. `ok:true`면 `projectRoot`를 리터럴 문자열로 들고 다음 단계로 간다.
|
|
45
|
+
|
|
46
|
+
## scope 해석
|
|
47
|
+
|
|
48
|
+
- 사용자가 task-group을 지목("alpha 그룹 요약") → `--task-group <group>`.
|
|
49
|
+
- "전체 task"/"프로젝트 전체"/범위 미지정 → `--task-group` 생략(전체 catalog).
|
|
50
|
+
- 진짜 모호하면 한 번만 묻는다: 한 task-group인지 프로젝트 전체인지. 특정 그룹을 임의로 추측하지 않는다.
|
|
51
|
+
|
|
52
|
+
## CLI 호출
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
okstra rollup --task-group <group> --project-root <projectRoot> --json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
전체 프로젝트면 `--task-group`을 뺀다. 출력은 항상 JSON이며 **모든 시간은 raw 밀리초**다.
|
|
59
|
+
|
|
60
|
+
## 출력 해석
|
|
61
|
+
|
|
62
|
+
최상위:
|
|
63
|
+
|
|
64
|
+
- `taskGroup` — 범위(`null`이면 프로젝트 전체), `taskCount` — task 수.
|
|
65
|
+
- `tasks[]` — task 1개당: `taskKey, taskGroup, taskId, taskType, workCategory, workStatus, currentPhase, currentPhaseState, nextRecommendedPhase, latestRunStatus, updatedAt, reportPath, runCount, cpuSumMs, wallClockMs, errorCount`.
|
|
66
|
+
- `totals` — `runs, cpuSumMs, wallClockMs, errors`, 그리고 `byWorkStatus` / `byWorkCategory` / `byCurrentPhase` / `byTaskType` (각각 `{값: 개수}` 맵).
|
|
67
|
+
|
|
68
|
+
수치 의미(반드시 지킬 것):
|
|
69
|
+
|
|
70
|
+
- `runCount`는 timeline의 **전체 run 수**다. `cpuSumMs`/`wallClockMs`는 Phase 7 usage에 도달한 run만 반영하므로 `runCount > 0`이어도 `0`일 수 있다.
|
|
71
|
+
- `cpuSumMs`는 lead + worker가 겹쳐 도는 **CPU 합**이지 wall-clock이 아니다. 라벨을 "CPU"로 쓰고, wall-clock은 사용자가 명시적으로 물을 때만 `wallClockMs`로 보여준다.
|
|
72
|
+
- `reportPath`는 프로젝트 상대경로이며 비어 있을 수 있다(아직 report가 없는 task).
|
|
73
|
+
- `taskCount`가 `0`이면 해당 범위에 okstra task가 없다고 말하고 멈춘다.
|
|
74
|
+
|
|
75
|
+
## 렌더
|
|
76
|
+
|
|
77
|
+
모든 `*Ms`는 `HH:MM:SS`로 변환한다(zero-pad, raw ms 절대 노출 금지 — `okstra-inspect time`과 같은 규칙). task는 `updatedAt` 내림차순 정렬.
|
|
78
|
+
|
|
79
|
+
```markdown
|
|
80
|
+
## okstra Rollup — <task-group 또는 "whole project"> (<taskCount> tasks)
|
|
81
|
+
|
|
82
|
+
| Task | Category | workStatus | Phase | Runs | CPU | Errors | Report |
|
|
83
|
+
|------|----------|------------|-------|------|-----|--------|--------|
|
|
84
|
+
| DEV-1 | bugfix | done | final-verification | 2 | 00:25:00 | 2 | ✓ |
|
|
85
|
+
| DEV-2 | feature | in-progress | implementation | 1 | 00:00:00 | 0 | — |
|
|
86
|
+
|
|
87
|
+
**Totals:** 3 runs · CPU 00:25:00 · 2 errors
|
|
88
|
+
**workStatus:** done 1 · in-progress 1 **category:** bugfix 1 · feature 1
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- `Report` 열: `reportPath`가 있으면 `✓`, 없으면 `—`.
|
|
92
|
+
- 상태/카테고리/phase 줄은 `totals`의 tally 맵을 **그대로** 옮긴다. `tasks[]` 배열을 직접 세지 않는다(집계는 CLI가 SSOT).
|
|
93
|
+
|
|
94
|
+
## digest 작성 (요약 — 스킬의 핵심 가치)
|
|
95
|
+
|
|
96
|
+
사용자가 "요약"/"정리"/"summarize"/"digest"를 요청한 경우(일반적인 경우):
|
|
97
|
+
|
|
98
|
+
1. `reportPath`가 비어 있지 않고 `<projectRoot>/<reportPath>` 파일이 실제로 존재하는 task마다 report를 읽고, **무엇을 달성했고 권장 다음 단계가 무엇인지** 1~2줄로 요약한다.
|
|
99
|
+
2. per-task 줄 위에, 그룹 차원 종합 2~4문장을 쓴다: 그룹 전반에서 무엇이 산출됐는지, 미결 작업이 어디 있는지(`byWorkStatus`/`byCurrentPhase` 활용), 에러 핫스팟(높은 `errorCount` task)이 있는지.
|
|
100
|
+
3. 각 per-task 주장은 report 경로(`<reportPath>`)로 인용해 독자가 바로 열어볼 수 있게 한다.
|
|
101
|
+
|
|
102
|
+
report가 없는 task는 요약을 지어내지 말고 현재 phase/workStatus를 대신 말한다. report 아닌 산출물을 읽어 빈칸을 메우지 않는다(artifact-home 규칙). report가 비었거나 없으면 그렇다고 말한다.
|
|
103
|
+
|
|
104
|
+
단일 task의 깊은 drill-down(전체 report, worker별 시간, 에러 분해, run 간 recap)이 필요하면 `/okstra-inspect`로 안내한다.
|
|
105
|
+
|
|
106
|
+
## 금지 패턴
|
|
107
|
+
|
|
108
|
+
- 집계 수치(합계, 분포)를 `tasks[]`에서 손으로 다시 세기. `totals`가 SSOT다.
|
|
109
|
+
- raw ms 노출. 항상 `HH:MM:SS`.
|
|
110
|
+
- `cpuSumMs`를 wall-clock인 양 표기.
|
|
111
|
+
- report가 없는 task에 대해 요약을 지어내기. 현재 phase/workStatus로 대체한다.
|
|
112
|
+
- okstra 산출물 밖(non-`.okstra`) 파일을 읽어 요약을 채우기.
|
|
113
|
+
- 단일 task 상세를 rollup에서 처리하기. `okstra-inspect`로 보낸다.
|
package/docs/kr/architecture.md
CHANGED
|
@@ -147,9 +147,9 @@ okstra 의 prepare 책임은 단일 python 진입점 [`okstra_ctl.run.prepare_ta
|
|
|
147
147
|
- [`prompts/lead/okstra-lead-contract.md`](../../prompts/lead/okstra-lead-contract.md) — main okstra lead contract. 런타임 리소스(`~/.okstra/prompts/lead/`)이며 agent skill 이 아닙니다.
|
|
148
148
|
- [`skills/okstra-setup/SKILL.md`](../../skills/okstra-setup/SKILL.md) — **첫 실행 부트스트랩**. `okstra install` + `project.json` 생성.
|
|
149
149
|
- [`skills/okstra-run/SKILL.md`](../../skills/okstra-run/SKILL.md) — **현재 claude 세션 안에서 okstra task 를 시작**하는 in-session 진입점. `prepare_task_bundle` 직접 호출.
|
|
150
|
-
- 사용자 호출 가능 스킬은 `skills/okstra-setup/SKILL.md`, `skills/okstra-brief/SKILL.md`, `skills/okstra-run/SKILL.md`, `skills/okstra-manager/SKILL.md`, `skills/okstra-memory/SKILL.md`, `skills/okstra-inspect/SKILL.md`, `skills/okstra-schedule/SKILL.md`, `skills/okstra-container-build/SKILL.md`
|
|
150
|
+
- 사용자 호출 가능 스킬은 `skills/okstra-setup/SKILL.md`, `skills/okstra-brief/SKILL.md`, `skills/okstra-run/SKILL.md`, `skills/okstra-manager/SKILL.md`, `skills/okstra-memory/SKILL.md`, `skills/okstra-inspect/SKILL.md`, `skills/okstra-rollup/SKILL.md`, `skills/okstra-schedule/SKILL.md`, `skills/okstra-container-build/SKILL.md` 9종뿐이며, 이것만 agent skill home 으로 복사됩니다 — brief 작성, phase 진행, cross-project manager task 조정, 전역 Memory Book 저장/검색, status/history/report/time/logs/cost/errors/recap read-side, task-group 단위 run 결과 종합(rollup), schedule 보조, 그리고 `okstra-container-build` 는 검증 완료된 task 코드를 docker compose 그룹으로 배포하고 컨테이너별 watcher 로 감시하는 비선형(PHASE_SEQUENCE 외부) 컨테이너 배포/감시 스킬. `okstra-manager` 는 `okstra manager` CLI JSON/launch packet 을 source of truth 로 사용하며, manager-owned plan/assignment/directive/snapshot/event 는 `~/.okstra/managers/<manager-id>/` 아래에 저장합니다. `okstra-rollup` 은 단일 task 집계기(`okstra-inspect` 의 time/errors/recap)를 task-group(또는 프로젝트 전체 catalog)으로 fan-out 하는 read-side 레이어로, deterministic 집계는 `okstra rollup` CLI 가 맡고 report 본문 종합 요약만 스킬(LLM)이 작성합니다. `okstra-inspect` 의 read-side facet 은 `skills/okstra-inspect/SKILL.md` 의 sub-command 표가 정본입니다. `okstra-inspect logs` 는 codex/antigravity wrapper 가 매 dispatch 마다 `runs/<task-type>/prompts/<worker>-prompt-<phase>-<seq>.log` 로 남기는 live-log sidecar 의 인벤토리·정리 안내(read-only), `okstra-inspect cost` 는 `okstra context-cost` 결과 요약, `okstra-inspect errors` 는 task 의 okstra-run 에러 로그를 타임스탬프 markdown error-report 로 모아 렌더하고 요약을 출력, `okstra-inspect recap` 은 task 의 run 간 phase 전·후 요약에 더해 `.okstra` 산출물에 대한 자유 Q&A 까지 답합니다.
|
|
151
151
|
- 내부 운영 계약 — `context-loader` / `team-contract` / `convergence` / `report-writer` 와 lead 계약 — 은 `prompts/lead/*.md` 로, 구현/검증 워커의 언어별 coding preflight 는 `prompts/coding-preflight/*` (overview 라우터 + clean-code + languages/frameworks/architectures 3단계 선택) 로 이동했습니다. 모두 `~/.okstra/prompts/` 에 설치되는 런타임 리소스이며 skill discovery 대상이 아닙니다. generated launch prompt 가 lead 에게 절대 경로를 제공하고, 재설치 시 과거 `okstra-context-loader` / `okstra-team-contract` / `okstra-convergence` / `okstra-report-writer` / `okstra-coding-preflight` / `okstra` skill 디렉터리는 exact-name prune 됩니다.
|
|
152
|
-
- 플러그인 매니페스트: [`../../.claude-plugin/plugin.json`](../../.claude-plugin/plugin.json) — `npx skills@latest add Devonshin/okstra` 보조 채널이 참조. 일반 셋업에는 `npx okstra@latest install` 을 사용한다. 플러그인 매니페스트는 사용자 진입점
|
|
152
|
+
- 플러그인 매니페스트: [`../../.claude-plugin/plugin.json`](../../.claude-plugin/plugin.json) — `npx skills@latest add Devonshin/okstra` 보조 채널이 참조. 일반 셋업에는 `npx okstra@latest install` 을 사용한다. 플러그인 매니페스트는 사용자 진입점 9개(`okstra-setup`, `okstra-brief`, `okstra-run`, `okstra-manager`, `okstra-memory`, `okstra-inspect`, `okstra-rollup`, `okstra-schedule`, `okstra-container-build`)만 노출한다.
|
|
153
153
|
- 설치 위치: `~/.claude/skills/<name>/SKILL.md` 또는 `~/.agents/skills/<name>/SKILL.md`.
|
|
154
154
|
- 릴리스 절차: [`../../RELEASING.md`](../../RELEASING.md) — npm publish 흐름과 release-please / manual fallback.
|
|
155
155
|
|
package/docs/kr/cli.md
CHANGED
|
@@ -634,6 +634,7 @@ chmod +x ~/.local/bin/okstra-ctl
|
|
|
634
634
|
| `okstra config <get\|set\|unset\|show> [key] [value] [--scope project\|global\|all]` | 영구 설정 관리 (예: `pr-template-path`). 원자적 JSON 쓰기 |
|
|
635
635
|
| `okstra memory <add\|list\|search\|show\|archive>` | `~/.okstra/memory-book` 전역 대화 메모리 관리. 프로젝트 `.okstra/` 와 분리된 user-home 저장소이며, `okstra 에 정리해서 보관해` 자연어 스킬의 CLI 기반 |
|
|
636
636
|
| `okstra manager <init\|discover-projects\|new\|task>` | 여러 프로젝트의 okstra task 를 manager-owned context 로 묶는 공개 CLI. `new project`, `new task-group`, `new task` 로 manager 계획을 만들고, `task assign` / `task note` / `task sync` / `task status` / `task run` 으로 프로젝트별 할당과 snapshot 을 관리한다. `new project --project-root` 는 이미 존재하는 디렉터리만 받으며, 그 안의 `.okstra/project.json` 이 없을 때만 setup-equivalent registration 을 수행한다. child task key 는 공개 문서에서 `project-id:task-group:task-id` 전체 형식을 사용하고, 같은 manager task 안에서 child task id 가 다르면 `--child-task-id` 로 exact child 를 지정한다. `task run` 은 child lead 를 직접 실행하지 않고 `prepared` launch metadata/event 와 child launch context packet 을 JSON 으로 반환한다 |
|
|
637
|
+
| `okstra rollup [--task-group <group>] [--project-root <dir>] [--cwd <dir>]` | okstra-rollup 스킬의 read-only 백엔드. catalog 의 모든 task(또는 한 task-group)에 대해 task 별 run 수·소요 시간(raw ms)·에러 수·최신 report 경로와 group 차원 합계 및 상태/카테고리/phase 분포를 JSON 으로 출력한다. `--task-group` 생략 시 프로젝트 전체 catalog 를 대상으로 한다. 시간은 raw ms 이며 HH:MM:SS 포맷·report 본문 종합은 호출자(skill)에 위임한다. 단일 task drill-down 은 `okstra inspect` 계열을 쓴다 |
|
|
637
638
|
| `okstra migrate [--apply] [--cwd <dir>] [--quiet]` | 한 번만 실행: 프로젝트 산출물 루트를 `.project-docs/okstra/` → `.okstra/` 로 이동. 기본 dry-run, `--apply` 로 실제 실행. `git mv` (git worktree) + 빈 `.project-docs/` 제거 + `<PROJECT>/CLAUDE.md` import 라인 + `.gitignore` + `~/.okstra/{recent,active}.jsonl` + `~/.okstra/worktrees/registry.json` 의 해당 프로젝트 row 동기화. 이미 `.okstra/` 가 존재하거나 레거시 디렉토리가 없으면 exit 1 로 거부. v0.x 말까지 유지 후 제거 예정 |
|
|
638
639
|
| `okstra task-list [--project-root <path>]` | `list_project_tasks` + `read_latest_task` 결과를 합쳐 task 카탈로그 + 최근 task 를 JSON 으로 반환 |
|
|
639
640
|
| `okstra task-show <task-key> [--project-root <path>]` | task-manifest.json 의 workflow / phase / status 요약 |
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
## 3. 소스 리포지토리 안 원본
|
|
30
30
|
|
|
31
|
-
- [`
|
|
31
|
+
- [`templates/prd/pr-body.template.md`](../templates/prd/pr-body.template.md) — `npx okstra install` 이 §2 의 default 위치로 복사하는 원본. 카피를 바꾸고 싶으면 이 파일을 수정한 뒤 다시 install 한다.
|
|
32
32
|
|
|
33
33
|
## 4. 설정 명령 — 영속화
|
|
34
34
|
|
|
@@ -174,6 +174,7 @@ Runtime/install asset changes follow this checklist:
|
|
|
174
174
|
| `spawn-followups`, `error-log` | `src/commands/execute/*.mjs` | Follow-up task bundle creation and run error-log append helpers |
|
|
175
175
|
| `memory` | `src/commands/memory/memory.mjs` | Store/find global conversation memory under `~/.okstra/memory-book` |
|
|
176
176
|
| `recap` | `src/commands/inspect/recap.mjs` | `okstra recap <assemble\|record>` Node wrapper backing the okstra-inspect `recap` facet |
|
|
177
|
+
| `rollup` | `src/commands/inspect/rollup.mjs` | `okstra rollup` thin shim into `scripts/okstra_ctl/rollup.py` — read-only cross-task roll-up backing the okstra-rollup skill |
|
|
177
178
|
| `container` | `src/commands/inspect/container.mjs` | `bin okstra container` thin shim into `scripts/okstra_ctl/container.py` for the okstra-container-build skill |
|
|
178
179
|
| `manager` | `src/commands/manager.mjs` | Thin shim into `scripts/okstra_ctl/manager_cli.py` for cross-project manager state and child launch packets |
|
|
179
180
|
|
|
@@ -231,6 +232,8 @@ Important modules:
|
|
|
231
232
|
| `run_index_row.py` | `~/.okstra` run-index row 의 생성/축약(slim)/복원(hydrate) 단일 참조점 — runId SSOT, projectId raw 보존 |
|
|
232
233
|
| `error_report.py`, `error_log_core.py`, `error_zip.py` | okstra-inspect errors/error-zip facet 백엔드 — `error_log_core` 는 `errors-*.jsonl` 글롭·파싱·집계 read-only 코어, `error_report` 는 errors facet 렌더, `error_zip` 은 cross-project run 디렉터리 수집·allowlist 익명화·클러스터 집계·zip 생성 |
|
|
233
234
|
| `log_report.py`, `time_report.py` | okstra-inspect logs/time facet 의 read-side 백엔드 (`okstra log-report` 는 wrapper sidecar 로그 인벤토리, `okstra time-report` 는 task 단위 시간 집계) |
|
|
235
|
+
| `rollup.py` | okstra-rollup 스킬의 read-side 백엔드 — catalog 를 task-group(또는 프로젝트 전체) 단위로 fan-out 해 task 별 run 수·소요 시간(raw ms)·에러 수·최신 report 경로와 group 차원 합계/상태·카테고리·phase 분포를 deterministic 하게 집계. `time_report`/`error_log_core` 함수 재사용, report 본문 종합은 skill 에 위임 |
|
|
236
|
+
| `json_registry.py` | 작은 okstra 레지스트리 공용 flock + atomic JSON persistence (`registry_lock`/`load_registry_json`/`save_registry_json`) — `container_registry`·`worktree_registry` 가 공유 |
|
|
234
237
|
| `stage_integrate.py` | whole-task stage 통합(머지)+worktree teardown 코어 (`integrate_stages`) — whole-task final-verification 진입·`okstra integrate-stages`·container up 이 공유 |
|
|
235
238
|
| `resolve_task_key.py` | bare task-id → `task-catalog.json` 후보 entry 해석 skill 공용 helper (`okstra resolve-task-key`) |
|
|
236
239
|
| `session.py`, `tmux.py`, `seeding.py`, `locks.py`, `invocation.py`, `sequence.py`, `ids.py`, `material.py` | Supporting lifecycle helpers |
|
|
@@ -301,7 +304,7 @@ Token/cost accounting:
|
|
|
301
304
|
|
|
302
305
|
### 4.10 `skills/`
|
|
303
306
|
|
|
304
|
-
|
|
307
|
+
9 user-facing skills (the only skills `okstra install` copies):
|
|
305
308
|
|
|
306
309
|
| Skill | User-invocable | Role |
|
|
307
310
|
|---|---:|---|
|
|
@@ -309,6 +312,7 @@ Token/cost accounting:
|
|
|
309
312
|
| `okstra-run` | yes | Start/resume okstra task in current Claude Code session |
|
|
310
313
|
| `okstra-memory` | yes | Store/search/archive global conversation memory under `~/.okstra/memory-book` |
|
|
311
314
|
| `okstra-inspect` | yes | Unified read-side — sub-commands `status` (lifecycle + workStatus), `history` (past runs / re-run / resume), `report` (find final-report), `time` (elapsed-time breakdown), `logs` (wrapper log inventory + cleanup), `cost` (task bundle context/read cost) |
|
|
315
|
+
| `okstra-rollup` | yes | Cross-task roll-up — aggregate runs/time/errors across a task-group (or whole project) and synthesize a digest from the report files |
|
|
312
316
|
| `okstra-schedule` | yes | Generate task-group schedule |
|
|
313
317
|
| `okstra-container-build` | yes | Non-linear deploy tool — deploy a verified task's code as a docker compose group and watch each container (sub-commands `up` / `status` / `logs` / `stop-watcher` / `down`) |
|
|
314
318
|
| `okstra-manager` | yes | Coordinate cross-project okstra tasks through manager-owned plans, assignments, sync snapshots, status, and child launch context packets |
|
|
@@ -321,8 +325,8 @@ Token/cost accounting:
|
|
|
321
325
|
| File | Role |
|
|
322
326
|
|---|---|
|
|
323
327
|
| `agents/workers/claude-worker.md` | Claude analyzer/verifier/executor spec |
|
|
324
|
-
| `agents/workers/codex-worker.
|
|
325
|
-
| `agents/workers/antigravity-worker.
|
|
328
|
+
| `agents/workers/codex-worker.params.json` | Codex analyzer/verifier/executor wrapper params (build 가 `_cli-wrapper-template.md` 로 `.md` 렌더) |
|
|
329
|
+
| `agents/workers/antigravity-worker.params.json` | Antigravity analyzer/verifier/executor wrapper params (build 가 `_cli-wrapper-template.md` 로 `.md` 렌더) |
|
|
326
330
|
| `agents/workers/report-writer-worker.md` | data.json SSOT author and audit sidecar writer |
|
|
327
331
|
|
|
328
332
|
The lead operating contract moved out of `agents/` to `prompts/lead/okstra-lead-contract.md` (a runtime resource installed to `~/.okstra/prompts/lead/`, not an agent skill).
|
|
@@ -217,5 +217,5 @@ flowchart TD
|
|
|
217
217
|
- [`scripts/okstra_ctl/wizard.py`](../../scripts/okstra_ctl/wizard.py)
|
|
218
218
|
- [`scripts/okstra_ctl/run.py`](../../scripts/okstra_ctl/run.py)
|
|
219
219
|
- [`scripts/okstra_ctl/pr_template.py`](../../scripts/okstra_ctl/pr_template.py)
|
|
220
|
-
- [`src/config.mjs`](../../src/config.mjs)
|
|
220
|
+
- [`src/commands/lifecycle/config.mjs`](../../src/commands/lifecycle/config.mjs)
|
|
221
221
|
- [`scripts/okstra_ctl/worktree.py`](../../scripts/okstra_ctl/worktree.py)
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Read-side cross-task roll-up for a task-group (or the whole project).
|
|
2
|
+
|
|
3
|
+
단일 task 집계기(time-report / error-report / recap)는 task-root 하나만 받는다.
|
|
4
|
+
이 모듈은 그 위에서 catalog 를 task-group 단위로 fan-out 해 task 별 run 수·소요
|
|
5
|
+
시간·에러 수·report 경로를 모으고, group 차원의 합계/분포를 deterministic 하게
|
|
6
|
+
roll-up 한다. report 본문 종합(자연어 요약)은 LLM(skill)에 남긴다 — 여기서는
|
|
7
|
+
report 경로만 노출한다. time-report 와 동일하게 raw ms 를 출력하고 HH:MM:SS
|
|
8
|
+
포맷·Markdown 표 렌더는 호출자(skill)에 위임한다.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from okstra_ctl.error_log_core import glob_error_logs, parse_records
|
|
18
|
+
from okstra_ctl.time_report import _load_runs, aggregate_time
|
|
19
|
+
from okstra_project import (
|
|
20
|
+
ResolverError,
|
|
21
|
+
list_project_tasks,
|
|
22
|
+
resolve_project_root,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _collect_task(entry: dict, project_root: Path) -> dict:
|
|
27
|
+
"""list_project_tasks entry → roll-up 한 줄. catalog 상태 필드 + time/error 측정.
|
|
28
|
+
|
|
29
|
+
runCount 는 timeline 의 전체 run 수(usable durationMs 없는 run 도 포함)이고
|
|
30
|
+
cpuSumMs/wallClockMs 는 durationMs 가 있는 run 만 반영하므로 0 일 수 있다.
|
|
31
|
+
"""
|
|
32
|
+
task_root = Path(entry["_resolvedTaskRoot"])
|
|
33
|
+
runs = _load_runs(task_root)
|
|
34
|
+
time_agg = aggregate_time(runs, project_root)
|
|
35
|
+
records, _ = parse_records(glob_error_logs(task_root))
|
|
36
|
+
return {
|
|
37
|
+
"taskKey": entry.get("taskKey", ""),
|
|
38
|
+
"taskGroup": entry.get("taskGroup", ""),
|
|
39
|
+
"taskId": entry.get("taskId", ""),
|
|
40
|
+
"taskType": entry.get("taskType", ""),
|
|
41
|
+
"workCategory": entry.get("workCategory", "") or "unknown",
|
|
42
|
+
"workStatus": entry.get("workStatus", "") or "unknown",
|
|
43
|
+
"currentPhase": entry.get("currentPhase", ""),
|
|
44
|
+
"currentPhaseState": entry.get("currentPhaseState", ""),
|
|
45
|
+
"nextRecommendedPhase": entry.get("nextRecommendedPhase", ""),
|
|
46
|
+
"latestRunStatus": entry.get("latestRunStatus", ""),
|
|
47
|
+
"updatedAt": entry.get("updatedAt", ""),
|
|
48
|
+
"reportPath": entry.get("latestReportPath", ""),
|
|
49
|
+
"runCount": len(runs),
|
|
50
|
+
"cpuSumMs": time_agg["grandTotal"]["cpuSumMs"],
|
|
51
|
+
"wallClockMs": sum(r["wallClockMs"] for r in time_agg["perRunWallClock"]),
|
|
52
|
+
"errorCount": len(records),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _tally(tasks: list[dict], key: str) -> dict:
|
|
57
|
+
"""task 들의 한 필드를 값별 개수로. 빈 값은 'unknown' 으로 접는다."""
|
|
58
|
+
counts: dict[str, int] = {}
|
|
59
|
+
for task in tasks:
|
|
60
|
+
bucket = task.get(key) or "unknown"
|
|
61
|
+
counts[bucket] = counts.get(bucket, 0) + 1
|
|
62
|
+
return dict(sorted(counts.items()))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def build_rollup(project_root: Path, task_group: str | None) -> dict:
|
|
66
|
+
entries = list_project_tasks(project_root, task_group=task_group or None)
|
|
67
|
+
tasks = [_collect_task(e, project_root) for e in entries]
|
|
68
|
+
totals = {
|
|
69
|
+
"runs": sum(t["runCount"] for t in tasks),
|
|
70
|
+
"cpuSumMs": sum(t["cpuSumMs"] for t in tasks),
|
|
71
|
+
"wallClockMs": sum(t["wallClockMs"] for t in tasks),
|
|
72
|
+
"errors": sum(t["errorCount"] for t in tasks),
|
|
73
|
+
"byWorkStatus": _tally(tasks, "workStatus"),
|
|
74
|
+
"byWorkCategory": _tally(tasks, "workCategory"),
|
|
75
|
+
"byCurrentPhase": _tally(tasks, "currentPhase"),
|
|
76
|
+
"byTaskType": _tally(tasks, "taskType"),
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
"ok": True,
|
|
80
|
+
"projectRoot": str(project_root),
|
|
81
|
+
"taskGroup": task_group or None,
|
|
82
|
+
"taskCount": len(tasks),
|
|
83
|
+
"tasks": tasks,
|
|
84
|
+
"totals": totals,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def main(argv: list[str] | None = None) -> int:
|
|
89
|
+
parser = argparse.ArgumentParser(
|
|
90
|
+
prog="okstra rollup",
|
|
91
|
+
description="Roll up run results across a task-group's tasks (raw ms).")
|
|
92
|
+
parser.add_argument("--task-group", default="",
|
|
93
|
+
help="scope to this task-group (default: whole project catalog)")
|
|
94
|
+
parser.add_argument("--project-root", default="")
|
|
95
|
+
parser.add_argument("--cwd", default=".")
|
|
96
|
+
parser.add_argument("--json", action="store_true", help="emit JSON (always on)")
|
|
97
|
+
args = parser.parse_args(argv)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
project_root = Path(
|
|
101
|
+
resolve_project_root(explicit_root=args.project_root, cwd=args.cwd)
|
|
102
|
+
).resolve()
|
|
103
|
+
except ResolverError as exc:
|
|
104
|
+
print(json.dumps({"ok": False, "stage": "resolve", "reason": str(exc)}))
|
|
105
|
+
return 2
|
|
106
|
+
|
|
107
|
+
result = build_rollup(project_root, args.task_group)
|
|
108
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
109
|
+
return 0
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: okstra-inspect
|
|
3
|
-
description:
|
|
4
|
-
Use for
|
|
3
|
+
description: >-
|
|
4
|
+
Use this for everything that happens AFTER a single okstra task has already run — inspecting it or light bookkeeping on it, never launching new work. The tell is usually a named task id (PROD-1623, dev-9184), often dropped without the word "okstra." Reach for it when the user wants one task's: status, current/next phase, blockers, or approval gate; its final report — where it is or whether it passed (결론·통과); its elapsed time (소요 시간) or context/read cost (컨텍스트 비용); its run history, re-run, or resume; to mark it done / in-progress / blocked / todo; or a failed run's error logs gathered into a report (에러 리포트). Also bundles cross-project okstra errors into an anonymized feedback zip (에러 환류). Use it even for a bare "mark it done" or "where's the report." NOT for starting a run (okstra-run), rollups/schedules (okstra-rollup / okstra-schedule), a brief (okstra-brief), setup (okstra-setup), or cross-project management (okstra-manager).
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# OKSTRA Inspect
|
|
@@ -28,12 +28,17 @@ Before any sub-command, run each of the following commands as a **separate Bash
|
|
|
28
28
|
If this exits non-zero, tell the user: "okstra not installed — run `/okstra-setup` first." Then stop. Do **not** try to invoke `npx -y okstra@latest ...` as a fallback.
|
|
29
29
|
|
|
30
30
|
2. `okstra check-project --json`
|
|
31
|
-
|
|
31
|
+
Resolves PROJECT_ROOT from the directory the Bash call runs in, looks for `.okstra/project.json`, and prints `{ok, projectRoot, projectJsonPath, projectId}` (plus a `stage` on failure). Parse the JSON from stdout.
|
|
32
32
|
|
|
33
|
-
-
|
|
34
|
-
- `ok: true` → carry `projectRoot` as a literal string and use it as the base for sub-command steps.
|
|
33
|
+
`check-project` only sees the cwd of the Bash call. When the session cwd already *is* the target project — the common case — the bare form is right. But when the user is asking about a project that is **not** the cwd (a sibling repo, a monorepo subdir, or a project named explicitly in the request), the bare form resolves to the wrong project or none and returns `ok:false`. That is a false negative, not a missing setup — do not hard-stop on it.
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
Branch on the result:
|
|
36
|
+
- `ok: true` → carry `projectRoot` as a literal string; it is the base for every sub-command step below.
|
|
37
|
+
- `ok: false` (or exit code 2 / a `stage` like `project_root_unresolved`) → before concluding "no setup", ask whether the user pointed at a specific project directory. If they did, re-run targeting it:
|
|
38
|
+
`okstra check-project --cwd <that-dir> --json`
|
|
39
|
+
`--cwd` is the sanctioned way to target a project without a leading `cd`/`export`: the command still starts with the literal `okstra` token, so the `Bash(okstra:*)` permission match holds (a leading `cd` is exactly what would break it). Only if this **also** returns `ok:false` do you tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
|
|
40
|
+
|
|
41
|
+
Carry the resolved `projectRoot` into the sub-commands: read file artifacts under `<projectRoot>/.okstra/...`, and for sub-command CLIs that accept it (e.g. `recap`, `context-cost`) pass `--cwd <projectRoot>` / `--project-root <projectRoot>` so they target the same project rather than the Bash cwd. Subsequent `okstra <subcmd>` calls self-bootstrap their Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
|
|
37
42
|
|
|
38
43
|
## Step 1: Dispatch by intent
|
|
39
44
|
|
|
@@ -253,6 +258,7 @@ When a user selects a specific task:
|
|
|
253
258
|
Builds a fresh run — new run-seq, new manifest, new report — using parameters from a previous `run-manifest-*.json`. Does NOT touch old artifacts; use `history.4` to continue an interrupted session.
|
|
254
259
|
|
|
255
260
|
1. Pick the source run-manifest: `runManifestPath` from `history.2`, or task's `latestRunManifestPath` from `history.1`. Note: `latestRunManifestPath` is projected from the timeline's latest run, so it is empty when the task has no recorded run yet (timeline absent) — in that case fall back to a `history.2` per-run `runManifestPath`, or ask the user.
|
|
261
|
+
**Migrated-task fallback (do not bail out on empty paths).** Tasks migrated from the old `.project-docs/okstra/...` layout often have empty `latestRunManifestPath` / per-run `runManifestPath` in the catalog and timeline even though the manifests exist on disk. Before asking the user, glob `<projectRoot>/.okstra/tasks/<group-seg>/<id-seg>/runs/**/manifests/run-manifest-*.json` and pick the latest by the `YYYY-MM-DD_HH-MM-SS` timestamp in the filename. Only if that glob is also empty do you ask the user.
|
|
256
262
|
2. Read the manifest and extract required arguments:
|
|
257
263
|
- `projectId` → `--project-id`
|
|
258
264
|
- `taskGroup` → `--task-group`
|
|
@@ -283,8 +289,9 @@ Continues an existing Claude session for an unfinished run. Does NOT create a ne
|
|
|
283
289
|
|
|
284
290
|
1. Read `latestResumeCommandPath` from `history.1` (or `resumeCommandPath` from a `history.2` timeline entry).
|
|
285
291
|
2. Verify the file exists on disk.
|
|
286
|
-
3. If
|
|
287
|
-
4. If
|
|
292
|
+
3. **Migrated-task fallback (do not declare "no resume" on an empty catalog path).** If the catalog/timeline path is empty or points at a non-existent `.project-docs/okstra/...` location, glob the on-disk scripts before concluding absent: `<projectRoot>/.okstra/tasks/<group-seg>/<id-seg>/runs/**/sessions/claude-resume-*.sh`, pick the latest by the `YYYY-MM-DD_HH-MM-SS` timestamp in the filename, and confirm it exists.
|
|
293
|
+
4. If a script is found: `bash <resume-command-path>`.
|
|
294
|
+
5. If the glob is also empty: `No resume script available for this run. Use 'history.3' to start a fresh run instead.`
|
|
288
295
|
|
|
289
296
|
---
|
|
290
297
|
|
|
@@ -320,7 +327,12 @@ D. **Specific task-type (fallback):** `latestReportPath` is task-type-agnostic.
|
|
|
320
327
|
|
|
321
328
|
### report.3 — Read + next-step guidance
|
|
322
329
|
|
|
323
|
-
|
|
330
|
+
Match the depth of read to the request — final reports routinely run 300+ lines / 50K+ tokens, so ingesting the whole file just to answer "핵심만 요약" is wasteful:
|
|
331
|
+
|
|
332
|
+
- **Summary / verdict intent** ("요약", "핵심만", "결론", "통과했어?", "verdict"): do **not** read the whole report first. Read the `final-<task-type-segment>-<NNN>.status` sidecar under `runs/<task-type-segment>/status/` (stage-isolated: `runs/<task-type-segment>/stage-<N>/status/`) for the machine verdict, then read only the report's leading summary block (the first "종합 판정" / "Executive" / verdict heading and its body) to quote the gist. Offer to read the full report if the user wants detail.
|
|
333
|
+
- **Full-read intent** ("전체", "다 읽어", "본문"): ingest the resolved report file.
|
|
334
|
+
|
|
335
|
+
After reading, surface follow-up options:
|
|
324
336
|
|
|
325
337
|
1. **구현 진행:** based on the report's "권장 다음 단계" section.
|
|
326
338
|
2. **추가 검증:** to launch a new okstra run with the same task-key, assemble the command through the `history` sub-command — it offers the full option set (base-ref, workers, render-only) and renders a host-correct invocation.
|
|
@@ -372,7 +384,7 @@ Convert every `*Ms` to `HH:MM:SS` (zero-pad; never show raw ms). Task types in `
|
|
|
372
384
|
|
|
373
385
|
- **By task type** — `| Task type | Runs | CPU sum | Lead | Workers |` from `byTaskType`, plus a `grandTotal` row. `CPU sum` (= Lead + Workers) overlaps because workers run inside the lead's window — it is *not* wall-clock. Surface wall-clock only when the user explicitly asks, from `perRunWallClock`.
|
|
374
386
|
- **Per worker** (per task type) — `| Worker | Runs | Total | Avg/run |` from `perWorker`. Render the worker as bare `workerId` when `agents` is empty, else `workerId (agent1, agent2)`.
|
|
375
|
-
- **Phase breakdown** — only when the user
|
|
387
|
+
- **Phase breakdown** — "단계별"/"stage별"/"어느 단계가 오래" in okstra most often means the **lifecycle stage (task-type)** view, which the *By task type* table above already answers — lead with that table, do not treat the request as unanswerable. Render the intra-run phase timeline only when the user clearly means within-a-run phases ("어느 phase가", "Phase 1~7", "phase timeline"): one table per run from `phaseTimelines`: `| Phase | Start | Wall to next |` using `firstAt`/`wallMsToNext` (`null` → `--`). When `phaseTimelines` is empty, do **not** headline "측정 불가" — the By-task-type table is the stage answer; mention the missing intra-run markers only as a trailing footnote.
|
|
376
388
|
- If `unavailable[]` is non-empty, append a trailing note listing each run with its reason. Never fold them into totals.
|
|
377
389
|
- Show the resolved `<task-key>` in the heading.
|
|
378
390
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: okstra-rollup
|
|
3
|
+
description: |
|
|
4
|
+
Use when the user wants run results from MULTIPLE okstra tasks collected and summarized at once — a task-group digest or a whole-project roll-up, not a single task. Aggregates per-task run count, elapsed time, error count, and latest report path, plus group-level totals and status/category/phase tallies, then synthesizes a cross-task prose summary from the report files. Make sure to use this skill whenever the user mentions "okstra rollup", "롤업", "task-group 요약", "그룹 단위 리포트", "여러 task 결과 모아", "전체 task 현황 요약", "cross-task summary", "그룹 종합 리포트", "모든 task 정리", "run 결과 한꺼번에", even if they don't say "rollup". For a SINGLE task's report/time/errors/recap use okstra-inspect instead; for a forward-looking work plan over non-done tasks use okstra-schedule.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# OKSTRA Rollup
|
|
8
|
+
|
|
9
|
+
Cross-task roll-up: collect every catalog task's run results (optionally scoped to one task-group) and summarize them together. The `rollup` CLI does all deterministic aggregation — counts, time sums, error tallies, status/category/phase distributions. You resolve scope, call it, render the table, and synthesize the prose digest from the report files. **Never recompute totals or re-tally by hand** — the CLI is the SSOT for the numbers.
|
|
10
|
+
|
|
11
|
+
This skill is read-only. It never mutates task artifacts.
|
|
12
|
+
|
|
13
|
+
## Step 0: Verify okstra runtime + project setup
|
|
14
|
+
|
|
15
|
+
Run each command as a **separate Bash tool call**, each starting with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap any of them in `if`, `eval`, `$(...)`, `VAR=...`, `||`, `&&`, or an `npx` fallback — those defeat the permission match.
|
|
16
|
+
|
|
17
|
+
1. `okstra ensure-installed --runtime claude-code`
|
|
18
|
+
Non-zero exit → tell the user: "okstra not installed — run `/okstra-setup` first." Then stop.
|
|
19
|
+
|
|
20
|
+
2. `okstra check-project --json`
|
|
21
|
+
Parse `{ok, projectRoot, ...}` from stdout. `ok: false` → "this project has no okstra setup. Run `/okstra-setup` first." Then stop. `ok: true` → carry `projectRoot` as a literal string.
|
|
22
|
+
|
|
23
|
+
## Step 1: Resolve scope
|
|
24
|
+
|
|
25
|
+
- User named a task-group (e.g. "alpha 그룹 요약") → use it as `--task-group <group>`.
|
|
26
|
+
- User asked for the whole project ("전체 task", "프로젝트 전체") or named no scope → omit `--task-group` (whole catalog).
|
|
27
|
+
- If genuinely ambiguous, ask once: one task-group, or the whole project? Do not silently guess a specific group.
|
|
28
|
+
|
|
29
|
+
## Step 2: Fetch the roll-up
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
okstra rollup --task-group <group> --project-root <projectRoot> --json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Omit `--task-group` for the whole project. The JSON shape (all durations are **raw milliseconds**):
|
|
36
|
+
|
|
37
|
+
- `taskGroup` — the scope (`null` = whole project), `taskCount` — number of tasks.
|
|
38
|
+
- `tasks[]` — per task: `taskKey, taskGroup, taskId, taskType, workCategory, workStatus, currentPhase, currentPhaseState, nextRecommendedPhase, latestRunStatus, updatedAt, reportPath, runCount, cpuSumMs, wallClockMs, errorCount`.
|
|
39
|
+
- `totals` — `runs, cpuSumMs, wallClockMs, errors`, plus `byWorkStatus`, `byWorkCategory`, `byCurrentPhase`, `byTaskType` (each a `{value: count}` map).
|
|
40
|
+
|
|
41
|
+
`runCount` counts every timeline run; `cpuSumMs`/`wallClockMs` only reflect runs that reached Phase 7 usage, so they can be `0` while `runCount > 0`. `reportPath` is project-relative and may be empty (task not yet reported).
|
|
42
|
+
|
|
43
|
+
If `taskCount` is `0`, tell the user there are no okstra tasks in that scope and stop.
|
|
44
|
+
|
|
45
|
+
## Step 3: Render the roll-up table
|
|
46
|
+
|
|
47
|
+
Convert every `*Ms` to `HH:MM:SS` (zero-pad; never show raw ms). `cpuSumMs` is CPU-sum (lead + workers overlap), not wall-clock — label it "CPU"; only surface `wallClockMs` when the user asks. Sort tasks by `updatedAt` desc.
|
|
48
|
+
|
|
49
|
+
```markdown
|
|
50
|
+
## okstra Rollup — <task-group or "whole project"> (<taskCount> tasks)
|
|
51
|
+
|
|
52
|
+
| Task | Category | workStatus | Phase | Runs | CPU | Errors | Report |
|
|
53
|
+
|------|----------|------------|-------|------|-----|--------|--------|
|
|
54
|
+
| DEV-1 | bugfix | done | final-verification | 2 | 00:25:00 | 2 | ✓ |
|
|
55
|
+
| DEV-2 | feature | in-progress | implementation | 1 | 00:00:00 | 0 | — |
|
|
56
|
+
|
|
57
|
+
**Totals:** 3 runs · CPU 00:25:00 · 2 errors
|
|
58
|
+
**workStatus:** done 1 · in-progress 1 **category:** bugfix 1 · feature 1
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Render `Report` as `✓` when `reportPath` is non-empty, else `—`. Build the status/category lines from the `totals` tally maps verbatim — do not count the `tasks[]` array yourself.
|
|
62
|
+
|
|
63
|
+
## Step 4: Synthesize the digest (the summary)
|
|
64
|
+
|
|
65
|
+
This is the skill's value-add over a bare table. When the user asked to "요약"/"summarize"/"digest"/"정리" (the common case), produce a short cross-task narrative:
|
|
66
|
+
|
|
67
|
+
1. For each task with a non-empty `reportPath` whose file exists under `<projectRoot>/<reportPath>`, read it and write a 1–2 line summary of what it accomplished and its recommended next step.
|
|
68
|
+
2. Above the per-task lines, write a 2–4 sentence group-level synthesis: what was delivered across the group, where the open work sits (use `byWorkStatus`/`byCurrentPhase`), and any error hot-spots (tasks with high `errorCount`).
|
|
69
|
+
3. Cite each per-task claim with the report path as `<reportPath>` so the reader can open it.
|
|
70
|
+
|
|
71
|
+
For tasks with no report, state the current phase/workStatus instead of inventing a summary — do not read non-report artifacts to fill the gap.
|
|
72
|
+
|
|
73
|
+
Keep the digest to what the reports actually say; if a report is missing or empty, say so rather than guessing. For a deeper single-task drill-down (full report, per-worker time, error breakdown, run-to-run recap), point the user to `/okstra-inspect`.
|
package/src/cli-registry.mjs
CHANGED
|
@@ -152,6 +152,13 @@ export const COMMAND_REGISTRY = [
|
|
|
152
152
|
category: "introspection",
|
|
153
153
|
summary: ["Assemble a task's run-to-run phase recap or append a recap log entry"],
|
|
154
154
|
},
|
|
155
|
+
{
|
|
156
|
+
name: "rollup",
|
|
157
|
+
module: "./commands/inspect/rollup.mjs",
|
|
158
|
+
export: "run",
|
|
159
|
+
category: "introspection",
|
|
160
|
+
summary: ["Roll up run results across a task-group's tasks (raw ms + tallies)"],
|
|
161
|
+
},
|
|
155
162
|
{
|
|
156
163
|
name: "container",
|
|
157
164
|
module: "./commands/inspect/container.mjs",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { runPythonModule } from "../../lib/python-helper.mjs";
|
|
2
|
+
|
|
3
|
+
const USAGE = `okstra rollup — roll up run results across a task-group's tasks
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
okstra rollup [--task-group <group>] [--project-root <dir>] [--cwd <dir>]
|
|
7
|
+
|
|
8
|
+
Read-only. Prints a JSON roll-up of every catalog task (optionally scoped to one
|
|
9
|
+
task-group): per-task run count, elapsed time (raw ms), error count, and the
|
|
10
|
+
latest report path, plus group-level totals and status/category/phase tallies.
|
|
11
|
+
With no --task-group it covers the whole project catalog. Time is raw ms; the
|
|
12
|
+
caller formats HH:MM:SS and synthesizes the prose digest from the report files.
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export async function run(args) {
|
|
16
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
17
|
+
process.stdout.write(USAGE);
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const result = await runPythonModule({
|
|
22
|
+
module: "okstra_ctl.rollup",
|
|
23
|
+
args,
|
|
24
|
+
stdio: "inherit-stdout",
|
|
25
|
+
});
|
|
26
|
+
return result.code;
|
|
27
|
+
}
|