@sublang/playbook 18.0.1 → 18.1.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 +4 -3
- package/docs/configuration.md +22 -12
- package/package.json +7 -1
- package/reference/sdlc/code.playbook/bin/launch-config.js +65 -35
- package/reference/sdlc/code.playbook/bin/playbook.js +2 -2
- package/reference/sdlc/code.playbook/seed-config.d.ts +12 -0
- package/reference/sdlc/code.playbook/seed-config.js +22 -0
package/README.md
CHANGED
|
@@ -32,8 +32,9 @@ Run `playbook` for an interactive tmux UI powered by [cligent](https://github.co
|
|
|
32
32
|
Out of the box, Playbook includes **CODE** for implementation, **REVIEW** for commit-based review and fixes, **DECIDE** for independently proposed and reviewed specification decisions, **DEV** for repository-aware planning of a development request, **BRANCH** for checking out a new branch for a GitHub issue, **PR** for publishing, checking, and merging that branch through a pull request, and **INSPECT** for explaining supplied material or a running interface without changing the repository.
|
|
33
33
|
CODE and DECIDE call REVIEW as a nested playbook; DEV calls CODE, or DECIDE and then CODE, and wraps them in BRANCH and PR when the request names a GitHub issue or asks for a pull request.
|
|
34
34
|
|
|
35
|
-
The shared starter config selects
|
|
36
|
-
|
|
35
|
+
The shared starter config selects the Captain's and players' adapter from
|
|
36
|
+
locally visible credentials: Claude first, then Codex, and where both are
|
|
37
|
+
configured only `dev.reviewer` seeds on Codex. If neither is
|
|
37
38
|
configured, it seeds Claude and prints a notice. CODE, REVIEW, DECIDE, BRANCH, and PR bind
|
|
38
39
|
their local roles explicitly to `dev.coder` and `dev.reviewer`, so nested and later
|
|
39
40
|
engagements share a conversation only where their bindings name the same
|
|
@@ -51,7 +52,7 @@ If an SDK is missing or older than cligent supports, Playbook prints the pinned
|
|
|
51
52
|
Prerequisites:
|
|
52
53
|
|
|
53
54
|
- Node.js >= 20.6.0
|
|
54
|
-
- Credentials for the configured adapter: authenticated [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) or `ANTHROPIC_API_KEY` for Claude; authenticated [Codex CLI](https://github.com/openai/codex) or `OPENAI_API_KEY` for Codex
|
|
55
|
+
- Credentials for the configured adapter: authenticated [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) or `ANTHROPIC_API_KEY` / `CLAUDE_CODE_OAUTH_TOKEN` for Claude; authenticated [Codex CLI](https://github.com/openai/codex) or `OPENAI_API_KEY` / `CODEX_ACCESS_TOKEN` for Codex
|
|
55
56
|
- [GitHub CLI](https://cli.github.com/) (`gh`) authenticated for the repository's GitHub remote, for `/branch`, `/pr`, and `/dev` requests that name an issue or ask for a pull request
|
|
56
57
|
|
|
57
58
|
Interactive `playbook` additionally needs tmux 3.3 or newer and [`glow`](https://github.com/charmbracelet/glow#installation) on `PATH`; headless `playbook run` does not.
|
package/docs/configuration.md
CHANGED
|
@@ -64,16 +64,23 @@ one; see [Modules supplied at launch](#modules-supplied-at-launch). Every
|
|
|
64
64
|
manifest role must be present exactly once. The launcher injects the rest — you do not write host wiring by
|
|
65
65
|
hand.
|
|
66
66
|
|
|
67
|
-
The launcher seeds
|
|
68
|
-
|
|
69
|
-
for Claude or GPT-6 Sol for Codex, with Captain and Coder at `high` effort
|
|
70
|
-
and Reviewer and Analyst at `xhigh`. If neither adapter is configured, it
|
|
71
|
-
seeds Claude and prints a notice. Credentials do not prove SDK availability
|
|
72
|
-
or remaining quota; launch still checks the configured adapter. Existing
|
|
73
|
-
configs are left unchanged.
|
|
67
|
+
The launcher seeds the Captain and all stable players from locally visible
|
|
68
|
+
credentials (an API key in the environment, or `~/.claude` / `~/.codex`):
|
|
74
69
|
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
| Credentials | Seeded agents |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| Claude only, or neither | every agent on Claude Opus 5.5 (with a notice when neither is configured) |
|
|
73
|
+
| Codex only | every agent on GPT-6 Sol |
|
|
74
|
+
| both | `dev.reviewer` on GPT-6 Sol, every other agent on Claude Opus 5.5 |
|
|
75
|
+
|
|
76
|
+
Captain, Coder, and Inspector run at `high` effort and Reviewer and Analyst at
|
|
77
|
+
`xhigh`. Credentials do not prove SDK availability or remaining quota; launch
|
|
78
|
+
still checks the configured adapter. Existing configs are left unchanged.
|
|
79
|
+
Embedding hosts seed the same agent settings through
|
|
80
|
+
`seedConfigText({ env, homeDir })` from `@sublang/playbook/seed-config`.
|
|
81
|
+
|
|
82
|
+
The Claude default is shown below. Each Codex agent also carries
|
|
83
|
+
`permissions.writablePaths: ['.git']`:
|
|
77
84
|
|
|
78
85
|
```yaml
|
|
79
86
|
captain:
|
|
@@ -273,9 +280,12 @@ It may not finish the specialist’s task or invent a decision; discarding work
|
|
|
273
280
|
Preparation has a 150-second limit.
|
|
274
281
|
|
|
275
282
|
Adapter readiness is intentionally light: `claude` is ready with local
|
|
276
|
-
Claude Code auth
|
|
277
|
-
|
|
278
|
-
|
|
283
|
+
Claude Code auth, `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or
|
|
284
|
+
`CLAUDE_CODE_OAUTH_TOKEN`; `codex` with local Codex CLI auth,
|
|
285
|
+
`OPENAI_API_KEY`, `CODEX_API_KEY`, or `CODEX_ACCESS_TOKEN`. Only presence
|
|
286
|
+
counts; no token is checked. A known adapter that is not ready blocks the
|
|
287
|
+
launch and prints the help text. The same check picks the starter config's
|
|
288
|
+
lineup.
|
|
279
289
|
|
|
280
290
|
## Skills across agents
|
|
281
291
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sublang/playbook",
|
|
3
|
-
"version": "18.0
|
|
3
|
+
"version": "18.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Composable XState v5 playbook runtime with compiled Captain, CODE, REVIEW, DECIDE, DEV, BRANCH, PR, and INSPECT workflows driven by GEARS specs.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -83,6 +83,8 @@
|
|
|
83
83
|
"reference/sdlc/code.playbook/host-capabilities.d.ts",
|
|
84
84
|
"reference/sdlc/code.playbook/machine-identity.js",
|
|
85
85
|
"reference/sdlc/code.playbook/machine-identity.d.ts",
|
|
86
|
+
"reference/sdlc/code.playbook/seed-config.js",
|
|
87
|
+
"reference/sdlc/code.playbook/seed-config.d.ts",
|
|
86
88
|
"reference/sdlc/code.playbook/code.gears.md",
|
|
87
89
|
"reference/sdlc/code.playbook/playbook.config.template.yaml",
|
|
88
90
|
"reference/sdlc/code.playbook/bin/playbook.js",
|
|
@@ -212,6 +214,10 @@
|
|
|
212
214
|
"types": "./reference/sdlc/code.playbook/machine-identity.d.ts",
|
|
213
215
|
"default": "./reference/sdlc/code.playbook/machine-identity.js"
|
|
214
216
|
},
|
|
217
|
+
"./seed-config": {
|
|
218
|
+
"types": "./reference/sdlc/code.playbook/seed-config.d.ts",
|
|
219
|
+
"default": "./reference/sdlc/code.playbook/seed-config.js"
|
|
220
|
+
},
|
|
215
221
|
"./slc/*": "./slc/*",
|
|
216
222
|
"./review/playbook": {
|
|
217
223
|
"types": "./reference/sdlc/review.playbook/review.playbook.d.ts",
|
|
@@ -1438,23 +1438,36 @@ export function adaptersFromComposedConfig(config) {
|
|
|
1438
1438
|
return [...adapters];
|
|
1439
1439
|
}
|
|
1440
1440
|
|
|
1441
|
+
// DR-097: each agent's documented environment forms, any one of which, or the
|
|
1442
|
+
// agent's sign-in folder, counts it as ready. No value is read.
|
|
1443
|
+
const CREDENTIAL_FORMS = {
|
|
1444
|
+
claude: {
|
|
1445
|
+
names: ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN"],
|
|
1446
|
+
folder: ".claude",
|
|
1447
|
+
},
|
|
1448
|
+
codex: {
|
|
1449
|
+
names: ["OPENAI_API_KEY", "CODEX_API_KEY", "CODEX_ACCESS_TOKEN"],
|
|
1450
|
+
folder: ".codex",
|
|
1451
|
+
},
|
|
1452
|
+
};
|
|
1453
|
+
|
|
1441
1454
|
export function checkReadiness(adapters, env = process.env, home = homedir()) {
|
|
1442
1455
|
const failingAdapters = [];
|
|
1443
1456
|
const unknownAdapters = [];
|
|
1444
1457
|
for (const adapter of adapters) {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1458
|
+
const forms = Object.hasOwn(CREDENTIAL_FORMS, adapter)
|
|
1459
|
+
? CREDENTIAL_FORMS[adapter]
|
|
1460
|
+
: undefined;
|
|
1461
|
+
if (!forms) {
|
|
1462
|
+
unknownAdapters.push(adapter);
|
|
1449
1463
|
continue;
|
|
1450
1464
|
}
|
|
1451
|
-
if (
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1465
|
+
if (
|
|
1466
|
+
!forms.names.some((name) => env[name]) &&
|
|
1467
|
+
!existsSync(join(home, forms.folder))
|
|
1468
|
+
) {
|
|
1469
|
+
failingAdapters.push(adapter);
|
|
1456
1470
|
}
|
|
1457
|
-
unknownAdapters.push(adapter);
|
|
1458
1471
|
}
|
|
1459
1472
|
return { failingAdapters, unknownAdapters };
|
|
1460
1473
|
}
|
|
@@ -1651,9 +1664,14 @@ const SEED_LINEUP = Object.freeze([
|
|
|
1651
1664
|
Object.freeze({ adapter: "codex", model: "gpt-6-sol" }),
|
|
1652
1665
|
]);
|
|
1653
1666
|
|
|
1667
|
+
// DR-096: where both adapters are ready, this player alone takes the
|
|
1668
|
+
// second row, so review comes from a different provider than the work.
|
|
1669
|
+
const SECOND_ADAPTER_PLAYER = "dev.reviewer";
|
|
1670
|
+
|
|
1654
1671
|
/**
|
|
1655
1672
|
* PBCLI-11: the lineup a seed would write here, and whether anything
|
|
1656
|
-
* actually probed ready.
|
|
1673
|
+
* actually probed ready. `reviewer` is the row `dev.reviewer` takes;
|
|
1674
|
+
* every other agent takes `lineup`.
|
|
1657
1675
|
*
|
|
1658
1676
|
* Only the credential check participates. The SDK probe constructs an
|
|
1659
1677
|
* adapter and awaits `isAvailable()`, which reads whatever happens to
|
|
@@ -1666,7 +1684,12 @@ export function selectSeedLineup(env = process.env, home = homedir()) {
|
|
|
1666
1684
|
(entry) =>
|
|
1667
1685
|
checkReadiness([entry.adapter], env, home).failingAdapters.length === 0,
|
|
1668
1686
|
);
|
|
1669
|
-
|
|
1687
|
+
const lineup = ready[0] ?? SEED_LINEUP[0];
|
|
1688
|
+
return {
|
|
1689
|
+
lineup,
|
|
1690
|
+
reviewer: ready.length > 1 ? ready[1] : lineup,
|
|
1691
|
+
anyReady: ready.length > 0,
|
|
1692
|
+
};
|
|
1670
1693
|
}
|
|
1671
1694
|
|
|
1672
1695
|
/**
|
|
@@ -1675,9 +1698,7 @@ export function selectSeedLineup(env = process.env, home = homedir()) {
|
|
|
1675
1698
|
* byte copy it always was. A different adapter rewrites the agent
|
|
1676
1699
|
* scalars in place, which the Document API carries comments through.
|
|
1677
1700
|
*/
|
|
1678
|
-
function seedTextForLineup(
|
|
1679
|
-
const template = readFileSync(templatePath, "utf8");
|
|
1680
|
-
if (lineup.adapter === SEED_LINEUP[0].adapter) return template;
|
|
1701
|
+
function seedTextForLineup(template, { lineup, reviewer }) {
|
|
1681
1702
|
const doc = parseYamlDocument(template);
|
|
1682
1703
|
const players = doc.getIn(["players"]);
|
|
1683
1704
|
const agentPaths = [
|
|
@@ -1687,18 +1708,35 @@ function seedTextForLineup(templatePath, lineup) {
|
|
|
1687
1708
|
String(pair.key),
|
|
1688
1709
|
]),
|
|
1689
1710
|
];
|
|
1711
|
+
let changed = false;
|
|
1690
1712
|
for (const path of agentPaths) {
|
|
1691
|
-
|
|
1692
|
-
|
|
1713
|
+
const entry = path[1] === SECOND_ADAPTER_PLAYER ? reviewer : lineup;
|
|
1714
|
+
if (entry.adapter === SEED_LINEUP[0].adapter) continue;
|
|
1715
|
+
changed = true;
|
|
1716
|
+
doc.setIn([...path, "adapter"], entry.adapter);
|
|
1717
|
+
doc.setIn([...path, "model"], entry.model);
|
|
1693
1718
|
// A codex agent writes git metadata under its sandbox; claude's
|
|
1694
1719
|
// auto mode needs no grant, so the key exists for one adapter only.
|
|
1695
|
-
|
|
1696
|
-
doc.setIn([...path, "permissions", "writablePaths"], [".git"]);
|
|
1697
|
-
} else {
|
|
1698
|
-
doc.deleteIn([...path, "permissions", "writablePaths"]);
|
|
1699
|
-
}
|
|
1720
|
+
doc.setIn([...path, "permissions", "writablePaths"], [".git"]);
|
|
1700
1721
|
}
|
|
1701
|
-
return String(doc);
|
|
1722
|
+
return changed ? String(doc) : template;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/**
|
|
1726
|
+
* PBCLI-100: the starter text a seed writes under this environment and
|
|
1727
|
+
* home. The public `seed-config` facade and the launcher both call it,
|
|
1728
|
+
* so every host seeding through it selects the same agent settings.
|
|
1729
|
+
*/
|
|
1730
|
+
export function seedConfigText(
|
|
1731
|
+
templatePath,
|
|
1732
|
+
env = process.env,
|
|
1733
|
+
homeDir = env.HOME ?? homedir(),
|
|
1734
|
+
) {
|
|
1735
|
+
const selection = selectSeedLineup(env, homeDir);
|
|
1736
|
+
return {
|
|
1737
|
+
text: seedTextForLineup(readFileSync(templatePath, "utf8"), selection),
|
|
1738
|
+
anyReady: selection.anyReady,
|
|
1739
|
+
};
|
|
1702
1740
|
}
|
|
1703
1741
|
|
|
1704
1742
|
function seedUserConfigIfMissing(
|
|
@@ -1710,21 +1748,13 @@ function seedUserConfigIfMissing(
|
|
|
1710
1748
|
) {
|
|
1711
1749
|
if (existsSync(userConfigPath)) return;
|
|
1712
1750
|
mkdirSync(dirname(userConfigPath), { recursive: true });
|
|
1713
|
-
const {
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
copyFileSync(templatePath, userConfigPath, constants.COPYFILE_EXCL);
|
|
1717
|
-
} else {
|
|
1718
|
-
// Still one exclusive create, so two seeding calls cannot disagree.
|
|
1719
|
-
writeFileSync(userConfigPath, seedTextForLineup(templatePath, lineup), {
|
|
1720
|
-
encoding: "utf8",
|
|
1721
|
-
flag: "wx",
|
|
1722
|
-
});
|
|
1723
|
-
}
|
|
1751
|
+
const { text, anyReady } = seedConfigText(templatePath, env, homeDir);
|
|
1752
|
+
// One exclusive create, so two seeding calls cannot disagree.
|
|
1753
|
+
writeFileSync(userConfigPath, text, { encoding: "utf8", flag: "wx" });
|
|
1724
1754
|
onNotice(`playbook: created config at ${userConfigPath}\n`);
|
|
1725
1755
|
if (!anyReady) {
|
|
1726
1756
|
onNotice(
|
|
1727
|
-
`playbook: no adapter probed ready; seeded ${
|
|
1757
|
+
`playbook: no adapter probed ready; seeded ${SEED_LINEUP[0].adapter}\n`,
|
|
1728
1758
|
);
|
|
1729
1759
|
}
|
|
1730
1760
|
}
|
|
@@ -993,9 +993,9 @@ function helpText({
|
|
|
993
993
|
"",
|
|
994
994
|
"Adapter setup:",
|
|
995
995
|
" claude: npm install -g @anthropic-ai/claude-agent-sdk, then run",
|
|
996
|
-
" Claude Code once or set ANTHROPIC_API_KEY.",
|
|
996
|
+
" Claude Code once or set ANTHROPIC_API_KEY (or CLAUDE_CODE_OAUTH_TOKEN).",
|
|
997
997
|
" codex: npm install -g @openai/codex-sdk, then run Codex CLI once",
|
|
998
|
-
" or set OPENAI_API_KEY.",
|
|
998
|
+
" or set OPENAI_API_KEY (or CODEX_API_KEY, CODEX_ACCESS_TOKEN).",
|
|
999
999
|
" Each SDK is an optional peer dependency, so you install only the",
|
|
1000
1000
|
" vendors your config actually names.",
|
|
1001
1001
|
"",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
+
|
|
4
|
+
export interface SeedConfigOptions {
|
|
5
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
6
|
+
readonly homeDir?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SeedConfigText {
|
|
9
|
+
readonly text: string;
|
|
10
|
+
readonly anyReady: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function seedConfigText(options?: SeedConfigOptions): SeedConfigText;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
+
|
|
4
|
+
// DR-096: the public `@sublang/playbook/seed-config` facade. The text is
|
|
5
|
+
// the launcher's own seed (playbook-cli-11); this module only fixes the
|
|
6
|
+
// bundled template and narrows the accepted options (playbook-cli-100).
|
|
7
|
+
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { seedConfigText as seedText } from './bin/launch-config.js';
|
|
10
|
+
|
|
11
|
+
const TEMPLATE_PATH = fileURLToPath(
|
|
12
|
+
new URL('./playbook.config.template.yaml', import.meta.url),
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export function seedConfigText(options = {}) {
|
|
16
|
+
const env = options.env ?? process.env;
|
|
17
|
+
return seedText(
|
|
18
|
+
TEMPLATE_PATH,
|
|
19
|
+
env,
|
|
20
|
+
...(options.homeDir !== undefined ? [options.homeDir] : []),
|
|
21
|
+
);
|
|
22
|
+
}
|