okstra 0.152.0 → 0.154.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/okstra +7 -0
- package/docs/cli.md +5 -1
- package/docs/for-ai/skills/okstra-schedule-gen.md +152 -232
- package/docs/project-structure-overview.md +2 -2
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/bin/okstra-antigravity-exec.sh +11 -6
- package/runtime/bin/okstra-wrapper-agy-stream.py +61 -0
- package/runtime/prompts/lead/convergence.md +3 -2
- package/runtime/prompts/lead/plan-body-verification.md +30 -1
- package/runtime/python/okstra_ctl/container.py +9 -10
- package/runtime/python/okstra_ctl/convergence_engine.py +2 -1
- package/runtime/python/okstra_ctl/handoff.py +4 -8
- package/runtime/python/okstra_ctl/implementation_outcome.py +10 -56
- package/runtime/python/okstra_ctl/model_discovery.py +22 -1
- package/runtime/python/okstra_ctl/mutation_probe.py +425 -2
- package/runtime/python/okstra_ctl/plan_run_root.py +15 -8
- package/runtime/python/okstra_ctl/run.py +8 -54
- package/runtime/python/okstra_ctl/schedule_semantics.py +1249 -0
- package/runtime/python/okstra_ctl/stage_map.py +288 -0
- package/runtime/python/okstra_ctl/wizard.py +24 -35
- package/runtime/python/okstra_project/state.py +19 -5
- package/runtime/skills/okstra-schedule-gen/SKILL.md +75 -35
- package/runtime/templates/reports/schedule.template.md +9 -9
- package/runtime/validators/detect_self_mock.py +27 -2
- package/runtime/validators/validate-implementation-plan-stages.py +24 -63
- package/runtime/validators/validate-run.py +110 -0
- package/runtime/validators/validate-schedule.py +78 -10
- package/src/commands/inspect/stage-map.mjs +1 -1
package/README.md
CHANGED
|
@@ -184,7 +184,7 @@ Use these slash commands inside a Claude Code session:
|
|
|
184
184
|
| `/okstra-inspect` | Unified read side. Subcommands: `status` (phase/state and workStatus updates), `history` (past tasks, reruns, resumes), `report` (find/read final reports), `time` (elapsed-time breakdown), `logs` (wrapper log sidecar inventory and cleanup suggestions), `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), and `recap` (run-to-run before/after summary plus free-form Q&A over a task's `.okstra` artifacts) |
|
|
185
185
|
| `/okstra-rollup` | Aggregate every task run in a task group or project, including per-task run counts, duration, errors, group totals, and a cross-task report digest |
|
|
186
186
|
| `/okstra-usage` | Show the current project's recent run coverage, raw and billable-equivalent tokens, known USD cost, CPU time, and wall-clock time grouped by task type (default: last 30 days) |
|
|
187
|
-
| `/okstra-schedule-gen` |
|
|
187
|
+
| `/okstra-schedule-gen` | Invoke as `/okstra-schedule-gen [task-group]` to generate a work schedule for an entire task group. Each non-done task is resolved through the source-aware `stage-map` response; your unfinished-stage choices are captured in a temporary selection contract, and only the same draft that passes deterministic selection validation followed by independent narrative verification is published |
|
|
188
188
|
| `/okstra-container-build` | Deploy a verified task's code as a local Docker Compose group and monitor per-container logs (subcommands: `up` / `status` / `logs` / `stop-watcher` / `down`) |
|
|
189
189
|
| `/okstra-manager` | Coordinate cross-project okstra tasks through manager-owned plans, assignments, one-way project sync snapshots, status, and child launch context packets |
|
|
190
190
|
| `/okstra-pr-gen` | Register PR body templates under `~/.okstra/template/pr/` and generate a PR description from a branch diff (subcommands: `template` / `branches` / `gen`). Global skill—needs a Git repo, not a registered okstra project |
|
package/bin/okstra
CHANGED
|
@@ -18,6 +18,13 @@ async function main(argv) {
|
|
|
18
18
|
const [cmd, ...rest] = args;
|
|
19
19
|
const loader = COMMANDS.get(cmd);
|
|
20
20
|
if (!loader) {
|
|
21
|
+
if (cmd === "schedule") {
|
|
22
|
+
process.stderr.write(
|
|
23
|
+
"unknown command: schedule\n" +
|
|
24
|
+
"Schedule generation runs in an agent host: /okstra-schedule-gen [task-group]\n",
|
|
25
|
+
);
|
|
26
|
+
return 2;
|
|
27
|
+
}
|
|
21
28
|
process.stderr.write(
|
|
22
29
|
`unknown command: ${cmd}\n` +
|
|
23
30
|
"hint: this okstra binary may predate the skill calling it — update it " +
|
package/docs/cli.md
CHANGED
|
@@ -324,6 +324,10 @@ Example:
|
|
|
324
324
|
scripts/okstra.sh --task-type error-analysis --project-id jobs --project-root /Volumes/Workspaces/workspace/projects/jobs ...
|
|
325
325
|
```
|
|
326
326
|
|
|
327
|
+
### Schedule generation skill
|
|
328
|
+
|
|
329
|
+
The public schedule entry point is the host skill `/okstra-schedule-gen [task-group]`; `stage-map` and `validate-schedule.py` are its backend contracts, not an additional schedule-generation shell command. For each candidate task, the skill branches on the `stage-map` `state` and `sourcePlanPath`, records the selected, completed, and full stage sets in a temporary `.selection.json`, and runs deterministic `--selection-json` validation before an independent narrative verifier. Only the same draft that passes both gates is promoted, and the temporary selection contract is removed after final validation.
|
|
330
|
+
|
|
327
331
|
### `--directive`
|
|
328
332
|
|
|
329
333
|
A free-text channel for passing user intent to the lead, workers, and downstream skills. The value is embedded in a `## Directive` section at the end of `instruction-set/analysis-material.md` and in `instruction-set/analysis-packet.md`, the analysis workers' primary input. It is also backed up to `instruction-set/directive.txt`.
|
|
@@ -737,7 +741,7 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
|
|
|
737
741
|
| `okstra migrate [--apply] [--cwd <dir>] [--quiet]` | One-time migration of the project artifact root from `.project-docs/okstra/` to `.okstra/`. It is a dry run by default; `--apply` performs the move with `git mv` in a Git worktree, removes an empty `.project-docs/`, and synchronizes the `<PROJECT>/CLAUDE.md` import line, `.gitignore`, the project's rows in `~/.okstra/{recent,active}.jsonl`, and `~/.okstra/worktrees/registry.json`. It exits 1 if `.okstra/` already exists or the legacy directory is absent. Scheduled for removal by the end of v0.x |
|
|
738
742
|
| `okstra task-list [--project-root <path>]` | Combine `list_project_tasks` and `read_latest_task` into JSON containing the task catalog and latest task |
|
|
739
743
|
| `okstra task-show <task-key> [--project-root <path>]` | Summarize workflow, phase, status, and artifacts from the Task Read-Side Snapshot |
|
|
740
|
-
| `okstra stage-map <task-key> [--cwd <dir>\|--project <dir>]` | Dump the task's implementation-planning Stage Map as JSON: `{ ok, taskKey, taskRoot, stages:[{stage_number,title,depends_on,step_count}], doneStages:[int] }`. `
|
|
744
|
+
| `okstra stage-map <task-key> [--cwd <dir>\|--project <dir>]` | Dump the task's implementation-planning Stage Map as JSON: `{ ok, taskKey, taskRoot, state, sourcePlanPath, stages:[{stage_number,title,depends_on,step_count}], doneStages:[int] }`. `state` is `ready` for one resolved source and `missing` when no Stage Map exists; corrupt or conflicting sources return structured non-zero errors instead of silently selecting another report. `doneStages` is read from the implementation-planning stage consumer state (with carry recovery). This is the read-side source `/okstra-schedule-gen [task-group]` uses to derive selectable unfinished stages and their completed dependency closure |
|
|
741
745
|
| `okstra incremental-scope <args…>` | Decide re-verify vs carry-forward scope for an `implementation-planning` clarification re-run. Thin shim into `scripts/okstra_ctl/incremental_scope.py` (deterministic pure function): it reads the dependency graph from the prior run `data.json`'s `implementationPlanning.stageMap` and returns `mode:"incremental"` only when the base-ref SHA is unchanged and the affected stages' `downstream_stage_closure` covers at most half of all stages; otherwise it signals a full re-run. Used to bound the cost of a clarification re-run |
|
|
742
746
|
| `okstra incremental-carry <args…>` | Merge carried-forward plan-item verdicts into an incremental re-run. Thin shim into `scripts/okstra_ctl/incremental_carry.py`: it takes the prior run's plan-item verdicts that the current run does not re-verify and merges them into the current `data.json` tagged with `carriedForwardFromSeq`. A `schemaVersion` drift raises `CarryError` and exits non-zero to force a full fallback. Runs after `incremental-scope` returns `mode:"incremental"` |
|
|
743
747
|
| `okstra code-review target --task-key <k> --stage <N> [--project-root <dir>] [--cwd <dir>] [--json]` / `okstra code-review target --branch <name> [--base <ref>] [--date <YYYY-MM-DD>] [--project-root <dir>] [--cwd <dir>] [--json]` | Resolve what a code review reads and where its result file goes. Output is always JSON, so `--json` only makes that explicit. `--project-root` and `--cwd` are shared pre-dispatch arguments and apply to both modes; `--cwd` is only consulted when `--project-root` is absent. Both modes return `{ ok, projectRoot, mode, worktreePath, branch, baseCommit, headCommit, reviewPath, round }`; stage mode additionally returns `taskKey`, `taskRoot`, and `stage`. Stage mode takes the diff base from the `base_ref` recorded on that stage's worktree-registry row when it was provisioned — not from a rule re-applied at review time — and names the result `.okstra/tasks/<task-group>/<task-id>/code-reviews/stage-<NN>.md`, where a re-review of the same stage becomes `-r2`, `-r3`, … (the `round` field). Only a legacy row provisioned before `base_ref` was recorded falls back to re-deriving the base through `stage_targets`, and a failure there is reported as `stage_base_unresolved`. `worktreePath` comes back empty whenever the stage worktree is not usable as a live checkout — the registry row is no longer `active` (whole-task final-verification released it), the row never carried a path, or the recorded directory is gone — and the review then reads the `branch` ref instead. Branch mode uses `--base` when given, otherwise the merge-base with the default branch (`refs/remotes/origin/HEAD`, else `main`/`master`), and names the result `.project-docs/code-reviews/<branch>/<YYYY-MM-DD>-<NN>.md`, where `<NN>` (the `round` field) is the next sequence number for that date — the highest already on disk plus one. Read-only: it resolves paths and creates no directory and no file, so the review directory does not exist until the caller writes the report. Backend for the okstra-code-review skill |
|
|
@@ -3,318 +3,238 @@
|
|
|
3
3
|
## Source
|
|
4
4
|
|
|
5
5
|
- Skill source: [`skills/okstra-schedule-gen/SKILL.md`](../../../skills/okstra-schedule-gen/SKILL.md)
|
|
6
|
-
-
|
|
7
|
-
-
|
|
6
|
+
- Schedule template: [`templates/reports/schedule.template.md`](../../../templates/reports/schedule.template.md)
|
|
7
|
+
- Schedule validator: [`validators/validate-schedule.py`](../../../validators/validate-schedule.py)
|
|
8
|
+
- Stage Map read side: [`scripts/okstra_project/state.py`](../../../scripts/okstra_project/state.py)
|
|
9
|
+
- Selection semantics: [`scripts/okstra_ctl/schedule_semantics.py`](../../../scripts/okstra_ctl/schedule_semantics.py)
|
|
10
|
+
- Work-category source of truth: [`scripts/okstra_ctl/work_categories.py`](../../../scripts/okstra_ctl/work_categories.py)
|
|
8
11
|
- workStatus inference reference: [`skills/okstra-inspect/SKILL.md`](../../../skills/okstra-inspect/SKILL.md)
|
|
9
12
|
|
|
10
|
-
## Purpose
|
|
13
|
+
## Purpose and invocation
|
|
11
14
|
|
|
12
|
-
`okstra-schedule-gen` gathers
|
|
15
|
+
`okstra-schedule-gen` gathers non-done tasks in a task group and produces one client-facing work schedule from user-selected unfinished implementation stages.
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
Public invocation:
|
|
15
18
|
|
|
16
19
|
```text
|
|
17
|
-
|
|
20
|
+
/okstra-schedule-gen [task-group]
|
|
18
21
|
```
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
This is a host skill, not a schedule-generation shell command. Use `stage-map` and `validate-schedule.py` only as backend contracts inside the skill.
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- The user requests a "schedule", "work plan", or work-schedule table for an entire task-group.
|
|
25
|
-
- `.okstra/discovery/task-catalog.json` contains that task-group and it has at least one task that is not `done`.
|
|
25
|
+
Output location:
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
```text
|
|
28
|
+
<PROJECT_ROOT>/.okstra/tasks/<task-group-segment>/schedule/<task-group-segment>-plan-<YYYY-MM-DD_HH-MM-SS>.md
|
|
29
|
+
```
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
- Actual phase execution: `okstra-run`
|
|
31
|
-
- An already-completed task-group: do not create a file; state that all tasks are done.
|
|
31
|
+
Do not use it for single-task status analysis or phase execution. Use `okstra-inspect status` and `okstra-run` for those jobs.
|
|
32
32
|
|
|
33
|
-
## Preflight
|
|
33
|
+
## Preflight and task-group resolution
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Run one literal-token preflight call:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
okstra preflight --runtime claude-code --json
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
## Resolving the task-group
|
|
41
|
+
On `ok: false`, point the user to `/okstra-setup` and stop. Otherwise:
|
|
44
42
|
|
|
45
43
|
1. Read `.okstra/discovery/task-catalog.json`.
|
|
46
|
-
2.
|
|
47
|
-
3.
|
|
48
|
-
4.
|
|
49
|
-
5. Read each matched
|
|
44
|
+
2. Resolve an explicit task-group from the invocation or host request. If none is unambiguous, ask the user to choose; never guess.
|
|
45
|
+
3. Lowercase the token and strip characters outside `[a-z0-9]`.
|
|
46
|
+
4. Apply the same transform to each catalog `taskGroupPathSegment` and compare only those normalized values.
|
|
47
|
+
5. Read each matched `task-manifest.json`; it is authoritative when the catalog is stale.
|
|
50
48
|
|
|
51
|
-
On zero matches,
|
|
49
|
+
On zero matches, report that the task group was not found and do not create a file.
|
|
52
50
|
|
|
53
|
-
##
|
|
51
|
+
## Candidate filter
|
|
54
52
|
|
|
55
|
-
|
|
53
|
+
`workStatus` is used only to decide which tasks are candidates. When it is missing or empty, use the `okstra-inspect` `status.4` inference table.
|
|
56
54
|
|
|
57
|
-
Exclude
|
|
55
|
+
- Exclude resolved `done` tasks.
|
|
56
|
+
- Include every other resolved state.
|
|
57
|
+
- If no task remains, report that all tasks are done and do not create a file.
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
- inferred `done`
|
|
59
|
+
Do not render `workStatus` as the detailed task status. The per-task `Status` value is `<taskType> / <currentPhase>`.
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
## Source-aware Stage Map resolution
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
- `in-progress`
|
|
66
|
-
- `blocked`
|
|
67
|
-
- `phase-done`
|
|
68
|
-
- other non-done inferred/display states
|
|
63
|
+
For every candidate task, call:
|
|
69
64
|
|
|
70
|
-
|
|
65
|
+
```bash
|
|
66
|
+
okstra stage-map <task-key> --json
|
|
67
|
+
```
|
|
71
68
|
|
|
72
|
-
|
|
69
|
+
The successful response has this boundary shape:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
{ ok, taskKey, taskRoot, state, sourcePlanPath,
|
|
73
|
+
stages:[{stage_number,title,depends_on,step_count}], doneStages:[int] }
|
|
74
|
+
```
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
Handle each result explicitly:
|
|
75
77
|
|
|
76
|
-
- `
|
|
77
|
-
- `
|
|
78
|
-
- `
|
|
79
|
-
- `workflow.currentPhaseState`
|
|
80
|
-
- `taskType`
|
|
81
|
-
- `workStatus`
|
|
82
|
-
- `latestReportPath`
|
|
78
|
+
- `state: ready`: use exactly `sourcePlanPath`; do not pick a report by mtime or `latestReportPath`. Select only `stages − doneStages`.
|
|
79
|
+
- `state: missing`: record an empty source and empty stage sets, mark the task `[NEEDS-PLANNING]`, and emit no forward Work Breakdown, Gantt, or day total for it.
|
|
80
|
+
- `ok: false` or another state: stop before drafting and report the structured `stage` and `reason`. A corrupt or conflicting source must never fall back to a guessed report.
|
|
83
81
|
|
|
84
|
-
|
|
82
|
+
A valid selected set is dependency-closed: every transitive prerequisite of a selected stage is either also selected or present in `doneStages`. Completed prerequisites remain evidence only and are never scheduled forward.
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
- Solution / Architecture
|
|
88
|
-
- Work Breakdown
|
|
89
|
-
- Verification Commands
|
|
90
|
-
- Rollback strategy
|
|
91
|
-
- Effort, Risk, Priority, Scope, Repos
|
|
84
|
+
If a ready task has no unfinished stages, render `_Complete — no remaining stage_` and omit forward effort.
|
|
92
85
|
|
|
93
|
-
|
|
86
|
+
## Stage selection
|
|
94
87
|
|
|
95
|
-
|
|
96
|
-
2. If none, also look at `runs/*/reports/final-report-*.md`.
|
|
97
|
-
3. If found, parse it and put a fallback note in the schedule task block.
|
|
98
|
-
4. If still nothing, mark it `[NEEDS-OKSTRA-RUN]` and use only the manifest metadata.
|
|
88
|
+
Offer up to three dependency-closed cumulative bundles in topological order, plus all remaining stages. A custom set is accepted only after closing it over unfinished prerequisites; completed prerequisites are preserved separately.
|
|
99
89
|
|
|
100
|
-
|
|
90
|
+
Skip the picker when the remaining work has only one possible bundle and use all unfinished stages. Record the final stage numbers as `selectedStages`.
|
|
101
91
|
|
|
102
|
-
##
|
|
92
|
+
## Temporary selection contract
|
|
103
93
|
|
|
104
|
-
|
|
94
|
+
Write a paired draft and selection input with one timestamp:
|
|
105
95
|
|
|
106
|
-
|
|
96
|
+
```text
|
|
97
|
+
.okstra/tasks/<task-group-segment>/schedule/.draft/<timestamp>.md
|
|
98
|
+
.okstra/tasks/<task-group-segment>/schedule/.draft/<timestamp>.selection.json
|
|
99
|
+
```
|
|
107
100
|
|
|
108
|
-
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
101
|
+
The selection file is a temporary verification input. It freezes the exact source, full stage map, completed stages, and user-selected forward work so both validators judge the same facts instead of re-resolving mutable task state.
|
|
102
|
+
|
|
103
|
+
Schema version 1:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"schemaVersion": 1,
|
|
108
|
+
"tasks": [
|
|
109
|
+
{
|
|
110
|
+
"taskKey": "demo:group:DEV-1",
|
|
111
|
+
"taskId": "DEV-1",
|
|
112
|
+
"state": "ready",
|
|
113
|
+
"sourcePlanPath": "/absolute/path/final-report-implementation-planning-001.md",
|
|
114
|
+
"selectedStages": [2, 3],
|
|
115
|
+
"doneStages": [1],
|
|
116
|
+
"stages": [
|
|
117
|
+
{
|
|
118
|
+
"stageNumber": 1,
|
|
119
|
+
"title": "Prepare port",
|
|
120
|
+
"dependsOn": [],
|
|
121
|
+
"stepCount": 2
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"stageNumber": 2,
|
|
125
|
+
"title": "Build adapter",
|
|
126
|
+
"dependsOn": [1],
|
|
127
|
+
"stepCount": 3
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"stageNumber": 3,
|
|
131
|
+
"title": "Wire consumer",
|
|
132
|
+
"dependsOn": [2],
|
|
133
|
+
"stepCount": 2
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
```
|
|
116
140
|
|
|
117
|
-
|
|
141
|
+
Include every candidate task. A `missing` task has an empty `sourcePlanPath`, `selectedStages`, `doneStages`, and `stages`. Convert the CLI stage-row keys to the camel-case selection boundary exactly as shown.
|
|
118
142
|
|
|
119
|
-
##
|
|
143
|
+
## Phase classification
|
|
120
144
|
|
|
121
|
-
|
|
145
|
+
Only these canonical categories are valid:
|
|
122
146
|
|
|
123
|
-
| workCategory | phase |
|
|
147
|
+
| workCategory | Default phase |
|
|
124
148
|
|---|---|
|
|
125
|
-
| `bugfix` | Phase 1
|
|
149
|
+
| `bugfix` | Phase 1 for High or Med-High risk; otherwise Phase 2 |
|
|
126
150
|
| `feature` | Phase 2 |
|
|
127
151
|
| `improvement` | Phase 2 |
|
|
128
152
|
| `refactor` | Phase 3 |
|
|
129
153
|
| `ops` | Phase 3 |
|
|
130
|
-
| `docs` / `doc` | Phase 2 |
|
|
131
|
-
| `unknown` or undefined | Phase 2, add a rationale note |
|
|
132
154
|
|
|
133
|
-
|
|
155
|
+
Priority overrides category: P0 maps to Phase 1, P1/P2 to Phase 2, and P3 to Phase 3. An unknown or missing raw category falls back to Phase 2 with a one-line rationale naming the raw value. Do not invent another category.
|
|
134
156
|
|
|
135
|
-
|
|
136
|
-
- `P1`, `P2`: Phase 2
|
|
137
|
-
- `P3`: Phase 3
|
|
157
|
+
## Template contract
|
|
138
158
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
## section contract
|
|
142
|
-
|
|
143
|
-
Follow the template's heading order and spelling. `validate-schedule.py` checks section order, title suffix, metadata, field labels, enum, Gantt axis, and more.
|
|
144
|
-
|
|
145
|
-
top-level contract sections:
|
|
159
|
+
Follow `schedule.template.md` exactly. The required top-level order is:
|
|
146
160
|
|
|
147
161
|
1. `## At a Glance`
|
|
148
|
-
2. `## Executive Summary`
|
|
162
|
+
2. `## Executive Summary`
|
|
149
163
|
3. `## Task Dependency Graph`
|
|
150
|
-
4. `##
|
|
151
|
-
5. `## Phase
|
|
152
|
-
6. `## Phase
|
|
153
|
-
7. `##
|
|
154
|
-
8. `##
|
|
155
|
-
9. `##
|
|
156
|
-
10. `##
|
|
157
|
-
|
|
158
|
-
optional
|
|
159
|
-
|
|
160
|
-
- `## Gantt Chart`: between `Task Dependency Graph` and `Phase 1`.
|
|
161
|
-
- `## Glossary`: last section. Use only when opaque codes remain in the body.
|
|
162
|
-
|
|
163
|
-
Keep the heading even when a phase has no task, and write `_none_`.
|
|
164
|
-
|
|
165
|
-
## top header
|
|
166
|
-
|
|
167
|
-
Shape:
|
|
168
|
-
|
|
169
|
-
```markdown
|
|
170
|
-
# <Title> — Work Schedule
|
|
171
|
-
|
|
172
|
-
> Generated: <YYYY-MM-DD HH:MM> | Project: <project-id> | Task Group: <task-group>
|
|
173
|
-
> Source: okstra <mode> (<N> tasks included, <M> done excluded)
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
The validator checks the title suffix `— Work Schedule`. For `<project-id>`, prefer `task-catalog.json`'s top-level `projectId`, and if absent use the first matched manifest's `projectId`. Do not invent a value.
|
|
177
|
-
|
|
178
|
-
## At a Glance
|
|
179
|
-
|
|
180
|
-
This totals line must be present exactly.
|
|
181
|
-
|
|
182
|
-
```markdown
|
|
183
|
-
**<N> tasks total / estimated effort: <X.X> ~ <Y.Y> days (Effort sum)**
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
The Effort-to-day mapping's SSOT is the template's `### Effort Sizing Criteria` table. Build the total day range by summing the lower/upper bounds.
|
|
187
|
-
|
|
188
|
-
enum values:
|
|
189
|
-
|
|
190
|
-
| Field | value |
|
|
191
|
-
|---|---|
|
|
192
|
-
| Effort | `S`, `M`, `L`, `XL`, `XXL` |
|
|
193
|
-
| Priority | `P0`, `P1`, `P2`, `P3` |
|
|
194
|
-
| Risk | `Very Low`, `Low`, `Medium`, `Med-High`, `High` |
|
|
195
|
-
| Phase | `1`, `2`, `3` |
|
|
196
|
-
|
|
197
|
-
`Med-High` is canonical.
|
|
198
|
-
|
|
199
|
-
## per-task block
|
|
164
|
+
4. optional `## Gantt Chart`
|
|
165
|
+
5. `## Phase 1: Critical Fixes`
|
|
166
|
+
6. `## Phase 2: Enhancements`
|
|
167
|
+
7. `## Phase 3: Architecture`
|
|
168
|
+
8. `## Execution Priority Matrix`
|
|
169
|
+
9. `## Cross-Task Dependencies & Shared Concerns`
|
|
170
|
+
10. `## Risk Mitigation Strategy`
|
|
171
|
+
11. `## Recommended Immediate Actions`
|
|
172
|
+
12. optional final `## Glossary`
|
|
200
173
|
|
|
201
|
-
|
|
174
|
+
Keep an empty required section and render `_none_`. Headings and field labels stay as English template literals; body prose is Korean.
|
|
202
175
|
|
|
203
|
-
|
|
204
|
-
2. `**Priority**`
|
|
205
|
-
3. `**Effort**`
|
|
206
|
-
4. `**Status**`
|
|
207
|
-
5. `**Risk**`
|
|
208
|
-
6. `**Scope**`
|
|
209
|
-
7. `**Repo**`
|
|
210
|
-
|
|
211
|
-
Then the subsection order:
|
|
212
|
-
|
|
213
|
-
1. `**Problem**:`
|
|
214
|
-
2. `**Solution**:`
|
|
215
|
-
3. `**Work Breakdown**:` — followed by a `| Step | File | Action | Detail |` table
|
|
216
|
-
4. `**Verification Commands**:` — followed by a ` ```bash ` fenced block
|
|
217
|
-
5. `**Rollback**:`
|
|
218
|
-
|
|
219
|
-
For a `[NEEDS-OKSTRA-RUN]` or `[PARSE-ERROR: <section>]` task, fill only the available fields but place the marker right below the task heading.
|
|
220
|
-
|
|
221
|
-
## Task Dependency Graph
|
|
222
|
-
|
|
223
|
-
When there is no dependency, literal:
|
|
176
|
+
Each scheduled task uses this stage-level Work Breakdown shape:
|
|
224
177
|
|
|
225
178
|
```markdown
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
````
|
|
179
|
+
| Stage | Title | Steps | Depends On | Days |
|
|
180
|
+
|---:|---|---:|---|---:|
|
|
181
|
+
| 2 | Build adapter | 3 | 1 (done) | 2.0 ~ 3.0 |
|
|
182
|
+
| 3 | Wire consumer | 2 | 2 | 1.0 ~ 2.0 |
|
|
232
183
|
```
|
|
233
|
-
DEV-1 -> DEV-2, DEV-3
|
|
234
|
-
DEV-2 -> DEV-4
|
|
235
|
-
```
|
|
236
|
-
````
|
|
237
|
-
|
|
238
|
-
Use only the ASCII arrow `->`.
|
|
239
|
-
|
|
240
|
-
## Gantt Chart
|
|
241
|
-
|
|
242
|
-
**directive override (highest priority):** before applying the heuristic below, first check the directive source's `## Directive` section. Resolution order (first hit): (1) the `--directive-file <abs-path>` argument, (2) `<PROJECT_ROOT>/.okstra/tasks/<task-group-segment>/schedule/instruction-set/analysis-material.md`, (3) if none, the heuristic as-is (normal path — no warning·stop). When the directive instructs Gantt render/skip, it overrides the heuristic·skip rules, and leave one line in that section: `> _Per Directive directive: <verbatim short excerpt>._`. When the directive supplies day allocation·phase weight·sub-task decomposition, reflect it verbatim in bar length.
|
|
243
184
|
|
|
244
|
-
|
|
185
|
+
Use the template's Effort Sizing Criteria values without redefining them. Allocate a task's range across selected stages in `stepCount` proportion: round every stage except the last to 0.5 day and let the last absorb the remainder. The stage ranges must sum to the task range, and finite task ranges must sum to the displayed total. XXL, missing, and complete tasks contribute no forward total.
|
|
245
186
|
|
|
246
|
-
|
|
187
|
+
An unrepresentable half-day allocation is a validation error. Do not substitute a fallback allocation algorithm; revise the task sizing or selected-stage scope.
|
|
247
188
|
|
|
248
|
-
|
|
249
|
-
- Even 1 task has an effort range.
|
|
250
|
-
- The source report has Part/Phase/Step decomposition.
|
|
251
|
-
- Total effort is 3 days or more.
|
|
252
|
-
|
|
253
|
-
Skip only when every task is XXL with no decomposition, or when all tasks lack both effort and decomposition. On skip, place the following blockquote at the `Gantt Chart` position.
|
|
254
|
-
|
|
255
|
-
```markdown
|
|
256
|
-
> _Gantt Chart skipped: <concrete reason referencing the actual data>._
|
|
257
|
-
```
|
|
189
|
+
## Gantt contract
|
|
258
190
|
|
|
259
|
-
|
|
191
|
+
Render a plain fenced relative-day Gantt when the selected stages have finite day ranges. Every forward row is identified by stage and repeats its Work Breakdown range:
|
|
260
192
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
| | | | |
|
|
265
|
-
Phase 1
|
|
266
|
-
DEV-1 (M) ██████ ! crit
|
|
267
|
-
Phase 2
|
|
268
|
-
DEV-2 (L) ██████░░ est
|
|
193
|
+
```text
|
|
194
|
+
DEV-1/S2 ████ days=2.0~3.0
|
|
195
|
+
DEV-1/S3 ████ days=1.0~2.0
|
|
269
196
|
```
|
|
270
|
-
````
|
|
271
197
|
|
|
272
|
-
The
|
|
198
|
+
The identifier format is `<TASK-ID>/S<stage-number>` and the annotation is `days=<lower>~<upper>`. Do not emit a row for a completed, unselected, missing, or unknown stage. Bar length is illustrative; `days=` is the validated duration contract.
|
|
273
199
|
|
|
274
|
-
|
|
200
|
+
Skip the chart only when no forward task has a finite day signal, and state the concrete reason. Do not use calendar dates, Mermaid, PlantUML, Graphviz, or another graph language.
|
|
275
201
|
|
|
276
|
-
|
|
202
|
+
A host-supplied directive or the first `## Directive` in the configured analysis material may override the render/skip heuristic, but it cannot override stage selection, dependency closure, or validated day arithmetic.
|
|
277
203
|
|
|
278
|
-
|
|
204
|
+
## Client-facing boundary
|
|
279
205
|
|
|
280
|
-
|
|
206
|
+
Assume the team has the required authority. Exclude approval waits, permission checks, stakeholder coordination, decision checklists, and internal blocker codes from forward engineering work. Gantt duration and totals represent engineering work only.
|
|
281
207
|
|
|
282
|
-
|
|
208
|
+
Resolve opaque source-report codes inline or in the optional final Glossary. Decision-item codes do not belong in the schedule.
|
|
283
209
|
|
|
284
|
-
## validation
|
|
210
|
+
## Two validation gates
|
|
285
211
|
|
|
286
|
-
|
|
212
|
+
Run both gates against the same draft and temporary selection contract.
|
|
287
213
|
|
|
288
|
-
|
|
289
|
-
python3 ~/.okstra/lib/validators/validate-schedule.py <output-path>
|
|
290
|
-
```
|
|
214
|
+
1. Deterministic gate:
|
|
291
215
|
|
|
292
|
-
|
|
216
|
+
```bash
|
|
217
|
+
python3 ~/.okstra/lib/validators/validate-schedule.py <draft> --selection-json <selection>
|
|
218
|
+
```
|
|
293
219
|
|
|
294
|
-
|
|
295
|
-
python3 validators/validate-schedule.py <output-path>
|
|
296
|
-
```
|
|
220
|
+
2. Only after that command passes, dispatch an independent LLM verifier with the draft and selection JSON, but not the lead's reasoning. It checks narrative coherence, phase rationale, executable order, engineering-only scope, and contradictions with the structured rows.
|
|
297
221
|
|
|
298
|
-
|
|
222
|
+
If either gate finds a defect, revise the same draft in place and restart from the deterministic gate. Allow at most two revision rounds across both gates. Never publish a draft that has not passed both gates in that order.
|
|
299
223
|
|
|
300
|
-
|
|
224
|
+
After both gates pass:
|
|
301
225
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
- Estimated effort: X.X ~ Y.Y days (Effort sum)
|
|
307
|
-
- Mode: lightweight
|
|
308
|
-
```
|
|
226
|
+
1. Move the same draft content to the collision-safe final path; do not re-render it.
|
|
227
|
+
2. Re-read it and run the installed format validator on the final path, falling back to the repository validator only when needed.
|
|
228
|
+
3. Delete the temporary selection file only after final validation passes.
|
|
229
|
+
4. Report completion in Korean with the output path, included/excluded counts, finite total range, and lead-plus-verifier mode.
|
|
309
230
|
|
|
310
231
|
## Forbidden patterns
|
|
311
232
|
|
|
312
|
-
-
|
|
313
|
-
-
|
|
314
|
-
-
|
|
315
|
-
-
|
|
316
|
-
-
|
|
317
|
-
-
|
|
318
|
-
-
|
|
319
|
-
-
|
|
320
|
-
- Overwriting an existing file on a timestamp collision. Append a `-2`, `-3` suffix.
|
|
233
|
+
- Guessing a planning report after `stage-map` reports a structured error.
|
|
234
|
+
- Treating `workStatus` as the detailed schedule status.
|
|
235
|
+
- Scheduling completed or non-selected stages.
|
|
236
|
+
- Publishing a task-level Gantt row without the `/S<stage-number>` suffix and `days=` range.
|
|
237
|
+
- Dispatching narrative validation before deterministic `--selection-json` validation.
|
|
238
|
+
- Re-rendering after validation instead of promoting the same draft.
|
|
239
|
+
- Deleting the selection contract before final validation.
|
|
240
|
+
- Publishing after more than two unsuccessful revision rounds.
|
|
@@ -243,7 +243,7 @@ Important modules:
|
|
|
243
243
|
| `build_tools.py` | allowlist SSOT for deciding whether a plan's command cell invokes the project build toolchain (`npm`/`pytest`/`cargo`/`gradle`/… behind transparent leaders like `sudo`/`env`). The planning worktree has no dependencies installed, so `validators/validate-run.py` uses this to warn (advisory) when a toolchain stage declares no install precondition. Intentionally an allowlist, not a denylist, so unknown tokens go undetected rather than firing on `grep`/`sed` in every plan |
|
|
244
244
|
| `stage_citations.py` | shared grammar SSOT for reading the Stage Map stage numbers a prose cell cites (`Stages 1, 2, and 3`, ranges, etc.). One definition serves two readers that must not drift — the coverage check in `validators/validate-run.py` proving every stage traces to a requirement, and `incremental_scope.py`'s back-trace resolving which stages an answered clarification touches |
|
|
245
245
|
| `self_mock_signals.py` | self-mock signal SSOT — language-keyed regexes (`SIGNALS`), the `EXT_TO_LANG` extension map, and the waiver-matching mechanics both gates share — `selfmock_path_key` (the one path-normalization), `waiver_entry_key` (the `(file, line, <discriminator>)` triple, with the hand-typed line coerced to `int`) and `partition_waived_entries` (the split into still-failing vs waived). Gate A passes the discriminator `signal`, gate B `mutant`; one definition means the two cannot disagree about whether a waiver matches a finding. The signals are each ported from a `prompts/coding-preflight/languages/<lang>.md` "Self-mock signals to refuse" bullet with the source `doc_keyword` retained so a drift guard fails when doc and module diverge. Patterns stay deliberately narrow (only the "stub the subject's own method, then assert the stub" shape and reaching into the subject's privates; subject identity is never inferred beyond the literal `sut` token). The static detector `validators/detect_self_mock.py`, the drift guard and `mutation_probe.py` MUST import from here; four documented shapes needing subject identity no regex has are left to the mutation gate (`mutation_probe.py`) |
|
|
246
|
-
| `mutation_probe.py` | gate B of the self-mock gate — the tool-agnostic mutation probe. `ADAPTERS` maps an `EXT_TO_LANG` language key to an adapter (`ts_js` → Stryker, `rust` → cargo-mutants, `java`/`kotlin` → PIT, which reports `unsupported` because its SCM scoping is a Maven-only goal and the report↔path mapping is unverified). `run_probe` owns everything that must not differ between tools: production-source selection, the refusal to run on an empty target set, the requirement that the diff name EVERY changed source, the adapter result-shape check and the user-acknowledged waiver application; adapters only parse. `evaluate` counts a mutant only when it covers a line the diff added or modified, and records the pre-cap `survivedTotal` so a trimmed report cannot be fully waived to PASS. Anything that stops a real inspection — no adapter, tool not installed, unreadable report, unknown outcome word, no conclusive trial, a diff that misses a changed source — answers `unsupported(<reason>)`, never `PASS`. `classify_reason` is the 3-class SSOT (capability-gap / nothing-to-verify / integrity-inspection, unknown → integrity) read by BOTH the cross-language merge here and the blocking decision in `validators/validate-run.py` |
|
|
246
|
+
| `mutation_probe.py` | gate B of the self-mock gate — the tool-agnostic mutation probe. `ADAPTERS` maps an `EXT_TO_LANG` language key to an adapter (`ts_js` → Stryker, `python` → Cosmic Ray, `rust` → cargo-mutants, `java`/`kotlin` → PIT, which reports `unsupported` because its SCM scoping is a Maven-only goal and the report↔path mapping is unverified). The Python adapter activates only when the worktree has both an installed `cosmic-ray` executable and `cosmic-ray.toml`; Okstra never installs it. It verifies that `module-path` covers every changed Python target and that no target is excluded, snapshots the configured Python source bytes and modes, restores them after every run, and filters the JSONL dump to diff-added lines. A completed session with no applicable operator on a changed line is `nothing-to-verify`; malformed configuration, command/report failures, incomplete trials, and failed source restoration are blocking `integrity-inspection` results. `run_probe` owns everything that must not differ between tools: production-source selection, the refusal to run on an empty target set, the requirement that the diff name EVERY changed source, the adapter result-shape check and the user-acknowledged waiver application; adapters only parse. `evaluate` counts a mutant only when it covers a line the diff added or modified, and records the pre-cap `survivedTotal` so a trimmed report cannot be fully waived to PASS. Anything that stops a real inspection — no adapter, tool not installed, unreadable report, unknown outcome word, no conclusive trial, a diff that misses a changed source — answers `unsupported(<reason>)`, never `PASS`. `classify_reason` is the 3-class SSOT (capability-gap / nothing-to-verify / integrity-inspection, unknown → integrity) read by BOTH the cross-language merge here and the blocking decision in `validators/validate-run.py` |
|
|
247
247
|
| `run_context.py` | Per-task mutex, run context and run-input persistence; `consumers_mutex` helper for atomic `consumers.jsonl` writes |
|
|
248
248
|
| `path_hints.py` | Compact path-hint persistence + legacy context hydration — stores `run-context` / `active-run-context` in the schemaVersion `2.0` `identity` + `pathHints` compact schema, and hydrates the legacy flat path keys (`RUN_MANIFEST_RELATIVE_PATH`, `TEAM_STATE_PATH`, etc.) in memory the moment the host-side reader reads them |
|
|
249
249
|
| `consumers.py` | Append-only `consumers.jsonl` writer + reader — records which `implementation` runs consumed which `implementation-planning` stage |
|
|
@@ -386,7 +386,7 @@ Optional (v1.0 backward-compatible) top-level keys:
|
|
|
386
386
|
| `validate-schedule.py` | Schedule section/order/code validation |
|
|
387
387
|
| `validate-implementation-plan-stages.py` | enforces the Stage Map structure — checks the S1–S8 rules (`## 5.5 Stage Map` + `## 5.5.<i> Stage <i>` sections, ≤ 8 steps per stage, etc.) |
|
|
388
388
|
| `validate_improvement_report.py` | enforces the 11-item contract of the improvement-discovery final-report. Automatically invoked by `validate-run.py` when `task_type == "improvement-discovery"` |
|
|
389
|
-
| `detect_self_mock.py` | self-mock detector — runs BOTH gates and writes the run's sidecar. Gate A (static) scans the changed TEST files for SUT-stub signals (patterns imported from the SSOT `scripts/okstra_ctl/self_mock_signals.py`, never redefined here), matching each file as one whole-file string so multi-line signals are caught. Writes a `qa/self-mock[-stage-<N>].json` sidecar and prints `QA-RESULT: PASS|FAIL` as its last line (exit 0 = no hits, exit 1 = at least one hit). The sidecar records `scannedFiles`/`skippedFiles` so the gate can prove every changed test file was actually scanned (a run that skips them cannot pass on empty input). An optional `--waivers <path>` moves hits matching `(file,line,signal)` from `staticDetect.hits` to `staticDetect.waived` (each carrying the user's `reason`/`acknowledgedBy`) and records the file as `waiverSource`. Gate B (mutation) runs in the same call: `--changed-file` takes the stage's WHOLE changed set (each adapter selects its own production sources out of it), `--diff` and `--worktree` scope it, and `scripts/okstra_ctl/mutation_probe.py` writes the result into the sidecar's `mutation` block; the received set is recorded as `changedFiles` so the gate can prove gate B was not handed an empty input. `overall` and the exit code follow BOTH gates — a mutation FAIL with a clean static scan still exits 1. The same `--waivers` file feeds both (gate A reads its `signal` entries, gate B its `mutant` ones). Its verdict feeds the fail-closed `_validate_selfmock` gate in `validate-run.py` (implementation / final-verification): a diff that touches test files with no readable PASS sidecar blocks the run; a `waived` entry missing `reason`/`acknowledgedBy`, or a `waiverSource` that is not the task's own `qa/self-mock-waivers.json`, also blocks |
|
|
389
|
+
| `detect_self_mock.py` | self-mock detector — runs BOTH gates and writes the run's sidecar. Gate A (static) scans the changed TEST files for SUT-stub signals (patterns imported from the SSOT `scripts/okstra_ctl/self_mock_signals.py`, never redefined here), matching each file as one whole-file string so multi-line signals are caught. Python strings and comments are token-masked without changing line positions before those regexes run, so examples in docstrings and comments do not become findings while executable `patch.object(self, ...)` and `sut._private` accesses remain detectable. Writes a `qa/self-mock[-stage-<N>].json` sidecar and prints `QA-RESULT: PASS|FAIL` as its last line (exit 0 = no hits, exit 1 = at least one hit). The sidecar records `scannedFiles`/`skippedFiles` so the gate can prove every changed test file was actually scanned (a run that skips them cannot pass on empty input). An optional `--waivers <path>` moves hits matching `(file,line,signal)` from `staticDetect.hits` to `staticDetect.waived` (each carrying the user's `reason`/`acknowledgedBy`) and records the file as `waiverSource`. Gate B (mutation) runs in the same call: `--changed-file` takes the stage's WHOLE changed set (each adapter selects its own production sources out of it), `--diff` and `--worktree` scope it, and `scripts/okstra_ctl/mutation_probe.py` writes the result into the sidecar's `mutation` block; the received set is recorded as `changedFiles` so the gate can prove gate B was not handed an empty input. `overall` and the exit code follow BOTH gates — a mutation FAIL with a clean static scan still exits 1. The same `--waivers` file feeds both (gate A reads its `signal` entries, gate B its `mutant` ones). Its verdict feeds the fail-closed `_validate_selfmock` gate in `validate-run.py` (implementation / final-verification): a diff that touches test files with no readable PASS sidecar blocks the run; a `waived` entry missing `reason`/`acknowledgedBy`, or a `waiverSource` that is not the task's own `qa/self-mock-waivers.json`, also blocks |
|
|
390
390
|
| `validate-workflow.sh` | End-to-end fixture workflow validation |
|
|
391
391
|
| `lib/*.sh` | Shared shell validator helpers and fixtures |
|
|
392
392
|
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -245,11 +245,15 @@ fi
|
|
|
245
245
|
# non-interactive dispatch from blocking on tool-permission prompts (the worker
|
|
246
246
|
# is sandboxed to the supplied `--add-dir` workspace).
|
|
247
247
|
#
|
|
248
|
-
# stdout:
|
|
249
|
-
#
|
|
250
|
-
#
|
|
251
|
-
#
|
|
252
|
-
#
|
|
248
|
+
# stdout: agy runs in `stream-json` so the live log records the tool calls it
|
|
249
|
+
# made — in the default `text` format agy prints only its closing
|
|
250
|
+
# summary, leaving ~2KB of self-report where codex leaves ~487KB of
|
|
251
|
+
# trace, and no way to check from outside whether the worker opened the
|
|
252
|
+
# evidence it was asked to verify. `okstra-wrapper-agy-stream.py`
|
|
253
|
+
# appends every event to the log and forwards ONLY the final response
|
|
254
|
+
# text to the wrapper's own stdout, so what the subagent's `BashOutput`
|
|
255
|
+
# captures for Phase 5 synthesis is unchanged by the format switch.
|
|
256
|
+
# agy stays a single addressable PID we can SIGTERM from the watchdog.
|
|
253
257
|
# stderr: appended to the live log only — keeps the wrapper's stderr clean.
|
|
254
258
|
# exit: agy's own exit code is preserved by `wait`.
|
|
255
259
|
# stdout mirror via a named FIFO instead of a `> >(tee …)` process substitution:
|
|
@@ -261,7 +265,7 @@ fi
|
|
|
261
265
|
stdout_fifo="${log_path}.stdout.fifo"
|
|
262
266
|
rm -f "$stdout_fifo"
|
|
263
267
|
mkfifo "$stdout_fifo"
|
|
264
|
-
|
|
268
|
+
python3 "$script_dir/okstra-wrapper-agy-stream.py" "$log_path" < "$stdout_fifo" &
|
|
265
269
|
stdout_tee_pid=$!
|
|
266
270
|
|
|
267
271
|
# Disable git's fsmonitor for every git command agy runs in this process tree —
|
|
@@ -275,6 +279,7 @@ export "GIT_CONFIG_VALUE_${_gc_idx}=false"
|
|
|
275
279
|
export GIT_CONFIG_COUNT="$(( _gc_idx + 1 ))"
|
|
276
280
|
|
|
277
281
|
agy --print "$(cat "$prompt_path")" --model "$model" "${add_dir_args[@]}" \
|
|
282
|
+
--output-format stream-json \
|
|
278
283
|
--print-timeout "$PRINT_TIMEOUT" --dangerously-skip-permissions \
|
|
279
284
|
2>> "$log_path" \
|
|
280
285
|
> "$stdout_fifo" &
|