@sublang/playbook 0.1.3 → 0.4.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 +98 -30
- package/package.json +24 -23
- package/reference/sdlc/code.playbook/bin/playbook-code.js +278 -0
- package/{code.fsm.d.ts → reference/sdlc/code.playbook/code.fsm.d.ts} +18 -0
- package/{code.fsm.introspect.d.ts → reference/sdlc/code.playbook/code.fsm.introspect.d.ts} +24 -0
- package/{code.fsm.introspect.js → reference/sdlc/code.playbook/code.fsm.introspect.js} +28 -1
- package/{code.fsm.introspect.ts → reference/sdlc/code.playbook/code.fsm.introspect.ts} +73 -6
- package/{code.fsm.js → reference/sdlc/code.playbook/code.fsm.js} +264 -115
- package/{code.fsm.ts → reference/sdlc/code.playbook/code.fsm.ts} +327 -119
- package/{code.gears.md → reference/sdlc/code.playbook/code.gears.md} +17 -16
- package/{code.playbook.d.ts → reference/sdlc/code.playbook/code.playbook.d.ts} +16 -6
- package/{code.playbook.js → reference/sdlc/code.playbook/code.playbook.js} +228 -129
- package/{code.playbook.ts → reference/sdlc/code.playbook/code.playbook.ts} +316 -122
- package/{code.tmux-play.js → reference/sdlc/code.playbook/code.tmux-play.js} +22 -10
- package/{code.tmux-play.ts → reference/sdlc/code.playbook/code.tmux-play.ts} +26 -13
- package/reference/sdlc/code.playbook/playbook-code.config.template.yaml +38 -0
- package/{tmux-play.config.yaml → reference/sdlc/code.playbook/tmux-play.config.yaml} +14 -10
- package/{tmux-play.production.config.yaml → reference/sdlc/code.playbook/tmux-play.production.config.yaml} +7 -4
- package/bin/playbook-code.js +0 -43
- /package/{code.tmux-play.d.ts → reference/sdlc/code.playbook/code.tmux-play.d.ts} +0 -0
|
@@ -2,31 +2,40 @@
|
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
3
|
import createPlaybookRuntime from './code.playbook.js';
|
|
4
4
|
// Captain factory per TMUX-014: `(options: unknown) => Captain`.
|
|
5
|
-
// `options` is whatever `captain.options` carries in the YAML config
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// `options` is whatever `captain.options` carries in the YAML config.
|
|
6
|
+
// The per-run player identity strings (`coderPlayer`, `reviewerPlayer`)
|
|
7
|
+
// are derived from `session.players` at init time per PBRT-4; any
|
|
8
|
+
// same-named keys in `options` are ignored.
|
|
8
9
|
export default function createCodeTmuxPlayCaptain(options) {
|
|
9
|
-
const runtime = createPlaybookRuntime(options);
|
|
10
10
|
// CaptainSession is bound at init time and persists across turns;
|
|
11
11
|
// CaptainContext is rebuilt per turn and carries the call
|
|
12
|
-
// primitives.
|
|
13
|
-
//
|
|
14
|
-
//
|
|
12
|
+
// primitives. The runtime is constructed in `init` so identity
|
|
13
|
+
// strings derived from `session.players` can flow into its options;
|
|
14
|
+
// PlaybookPorts is built once at init, so the per-turn context
|
|
15
|
+
// lives in a closure-scoped slot the port callbacks query lazily.
|
|
16
|
+
let runtime;
|
|
15
17
|
let activeContext;
|
|
16
18
|
return {
|
|
17
19
|
async init(session) {
|
|
20
|
+
const coderPlayer = session.players.find((p) => p.id === 'coder')?.adapter;
|
|
21
|
+
const reviewerPlayer = session.players.find((p) => p.id === 'reviewer')?.adapter;
|
|
22
|
+
runtime = createPlaybookRuntime({
|
|
23
|
+
...options,
|
|
24
|
+
coderPlayer,
|
|
25
|
+
reviewerPlayer,
|
|
26
|
+
});
|
|
18
27
|
const ports = {
|
|
19
28
|
callPlayer: async (playerId, prompt, _signal) => {
|
|
20
29
|
if (!activeContext) {
|
|
21
30
|
throw new Error('callPlayer invoked outside a Boss turn');
|
|
22
31
|
}
|
|
23
|
-
//
|
|
32
|
+
// PlayerRunResult per TMUX-033 already matches PlayerResult
|
|
24
33
|
// (`status: 'ok' | 'aborted' | 'error'`, `finalText?`,
|
|
25
34
|
// `error?`). cligent honors context.signal internally;
|
|
26
35
|
// the runtime's signal is the same source forwarded
|
|
27
36
|
// through handleBossInput, so dropping `_signal` is
|
|
28
37
|
// safe.
|
|
29
|
-
const r = await activeContext.
|
|
38
|
+
const r = await activeContext.callPlayer(playerId, prompt);
|
|
30
39
|
return {
|
|
31
40
|
status: r.status,
|
|
32
41
|
finalText: r.finalText,
|
|
@@ -56,6 +65,9 @@ export default function createCodeTmuxPlayCaptain(options) {
|
|
|
56
65
|
await runtime.init(ports);
|
|
57
66
|
},
|
|
58
67
|
async handleBossTurn(turn, context) {
|
|
68
|
+
if (!runtime) {
|
|
69
|
+
throw new Error('init must be called first');
|
|
70
|
+
}
|
|
59
71
|
activeContext = context;
|
|
60
72
|
try {
|
|
61
73
|
// Forward the Boss prompt + cligent's per-turn signal into
|
|
@@ -71,7 +83,7 @@ export default function createCodeTmuxPlayCaptain(options) {
|
|
|
71
83
|
}
|
|
72
84
|
},
|
|
73
85
|
async dispose() {
|
|
74
|
-
await runtime
|
|
86
|
+
await runtime?.dispose();
|
|
75
87
|
},
|
|
76
88
|
};
|
|
77
89
|
}
|
|
@@ -18,37 +18,47 @@ import createPlaybookRuntime, {
|
|
|
18
18
|
} from './code.playbook.js';
|
|
19
19
|
|
|
20
20
|
// Captain factory per TMUX-014: `(options: unknown) => Captain`.
|
|
21
|
-
// `options` is whatever `captain.options` carries in the YAML config
|
|
22
|
-
//
|
|
23
|
-
//
|
|
21
|
+
// `options` is whatever `captain.options` carries in the YAML config.
|
|
22
|
+
// The per-run player identity strings (`coderPlayer`, `reviewerPlayer`)
|
|
23
|
+
// are derived from `session.players` at init time per PBRT-4; any
|
|
24
|
+
// same-named keys in `options` are ignored.
|
|
24
25
|
export default function createCodeTmuxPlayCaptain(
|
|
25
26
|
options: unknown,
|
|
26
27
|
): Captain {
|
|
27
|
-
const runtime: PlaybookRuntime = createPlaybookRuntime(
|
|
28
|
-
options as CodePlaybookOptions,
|
|
29
|
-
);
|
|
30
|
-
|
|
31
28
|
// CaptainSession is bound at init time and persists across turns;
|
|
32
29
|
// CaptainContext is rebuilt per turn and carries the call
|
|
33
|
-
// primitives.
|
|
34
|
-
//
|
|
35
|
-
//
|
|
30
|
+
// primitives. The runtime is constructed in `init` so identity
|
|
31
|
+
// strings derived from `session.players` can flow into its options;
|
|
32
|
+
// PlaybookPorts is built once at init, so the per-turn context
|
|
33
|
+
// lives in a closure-scoped slot the port callbacks query lazily.
|
|
34
|
+
let runtime: PlaybookRuntime | undefined;
|
|
36
35
|
let activeContext: CaptainContext | undefined;
|
|
37
36
|
|
|
38
37
|
return {
|
|
39
38
|
async init(session: CaptainSession): Promise<void> {
|
|
39
|
+
const coderPlayer = session.players.find(
|
|
40
|
+
(p) => p.id === 'coder',
|
|
41
|
+
)?.adapter;
|
|
42
|
+
const reviewerPlayer = session.players.find(
|
|
43
|
+
(p) => p.id === 'reviewer',
|
|
44
|
+
)?.adapter;
|
|
45
|
+
runtime = createPlaybookRuntime({
|
|
46
|
+
...(options as CodePlaybookOptions),
|
|
47
|
+
coderPlayer,
|
|
48
|
+
reviewerPlayer,
|
|
49
|
+
});
|
|
40
50
|
const ports: PlaybookPorts = {
|
|
41
51
|
callPlayer: async (playerId, prompt, _signal) => {
|
|
42
52
|
if (!activeContext) {
|
|
43
53
|
throw new Error('callPlayer invoked outside a Boss turn');
|
|
44
54
|
}
|
|
45
|
-
//
|
|
55
|
+
// PlayerRunResult per TMUX-033 already matches PlayerResult
|
|
46
56
|
// (`status: 'ok' | 'aborted' | 'error'`, `finalText?`,
|
|
47
57
|
// `error?`). cligent honors context.signal internally;
|
|
48
58
|
// the runtime's signal is the same source forwarded
|
|
49
59
|
// through handleBossInput, so dropping `_signal` is
|
|
50
60
|
// safe.
|
|
51
|
-
const r = await activeContext.
|
|
61
|
+
const r = await activeContext.callPlayer(playerId, prompt);
|
|
52
62
|
return {
|
|
53
63
|
status: r.status,
|
|
54
64
|
finalText: r.finalText,
|
|
@@ -89,6 +99,9 @@ export default function createCodeTmuxPlayCaptain(
|
|
|
89
99
|
turn: BossTurn,
|
|
90
100
|
context: CaptainContext,
|
|
91
101
|
): Promise<void> {
|
|
102
|
+
if (!runtime) {
|
|
103
|
+
throw new Error('init must be called first');
|
|
104
|
+
}
|
|
92
105
|
activeContext = context;
|
|
93
106
|
try {
|
|
94
107
|
// Forward the Boss prompt + cligent's per-turn signal into
|
|
@@ -104,7 +117,7 @@ export default function createCodeTmuxPlayCaptain(
|
|
|
104
117
|
},
|
|
105
118
|
|
|
106
119
|
async dispose(): Promise<void> {
|
|
107
|
-
await runtime
|
|
120
|
+
await runtime?.dispose();
|
|
108
121
|
},
|
|
109
122
|
};
|
|
110
123
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
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 config.
|
|
5
|
+
# First run copies this file to:
|
|
6
|
+
# ${XDG_CONFIG_HOME:-$HOME/.config}/playbook/playbook-code.config.yaml
|
|
7
|
+
#
|
|
8
|
+
# Safe tuning points:
|
|
9
|
+
# - Change captain.adapter / captain.model to pick the judge/Captain agent.
|
|
10
|
+
# - Change each player's adapter to pick the Coder and Reviewer agents.
|
|
11
|
+
#
|
|
12
|
+
# PBRT-4 host-configuration invariants:
|
|
13
|
+
# - captain.from must keep pointing at the CODE tmux-play adapter module.
|
|
14
|
+
# - players[].id must remain exactly "coder" and "reviewer"; the runtime routes
|
|
15
|
+
# to those baked ids and does not remap them. The adapter derives the
|
|
16
|
+
# per-run player identity strings (substituted into player prompts) from
|
|
17
|
+
# each entry's adapter.
|
|
18
|
+
|
|
19
|
+
captain:
|
|
20
|
+
# PBRT-4 invariant: keep this adapter module path unchanged.
|
|
21
|
+
from: "@sublang/playbook/code/tmux-play"
|
|
22
|
+
# Tunable: the Captain/Judge adapter and model.
|
|
23
|
+
adapter: claude
|
|
24
|
+
model: claude-opus-4-7
|
|
25
|
+
permissions:
|
|
26
|
+
mode: auto
|
|
27
|
+
|
|
28
|
+
players:
|
|
29
|
+
# PBRT-4 invariant: keep id: coder. Tune adapter/model if desired.
|
|
30
|
+
- id: coder
|
|
31
|
+
adapter: claude
|
|
32
|
+
permissions:
|
|
33
|
+
mode: auto
|
|
34
|
+
# PBRT-4 invariant: keep id: reviewer. Tune adapter/model if desired.
|
|
35
|
+
- id: reviewer
|
|
36
|
+
adapter: codex
|
|
37
|
+
permissions:
|
|
38
|
+
mode: auto
|
|
@@ -10,24 +10,28 @@ captain:
|
|
|
10
10
|
# @sublang/playbook is published, swap this for the package
|
|
11
11
|
# specifier — e.g.
|
|
12
12
|
# from: "@sublang/playbook/code/tmux-play"
|
|
13
|
-
# (final form confirmed at publish time per DR-004 §11).
|
|
13
|
+
# (final form confirmed at publish time per DR-004 §11). Players,
|
|
14
14
|
# options, and the rest of the config are unchanged.
|
|
15
15
|
from: ./code.tmux-play.js
|
|
16
16
|
adapter: claude
|
|
17
17
|
model: claude-opus-4-7
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
# Substituted into <reviewer-llm>.
|
|
23
|
-
reviewerPlayer: codex
|
|
18
|
+
# Agents run in cligent's classifier/reviewer-protected auto mode
|
|
19
|
+
# (cligent DR-005): claude → permissionMode auto, codex → auto_review.
|
|
20
|
+
permissions:
|
|
21
|
+
mode: auto
|
|
24
22
|
|
|
25
|
-
# `
|
|
23
|
+
# `players[].id` shall match the baked playerId strings the adapter
|
|
26
24
|
# routes to. The CODE playbook bakes Coder→'coder' and
|
|
27
25
|
# Reviewer→'reviewer' at link time (DR-004 §2); the adapter does
|
|
28
|
-
# not remap, so changing these ids would break
|
|
29
|
-
|
|
26
|
+
# not remap, so changing these ids would break callPlayer routing.
|
|
27
|
+
# Each entry's `adapter` is also substituted into the <coder-llm> /
|
|
28
|
+
# <reviewer-llm> placeholders in player prompts (DR-004 §6).
|
|
29
|
+
players:
|
|
30
30
|
- id: coder
|
|
31
31
|
adapter: claude
|
|
32
|
+
permissions:
|
|
33
|
+
mode: auto
|
|
32
34
|
- id: reviewer
|
|
33
35
|
adapter: codex
|
|
36
|
+
permissions:
|
|
37
|
+
mode: auto
|
|
@@ -10,12 +10,15 @@ captain:
|
|
|
10
10
|
from: "@sublang/playbook/code/tmux-play"
|
|
11
11
|
adapter: claude
|
|
12
12
|
model: claude-opus-4-7
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
reviewerPlayer: codex
|
|
13
|
+
permissions:
|
|
14
|
+
mode: auto
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
players:
|
|
18
17
|
- id: coder
|
|
19
18
|
adapter: claude
|
|
19
|
+
permissions:
|
|
20
|
+
mode: auto
|
|
20
21
|
- id: reviewer
|
|
21
22
|
adapter: codex
|
|
23
|
+
permissions:
|
|
24
|
+
mode: auto
|
package/bin/playbook-code.js
DELETED
|
@@ -1,43 +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
|
-
// Thin shim that resolves the bundled tmux-play.production.config.yaml
|
|
6
|
-
// relative to this script's own location and execs cligent's tmux-play
|
|
7
|
-
// CLI from inside @sublang/playbook's own node_modules tree. We resolve
|
|
8
|
-
// the CLI through cligent's `./tmux-play` subpath export instead of
|
|
9
|
-
// relying on `tmux-play` being on PATH — npm global installs only link
|
|
10
|
-
// bins from top-level packages, so a transitive cligent's bin won't
|
|
11
|
-
// reach the user's PATH but its files are still resolvable here.
|
|
12
|
-
|
|
13
|
-
import { spawn } from 'node:child_process';
|
|
14
|
-
import { dirname, join, resolve } from 'node:path';
|
|
15
|
-
import { fileURLToPath } from 'node:url';
|
|
16
|
-
|
|
17
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
const configPath = resolve(here, '..', 'tmux-play.production.config.yaml');
|
|
19
|
-
|
|
20
|
-
// cligent's `./tmux-play` subpath export only declares "import"
|
|
21
|
-
// (no "require"/"default"), so createRequire(...).resolve trips
|
|
22
|
-
// ERR_PACKAGE_PATH_NOT_EXPORTED. import.meta.resolve uses ESM rules
|
|
23
|
-
// and follows the "import" condition — works from Node 20.6+.
|
|
24
|
-
const tmuxPlayIndexUrl = import.meta.resolve('@sublang/cligent/tmux-play');
|
|
25
|
-
const tmuxPlayBin = join(dirname(fileURLToPath(tmuxPlayIndexUrl)), 'cli.js');
|
|
26
|
-
|
|
27
|
-
const child = spawn(
|
|
28
|
-
process.execPath,
|
|
29
|
-
[tmuxPlayBin, '--config', configPath, ...process.argv.slice(2)],
|
|
30
|
-
{ stdio: 'inherit' },
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
child.on('error', (err) => {
|
|
34
|
-
process.stderr.write(
|
|
35
|
-
`playbook-code: failed to launch tmux-play (${tmuxPlayBin}): ${err.message}\n`,
|
|
36
|
-
);
|
|
37
|
-
process.exit(127);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
child.on('exit', (code, signal) => {
|
|
41
|
-
if (signal) process.kill(process.pid, signal);
|
|
42
|
-
else process.exit(code ?? 0);
|
|
43
|
-
});
|
|
File without changes
|