@walwal-harness/cli 7.1.23 → 7.1.25
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/CHANGELOG.md +19 -0
- package/README.md +8 -0
- package/apps/harness-dashboard/components/OrgTree.tsx +54 -58
- package/assets/templates/AGENTS.md.template +12 -0
- package/assets/templates/config.json +2 -0
- package/bin/init.js +138 -30
- package/commands/goal.md +5 -0
- package/commands/hot-fix.md +5 -0
- package/commands/submission.md +5 -0
- package/package.json +1 -1
- package/scripts/harness-wake.sh +59 -14
package/CHANGELOG.md
CHANGED
|
@@ -31,6 +31,25 @@ docmeta:
|
|
|
31
31
|
|
|
32
32
|
## Unreleased
|
|
33
33
|
|
|
34
|
+
## 7.1.25 — Absolute Claude hook paths + Codex adapter docs (2026-05-26)
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- Claude hooks and statusline now install with project-root absolute script paths, preventing `UserPromptSubmit hook error: scripts/harness-user-prompt-submit.sh: No such file or directory` when Claude runs hooks outside the project root.
|
|
38
|
+
- Existing relative-path harness hooks are migrated to absolute paths on re-init.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- Codex command and AGENTS templates now explain that `.codex/skills/**/SKILL.md` is the Codex runtime protocol and `.codex/agents/` is not required.
|
|
42
|
+
- `migrate` appends a Codex Runtime Adapter block to existing AGENTS.md files when missing.
|
|
43
|
+
|
|
44
|
+
## 7.1.24 — Hourly CEO wake loop + migrate script refresh (2026-05-22)
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
- Hourly wake prompting now explicitly wakes CEO to convene CXX + OPS, collect current progress and decisions, dispatch one NOW action, and continue through CQO/OPS verification without asking Owner for next steps.
|
|
48
|
+
- Hourly wake prompts now include external executive-function scaffolding: body-doubling status checks, context-transition summaries, time-guardian hidden-task detection, and NOW/NEXT/PARKED open-loop triage.
|
|
49
|
+
- Hourly wake now supports `HARNESS_WAKE_MODEL` / `company_mode.hourly_wake_model` for Claude model pinning.
|
|
50
|
+
- `migrate` now refreshes package-owned runtime scripts, including `scripts/harness-wake.sh`, while archiving the previous target scripts directory.
|
|
51
|
+
- Dashboard Org Tree now uses a left-to-right depth layout while keeping zoom, ctrl/meta-wheel zoom, and drag panning.
|
|
52
|
+
|
|
34
53
|
## 7.1.23 — Dashboard CDO preview + radial org tree (2026-05-21)
|
|
35
54
|
|
|
36
55
|
### Added
|
package/README.md
CHANGED
|
@@ -209,6 +209,14 @@ Features:
|
|
|
209
209
|
- **Document tab** — per-CXX markdown doc viewer
|
|
210
210
|
- **Owner Acceptance Gate** — Owner is final acceptance only; CEO/CXX must complete worker-backed verification before asking for Owner review
|
|
211
211
|
|
|
212
|
+
Hourly autonomous wake:
|
|
213
|
+
|
|
214
|
+
- `HARNESS_WAKE_EXECUTOR=claude|codex` selects `claude -p` or `codex exec`.
|
|
215
|
+
- `HARNESS_WAKE_MODEL=<model>` optionally pins the Claude model for wake ticks.
|
|
216
|
+
- Each wake tick prompts CEO to convene CXX + OPS, collect progress and decisions, dispatch the next action without asking Owner, and require CQO/OPS verification until the active goal works.
|
|
217
|
+
- `npx @walwal-harness/cli migrate` refreshes package-owned runtime scripts, so existing projects receive wake prompt updates without a full re-init.
|
|
218
|
+
- In Codex, `.codex/skills/**/SKILL.md` files are the runtime protocol. `.codex/agents/` is not required; Codex should manually read the relevant skill if it is not auto-listed.
|
|
219
|
+
|
|
212
220
|
---
|
|
213
221
|
|
|
214
222
|
## Harness Runtime Paths
|
|
@@ -243,29 +243,23 @@ export function OrgTree({ snapshot, activeNodeId, onNodeClick }: OrgTreeProps) {
|
|
|
243
243
|
dragRef.current = null;
|
|
244
244
|
}, []);
|
|
245
245
|
|
|
246
|
-
const canvas = { width:
|
|
247
|
-
const
|
|
246
|
+
const canvas = { width: 1280, height: 500 };
|
|
247
|
+
const ownerPos = { x: 70, y: 196 };
|
|
248
|
+
const ceoPos = { x: 320, y: 196 };
|
|
249
|
+
const cxxX = 610;
|
|
250
|
+
const workerX = 890;
|
|
248
251
|
|
|
249
252
|
const layout = useMemo(() => {
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
const workerRadius = 150;
|
|
253
|
+
const startY = 50;
|
|
254
|
+
const rowGap = 86;
|
|
253
255
|
|
|
254
256
|
return cxxNodes.map((node, index) => {
|
|
255
|
-
const
|
|
256
|
-
const
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
const outwardY = Math.sin(angle) * workerRadius * 0.48;
|
|
260
|
-
return {
|
|
261
|
-
node,
|
|
262
|
-
x,
|
|
263
|
-
y,
|
|
264
|
-
workerX: x + outwardX,
|
|
265
|
-
workerY: y + outwardY,
|
|
266
|
-
};
|
|
257
|
+
const nodeWorkers = workers.filter((w) => w.owner === node.role);
|
|
258
|
+
const y = startY + index * rowGap;
|
|
259
|
+
const workerHeight = Math.max(58, nodeWorkers.length * 54 + Math.max(0, nodeWorkers.length - 1) * 6);
|
|
260
|
+
return { node, x: cxxX, y, workerX, workerY: y - workerHeight / 2 + 30, nodeWorkers };
|
|
267
261
|
});
|
|
268
|
-
}, [cxxNodes]);
|
|
262
|
+
}, [cxxNodes, workers]);
|
|
269
263
|
|
|
270
264
|
return (
|
|
271
265
|
<div className="select-none">
|
|
@@ -329,48 +323,51 @@ export function OrgTree({ snapshot, activeNodeId, onNodeClick }: OrgTreeProps) {
|
|
|
329
323
|
transform: `scale(${zoom})`,
|
|
330
324
|
}}
|
|
331
325
|
>
|
|
332
|
-
<svg
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
key={`${node.id}-workers`}
|
|
356
|
-
x1={x}
|
|
357
|
-
y1={y}
|
|
358
|
-
x2={workerX}
|
|
359
|
-
y2={workerY}
|
|
326
|
+
<svg className="pointer-events-none absolute inset-0" width={canvas.width} height={canvas.height}>
|
|
327
|
+
<path
|
|
328
|
+
d={`M ${ownerPos.x + 235} ${ownerPos.y + 56} H ${ceoPos.x}`}
|
|
329
|
+
stroke="#374151"
|
|
330
|
+
strokeWidth="1.5"
|
|
331
|
+
fill="none"
|
|
332
|
+
/>
|
|
333
|
+
<path
|
|
334
|
+
d={`M ${ceoPos.x + 235} ${ceoPos.y + 56} H ${cxxX - 54}`}
|
|
335
|
+
stroke="#374151"
|
|
336
|
+
strokeWidth="1.5"
|
|
337
|
+
fill="none"
|
|
338
|
+
/>
|
|
339
|
+
<path
|
|
340
|
+
d={`M ${cxxX - 54} ${layout[0]?.y ?? 80} V ${layout[layout.length - 1]?.y ?? 420}`}
|
|
341
|
+
stroke="#374151"
|
|
342
|
+
strokeWidth="1.5"
|
|
343
|
+
fill="none"
|
|
344
|
+
/>
|
|
345
|
+
{layout.map(({ node, x, y, nodeWorkers }) => (
|
|
346
|
+
<g key={node.id}>
|
|
347
|
+
<path
|
|
348
|
+
d={`M ${x - 54} ${y} H ${x - 10}`}
|
|
360
349
|
stroke="#374151"
|
|
350
|
+
strokeDasharray="4 5"
|
|
361
351
|
strokeWidth="1.5"
|
|
352
|
+
fill="none"
|
|
362
353
|
/>
|
|
363
|
-
|
|
364
|
-
|
|
354
|
+
{nodeWorkers.length > 0 && (
|
|
355
|
+
<path
|
|
356
|
+
d={`M ${x + 166} ${y} H ${workerX - 16}`}
|
|
357
|
+
stroke="#374151"
|
|
358
|
+
strokeWidth="1.5"
|
|
359
|
+
fill="none"
|
|
360
|
+
/>
|
|
361
|
+
)}
|
|
362
|
+
</g>
|
|
363
|
+
))}
|
|
365
364
|
</svg>
|
|
366
365
|
|
|
367
|
-
<div className="absolute left
|
|
368
|
-
<OrgCard
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
onClick={onNodeClick}
|
|
373
|
-
/>
|
|
366
|
+
<div className="absolute" style={{ left: ownerPos.x, top: ownerPos.y }}>
|
|
367
|
+
<OrgCard node={ownerNode} size="lg" active={activeNodeId === "owner"} onClick={onNodeClick} />
|
|
368
|
+
</div>
|
|
369
|
+
|
|
370
|
+
<div className="absolute" style={{ left: ceoPos.x, top: ceoPos.y }}>
|
|
374
371
|
<OrgCard
|
|
375
372
|
node={ceoNode}
|
|
376
373
|
size="lg"
|
|
@@ -379,8 +376,7 @@ export function OrgTree({ snapshot, activeNodeId, onNodeClick }: OrgTreeProps) {
|
|
|
379
376
|
/>
|
|
380
377
|
</div>
|
|
381
378
|
|
|
382
|
-
{layout.map(({ node, x, y, workerX, workerY }) => {
|
|
383
|
-
const nodeWorkers = workers.filter((w) => w.owner === node.role);
|
|
379
|
+
{layout.map(({ node, x, y, workerX, workerY, nodeWorkers }) => {
|
|
384
380
|
const hasActiveWorker = nodeWorkers.some((w) => w.active);
|
|
385
381
|
const displayNode = hasActiveWorker
|
|
386
382
|
? { ...node, status: "typing" as const, activity: "Worker active" }
|
|
@@ -390,7 +386,7 @@ export function OrgTree({ snapshot, activeNodeId, onNodeClick }: OrgTreeProps) {
|
|
|
390
386
|
<div
|
|
391
387
|
key={node.id}
|
|
392
388
|
className="absolute"
|
|
393
|
-
style={{ left: x
|
|
389
|
+
style={{ left: x, top: y - 38 }}
|
|
394
390
|
>
|
|
395
391
|
<OrgCard
|
|
396
392
|
node={displayNode}
|
|
@@ -19,6 +19,18 @@ Internal routing uses agent/skill wiring only.
|
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
+
## 1.1 Codex Runtime Adapter
|
|
23
|
+
|
|
24
|
+
Codex does not need `.codex/agents/` to run this harness. The installed Codex contract is:
|
|
25
|
+
|
|
26
|
+
- Owner entrypoints are `.codex/commands/goal.md`, `.codex/commands/submission.md`, and `.codex/commands/hot-fix.md`.
|
|
27
|
+
- Harness role protocols live in `.codex/skills/harness-{ceo,coo,cdo,cto,cqo,ops,hiring,resource-manager,brick-office}/SKILL.md`.
|
|
28
|
+
- If Codex does not auto-list these as available skills, manually read the relevant `SKILL.md` file and follow it. Do not report that the harness is unavailable just because `.codex/agents/` is absent.
|
|
29
|
+
- When a command asks for a "fresh session context", Codex must create a role-scoped context by reading only the role skill, the active mission files, and required conventions/gotchas before writing that role's `{cxx}.md`. If Codex sub-agent tooling is explicitly available and requested, it may be used; otherwise execute the role protocol sequentially with clear role boundaries.
|
|
30
|
+
- Codex must not treat `.codex/skills` as passive documentation. These files are the installed runtime protocol for Codex.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
22
34
|
## 2. Think Before Acting
|
|
23
35
|
|
|
24
36
|
Don't assume. Don't hide confusion.
|
|
@@ -186,6 +186,8 @@
|
|
|
186
186
|
"worker_spawn_description": "claude 이면 idle worker 배정 시 `claude -p` 백그라운드 프로세스를 띄운다. record 로 두면 prompt/log만 생성한다.",
|
|
187
187
|
"hourly_wake_executor": "claude",
|
|
188
188
|
"hourly_wake_executor_description": "claude 이면 `claude -p`, codex 이면 `codex exec` 로 매시간 autonomous tick 을 실행한다.",
|
|
189
|
+
"hourly_wake_model": "",
|
|
190
|
+
"hourly_wake_model_description": "Claude executor 에서 특정 모델을 강제하려면 HARNESS_WAKE_MODEL 또는 이 값을 사용한다. 비워두면 CLI 기본 모델을 사용한다.",
|
|
189
191
|
"hourly_wake_mode": "headless",
|
|
190
192
|
"hourly_wake_mode_description": "headless 는 백그라운드 실행, tmux 는 매시간 별도 tmux 세션, record 는 prompt/log만 생성한다.",
|
|
191
193
|
"autonomous": true,
|
package/bin/init.js
CHANGED
|
@@ -99,6 +99,19 @@ function copyDir(src, dest) {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
function chmodShellScripts(dir) {
|
|
103
|
+
if (!fs.existsSync(dir)) return;
|
|
104
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
105
|
+
for (const entry of entries) {
|
|
106
|
+
const full = path.join(dir, entry.name);
|
|
107
|
+
if (entry.isDirectory()) {
|
|
108
|
+
chmodShellScripts(full);
|
|
109
|
+
} else if (entry.name.endsWith('.sh')) {
|
|
110
|
+
try { fs.chmodSync(full, '755'); } catch (e) {}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
102
115
|
function fileExists(p) {
|
|
103
116
|
return fs.existsSync(p);
|
|
104
117
|
}
|
|
@@ -155,6 +168,33 @@ function mergeClaudePermissionAllow(settings, entries) {
|
|
|
155
168
|
return changed;
|
|
156
169
|
}
|
|
157
170
|
|
|
171
|
+
function shellQuote(value) {
|
|
172
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function harnessScriptCommand(scriptName) {
|
|
176
|
+
return `bash ${shellQuote(path.join(PROJECT_ROOT, 'scripts', scriptName))}`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function replaceHarnessHookCommand(entries, marker, command) {
|
|
180
|
+
let changed = false;
|
|
181
|
+
for (const entry of entries) {
|
|
182
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
183
|
+
if (Array.isArray(entry.hooks)) {
|
|
184
|
+
for (const hook of entry.hooks) {
|
|
185
|
+
if (hook && hook.command && hook.command.includes(marker) && hook.command !== command) {
|
|
186
|
+
hook.command = command;
|
|
187
|
+
changed = true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} else if (entry.type === 'command' && entry.command && entry.command.includes(marker) && entry.command !== command) {
|
|
191
|
+
entry.command = command;
|
|
192
|
+
changed = true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return changed;
|
|
196
|
+
}
|
|
197
|
+
|
|
158
198
|
const V7_REMOVED_CONVENTION_FILES = [
|
|
159
199
|
'conductor.md',
|
|
160
200
|
'coo-developer.md',
|
|
@@ -728,25 +768,29 @@ function installScripts() {
|
|
|
728
768
|
|
|
729
769
|
if (fs.existsSync(scriptsSrc)) {
|
|
730
770
|
copyDir(scriptsSrc, scriptsDest);
|
|
731
|
-
|
|
732
|
-
// chmod +x for all .sh files (recursive)
|
|
733
|
-
function chmodRecursive(dir) {
|
|
734
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
735
|
-
for (const entry of entries) {
|
|
736
|
-
const full = path.join(dir, entry.name);
|
|
737
|
-
if (entry.isDirectory()) {
|
|
738
|
-
chmodRecursive(full);
|
|
739
|
-
} else if (entry.name.endsWith('.sh')) {
|
|
740
|
-
try { fs.chmodSync(full, '755'); } catch (e) {}
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
chmodRecursive(scriptsDest);
|
|
771
|
+
chmodShellScripts(scriptsDest);
|
|
745
772
|
}
|
|
746
773
|
|
|
747
774
|
log('Scripts installation complete');
|
|
748
775
|
}
|
|
749
776
|
|
|
777
|
+
function refreshScriptsForMigrate({ dryRun = false, backupDir = null } = {}) {
|
|
778
|
+
const scriptsSrc = path.join(PKG_ROOT, 'scripts');
|
|
779
|
+
const scriptsDest = path.join(PROJECT_ROOT, 'scripts');
|
|
780
|
+
if (!fs.existsSync(scriptsSrc)) return;
|
|
781
|
+
|
|
782
|
+
log(' scripts/: package runtime scripts refresh');
|
|
783
|
+
if (dryRun) return;
|
|
784
|
+
|
|
785
|
+
if (backupDir && fs.existsSync(scriptsDest)) {
|
|
786
|
+
const scriptsBackup = path.join(backupDir, 'scripts');
|
|
787
|
+
copyDir(scriptsDest, scriptsBackup);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
copyDir(scriptsSrc, scriptsDest);
|
|
791
|
+
chmodShellScripts(scriptsDest);
|
|
792
|
+
}
|
|
793
|
+
|
|
750
794
|
// ─────────────────────────────────────────
|
|
751
795
|
// 3a. Commands → .claude/commands/
|
|
752
796
|
// ─────────────────────────────────────────
|
|
@@ -803,7 +847,7 @@ function installSessionHook() {
|
|
|
803
847
|
|
|
804
848
|
// Migrate any legacy flat entries (created by older walwal-harness versions)
|
|
805
849
|
// into the correct matcher + hooks-array shape that Claude Code expects.
|
|
806
|
-
const hookCmd = '
|
|
850
|
+
const hookCmd = harnessScriptCommand('harness-session-start.sh');
|
|
807
851
|
let migrated = false;
|
|
808
852
|
settings.hooks.SessionStart = settings.hooks.SessionStart
|
|
809
853
|
.map((entry) => {
|
|
@@ -828,6 +872,8 @@ function installSessionHook() {
|
|
|
828
872
|
)
|
|
829
873
|
);
|
|
830
874
|
|
|
875
|
+
const hookUpdated = replaceHarnessHookCommand(settings.hooks.SessionStart, 'harness-session-start', hookCmd);
|
|
876
|
+
|
|
831
877
|
if (!alreadyInstalled) {
|
|
832
878
|
settings.hooks.SessionStart.push({
|
|
833
879
|
matcher: '',
|
|
@@ -835,9 +881,9 @@ function installSessionHook() {
|
|
|
835
881
|
});
|
|
836
882
|
writeClaudeSettings(settings);
|
|
837
883
|
log('SessionStart hook installed in .claude/settings.json');
|
|
838
|
-
} else if (migrated) {
|
|
884
|
+
} else if (migrated || hookUpdated) {
|
|
839
885
|
writeClaudeSettings(settings);
|
|
840
|
-
log('SessionStart hook migrated
|
|
886
|
+
log('SessionStart hook migrated in .claude/settings.json');
|
|
841
887
|
} else {
|
|
842
888
|
log('SessionStart hook already installed');
|
|
843
889
|
}
|
|
@@ -855,16 +901,9 @@ function installStatusline() {
|
|
|
855
901
|
|
|
856
902
|
let settings = readClaudeSettings();
|
|
857
903
|
|
|
858
|
-
// Check if statusLine is already configured
|
|
859
|
-
if (settings.statusLine && settings.statusLine.command &&
|
|
860
|
-
settings.statusLine.command.includes('harness-statusline')) {
|
|
861
|
-
log('Statusline already installed');
|
|
862
|
-
return;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
904
|
settings.statusLine = {
|
|
866
905
|
type: 'command',
|
|
867
|
-
command: '
|
|
906
|
+
command: harnessScriptCommand('harness-statusline.sh'),
|
|
868
907
|
refreshInterval: 3
|
|
869
908
|
};
|
|
870
909
|
|
|
@@ -887,7 +926,7 @@ function installUserPromptSubmitHook() {
|
|
|
887
926
|
if (!settings.hooks) settings.hooks = {};
|
|
888
927
|
if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
|
|
889
928
|
|
|
890
|
-
const hookCmd = '
|
|
929
|
+
const hookCmd = harnessScriptCommand('harness-user-prompt-submit.sh');
|
|
891
930
|
|
|
892
931
|
// Detect existing harness UserPromptSubmit hook (any shape)
|
|
893
932
|
const alreadyInstalled = settings.hooks.UserPromptSubmit.some((entry) => {
|
|
@@ -903,6 +942,8 @@ function installUserPromptSubmitHook() {
|
|
|
903
942
|
return false;
|
|
904
943
|
});
|
|
905
944
|
|
|
945
|
+
const hookUpdated = replaceHarnessHookCommand(settings.hooks.UserPromptSubmit, 'harness-user-prompt-submit', hookCmd);
|
|
946
|
+
|
|
906
947
|
if (!alreadyInstalled) {
|
|
907
948
|
settings.hooks.UserPromptSubmit.push({
|
|
908
949
|
matcher: '',
|
|
@@ -913,6 +954,9 @@ function installUserPromptSubmitHook() {
|
|
|
913
954
|
log(' → /goal, /submission, and /hot-fix are guided through harness-ceo and CXX workflow');
|
|
914
955
|
log(' → Opt-out per message: say "harness skip" or "without harness"');
|
|
915
956
|
log(' → Disable globally: set .harness/config.json behavior.auto_route_ceo = false');
|
|
957
|
+
} else if (hookUpdated) {
|
|
958
|
+
writeClaudeSettings(settings);
|
|
959
|
+
log('UserPromptSubmit hook migrated in .claude/settings.json');
|
|
916
960
|
} else {
|
|
917
961
|
log('UserPromptSubmit hook already installed');
|
|
918
962
|
}
|
|
@@ -933,7 +977,7 @@ function installStopHook() {
|
|
|
933
977
|
if (!settings.hooks) settings.hooks = {};
|
|
934
978
|
if (!settings.hooks.Stop) settings.hooks.Stop = [];
|
|
935
979
|
|
|
936
|
-
const hookCmd = '
|
|
980
|
+
const hookCmd = harnessScriptCommand('harness-stop.sh');
|
|
937
981
|
|
|
938
982
|
const alreadyInstalled = settings.hooks.Stop.some((entry) => {
|
|
939
983
|
if (!entry || typeof entry !== 'object') return false;
|
|
@@ -948,6 +992,8 @@ function installStopHook() {
|
|
|
948
992
|
return false;
|
|
949
993
|
});
|
|
950
994
|
|
|
995
|
+
const hookUpdated = replaceHarnessHookCommand(settings.hooks.Stop, 'harness-stop', hookCmd);
|
|
996
|
+
|
|
951
997
|
if (!alreadyInstalled) {
|
|
952
998
|
settings.hooks.Stop.push({
|
|
953
999
|
matcher: '',
|
|
@@ -958,6 +1004,9 @@ function installStopHook() {
|
|
|
958
1004
|
log(' → Conductor 가 running 인 동안 turn 종료 시 자동으로 다음 tick 으로 연쇄');
|
|
959
1005
|
log(' → 비활성: .harness/config.json behavior.auto_chain_on_stop = false');
|
|
960
1006
|
log(' → 상한: behavior.auto_chain_max_per_sprint (기본 200)');
|
|
1007
|
+
} else if (hookUpdated) {
|
|
1008
|
+
writeClaudeSettings(settings);
|
|
1009
|
+
log('Stop hook migrated in .claude/settings.json');
|
|
961
1010
|
} else {
|
|
962
1011
|
log('Stop hook already installed');
|
|
963
1012
|
}
|
|
@@ -1205,10 +1254,12 @@ function detectMigrationNeeded() {
|
|
|
1205
1254
|
configMissingCompanyMode: false,
|
|
1206
1255
|
configLegacyRouting: false,
|
|
1207
1256
|
configRuntimeVerificationMissing: false,
|
|
1257
|
+
configWakeModelMissing: false,
|
|
1208
1258
|
coreSkillsStale: false,
|
|
1209
1259
|
hrResourcePoolStale: false,
|
|
1210
1260
|
harnessMdStale: false,
|
|
1211
1261
|
agentsMissingOpsVerificationRules: false,
|
|
1262
|
+
agentsMissingCodexAdapterRules: false,
|
|
1212
1263
|
rosterCodexPathsMissing: false,
|
|
1213
1264
|
resourceIndexCodexWordingMissing: false,
|
|
1214
1265
|
memoryMissingSystemEntries: [],
|
|
@@ -1307,6 +1358,9 @@ function detectMigrationNeeded() {
|
|
|
1307
1358
|
if (!c.runtime?.verification?.watch_during_cqo || !c.runtime?.verification?.require_ops_evidence_for_pass) {
|
|
1308
1359
|
flags.configRuntimeVerificationMissing = true;
|
|
1309
1360
|
}
|
|
1361
|
+
if (c.company_mode && c.company_mode.hourly_wake_model === undefined) {
|
|
1362
|
+
flags.configWakeModelMissing = true;
|
|
1363
|
+
}
|
|
1310
1364
|
if (
|
|
1311
1365
|
c.behavior?.auto_route_dispatcher !== undefined ||
|
|
1312
1366
|
c.behavior?.auto_route_dispatcher_description ||
|
|
@@ -1326,6 +1380,12 @@ function detectMigrationNeeded() {
|
|
|
1326
1380
|
) {
|
|
1327
1381
|
flags.agentsMissingOpsVerificationRules = true;
|
|
1328
1382
|
}
|
|
1383
|
+
if (
|
|
1384
|
+
!agentsBody.includes('Codex Runtime Adapter') &&
|
|
1385
|
+
!agentsBody.includes('.codex/skills/harness-ceo/SKILL.md')
|
|
1386
|
+
) {
|
|
1387
|
+
flags.agentsMissingCodexAdapterRules = true;
|
|
1388
|
+
}
|
|
1329
1389
|
} catch {}
|
|
1330
1390
|
}
|
|
1331
1391
|
if (fs.existsSync(rosterPath)) {
|
|
@@ -1439,6 +1499,10 @@ function showMigrationProposal(flags) {
|
|
|
1439
1499
|
console.log(' • config.json: runtime.verification 섹션 추가 가능');
|
|
1440
1500
|
console.log(' CQO 검수 중 OPS runtime watch 와 PASS 차단 조건을 설정합니다.');
|
|
1441
1501
|
}
|
|
1502
|
+
if (flags.configWakeModelMissing) {
|
|
1503
|
+
console.log(' • config.json: company_mode.hourly_wake_model 추가 가능');
|
|
1504
|
+
console.log(' Claude wake tick 에서 --model 을 환경변수/설정으로 지정할 수 있습니다.');
|
|
1505
|
+
}
|
|
1442
1506
|
if (flags.configLegacyRouting) {
|
|
1443
1507
|
console.log(' • config.json: legacy dispatcher/conductor wording → v7 CEO/CXX wording');
|
|
1444
1508
|
}
|
|
@@ -1499,11 +1563,13 @@ function runMigrate(opts = {}) {
|
|
|
1499
1563
|
// Always refresh owner-facing commands (full replacement regardless of current state).
|
|
1500
1564
|
if (dryRun) {
|
|
1501
1565
|
log(' (dry-run) commands: /goal, /submission, /hot-fix 교체 예정');
|
|
1566
|
+
log(' (dry-run) scripts/: package runtime scripts refresh 예정');
|
|
1502
1567
|
} else {
|
|
1503
1568
|
installCommands();
|
|
1504
1569
|
}
|
|
1505
1570
|
|
|
1506
1571
|
const flags = detectMigrationNeeded();
|
|
1572
|
+
const runtimeScriptsAlwaysRefresh = true;
|
|
1507
1573
|
const gotchaMissingTotal = Object.values(flags.gotchaMissingEntries || {}).reduce((n, a) => n + a.length, 0);
|
|
1508
1574
|
const conventionMissingTotal = Object.values(flags.conventionMissingEntries || {}).reduce((n, a) => n + a.length, 0);
|
|
1509
1575
|
if (
|
|
@@ -1512,6 +1578,7 @@ function runMigrate(opts = {}) {
|
|
|
1512
1578
|
!flags.configMissingCompanyMode &&
|
|
1513
1579
|
!flags.configLegacyRouting &&
|
|
1514
1580
|
!flags.configRuntimeVerificationMissing &&
|
|
1581
|
+
!flags.configWakeModelMissing &&
|
|
1515
1582
|
!flags.coreSkillsStale &&
|
|
1516
1583
|
!flags.hrResourcePoolStale &&
|
|
1517
1584
|
!flags.harnessMdStale &&
|
|
@@ -1521,7 +1588,8 @@ function runMigrate(opts = {}) {
|
|
|
1521
1588
|
(!flags.memoryMissingSystemEntries || flags.memoryMissingSystemEntries.length === 0) &&
|
|
1522
1589
|
gotchaMissingTotal === 0 &&
|
|
1523
1590
|
conventionMissingTotal === 0 &&
|
|
1524
|
-
!flags.bundleVersionStale
|
|
1591
|
+
!flags.bundleVersionStale &&
|
|
1592
|
+
!runtimeScriptsAlwaysRefresh
|
|
1525
1593
|
) {
|
|
1526
1594
|
console.log('');
|
|
1527
1595
|
let pkgVer = 'unknown';
|
|
@@ -1537,6 +1605,10 @@ function runMigrate(opts = {}) {
|
|
|
1537
1605
|
const backupDir = path.join(HARNESS_DIR, 'archive', `migration-${ts}`);
|
|
1538
1606
|
if (!dryRun) ensureDir(backupDir);
|
|
1539
1607
|
|
|
1608
|
+
// Runtime scripts are package-owned. Migrate must refresh them so existing
|
|
1609
|
+
// projects receive wake/meeting/OPS fixes without requiring a full init.
|
|
1610
|
+
refreshScriptsForMigrate({ dryRun, backupDir });
|
|
1611
|
+
|
|
1540
1612
|
// 1. progress.json
|
|
1541
1613
|
const progressPath = path.join(HARNESS_DIR, 'progress.json');
|
|
1542
1614
|
if ((flags.progressV3toV4 || flags.progressLegacyRouting) && fs.existsSync(progressPath)) {
|
|
@@ -1568,10 +1640,11 @@ function runMigrate(opts = {}) {
|
|
|
1568
1640
|
// 2. config.json — inject company_mode/runtime verification from template if missing
|
|
1569
1641
|
const configPath = path.join(HARNESS_DIR, 'config.json');
|
|
1570
1642
|
const tplPath = path.join(PKG_ROOT, 'assets', 'templates', 'config.json');
|
|
1571
|
-
if ((flags.configMissingCompanyMode || flags.configLegacyRouting || flags.configRuntimeVerificationMissing) && fs.existsSync(configPath) && fs.existsSync(tplPath)) {
|
|
1643
|
+
if ((flags.configMissingCompanyMode || flags.configLegacyRouting || flags.configRuntimeVerificationMissing || flags.configWakeModelMissing) && fs.existsSync(configPath) && fs.existsSync(tplPath)) {
|
|
1572
1644
|
const original = fs.readFileSync(configPath, 'utf8');
|
|
1573
1645
|
const c = JSON.parse(original);
|
|
1574
1646
|
const tpl = JSON.parse(fs.readFileSync(tplPath, 'utf8'));
|
|
1647
|
+
let configChanged = false;
|
|
1575
1648
|
if (tpl.company_mode) {
|
|
1576
1649
|
if (flags.configMissingCompanyMode || flags.configLegacyRouting) {
|
|
1577
1650
|
c.company_mode = {
|
|
@@ -1588,14 +1661,23 @@ function runMigrate(opts = {}) {
|
|
|
1588
1661
|
delete c.behavior.auto_route_dispatcher;
|
|
1589
1662
|
delete c.behavior.auto_route_dispatcher_description;
|
|
1590
1663
|
delete c.mode_selection;
|
|
1664
|
+
configChanged = true;
|
|
1591
1665
|
log(' config.json: v7 CEO/CXX routing 섹션 동기화');
|
|
1592
1666
|
}
|
|
1667
|
+
if (flags.configWakeModelMissing) {
|
|
1668
|
+
c.company_mode = c.company_mode || {};
|
|
1669
|
+
c.company_mode.hourly_wake_model = tpl.company_mode.hourly_wake_model ?? '';
|
|
1670
|
+
c.company_mode.hourly_wake_model_description = tpl.company_mode.hourly_wake_model_description;
|
|
1671
|
+
configChanged = true;
|
|
1672
|
+
log(' config.json: hourly_wake_model 섹션 동기화');
|
|
1673
|
+
}
|
|
1593
1674
|
if (flags.configRuntimeVerificationMissing && tpl.runtime?.verification) {
|
|
1594
1675
|
c.runtime = c.runtime || {};
|
|
1595
1676
|
c.runtime.verification = deepMerge(tpl.runtime.verification, c.runtime.verification || {});
|
|
1677
|
+
configChanged = true;
|
|
1596
1678
|
log(' config.json: runtime.verification 섹션 동기화');
|
|
1597
1679
|
}
|
|
1598
|
-
if (!dryRun) {
|
|
1680
|
+
if (!dryRun && configChanged) {
|
|
1599
1681
|
fs.writeFileSync(path.join(backupDir, 'config.json'), original);
|
|
1600
1682
|
fs.writeFileSync(configPath, JSON.stringify(c, null, 2) + '\n');
|
|
1601
1683
|
}
|
|
@@ -1809,6 +1891,32 @@ function runMigrate(opts = {}) {
|
|
|
1809
1891
|
}
|
|
1810
1892
|
}
|
|
1811
1893
|
|
|
1894
|
+
|
|
1895
|
+
if (flags.agentsMissingCodexAdapterRules) {
|
|
1896
|
+
const agentsPath = path.join(PROJECT_ROOT, 'AGENTS.md');
|
|
1897
|
+
if (fs.existsSync(agentsPath)) {
|
|
1898
|
+
const original = fs.readFileSync(agentsPath, 'utf8');
|
|
1899
|
+
const block = [
|
|
1900
|
+
'',
|
|
1901
|
+
'---',
|
|
1902
|
+
'',
|
|
1903
|
+
'## walwal-harness Codex Runtime Adapter',
|
|
1904
|
+
'',
|
|
1905
|
+
'- Codex does not require .codex/agents/; repo-local .codex/skills/**/SKILL.md files are the Codex runtime protocol.',
|
|
1906
|
+
'- For /goal, /submission, and /hot-fix, read .codex/commands/{goal,submission,hot-fix}.md and .codex/skills/harness-ceo/SKILL.md if Codex does not auto-load them.',
|
|
1907
|
+
'- Do not report walwal-harness as unavailable just because skills are not listed in the global Available Skills panel.',
|
|
1908
|
+
'- Emulate fresh CXX context by reading the role skill, active mission files, and required conventions/gotchas before writing that role\'s {cxx}.md.',
|
|
1909
|
+
''
|
|
1910
|
+
].join('\n');
|
|
1911
|
+
log(' AGENTS.md: Codex runtime adapter rules append');
|
|
1912
|
+
if (!dryRun) {
|
|
1913
|
+
fs.writeFileSync(path.join(backupDir, 'AGENTS.codex-adapter.md'), original);
|
|
1914
|
+
const sep = original.endsWith('\n') ? '' : '\n';
|
|
1915
|
+
fs.writeFileSync(agentsPath, original + sep + block);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1812
1920
|
// 7. Bundle version stamp — record which package version was last applied.
|
|
1813
1921
|
if (flags.bundleVersionStale) {
|
|
1814
1922
|
const { current, installed } = flags.bundleVersionStale;
|
package/commands/goal.md
CHANGED
|
@@ -7,6 +7,11 @@ argument-hint: "<goal request>"
|
|
|
7
7
|
|
|
8
8
|
Route the Owner request to the installed `harness-ceo` agent/skill.
|
|
9
9
|
|
|
10
|
+
Codex adapter:
|
|
11
|
+
- If Codex does not auto-load `harness-ceo`, manually read `.codex/skills/harness-ceo/SKILL.md` and follow it.
|
|
12
|
+
- Absence of `.codex/agents/` is not a failure. `.codex/skills/**/SKILL.md` is the Codex runtime protocol.
|
|
13
|
+
- For CXX "fresh session context", use role-scoped context: read the CXX skill, active mission files, and required conventions/gotchas before writing that role's `{cxx}.md`.
|
|
14
|
+
|
|
10
15
|
Required flow:
|
|
11
16
|
1. Create or update a goal under `.harness/documents/goal-{goal_index}-{goal_name}/`.
|
|
12
17
|
2. Record CEO decisions in `.harness/documents/goal-{goal_index}-{goal_name}/ceo.md`.
|
package/commands/hot-fix.md
CHANGED
|
@@ -7,6 +7,11 @@ argument-hint: "<incident or fix request>"
|
|
|
7
7
|
|
|
8
8
|
Route the Owner request to the installed `harness-ceo` agent/skill as an emergency mission under the active goal.
|
|
9
9
|
|
|
10
|
+
Codex adapter:
|
|
11
|
+
- If Codex does not auto-load `harness-ceo`, manually read `.codex/skills/harness-ceo/SKILL.md` and follow it.
|
|
12
|
+
- Absence of `.codex/agents/` is not a failure. `.codex/skills/**/SKILL.md` is the Codex runtime protocol.
|
|
13
|
+
- For CXX "fresh session context", use role-scoped context: read the CXX skill, active mission files, and required conventions/gotchas before writing that role's `{cxx}.md`.
|
|
14
|
+
|
|
10
15
|
Required flow:
|
|
11
16
|
1. Locate the active goal document root under `.harness/documents/{goal_name}/`. If no active goal exists, CEO must create/select one before proceeding.
|
|
12
17
|
2. Create `.harness/documents/{goal_name}/hotfix-{hotfix_index}-{hotfix_name}/ceo.md`.
|
package/commands/submission.md
CHANGED
|
@@ -7,6 +7,11 @@ argument-hint: "<additional requirement>"
|
|
|
7
7
|
|
|
8
8
|
Route the Owner request to the installed `harness-ceo` agent/skill as an additional requirement under the active goal.
|
|
9
9
|
|
|
10
|
+
Codex adapter:
|
|
11
|
+
- If Codex does not auto-load `harness-ceo`, manually read `.codex/skills/harness-ceo/SKILL.md` and follow it.
|
|
12
|
+
- Absence of `.codex/agents/` is not a failure. `.codex/skills/**/SKILL.md` is the Codex runtime protocol.
|
|
13
|
+
- For CXX "fresh session context", use role-scoped context: read the CXX skill, active mission files, and required conventions/gotchas before writing that role's `{cxx}.md`.
|
|
14
|
+
|
|
10
15
|
Required flow:
|
|
11
16
|
1. Locate the active goal document root under `.harness/documents/{goal_name}/`. If no active goal exists, CEO must ask the Owner to create or select one with `/goal` before proceeding.
|
|
12
17
|
2. Create a submission record under `.harness/documents/{goal_name}/submission-{submission_index}-{submission_name}/`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.25",
|
|
4
4
|
"description": "Company-style AI agent harness for Claude and Codex. Installs commands, CXX agents, skills, HR-Resource hiring pool, and project-local .harness runtime state.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"walwal-harness": "bin/init.js"
|
package/scripts/harness-wake.sh
CHANGED
|
@@ -130,10 +130,17 @@ start_headless_tick() {
|
|
|
130
130
|
local idle="$2"
|
|
131
131
|
local force="$3"
|
|
132
132
|
local review_path="$4"
|
|
133
|
-
local mode executor agent_bin stamp wake_dir ops_dir prompt_rel log_rel prompt_path log_path pid_file old_pid pid status tmux_session session_name session_safe
|
|
133
|
+
local mode executor wake_model agent_bin stamp wake_dir ops_dir prompt_rel log_rel prompt_path log_path pid_file old_pid pid status tmux_session session_name session_safe
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
if command -v jq >/dev/null 2>&1 && [ -f "$project_root/.harness/config.json" ]; then
|
|
136
|
+
mode="${HARNESS_WAKE_MODE:-$(jq -r '.company_mode.hourly_wake_mode // "headless"' "$project_root/.harness/config.json" 2>/dev/null || echo headless)}"
|
|
137
|
+
executor="${HARNESS_WAKE_EXECUTOR:-$(jq -r '.company_mode.hourly_wake_executor // "claude"' "$project_root/.harness/config.json" 2>/dev/null || echo claude)}"
|
|
138
|
+
wake_model="${HARNESS_WAKE_MODEL:-$(jq -r '.company_mode.hourly_wake_model // ""' "$project_root/.harness/config.json" 2>/dev/null || true)}"
|
|
139
|
+
else
|
|
140
|
+
mode="${HARNESS_WAKE_MODE:-headless}"
|
|
141
|
+
executor="${HARNESS_WAKE_EXECUTOR:-claude}"
|
|
142
|
+
wake_model="${HARNESS_WAKE_MODEL:-}"
|
|
143
|
+
fi
|
|
137
144
|
stamp="$(date -u "+%Y%m%dT%H%M%SZ")"
|
|
138
145
|
wake_dir="$project_root/.harness/actions/wake"
|
|
139
146
|
ops_dir="$project_root/.harness/ops/wake"
|
|
@@ -154,28 +161,58 @@ start_headless_tick() {
|
|
|
154
161
|
fi
|
|
155
162
|
|
|
156
163
|
cat > "$prompt_path" <<EOF
|
|
157
|
-
You are the walwal-harness
|
|
164
|
+
You are harness-ceo waking for the walwal-harness hourly executive loop.
|
|
158
165
|
|
|
159
166
|
Project root: $project_root
|
|
160
167
|
Wake reason: idle=${idle}s force=${force}
|
|
161
168
|
Hourly review: ${review_path:-"(not available)"}
|
|
162
169
|
|
|
170
|
+
One-line command:
|
|
171
|
+
Convene CXX + OPS, collect current progress and decisions, decide the next action as CEO with the active goal in mind, drive all teams without asking Owner, and use CQO verification until the goal demonstrably works.
|
|
172
|
+
|
|
163
173
|
Policy:
|
|
164
|
-
- Owner input after the initial GOAL is an interrupt or additional request, never a reason to wait.
|
|
174
|
+
- Owner already provided the GOAL. Owner input after the initial GOAL is an interrupt or additional request, never a reason to wait.
|
|
175
|
+
- Do not ask Owner what to do next. Convert uncertainty into CXX/OPS tasks, CQO checks, or a bounded assumption written to artifacts.
|
|
165
176
|
- Never set conductor.state to waiting_owner. Normalize any waiting_owner state to waiting_meeting and continue.
|
|
166
|
-
- meeting-manager is the
|
|
177
|
+
- meeting-manager is the synchronization loop, not the stopping point. Incidents, service warnings, goal drift, and escalations go to meeting-manager first, then route execution.
|
|
167
178
|
- Keep this to one bounded autonomous tick. Do not run an endless loop.
|
|
168
179
|
- Do not write future timestamps to .harness/progress.log or artifacts.
|
|
169
180
|
- Keep terminal output concise; write evidence to project files.
|
|
181
|
+
- Treat paperwork-only output as insufficient. A meeting that does not create or advance executable next actions is incomplete.
|
|
182
|
+
|
|
183
|
+
ADHD/autonomy support pattern:
|
|
184
|
+
- Act as an external executive-function scaffold: break blocked work into tiny executable next steps, name the first action, and start it.
|
|
185
|
+
- Use body-doubling behavior: check the active work state, ask each role for a concise status through artifacts, and keep the loop moving.
|
|
186
|
+
- Use context-transition support: summarize what just finished, what starts next, and the handoff artifact path.
|
|
187
|
+
- Use time-guardian behavior: identify hidden sub-tasks that make the work longer than expected, set a realistic next checkpoint, and avoid vague "later" states.
|
|
188
|
+
- Use open-loop triage: classify discovered work as NOW / NEXT / PARKED. Execute or dispatch NOW items only.
|
|
170
189
|
|
|
171
190
|
Tasks:
|
|
172
191
|
1. Read CONVENTIONS.md if present, .harness/conventions/shared.md, relevant .harness/conventions/<role>.md, .harness/gotchas/<role>.md, .harness/memory.md, .harness/progress.json, and the hourly review above.
|
|
173
|
-
2.
|
|
174
|
-
3.
|
|
175
|
-
4.
|
|
176
|
-
5.
|
|
177
|
-
6.
|
|
178
|
-
|
|
192
|
+
2. Determine the active GOAL/submission/hot-fix from progress.json and .harness/documents. Restate it in one sentence for yourself.
|
|
193
|
+
3. Convene the executive loop through artifacts: CEO + COO + CDO + CTO + CQO + OPS. Collect only concise status, blocker, decision-needed, and evidence-path information.
|
|
194
|
+
4. If service_ops.requested_mode is monitor, run or trigger one service-ops monitor pass and record last_check. stream_active may be true only during the pass and must be false when finished.
|
|
195
|
+
5. Treat the hourly review as Executive Meeting Minutes. Verify it contains role positions, discussion, decision JSON, action items, and CQO/OPS evidence requirements.
|
|
196
|
+
6. As CEO, decide exactly one NOW action package:
|
|
197
|
+
- owner CXX/role,
|
|
198
|
+
- action_type,
|
|
199
|
+
- deliverable_path,
|
|
200
|
+
- success_condition,
|
|
201
|
+
- verifier, preferably CQO when goal behavior must be proven.
|
|
202
|
+
7. Route all teams needed for that NOW action. Do not stop after hiring, planning, status reporting, or meeting note creation.
|
|
203
|
+
8. Use CQO to verify whether the goal is actually satisfied. If verification fails or is missing, route implementation/fix work again.
|
|
204
|
+
9. If there is an active incident or operational warning, make sure meeting-manager has shared context and an action decision that names CTO/CQO/OPS responsibilities.
|
|
205
|
+
10. Split meaningful progress from paperwork-only artifacts in your summary.
|
|
206
|
+
11. Append a short factual summary to .harness/progress.log and update normal harness artifacts used by this project.
|
|
207
|
+
|
|
208
|
+
Required final shape for this tick:
|
|
209
|
+
- GOAL:
|
|
210
|
+
- CXX/OPS status collected:
|
|
211
|
+
- CEO decision:
|
|
212
|
+
- NOW action dispatched:
|
|
213
|
+
- CQO/OPS verification required:
|
|
214
|
+
- NEXT checkpoint:
|
|
215
|
+
- Evidence paths:
|
|
179
216
|
EOF
|
|
180
217
|
|
|
181
218
|
case "$mode" in
|
|
@@ -189,7 +226,11 @@ EOF
|
|
|
189
226
|
if [ "$executor" = "codex" ]; then
|
|
190
227
|
"$agent_bin" exec -C "$project_root" "$(cat "$prompt_path")" > "$log_path" 2>&1
|
|
191
228
|
else
|
|
192
|
-
|
|
229
|
+
if [ -n "$wake_model" ]; then
|
|
230
|
+
"$agent_bin" -p "$(cat "$prompt_path")" --model "$wake_model" > "$log_path" 2>&1
|
|
231
|
+
else
|
|
232
|
+
"$agent_bin" -p "$(cat "$prompt_path")" > "$log_path" 2>&1
|
|
233
|
+
fi
|
|
193
234
|
fi
|
|
194
235
|
) &
|
|
195
236
|
pid=$!
|
|
@@ -208,7 +249,11 @@ EOF
|
|
|
208
249
|
if [ "$executor" = "codex" ]; then
|
|
209
250
|
tmux new-session -d -s "$session_name" "cd '$project_root' && '$agent_bin' exec -C '$project_root' - < '$prompt_path' 2>&1 | tee '$log_path'"
|
|
210
251
|
else
|
|
211
|
-
|
|
252
|
+
if [ -n "$wake_model" ]; then
|
|
253
|
+
tmux new-session -d -s "$session_name" "cd '$project_root' && '$agent_bin' -p \"\$(cat '$prompt_path')\" --model '$wake_model' 2>&1 | tee '$log_path'"
|
|
254
|
+
else
|
|
255
|
+
tmux new-session -d -s "$session_name" "cd '$project_root' && '$agent_bin' -p \"\$(cat '$prompt_path')\" 2>&1 | tee '$log_path'"
|
|
256
|
+
fi
|
|
212
257
|
fi
|
|
213
258
|
status="spawned-tmux executor=$executor session=$session_name"
|
|
214
259
|
else
|