wei-wuwei 0.1.0 → 0.2.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/CHANGELOG.md +19 -0
- package/README.md +2 -1
- package/agents/wuwei-implementer/agent.md +34 -0
- package/bin/wuwei.js +123 -4
- package/package.json +1 -1
- package/skills/wuwei-execute-phase/skill.md +10 -5
- package/skills/wuwei-plan-phases/skill.md +8 -5
- package/wuwei.config.json +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ One section per published version, newest first. `wuwei update` prints the
|
|
|
4
4
|
sections between the installed version and the new one before it asks;
|
|
5
5
|
`wuwei check` fails when package.json's version has no section here.
|
|
6
6
|
|
|
7
|
+
## 0.2.0
|
|
8
|
+
|
|
9
|
+
Execute runs at a fixed tier; the model policy now covers review only.
|
|
10
|
+
|
|
11
|
+
- `wuwei-execute-phase` runs at `sonnet` / `xhigh` under every policy: its
|
|
12
|
+
own frontmatter sets the tier for the turn it runs in, and step 2 always
|
|
13
|
+
dispatches the new `wuwei-implementer` agent (pinned `sonnet` / `xhigh`)
|
|
14
|
+
with no model or effort on the call. A stop names `/wuwei-execute-phase`,
|
|
15
|
+
since a reply in chat continues on the session's model.
|
|
16
|
+
- **Changed:** `model policy` under `## Sign-off` is `inherit` | `roles` for
|
|
17
|
+
the review tier (`roles` sends `review:design` and `review:defects` on
|
|
18
|
+
opus). `implementation=…` is refused by `plan-check` and `claim`.
|
|
19
|
+
- `wuwei claim` prints how steps 2 and 5 dispatch, and for Codex how to
|
|
20
|
+
start the session; `wuwei check` ties the skill's and the agent's tier to
|
|
21
|
+
`modelRoles` in `wuwei.config.json`.
|
|
22
|
+
- Codex: `## Sign-off` takes `codex: <model> / <effort>`; `wuwei init`
|
|
23
|
+
offers a `[profiles.wuwei-execute]` in `$CODEX_HOME/config.toml` (removed
|
|
24
|
+
by `wuwei uninstall`); `claim` prints `codex --profile wuwei-execute`.
|
|
25
|
+
|
|
7
26
|
## 0.1.0
|
|
8
27
|
|
|
9
28
|
First release.
|
package/README.md
CHANGED
|
@@ -59,7 +59,8 @@ cd wuwei
|
|
|
59
59
|
npm run check # lint frontmatter
|
|
60
60
|
npm run build # emit repo-local artifacts (.claude/, .agents/, AGENTS.md, CLAUDE.md)
|
|
61
61
|
node bin/wuwei.js init <project> # repo scope: templates + .wuwei/ skeleton, then
|
|
62
|
-
# offers the tools below and
|
|
62
|
+
# offers the tools below (and, with Codex, an execute
|
|
63
|
+
# profile in $CODEX_HOME/config.toml) and names the next step
|
|
63
64
|
node bin/wuwei.js install # user scope: skills/agents → ~/.claude, rules → ~/.claude/CLAUDE.md;
|
|
64
65
|
# Codex: skills → ~/.agents/skills, rules → $CODEX_HOME/AGENTS.md
|
|
65
66
|
node bin/wuwei.js status <project> # read-only: streams/claims from .wuwei, drift vs STATE.md flagged
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wuwei-implementer
|
|
3
|
+
description: >-
|
|
4
|
+
The implementation subagent of wuwei-execute-phase step 2, under every model
|
|
5
|
+
policy: one task of a phase, dispatched fresh with SPEC, the phase context,
|
|
6
|
+
the ladder and step 4's floor. Exists so implementation runs at sonnet /
|
|
7
|
+
xhigh — a dispatch alone can set a model but not an effort.
|
|
8
|
+
model: sonnet
|
|
9
|
+
effort: xhigh
|
|
10
|
+
version: 0.1.0
|
|
11
|
+
targets: [claude-code]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# wuwei-implementer
|
|
15
|
+
|
|
16
|
+
## Mandate
|
|
17
|
+
|
|
18
|
+
Implement the one task you were dispatched with, inside the phase you were
|
|
19
|
+
given, and nothing beyond it. What arrives with the dispatch governs: SPEC,
|
|
20
|
+
the phase context, the ladder (climb it before writing), and step 4's floor.
|
|
21
|
+
`wuwei-tdd` governs the task; commit as `wuwei-commit-hygiene` says — one
|
|
22
|
+
concern per commit, the why in the body.
|
|
23
|
+
|
|
24
|
+
## Out of scope
|
|
25
|
+
|
|
26
|
+
- Another task, or the phase's next step — hand back instead.
|
|
27
|
+
- A planning defect (a missing `depends-on`, an interface not there yet):
|
|
28
|
+
report it; do not work around it.
|
|
29
|
+
- Review of your own work — a fresh subagent does that.
|
|
30
|
+
|
|
31
|
+
## Output contract
|
|
32
|
+
|
|
33
|
+
What landed (commit shas, one line each), the tests that ran and their
|
|
34
|
+
result, and anything you stopped short of — with why.
|
package/bin/wuwei.js
CHANGED
|
@@ -187,10 +187,8 @@ function validate(items) {
|
|
|
187
187
|
errs.push(`${it.rel}: description too long (${it.data.description.length} > 400)`)
|
|
188
188
|
if (it.data.mode && !MODES.includes(it.data.mode))
|
|
189
189
|
errs.push(`${it.rel}: mode must be one of ${MODES.join('|')}`)
|
|
190
|
-
// a skill
|
|
191
|
-
// and
|
|
192
|
-
if ((it.data.model || it.data.effort) && it.kind !== 'agents')
|
|
193
|
-
errs.push(`${it.rel}: model/effort apply to agents only — a skill is not a subagent`)
|
|
190
|
+
// on a skill, Claude Code applies model/effort to the turn the skill runs
|
|
191
|
+
// in (and only that turn — the session's resume on the next prompt)
|
|
194
192
|
if (it.data.model && !MODEL_ALIASES.includes(it.data.model)
|
|
195
193
|
&& !/^claude-[a-z0-9.-]+$/.test(it.data.model))
|
|
196
194
|
errs.push(`${it.rel}: model must be ${MODEL_ALIASES.join('|')} or a full id (claude-…)`)
|
|
@@ -536,6 +534,21 @@ function lint(items) {
|
|
|
536
534
|
for (const rel of Object.keys(budgets))
|
|
537
535
|
if (!items.some(it => it.rel === rel || (it.refs ?? []).some(r => r.rel === rel)))
|
|
538
536
|
errs.push(`wuwei.config.json: wordBudgets names ${rel}, which is not a definition or reference`)
|
|
537
|
+
// modelRoles names the tier; the agent file is what dispatch actually reads
|
|
538
|
+
const impl = CONFIG.modelRoles?.implementation
|
|
539
|
+
if (impl) {
|
|
540
|
+
const ag = items.find(it => it.kind === 'agents' && it.data.name === impl.agent)
|
|
541
|
+
if (!ag) errs.push(`wuwei.config.json: modelRoles.implementation.agent ${impl.agent} is not an agent`)
|
|
542
|
+
else for (const f of ['model', 'effort']) if (ag.data[f] !== impl[f])
|
|
543
|
+
errs.push(`${ag.rel}: ${f} ${ag.data[f] ?? '(unset)'} ≠ modelRoles.implementation.${f} ${impl[f]} — the roles tier is what this file pins`)
|
|
544
|
+
}
|
|
545
|
+
// the skill that runs implementation runs its own turn at the same tier
|
|
546
|
+
const sk = impl?.skill && items.find(it => it.kind === 'skills' && it.data.name === impl.skill)
|
|
547
|
+
if (impl?.skill && !sk) errs.push(`wuwei.config.json: modelRoles.implementation.skill ${impl.skill} is not a skill`)
|
|
548
|
+
else if (sk) for (const f of ['model', 'effort']) if (sk.data[f] !== impl[f])
|
|
549
|
+
errs.push(`${sk.rel}: ${f} ${sk.data[f] ?? '(unset)'} ≠ modelRoles.implementation.${f} ${impl[f]} — execute runs at the implementer's tier`)
|
|
550
|
+
for (const l of CONFIG.modelRoles?.review?.lenses ?? [])
|
|
551
|
+
if (!items.some(it => it.kind === 'agents' && it.data.name === l)) errs.push(`wuwei.config.json: modelRoles.review.lenses names ${l}, which is not an agent`)
|
|
539
552
|
// update prints this file's sections — a version without one updates in silence
|
|
540
553
|
const changelog = existsSync(join(ROOT, 'CHANGELOG.md')) ? readFileSync(join(ROOT, 'CHANGELOG.md'), 'utf8') : ''
|
|
541
554
|
if (!new RegExp(`^## v?${escapeRe(PKG.version)}\\b`, 'm').test(changelog))
|
|
@@ -692,6 +705,7 @@ async function cmdUninstall(target) {
|
|
|
692
705
|
}
|
|
693
706
|
if (stripManagedBlock(join(codexHome(), 'AGENTS.md')))
|
|
694
707
|
removed.push(join(codexHome(), 'AGENTS.md') + ' (managed block)')
|
|
708
|
+
if (removeCodexProfile()) removed.push(`${codexConfigPath()} ([profiles.${CODEX_PROFILE}])`)
|
|
695
709
|
if (existsSync(manifestPath())) { rmSync(manifestPath()); removed.push(manifestPath()) }
|
|
696
710
|
console.log(`user scope ← ${base}`)
|
|
697
711
|
for (const r of removed) console.log(` removed ${r}`)
|
|
@@ -1152,6 +1166,51 @@ function claimPhases(dest, stream) {
|
|
|
1152
1166
|
return { streamBranch, integration, remote, behind, phases }
|
|
1153
1167
|
}
|
|
1154
1168
|
|
|
1169
|
+
// SPEC ## Sign-off's model policy: `inherit` (default, also when the line is
|
|
1170
|
+
// absent) or `roles`, for the review tier. Implementation is not on the menu:
|
|
1171
|
+
// it is always wuwei-implementer at modelRoles.implementation's tier
|
|
1172
|
+
function modelPolicy(spec) {
|
|
1173
|
+
const raw = (signOffField(spec ?? '', 'model policy') ?? 'inherit').trim()
|
|
1174
|
+
const v = (raw.match(/\breview\s*[=:]\s*(\w+)/i)?.[1] ?? raw).toLowerCase()
|
|
1175
|
+
const error = /\bimplementation\b/i.test(raw)
|
|
1176
|
+
? `model policy '${raw}': implementation is fixed (wuwei-implementer, ${CONFIG.modelRoles?.implementation?.model} / ${CONFIG.modelRoles?.implementation?.effort}) — the policy is inherit or roles, for review`
|
|
1177
|
+
: !['inherit', 'roles'].includes(v) ? `model policy '${raw}': inherit or roles` : null
|
|
1178
|
+
// Codex, when it runs execute: `codex: <model> / <effort>` — the session it is started as
|
|
1179
|
+
const cx = (spec ?? '').split(/^## Sign-off\s*$/m)[1]?.split(/^## /m)[0]?.split('\n').find(l => /^[-*|\s]*codex\s*[:|]/i.test(l))
|
|
1180
|
+
const cv = cx ? (cx.match(/`([^`]+)`/)?.[1] ?? cx.split(/[:|]/).map(x => x.trim()).filter(Boolean).at(-1)) : null
|
|
1181
|
+
const cm = cv?.match(/^(\S+)\s*\/\s*(\S+)$/)
|
|
1182
|
+
const codex = cm ? { model: cm[1], effort: cm[2] } : null
|
|
1183
|
+
const codexError = cx && !cm ? `codex '${cv}': write it as <model> / <effort>`
|
|
1184
|
+
: codex && !CODEX_EFFORTS.includes(codex.effort) ? `codex effort '${codex.effort}': one of ${CODEX_EFFORTS.join('|')}` : null
|
|
1185
|
+
return { raw, tiers: { review: v }, error: error ?? codexError, codex }
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
function codexLine(want) {
|
|
1189
|
+
const { profile } = codexProfile()
|
|
1190
|
+
if (!want) return profile
|
|
1191
|
+
? `codex start it with \`codex --profile ${CODEX_PROFILE}\` (${profile.model} / ${profile.effort}) — none recorded under ## Sign-off`
|
|
1192
|
+
: 'codex the model Codex was started with — no `codex:` under ## Sign-off'
|
|
1193
|
+
return profile?.model === want.model && profile?.effort === want.effort
|
|
1194
|
+
? `codex start it with \`codex --profile ${CODEX_PROFILE}\` (${want.model} / ${want.effort})`
|
|
1195
|
+
: `codex start it with \`${codexLaunch(want.model, want.effort)}\`${profile ? ` — profile ${CODEX_PROFILE} is ${profile.model} / ${profile.effort}, not what ## Sign-off says` : ''}`
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
// what step 2 and step 5 dispatch, from the policy and wuwei.config.json's
|
|
1199
|
+
// modelRoles — printed, so the tier is read off the screen, not remembered
|
|
1200
|
+
function dispatchLines(policy) {
|
|
1201
|
+
const roles = CONFIG.modelRoles ?? {}
|
|
1202
|
+
const impl = roles.implementation ?? {}, rev = roles.review ?? {}
|
|
1203
|
+
return [
|
|
1204
|
+
`implement (step 2) subagent_type ${impl.agent}, no model or effort on the call — ${impl.model} / ${impl.effort}, pinned in its agent file`,
|
|
1205
|
+
policy.tiers.review === 'roles'
|
|
1206
|
+
? `${(rev.lenses ?? []).map(l => l.replace(/^wuwei-review-/, 'review:')).join(', ')} (step 5) model: ${rev.model} — effort from the agent file`
|
|
1207
|
+
: `${(rev.lenses ?? []).map(l => l.replace(/^wuwei-review-/, 'review:')).join(', ')} (step 5) no model — the session's model; effort from the agent file`,
|
|
1208
|
+
'other lenses no model — the session\'s; wuwei-security-auditor stays fable / high (pinned)',
|
|
1209
|
+
'inline (no subagents, Codex) the session\'s, whatever the policy — say so in the round record',
|
|
1210
|
+
codexLine(policy.codex),
|
|
1211
|
+
]
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1155
1214
|
function cmdClaim(argv) {
|
|
1156
1215
|
const flag = name => { const i = argv.indexOf(name); return i === -1 ? null : argv[i + 1] }
|
|
1157
1216
|
const positional = argv.filter((a, i) => !a.startsWith('--') && argv[i - 1] !== '--by')
|
|
@@ -1175,6 +1234,10 @@ function cmdClaim(argv) {
|
|
|
1175
1234
|
for (const p of r.phases)
|
|
1176
1235
|
console.log(` ${p.name.padEnd(w)} ${p.state}${p.review === 'human' ? ' ← review: human' : ''}`)
|
|
1177
1236
|
|
|
1237
|
+
const policy = modelPolicy(existsSync(join(streamsDir, stream, 'SPEC.md')) ? readFileSync(join(streamsDir, stream, 'SPEC.md'), 'utf8') : '')
|
|
1238
|
+
if (policy.error) { console.error(`\n✗ ${policy.error} — fix ## Sign-off (wuwei plan-check)`); process.exit(1) }
|
|
1239
|
+
console.log(`\ndispatch · model policy: ${policy.raw}\n${dispatchLines(policy).map(l => ` ${l}`).join('\n')}`)
|
|
1240
|
+
|
|
1178
1241
|
if (!phaseArg) {
|
|
1179
1242
|
const ready = r.phases.filter(p => p.state === 'ready')
|
|
1180
1243
|
console.log(ready.length ? `\nclaim one: wuwei claim ${stream} ${ready[0].no}` : '\nnothing ready to claim')
|
|
@@ -1283,6 +1346,8 @@ function cmdPlanCheck(argv) {
|
|
|
1283
1346
|
const open = signLines.filter(l => /:\s*(\?|tbd|todo|pending|—|-|<[^>]+>)?\s*$/i.test(l) && /\w/.test(l.split(':')[0]) && /^\s*[-*|]/.test(l))
|
|
1284
1347
|
const auto = signLines.some(l => /\bmode\b[^|\n]*\bauto\b/i.test(l))
|
|
1285
1348
|
if (auto && open.length) errs.push(`drive mode is auto while ## Sign-off still has open fields: ${open.map(l => l.trim()).join(' / ')} — a partly answered sign-off stays manual`)
|
|
1349
|
+
const policy = spec ? modelPolicy(spec) : null
|
|
1350
|
+
if (policy?.error) errs.push(`## Sign-off: ${policy.error}`)
|
|
1286
1351
|
const tracker = signLines.find(l => /tracker/i.test(l)) ?? ''
|
|
1287
1352
|
const linked = tracker && !/\bnone\b/i.test(tracker)
|
|
1288
1353
|
|
|
@@ -2620,6 +2685,59 @@ async function promptYesNo(question, def = false) {
|
|
|
2620
2685
|
}
|
|
2621
2686
|
}
|
|
2622
2687
|
|
|
2688
|
+
async function promptText(question, def = '') {
|
|
2689
|
+
if (!isInteractive()) return def
|
|
2690
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
2691
|
+
try { return (await rl.question(`${question}${def ? ` [${def}]` : ''} `)).trim() || def } catch { return def } finally { rl.close() }
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
// ── Codex: a skill cannot set its model — the session is started with one.
|
|
2695
|
+
// So execute's tier on Codex is a profile in $CODEX_HOME/config.toml that the
|
|
2696
|
+
// session is launched under (codex --profile wuwei-execute); init offers to
|
|
2697
|
+
// write it, claim prints how to start. Effort values: codex-rs ReasoningEffort
|
|
2698
|
+
const CODEX_PROFILE = 'wuwei-execute'
|
|
2699
|
+
const CODEX_EFFORTS = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra', 'persistent']
|
|
2700
|
+
const CODEX_PROFILE_MARK = '# added by wuwei init — wuwei-execute-phase runs under this profile; `wuwei uninstall` removes it'
|
|
2701
|
+
const codexConfigPath = () => join(codexHome(), 'config.toml')
|
|
2702
|
+
// [profiles.wuwei-execute]'s model and effort, or null; plus the top-level model
|
|
2703
|
+
function codexProfile() {
|
|
2704
|
+
const text = existsSync(codexConfigPath()) ? readFileSync(codexConfigPath(), 'utf8') : ''
|
|
2705
|
+
const sec = text.split(/^(?=\[)/m).find(s => new RegExp(`^\\[profiles\\.(?:"${CODEX_PROFILE}"|${CODEX_PROFILE})\\]`).test(s))
|
|
2706
|
+
const val = (s, k) => s?.match(new RegExp(`^${k}\\s*=\\s*"([^"]*)"`, 'm'))?.[1] ?? null
|
|
2707
|
+
const top = text.split(/^\[/m)[0]
|
|
2708
|
+
return { profile: sec ? { model: val(sec, 'model'), effort: val(sec, 'model_reasoning_effort') } : null, topModel: val(top, 'model') }
|
|
2709
|
+
}
|
|
2710
|
+
const codexLaunch = (model, effort) => `codex -m ${model} -c model_reasoning_effort=${effort}`
|
|
2711
|
+
|
|
2712
|
+
async function offerCodexProfile(dry) {
|
|
2713
|
+
if (!existsSync(codexHome())) return
|
|
2714
|
+
const { profile, topModel } = codexProfile()
|
|
2715
|
+
if (profile) { console.log(`
|
|
2716
|
+
codex: profile ${CODEX_PROFILE} — ${profile.model ?? '(model unset)'} / ${profile.effort ?? '(effort unset)'} · start execute with \`codex --profile ${CODEX_PROFILE}\``); return }
|
|
2717
|
+
const snippet = m => [CODEX_PROFILE_MARK, `[profiles.${CODEX_PROFILE}]`, `model = "${m.model}"`, `model_reasoning_effort = "${m.effort}"`].join('\n')
|
|
2718
|
+
console.log(`
|
|
2719
|
+
codex: a skill can't choose Codex's model — the session is started with one. wuwei-execute-phase runs under a profile you launch it with.`)
|
|
2720
|
+
if (!isInteractive()) { console.log(` add to ${codexConfigPath()}:\n${snippet({ model: '<model>', effort: 'xhigh' }).replace(/^/gm, ' ')}`); return }
|
|
2721
|
+
if (!await promptYesNo(` Add [profiles.${CODEX_PROFILE}] to ${codexConfigPath()}?`, true)) { console.log(' skipped — re-run `wuwei init` anytime'); return }
|
|
2722
|
+
const model = await promptText(' model for execute:', topModel ?? '')
|
|
2723
|
+
let effort = await promptText(` reasoning effort (${CODEX_EFFORTS.join('|')}):`, 'xhigh')
|
|
2724
|
+
if (!model || !CODEX_EFFORTS.includes(effort)) { console.log(` ✗ ${!model ? 'no model given' : `effort must be one of ${CODEX_EFFORTS.join('|')}`} — nothing written`); return }
|
|
2725
|
+
if (dry) { console.log(` [dry] would append:\n${snippet({ model, effort }).replace(/^/gm, ' ')}`); return }
|
|
2726
|
+
const prev = existsSync(codexConfigPath()) ? readFileSync(codexConfigPath(), 'utf8') : ''
|
|
2727
|
+
writeFileSync(codexConfigPath(), (prev ? prev.replace(/\n*$/, '\n\n') : '') + snippet({ model, effort }) + '\n')
|
|
2728
|
+
console.log(` ✓ written · start execute with \`codex --profile ${CODEX_PROFILE}\`, and write \`codex: ${model} / ${effort}\` under the stream's ## Sign-off`)
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
// only the section init wrote (its marker line above it), never one you made
|
|
2732
|
+
function removeCodexProfile() {
|
|
2733
|
+
if (!existsSync(codexConfigPath())) return false
|
|
2734
|
+
const text = readFileSync(codexConfigPath(), 'utf8')
|
|
2735
|
+
const re = new RegExp(`\\n*${escapeRe(CODEX_PROFILE_MARK)}\\n\\[profiles\\.${CODEX_PROFILE}\\][^\\[]*`)
|
|
2736
|
+
if (!re.test(text)) return false
|
|
2737
|
+
writeFileSync(codexConfigPath(), text.replace(re, '\n').replace(/^\n+/, '').replace(/\n*$/, '\n'))
|
|
2738
|
+
return true
|
|
2739
|
+
}
|
|
2740
|
+
|
|
2623
2741
|
async function cmdInit(target, dry) {
|
|
2624
2742
|
// bootstrap a project: repo-scope install into the target (default: cwd) —
|
|
2625
2743
|
// the entrypoint `npx <pkg> init` runs from anywhere, package files as ROOT
|
|
@@ -2664,6 +2782,7 @@ async function cmdInit(target, dry) {
|
|
|
2664
2782
|
|
|
2665
2783
|
await offerCodegraphSetup(dest, dry, existing)
|
|
2666
2784
|
await offerScannerSetup(dry)
|
|
2785
|
+
await offerCodexProfile(dry)
|
|
2667
2786
|
|
|
2668
2787
|
const installHint = isSkillsInstalled()
|
|
2669
2788
|
? ''
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wei-wuwei",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "為無為,則無不治 — a source-of-truth skills & agents framework that compiles to Claude Code and Codex (Cursor and Gemini CLI planned). Write once, run in every agent, govern without forcing.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,8 @@ description: >-
|
|
|
9
9
|
simplify/review/fix → PR — one phase, or a range on request.
|
|
10
10
|
version: 0.2.0
|
|
11
11
|
mode: triggered
|
|
12
|
+
model: sonnet
|
|
13
|
+
effort: xhigh
|
|
12
14
|
targets: [claude-code, codex]
|
|
13
15
|
tags: [execution, workflow]
|
|
14
16
|
---
|
|
@@ -98,9 +100,12 @@ Stopping early is normal — say where, why, and what resumes:
|
|
|
98
100
|
```
|
|
99
101
|
stopped at 03-appcheck-wiring · bound e2e red (auth/refresh.spec.ts)
|
|
100
102
|
02 merged. 03 claimed, branch pushed, not merged.
|
|
101
|
-
next invocation resumes at 03
|
|
103
|
+
next invocation resumes at 03: /wuwei-execute-phase
|
|
102
104
|
```
|
|
103
105
|
|
|
106
|
+
Name the invocation: this skill's sonnet / xhigh holds for the turn it runs
|
|
107
|
+
in, and a reply in chat continues on the session's model.
|
|
108
|
+
|
|
104
109
|
**Scope is distance, never permission.** `mode` is the stream's standing
|
|
105
110
|
authorisation from sign-off; scope is only how far one invocation tries to
|
|
106
111
|
get.
|
|
@@ -152,8 +157,9 @@ get.
|
|
|
152
157
|
context, **the ladder below**, and **step 4's floor** — and nothing else.
|
|
153
158
|
The ladder is a question the subagent writing the helper has to ask; an
|
|
154
159
|
orchestrator holding it while the subagent writes is the same as not having
|
|
155
|
-
it. Dispatch
|
|
156
|
-
|
|
160
|
+
it. Dispatch `wuwei-implementer` with **no `model` or `effort` on the
|
|
161
|
+
call** — its file pins sonnet / xhigh, and a value on the call beats it.
|
|
162
|
+
Inline instead → watch for
|
|
157
163
|
drift, keep the phase small, and note that the model policy has nothing to
|
|
158
164
|
attach to.
|
|
159
165
|
|
|
@@ -333,8 +339,7 @@ get.
|
|
|
333
339
|
`ACCEPTANCE.md`, the **base ref just synced to**, the **tool facts**
|
|
334
340
|
(`wuwei-review` §1, §5), and **the scope** (`wuwei-review` §6) — plus, for the
|
|
335
341
|
document panel, **the intended reader and the facts this phase
|
|
336
|
-
establishes** (`wuwei-review` §3). Models:
|
|
337
|
-
`## Sign-off` policy for design and defects, the session's for the rest
|
|
342
|
+
establishes** (`wuwei-review` §3). Models: as `wuwei claim` printed them
|
|
338
343
|
(`wuwei-review` §2).
|
|
339
344
|
|
|
340
345
|
**Round 1 is full scope; every round after it is delta** — dispatched with
|
|
@@ -202,15 +202,18 @@ tags: [planning, workflow]
|
|
|
202
202
|
- **merge method**: unsquashed by default — a merge commit — so ask only
|
|
203
203
|
whether this stream squashes instead, plus which methods the repo allows
|
|
204
204
|
(table below);
|
|
205
|
-
- **model policy
|
|
205
|
+
- **model policy** for review: `inherit` (default) | `roles`. Ask only
|
|
206
206
|
whether to change the default;
|
|
207
|
+
- **Codex**, when it runs execute: the model and effort it is started
|
|
208
|
+
with — `codex: <model> / <effort>`. A skill cannot set Codex's model;
|
|
209
|
+
`wuwei claim` prints the launch (`wuwei init` offers the profile);
|
|
207
210
|
- whether the plan goes through **its own PR** first — if so, steps 8–9
|
|
208
211
|
wait for that PR to merge.
|
|
209
212
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
Implementation is not on the menu: execute runs, and dispatches its
|
|
214
|
+
implementer, at sonnet / xhigh (`wuwei.config.json` `modelRoles`). The
|
|
215
|
+
policy sets **review** — step 5's `review:design` and `review:defects`,
|
|
216
|
+
which dispatch as subagents. The panel's other lenses inherit the session's
|
|
214
217
|
model, **except `wuwei-security-auditor`**, which pins `fable` / `high` in
|
|
215
218
|
frontmatter and holds under every policy including `inherit`. Say so,
|
|
216
219
|
rather than letting a `roles` answer imply it was chosen.
|
package/wuwei.config.json
CHANGED
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"failOnDrift": true,
|
|
17
17
|
"stampGeneratedHeader": true
|
|
18
18
|
},
|
|
19
|
+
"modelRoles": {
|
|
20
|
+
"implementation": { "skill": "wuwei-execute-phase", "agent": "wuwei-implementer", "model": "sonnet", "effort": "xhigh" },
|
|
21
|
+
"review": { "lenses": ["wuwei-review-design", "wuwei-review-defects"], "model": "opus" }
|
|
22
|
+
},
|
|
19
23
|
"wordBudgets": {
|
|
20
24
|
"skills/wuwei-brainstorm/skill.md": 500,
|
|
21
25
|
"skills/wuwei-commit-hygiene/skill.md": 200,
|