@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.
Files changed (20) hide show
  1. package/README.md +98 -30
  2. package/package.json +24 -23
  3. package/reference/sdlc/code.playbook/bin/playbook-code.js +278 -0
  4. package/{code.fsm.d.ts → reference/sdlc/code.playbook/code.fsm.d.ts} +18 -0
  5. package/{code.fsm.introspect.d.ts → reference/sdlc/code.playbook/code.fsm.introspect.d.ts} +24 -0
  6. package/{code.fsm.introspect.js → reference/sdlc/code.playbook/code.fsm.introspect.js} +28 -1
  7. package/{code.fsm.introspect.ts → reference/sdlc/code.playbook/code.fsm.introspect.ts} +73 -6
  8. package/{code.fsm.js → reference/sdlc/code.playbook/code.fsm.js} +264 -115
  9. package/{code.fsm.ts → reference/sdlc/code.playbook/code.fsm.ts} +327 -119
  10. package/{code.gears.md → reference/sdlc/code.playbook/code.gears.md} +17 -16
  11. package/{code.playbook.d.ts → reference/sdlc/code.playbook/code.playbook.d.ts} +16 -6
  12. package/{code.playbook.js → reference/sdlc/code.playbook/code.playbook.js} +228 -129
  13. package/{code.playbook.ts → reference/sdlc/code.playbook/code.playbook.ts} +316 -122
  14. package/{code.tmux-play.js → reference/sdlc/code.playbook/code.tmux-play.js} +22 -10
  15. package/{code.tmux-play.ts → reference/sdlc/code.playbook/code.tmux-play.ts} +26 -13
  16. package/reference/sdlc/code.playbook/playbook-code.config.template.yaml +38 -0
  17. package/{tmux-play.config.yaml → reference/sdlc/code.playbook/tmux-play.config.yaml} +14 -10
  18. package/{tmux-play.production.config.yaml → reference/sdlc/code.playbook/tmux-play.production.config.yaml} +7 -4
  19. package/bin/playbook-code.js +0 -43
  20. /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
- // for the CODE playbook this is `CodePlaybookOptions` (coderPlayer,
7
- // reviewerPlayer, plus the rest of CodingInput).
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. PlaybookPorts is built once at init, so the per-turn
13
- // context lives in a closure-scoped slot the port callbacks query
14
- // lazily.
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
- // RoleRunResult per TMUX-033 already matches PlayerResult
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.callRole(playerId, prompt);
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.dispose();
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
- // for the CODE playbook this is `CodePlaybookOptions` (coderPlayer,
23
- // reviewerPlayer, plus the rest of CodingInput).
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. PlaybookPorts is built once at init, so the per-turn
34
- // context lives in a closure-scoped slot the port callbacks query
35
- // lazily.
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
- // RoleRunResult per TMUX-033 already matches PlayerResult
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.callRole(playerId, prompt);
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.dispose();
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). Roles,
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
- options:
19
- # Substituted into the <coder-llm> placeholder in player prompts
20
- # (DR-004 §6).
21
- coderPlayer: claude
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
- # `roles[].id` shall match the baked playerId strings the adapter
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 callRole routing.
29
- roles:
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
- options:
14
- coderPlayer: claude
15
- reviewerPlayer: codex
13
+ permissions:
14
+ mode: auto
16
15
 
17
- roles:
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
@@ -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
- });