@sublang/playbook 0.7.0 → 0.9.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.
@@ -1,453 +0,0 @@
1
- #!/usr/bin/env node
2
- // SPDX-License-Identifier: Apache-2.0
3
- // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
4
-
5
- import { spawn } from 'node:child_process';
6
- import {
7
- constants,
8
- copyFileSync,
9
- existsSync,
10
- mkdirSync,
11
- mkdtempSync,
12
- readFileSync,
13
- realpathSync,
14
- rmSync,
15
- writeFileSync,
16
- } from 'node:fs';
17
- import { homedir, tmpdir } from 'node:os';
18
- import { dirname, join, resolve } from 'node:path';
19
- import { fileURLToPath } from 'node:url';
20
- import {
21
- findTmuxPlayConfig,
22
- loadTmuxPlayConfig,
23
- } from '@sublang/cligent/tmux-play';
24
- import {
25
- parse as parseYaml,
26
- parseDocument as parseYamlDocument,
27
- stringify as stringifyYaml,
28
- } from 'yaml';
29
-
30
- const here = dirname(fileURLToPath(import.meta.url));
31
- const templatePath = resolve(here, '..', 'playbook-code.config.template.yaml');
32
- const READINESS_FAILURE_EXIT_CODE = 2;
33
- const COMPOSITION_FAILURE_EXIT_CODE = 1;
34
- const DEFAULT_NOTIFICATION_BLOCK = [
35
- '',
36
- '# tmux-play host notifications. Omitted turn_aborted resolves to off.',
37
- 'notifications:',
38
- ' player_finished: bell',
39
- ' turn_finished: desktop',
40
- '',
41
- ].join('\n');
42
-
43
- // PBCODE-16: the composer injects `captain.from` (the Playbook
44
- // Captain shell adapter module) and the `coder` / `reviewer` player
45
- // ids, so the user-edited overlay carries neither.
46
- export const PLAYBOOK_CAPTAIN_MODULE = '@sublang/playbook/playbook-captain';
47
- export const CODE_ADAPTER_MODULE = PLAYBOOK_CAPTAIN_MODULE;
48
- const CODE_ROLES = ['coder', 'reviewer'];
49
- // Accepted `players` keys: the two fixed CODE roles plus the optional
50
- // `committer` alias (a string naming one of the roles); PBCODE-17.
51
- const CODE_PLAYER_KEYS = [...CODE_ROLES, 'committer'];
52
- // Captain-judge fields inherited from a base config when the overlay
53
- // leaves them unset (PBCODE-16); `adapter` is handled separately
54
- // because it is required in the composed config.
55
- const CAPTAIN_INHERITED_FIELDS = ['model', 'reasoningEffort', 'permissions'];
56
- const PLAYER_FIELDS = ['model', 'reasoningEffort', 'permissions'];
57
-
58
- export async function runPlaybookCodeCli(options = {}) {
59
- const argv = [...(options.argv ?? process.argv.slice(2))];
60
- const env = options.env ?? process.env;
61
- const stdout = options.stdout ?? process.stdout;
62
- const stderr = options.stderr ?? process.stderr;
63
- const spawnFn = options.spawn ?? spawn;
64
- const tmuxPlayBin = options.tmuxPlayBin ?? resolveTmuxPlayBin();
65
- const home = options.homeDir ?? env.HOME ?? homedir();
66
- const cwd = options.cwd ?? process.cwd();
67
- const configHome = resolveConfigHome(env, home);
68
- const userConfigPath = resolveUserConfigPath(env, home);
69
-
70
- if (argv.includes('--help') || argv.includes('-h')) {
71
- stdout.write(helpText({ userConfigPath }));
72
- return { code: 0 };
73
- }
74
-
75
- // PBCODE-1: explicit `--config <path>` bypasses seeding, readiness,
76
- // and composition — the path is launched verbatim.
77
- if (hasExplicitConfig(argv)) {
78
- return await launchTmuxPlay(spawnFn, [tmuxPlayBin, ...argv], stderr);
79
- }
80
-
81
- seedUserConfigIfMissing(userConfigPath, stderr);
82
- migrateUserConfigNotificationsIfMissing(userConfigPath, stderr);
83
-
84
- // PBCODE-16/17: compose the launched config from the overlay plus an
85
- // optional base tmux-play config. Composition failures (missing role,
86
- // non-CODE role id, missing `captain.adapter`) surface a path-named
87
- // error and abort before launch.
88
- let composed;
89
- try {
90
- composed = await composeLaunchConfig({
91
- overlayPath: userConfigPath,
92
- cwd,
93
- configHome,
94
- });
95
- } catch (error) {
96
- stderr.write(`playbook-code: ${errorMessage(error)}\n`);
97
- return { code: COMPOSITION_FAILURE_EXIT_CODE };
98
- }
99
-
100
- // PBCODE-8: readiness reads the adapters of the composed config —
101
- // including a captain adapter that may have been inherited from the
102
- // base — not the raw overlay.
103
- const readiness = checkReadiness(
104
- adaptersFromComposedConfig(composed),
105
- env,
106
- home,
107
- );
108
- for (const adapter of readiness.unknownAdapters) {
109
- stderr.write(
110
- `playbook-code: warning: no readiness check for adapter "${adapter}"\n`,
111
- );
112
- }
113
- if (readiness.failingAdapters.length > 0) {
114
- stderr.write(
115
- helpText({
116
- userConfigPath,
117
- failingAdapters: readiness.failingAdapters,
118
- }),
119
- );
120
- return { code: READINESS_FAILURE_EXIT_CODE };
121
- }
122
-
123
- // PBCODE-16: materialize the composed config to a temp file, launch
124
- // against it, and remove it before the shim exits — on normal exit,
125
- // on non-zero child exit, and before re-raising a forwarded signal.
126
- const { dir: tempDir, path: composedPath } = writeComposedConfig(composed);
127
- try {
128
- return await launchTmuxPlay(
129
- spawnFn,
130
- [tmuxPlayBin, '--config', composedPath, ...argv],
131
- stderr,
132
- );
133
- } finally {
134
- rmSync(tempDir, { recursive: true, force: true });
135
- }
136
- }
137
-
138
- export function resolveConfigHome(env = process.env, home = homedir()) {
139
- return env.XDG_CONFIG_HOME || join(home, '.config');
140
- }
141
-
142
- export function resolveUserConfigPath(env = process.env, home = homedir()) {
143
- return join(resolveConfigHome(env, home), 'playbook', 'playbook-code.config.yaml');
144
- }
145
-
146
- // PBCODE-17: read the overlay, locate an optional base tmux-play config
147
- // with cligent's exported `findTmuxPlayConfig` (never the bare
148
- // `loadTmuxPlayConfig`, which writes a default config when none is
149
- // found), load the base only when a path is located, and compose.
150
- async function composeLaunchConfig({ overlayPath, cwd, configHome }) {
151
- const overlay = parseYaml(readFileSync(overlayPath, 'utf8')) ?? {};
152
- const basePath = findTmuxPlayConfig(cwd, configHome);
153
- let base;
154
- if (basePath) {
155
- base = (await loadTmuxPlayConfig({ configPath: basePath })).config;
156
- }
157
- return composeRuntimeConfig(overlay, base);
158
- }
159
-
160
- /**
161
- * Compose the launched tmux-play config (PBCODE-16/17) from the CODE
162
- * overlay and an optional base tmux-play config.
163
- *
164
- * @returns {import('@sublang/cligent/tmux-play').TmuxPlayConfig}
165
- */
166
- export function composeRuntimeConfig(
167
- overlay,
168
- base,
169
- adapterModule = PLAYBOOK_CAPTAIN_MODULE,
170
- ) {
171
- const overlayConfig = requireObject(overlay, 'config');
172
- const overlayPlayers = requireObject(overlayConfig.players, 'players');
173
-
174
- // Reject any players key other than the two fixed CODE roles and the
175
- // optional `committer` alias.
176
- for (const key of Object.keys(overlayPlayers)) {
177
- if (!CODE_PLAYER_KEYS.includes(key)) {
178
- throw new Error(`Unknown config field players.${key}`);
179
- }
180
- }
181
-
182
- // PBCODE-17: `players.committer` is an optional string naming `coder`
183
- // or `reviewer`. The composer resolves it into the composed
184
- // `captain.options.code.committer` below and emits no extra
185
- // `players[]` entry, so the roster stays coder + reviewer.
186
- let committerAlias;
187
- if (overlayPlayers.committer !== undefined) {
188
- const value = overlayPlayers.committer;
189
- if (!CODE_ROLES.includes(value)) {
190
- throw new Error(
191
- `players.committer must name one of: ${CODE_ROLES.join(', ')}`,
192
- );
193
- }
194
- committerAlias = value;
195
- }
196
-
197
- // One composed `players[]` entry per role, with `id` = the role key
198
- // and that role's required `adapter` plus optional fields.
199
- const players = CODE_ROLES.map((role) => {
200
- if (overlayPlayers[role] === undefined) {
201
- throw new Error(`Missing required field players.${role}`);
202
- }
203
- const block = requireObject(overlayPlayers[role], `players.${role}`);
204
- if (block.adapter === undefined) {
205
- throw new Error(`Missing required field players.${role}.adapter`);
206
- }
207
- const entry = { id: role, adapter: block.adapter };
208
- for (const field of PLAYER_FIELDS) {
209
- if (block[field] !== undefined) entry[field] = block[field];
210
- }
211
- return entry;
212
- });
213
-
214
- const overlayCaptain =
215
- overlayConfig.captain === undefined
216
- ? {}
217
- : requireObject(overlayConfig.captain, 'captain');
218
- const baseCaptain = isObject(base?.captain) ? base.captain : {};
219
-
220
- // `captain.adapter` comes from the overlay when present, else the
221
- // base; composition fails with a path-named error when neither
222
- // supplies it. Role adapters are required in the overlay and are not
223
- // inherited.
224
- const captainAdapter = overlayCaptain.adapter ?? baseCaptain.adapter;
225
- if (captainAdapter === undefined) {
226
- throw new Error('Missing required field captain.adapter');
227
- }
228
-
229
- const captain = { from: adapterModule, adapter: captainAdapter };
230
- for (const field of CAPTAIN_INHERITED_FIELDS) {
231
- const value = overlayCaptain[field] ?? baseCaptain[field];
232
- if (value !== undefined) captain[field] = value;
233
- }
234
-
235
- // PBCODE-17: the shim is the sole writer of the composed
236
- // `captain.options.code.committer`. Read the overlay's
237
- // `captain.options.code` (carried through unchanged), reject a
238
- // directly-set `committer` there, then layer the resolved
239
- // `players.committer` alias on top.
240
- let overlayCode;
241
- if (overlayCaptain.options !== undefined) {
242
- const captainOptions = requireObject(
243
- overlayCaptain.options,
244
- 'captain.options',
245
- );
246
- if (captainOptions.code !== undefined) {
247
- overlayCode = requireObject(captainOptions.code, 'captain.options.code');
248
- if (overlayCode.committer !== undefined) {
249
- throw new Error(
250
- 'captain.options.code.committer is composer-owned; ' +
251
- 'set players.committer instead',
252
- );
253
- }
254
- }
255
- }
256
- if (overlayCode !== undefined || committerAlias !== undefined) {
257
- const code = { ...(overlayCode ?? {}) };
258
- if (committerAlias !== undefined) code.committer = committerAlias;
259
- captain.options = { code };
260
- }
261
-
262
- // Inherit host-owned top-level fields from the base when the overlay
263
- // omits them; never the base `players[]` roster.
264
- const composed = {};
265
- const theme = overlayConfig.theme ?? base?.theme;
266
- if (theme !== undefined) composed.theme = theme;
267
- const layout = overlayConfig.layout ?? base?.layout;
268
- if (layout !== undefined) composed.layout = layout;
269
- const notifications = overlayConfig.notifications ?? base?.notifications;
270
- if (notifications !== undefined) composed.notifications = notifications;
271
- composed.captain = captain;
272
- composed.players = players;
273
- return composed;
274
- }
275
-
276
- export function adaptersFromComposedConfig(config) {
277
- const adapters = new Set();
278
- const captainAdapter = config?.captain?.adapter;
279
- if (captainAdapter) adapters.add(captainAdapter);
280
- for (const player of config?.players ?? []) {
281
- if (player?.adapter) adapters.add(player.adapter);
282
- }
283
- return [...adapters];
284
- }
285
-
286
- export function checkReadiness(adapters, env = process.env, home = homedir()) {
287
- const failingAdapters = [];
288
- const unknownAdapters = [];
289
-
290
- for (const adapter of adapters) {
291
- if (adapter === 'claude') {
292
- if (!env.ANTHROPIC_API_KEY && !existsSync(join(home, '.claude'))) {
293
- failingAdapters.push(adapter);
294
- }
295
- continue;
296
- }
297
- if (adapter === 'codex') {
298
- if (!env.OPENAI_API_KEY && !existsSync(join(home, '.codex'))) {
299
- failingAdapters.push(adapter);
300
- }
301
- continue;
302
- }
303
- unknownAdapters.push(adapter);
304
- }
305
-
306
- return { failingAdapters, unknownAdapters };
307
- }
308
-
309
- function writeComposedConfig(composed) {
310
- const dir = mkdtempSync(join(tmpdir(), 'playbook-code-'));
311
- // cligent's loader only accepts a `.yaml` extension; name the temp
312
- // file accordingly.
313
- const path = join(dir, 'tmux-play.config.yaml');
314
- writeFileSync(path, stringifyYaml(composed));
315
- return { dir, path };
316
- }
317
-
318
- function seedUserConfigIfMissing(userConfigPath, stderr) {
319
- if (existsSync(userConfigPath)) return;
320
- mkdirSync(dirname(userConfigPath), { recursive: true });
321
- copyFileSync(templatePath, userConfigPath, constants.COPYFILE_EXCL);
322
- stderr.write(`playbook-code: created config at ${userConfigPath}\n`);
323
- }
324
-
325
- function migrateUserConfigNotificationsIfMissing(userConfigPath, stderr) {
326
- const source = readFileSync(userConfigPath, 'utf8');
327
- let parsed;
328
- try {
329
- const document = parseYamlDocument(source);
330
- if (document.errors.length > 0) return;
331
- parsed = document.contents === null ? {} : document.toJS();
332
- } catch {
333
- return;
334
- }
335
- if (!isObject(parsed) || hasOwn(parsed, 'notifications')) return;
336
- const separator = source.endsWith('\n') ? '' : '\n';
337
- writeFileSync(
338
- userConfigPath,
339
- `${source}${separator}${DEFAULT_NOTIFICATION_BLOCK}`,
340
- );
341
- stderr.write(
342
- `playbook-code: added notifications defaults to config at ${userConfigPath}\n`,
343
- );
344
- }
345
-
346
- function hasExplicitConfig(argv) {
347
- return argv.some((arg) => arg === '--config' || arg.startsWith('--config='));
348
- }
349
-
350
- function helpText({ userConfigPath, failingAdapters = [] }) {
351
- const failures =
352
- failingAdapters.length > 0
353
- ? [
354
- `Adapters not ready: ${failingAdapters.join(', ')}`,
355
- '',
356
- ]
357
- : [];
358
- return [
359
- ...failures,
360
- 'Usage:',
361
- ' playbook-code [--config <path>] [tmux-play options]',
362
- ' playbook-code --help',
363
- '',
364
- `Default config: ${userConfigPath}`,
365
- '',
366
- 'Adapter setup:',
367
- ' claude: run Claude Code once or set ANTHROPIC_API_KEY.',
368
- ' codex: run Codex CLI once or set OPENAI_API_KEY.',
369
- '',
370
- 'Agent swap recipe:',
371
- ' - change captain.adapter and captain.model for the Captain/Judge',
372
- ' - change adapter and model under players.coder and players.reviewer',
373
- ' for the Coder and Reviewer; model when pinned, else adapter, is',
374
- ' substituted into <coder-llm>/<reviewer-llm> player prompts (PBRT-4)',
375
- ' - the composer injects captain.from; the role keys coder and',
376
- ' reviewer are fixed',
377
- '',
378
- ].join('\n');
379
- }
380
-
381
- async function launchTmuxPlay(spawnFn, childArgs, stderr) {
382
- return await new Promise((resolveResult) => {
383
- let child;
384
- try {
385
- child = spawnFn(process.execPath, childArgs, { stdio: 'inherit' });
386
- } catch (error) {
387
- stderr.write(`playbook-code: failed to launch tmux-play: ${errorMessage(error)}\n`);
388
- resolveResult({ code: 127 });
389
- return;
390
- }
391
-
392
- let settled = false;
393
- const settle = (result) => {
394
- if (settled) return;
395
- settled = true;
396
- resolveResult(result);
397
- };
398
-
399
- child.on('error', (err) => {
400
- stderr.write(
401
- `playbook-code: failed to launch tmux-play: ${errorMessage(err)}\n`,
402
- );
403
- settle({ code: 127 });
404
- });
405
-
406
- child.on('exit', (code, signal) => {
407
- if (signal) settle({ signal });
408
- else settle({ code: code ?? 0 });
409
- });
410
- });
411
- }
412
-
413
- function resolveTmuxPlayBin() {
414
- const tmuxPlayIndexUrl = import.meta.resolve('@sublang/cligent/tmux-play');
415
- return join(dirname(fileURLToPath(tmuxPlayIndexUrl)), 'cli.js');
416
- }
417
-
418
- function isObject(value) {
419
- return typeof value === 'object' && value !== null && !Array.isArray(value);
420
- }
421
-
422
- function hasOwn(value, key) {
423
- return Object.prototype.hasOwnProperty.call(value, key);
424
- }
425
-
426
- function requireObject(value, path) {
427
- if (!isObject(value)) {
428
- throw new Error(`${path} must be an object`);
429
- }
430
- return value;
431
- }
432
-
433
- function errorMessage(error) {
434
- return error instanceof Error ? error.message : String(error);
435
- }
436
-
437
- function isCliEntry(
438
- argv1 = process.argv[1],
439
- moduleUrl = import.meta.url,
440
- ) {
441
- if (!argv1) return false;
442
- try {
443
- return realpathSync(argv1) === realpathSync(fileURLToPath(moduleUrl));
444
- } catch {
445
- return false;
446
- }
447
- }
448
-
449
- if (isCliEntry()) {
450
- const result = await runPlaybookCodeCli();
451
- if (result.signal) process.kill(process.pid, result.signal);
452
- else process.exit(result.code ?? 0);
453
- }
@@ -1,4 +0,0 @@
1
- import type { Captain } from '@sublang/cligent/tmux-play';
2
- export { codeCopyPasteGuardNames, codeStateCountLabels, codePlaybookRegistryEntry, createCodeRuntimeOptions, validateCodeOptions, } from './code.registry.js';
3
- export type { CodeOptions, CodePlaybookRegistryEntry, CreateCodeRuntimeOptions, RegistryPlayer, } from './code.registry.js';
4
- export default function createCodeTmuxPlayCaptain(options: unknown): Captain;
@@ -1,11 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
- import createPlaybookCaptainShell from './playbook-captain.js';
4
- export { codeCopyPasteGuardNames, codeStateCountLabels, codePlaybookRegistryEntry, createCodeRuntimeOptions, validateCodeOptions, } from './code.registry.js';
5
- // Compatibility shim for the historic `./code/tmux-play` package
6
- // export. Public launch paths now target the Playbook Captain shell
7
- // directly; explicit configs that still import this module get the
8
- // same shell with CODE registered.
9
- export default function createCodeTmuxPlayCaptain(options) {
10
- return createPlaybookCaptainShell(options);
11
- }
@@ -1,29 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
-
4
- import type { Captain } from '@sublang/cligent/tmux-play';
5
- import createPlaybookCaptainShell from './playbook-captain.js';
6
-
7
- export {
8
- codeCopyPasteGuardNames,
9
- codeStateCountLabels,
10
- codePlaybookRegistryEntry,
11
- createCodeRuntimeOptions,
12
- validateCodeOptions,
13
- } from './code.registry.js';
14
- export type {
15
- CodeOptions,
16
- CodePlaybookRegistryEntry,
17
- CreateCodeRuntimeOptions,
18
- RegistryPlayer,
19
- } from './code.registry.js';
20
-
21
- // Compatibility shim for the historic `./code/tmux-play` package
22
- // export. Public launch paths now target the Playbook Captain shell
23
- // directly; explicit configs that still import this module get the
24
- // same shell with CODE registered.
25
- export default function createCodeTmuxPlayCaptain(
26
- options: unknown,
27
- ): Captain {
28
- return createPlaybookCaptainShell(options);
29
- }
@@ -1,72 +0,0 @@
1
- # SPDX-License-Identifier: Apache-2.0
2
- # SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
-
4
- # Seed template for the user-level playbook-code CODE overlay.
5
- # First run copies this file to:
6
- # ${XDG_CONFIG_HOME:-$HOME/.config}/playbook/playbook-code.config.yaml
7
- #
8
- # This is a CODE *overlay*, not a full tmux-play config: playbook-code
9
- # composes the launched tmux-play config from it at startup (PBCODE-16).
10
- # The composer injects captain.from (the Playbook Captain shell adapter)
11
- # and the coder / reviewer player ids, and can inherit theme plus
12
- # captain-judge defaults from an existing tmux-play config — so you only
13
- # tune the fields below.
14
- #
15
- # Safe tuning points:
16
- # - captain.adapter / model / reasoningEffort pick the judge/Captain
17
- # agent and its reasoning tier.
18
- # - players.coder and players.reviewer pick the Coder and Reviewer
19
- # agents; set each role's adapter (required) and optional model /
20
- # reasoningEffort / permissions. Each role's model when pinned, else
21
- # its adapter, doubles as the identity string the adapter substitutes
22
- # into <coder-llm> / <reviewer-llm> in player prompts (PBRT-4), so pin
23
- # a model if you want the Committer's commit-message trailers to name
24
- # the concrete model.
25
- # - Codex roles that run git under mode: auto need .git in
26
- # permissions.writablePaths so git metadata writes stay profile-scoped
27
- # instead of requiring bypass permissions.
28
- # - players.committer optionally aliases the Committer to a role
29
- # (coder or reviewer); that role's pane runs the commit turn. The
30
- # seeded value is reviewer; omit it to fall back to the coder.
31
- # - layout sizes the tmux window (layout.window: columns × rows) and
32
- # sets the relative column widths (layout.columnWeights) for the
33
- # Boss/Captain, Coder, and Reviewer columns.
34
- # - notifications configures tmux-play's host notifications. Supported
35
- # events are player_finished, turn_finished, and turn_aborted; each
36
- # event accepts off, bell, or desktop. The seeded defaults match
37
- # cligent's generated home config, with omitted turn_aborted resolving
38
- # to off.
39
-
40
- layout:
41
- window:
42
- columns: 174
43
- rows: 49
44
- columnWeights: [4, 6, 6]
45
-
46
- notifications:
47
- player_finished: bell
48
- turn_finished: desktop
49
-
50
- captain:
51
- adapter: claude
52
- model: claude-sonnet-4-6
53
- reasoningEffort: high
54
- permissions:
55
- mode: auto
56
-
57
- players:
58
- coder:
59
- adapter: codex
60
- model: gpt-5.5
61
- reasoningEffort: xhigh
62
- permissions:
63
- mode: auto
64
- writablePaths:
65
- - .git
66
- reviewer:
67
- adapter: claude
68
- model: claude-opus-4-8
69
- reasoningEffort: xhigh
70
- permissions:
71
- mode: auto
72
- committer: reviewer
@@ -1,50 +0,0 @@
1
- # SPDX-License-Identifier: Apache-2.0
2
- # SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
-
4
- # Example tmux-play config for the CODE playbook through the
5
- # Playbook Captain shell — DR-008 §6.
6
- # Run with:
7
- # tmux-play --config tmux-play.config.yaml
8
-
9
- notifications:
10
- player_finished: bell
11
- turn_finished: desktop
12
-
13
- captain:
14
- # Dev form: sibling-path import of the compiled shell adapter. After
15
- # @sublang/playbook is published, swap this for:
16
- # from: "@sublang/playbook/playbook-captain"
17
- # Existing explicit configs that still import ./code.tmux-play.js or
18
- # @sublang/playbook/code/tmux-play continue through a compatibility
19
- # shim that delegates to the same shell with CODE registered.
20
- from: ./playbook-captain.js
21
- adapter: claude
22
- model: claude-sonnet-4-6
23
- reasoningEffort: high
24
- # Agents run in cligent's classifier/reviewer-protected auto mode
25
- # (cligent DR-005): claude → permissionMode auto, codex → auto_review.
26
- permissions:
27
- mode: auto
28
-
29
- # `players[].id` shall match the baked playerId strings the adapter
30
- # routes to. The CODE playbook bakes Coder→'coder' and
31
- # Reviewer→'reviewer' at link time (DR-004 §2); the adapter does
32
- # not remap, so changing these ids would break callPlayer routing.
33
- # Each entry's `model` (when pinned) or `adapter` (otherwise) is
34
- # substituted into the <coder-llm> / <reviewer-llm> placeholders in
35
- # player prompts (DR-004 §6, PBRT-4) — pin a concrete `model:` if
36
- # you want the Committer's commit-message trailers to name the
37
- # specific model rather than the adapter family.
38
- players:
39
- - id: coder
40
- adapter: claude
41
- model: claude-opus-4-7
42
- reasoningEffort: xhigh
43
- permissions:
44
- mode: auto
45
- - id: reviewer
46
- adapter: codex
47
- model: gpt-5.5
48
- reasoningEffort: xhigh
49
- permissions:
50
- mode: auto
@@ -1,33 +0,0 @@
1
- # SPDX-License-Identifier: Apache-2.0
2
- # SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
-
4
- # Production tmux-play config for the published CODE playbook package.
5
- # This intentionally imports the Playbook Captain shell through the
6
- # package export, not a local repo path. The runtime still expects the
7
- # host app to provide the @sublang/cligent peer dependency.
8
-
9
- notifications:
10
- player_finished: bell
11
- turn_finished: desktop
12
-
13
- captain:
14
- from: "@sublang/playbook/playbook-captain"
15
- adapter: claude
16
- model: claude-sonnet-4-6
17
- reasoningEffort: high
18
- permissions:
19
- mode: auto
20
-
21
- players:
22
- - id: coder
23
- adapter: claude
24
- model: claude-opus-4-7
25
- reasoningEffort: xhigh
26
- permissions:
27
- mode: auto
28
- - id: reviewer
29
- adapter: codex
30
- model: gpt-5.5
31
- reasoningEffort: xhigh
32
- permissions:
33
- mode: auto