@punk6529/playbook 0.2.3 → 0.2.5

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 CHANGED
@@ -166,6 +166,16 @@ playbook query docker --json # JSON 格式输出
166
166
 
167
167
  这个命令是 AI skill 的底层工具。`/playbook-advisor` 和 `/playbook-query` skill 在内部调用它来搜索知识库,节省 token 开销。
168
168
 
169
+ ### `playbook show`
170
+
171
+ 输出指定案例的完整内容。AI skill 用它查看案例详情(L2),无需知道文件路径。
172
+
173
+ ```bash
174
+ playbook show case-001-docker-bind # 输出案例文件内容
175
+ ```
176
+
177
+ 先查项目 INDEX,再查全局 INDEX,找到后输出对应文件内容到 stdout。
178
+
169
179
  ### `playbook hit`
170
180
 
171
181
  记录一次案例阅读命中,用于追踪案例的实际使用频率。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@punk6529/playbook",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "CLI for initializing and updating Playbook & Case workflows.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -1,11 +1,12 @@
1
1
  const { CliError } = require("./errors");
2
- const { parseProjectCommandArgs, parseGlobalInitArgs, parseQueryArgs, parsePromoteArgs, parseHitArgs } = require("./options");
2
+ const { parseProjectCommandArgs, parseGlobalInitArgs, parseQueryArgs, parsePromoteArgs, parseHitArgs, parseShowArgs } = require("./options");
3
3
  const { initProject } = require("./commands/init-project");
4
4
  const { initGlobal } = require("./commands/global-init");
5
5
  const { updateProject } = require("./commands/update-project");
6
6
  const { queryIndex, aggregateTags, formatPlainText, formatJson, formatTagsOnly } = require("./commands/query");
7
7
  const { promoteEntries } = require("./commands/promote");
8
8
  const { hitEntry } = require("./commands/hit");
9
+ const { showEntry } = require("./commands/show");
9
10
  const { printSummary } = require("./reporting");
10
11
 
11
12
  function usageText() {
@@ -18,6 +19,7 @@ function usageText() {
18
19
  " playbook promote <entry-id...> [--force]",
19
20
  " playbook promote --all [--force]",
20
21
  " playbook hit <entry-id>",
22
+ " playbook show <entry-id>",
21
23
  "",
22
24
  "Notes:",
23
25
  " - `init` and `update` are project-scoped commands.",
@@ -178,6 +180,30 @@ function runHit(args, io) {
178
180
  return 0;
179
181
  }
180
182
 
183
+ function showHelpText() {
184
+ return [
185
+ "Usage: playbook show <entry-id>",
186
+ "",
187
+ "Display the full content of a playbook entry.",
188
+ "Searches project INDEX first, then global INDEX.",
189
+ "",
190
+ "Options:",
191
+ " -h, --help Show this help",
192
+ ].join("\n");
193
+ }
194
+
195
+ function runShow(args, io) {
196
+ const options = parseShowArgs(args);
197
+ if (options.help) {
198
+ writeStdout(io, showHelpText());
199
+ return 0;
200
+ }
201
+
202
+ const result = showEntry(options);
203
+ writeStdout(io, result.content);
204
+ return 0;
205
+ }
206
+
181
207
  function runGlobal(args, io) {
182
208
  const subcommand = args[0];
183
209
  if (!subcommand) {
@@ -235,11 +261,15 @@ async function runCli(argv, io = process) {
235
261
  return runHit(argv.slice(1), io);
236
262
  }
237
263
 
264
+ if (command === "show") {
265
+ return runShow(argv.slice(1), io);
266
+ }
267
+
238
268
  if (command === "global") {
239
269
  return runGlobal(argv.slice(1), io);
240
270
  }
241
271
 
242
- throw new CliError(`Unknown command: ${command}. Supported: init, global, update, query, promote, hit`);
272
+ throw new CliError(`Unknown command: ${command}. Supported: init, global, update, query, promote, hit, show`);
243
273
  } catch (error) {
244
274
  if (error instanceof CliError) {
245
275
  writeStderr(io, `Error: ${error.message}`);
@@ -0,0 +1,57 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const os = require("os");
4
+ const { CliError } = require("../errors");
5
+
6
+ function readIndex(indexPath) {
7
+ try {
8
+ const content = fs.readFileSync(indexPath, "utf8");
9
+ return JSON.parse(content);
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+
15
+ function showEntry(options) {
16
+ const { entryId, targetPath = "." } = options;
17
+
18
+ const projectIndexPath = path.resolve(targetPath, "docs/playbook/INDEX.json");
19
+ const globalIndexPath = path.join(os.homedir(), ".playbook/repo/INDEX.json");
20
+
21
+ // Try project first
22
+ const projectIndex = readIndex(projectIndexPath);
23
+ if (projectIndex && projectIndex[entryId]) {
24
+ const entryPath = projectIndex[entryId].path;
25
+ const fullPath = path.resolve(targetPath, "docs/playbook", entryPath);
26
+ const content = readFile(fullPath, entryId);
27
+ return { entryId, scope: "project", content };
28
+ }
29
+
30
+ // Fall back to global
31
+ const globalIndex = readIndex(globalIndexPath);
32
+ if (globalIndex && globalIndex[entryId]) {
33
+ const entryPath = globalIndex[entryId].path;
34
+ const fullPath = path.join(os.homedir(), ".playbook/repo", entryPath);
35
+ const content = readFile(fullPath, entryId);
36
+ return { entryId, scope: "global", content };
37
+ }
38
+
39
+ // Not found
40
+ if (!projectIndex && !globalIndex) {
41
+ throw new CliError("No INDEX.json found. Run 'playbook init' or 'playbook global init' first.");
42
+ }
43
+
44
+ throw new CliError(`Entry '${entryId}' not found in project or global INDEX.`);
45
+ }
46
+
47
+ function readFile(fullPath, entryId) {
48
+ try {
49
+ return fs.readFileSync(fullPath, "utf8");
50
+ } catch {
51
+ throw new CliError(`File not found for entry '${entryId}': ${fullPath}`);
52
+ }
53
+ }
54
+
55
+ module.exports = {
56
+ showEntry,
57
+ };
package/src/manifest.js CHANGED
@@ -9,18 +9,6 @@ const PROJECT_MANAGED_FILES = [
9
9
  relativePath: "docs/playbook/INDEX.json",
10
10
  templatePath: "project/docs/playbook/INDEX.json",
11
11
  },
12
- {
13
- relativePath: "docs/playbook/cases/_example-001-delete-me.md",
14
- templatePath: "project/docs/playbook/cases/_example-001-delete-me.md",
15
- },
16
- {
17
- relativePath: "docs/playbook/patterns/_example-001-delete-me.md",
18
- templatePath: "project/docs/playbook/patterns/_example-001-delete-me.md",
19
- },
20
- {
21
- relativePath: "docs/playbook/checklists/_example-001-delete-me.md",
22
- templatePath: "project/docs/playbook/checklists/_example-001-delete-me.md",
23
- },
24
12
  {
25
13
  relativePath: ".codex/skills/playbook-query/SKILL.md",
26
14
  templatePath: "project/skills/playbook-query/SKILL.md",
package/src/options.js CHANGED
@@ -240,11 +240,31 @@ function parseHitArgs(args) {
240
240
  return { help: false, entryId };
241
241
  }
242
242
 
243
+ function parseShowArgs(args) {
244
+ for (const arg of args) {
245
+ if (arg === "-h" || arg === "--help") {
246
+ return { help: true };
247
+ }
248
+ }
249
+
250
+ const entryId = args[0];
251
+ if (!entryId || entryId.startsWith("-")) {
252
+ throw new CliError("playbook show requires an entry ID");
253
+ }
254
+
255
+ if (args.length > 1) {
256
+ throw new CliError("playbook show accepts exactly one entry ID");
257
+ }
258
+
259
+ return { help: false, entryId };
260
+ }
261
+
243
262
  module.exports = {
244
263
  parseProjectCommandArgs,
245
264
  parseGlobalInitArgs,
246
265
  parseQueryArgs,
247
266
  parsePromoteArgs,
248
267
  parseHitArgs,
268
+ parseShowArgs,
249
269
  parseToolsValue,
250
270
  };
@@ -1,9 +1 @@
1
- {
2
- "_example-001-delete-me": {
3
- "type": "case",
4
- "title": "Example entry — delete this and add real entries via playbook-case skill",
5
- "tags": ["workflow"],
6
- "path": "cases/_example-001-delete-me.md",
7
- "created": "2026-01-01"
8
- }
9
- }
1
+ {}
@@ -1,23 +1 @@
1
- {
2
- "case-001-example-node-version-mismatch": {
3
- "type": "case",
4
- "title": "Deployment failed due to Node.js version mismatch",
5
- "tags": ["env", "ci"],
6
- "path": "cases/_example-001-delete-me.md",
7
- "created": "2026-01-01"
8
- },
9
- "pattern-001-example-pin-runtime-versions": {
10
- "type": "pattern",
11
- "title": "Pin runtime versions before upgrading dependencies",
12
- "tags": ["env", "workflow"],
13
- "path": "patterns/_example-001-delete-me.md",
14
- "created": "2026-01-01"
15
- },
16
- "checklist-001-example-pre-publish": {
17
- "type": "checklist",
18
- "title": "Pre-publish checklist",
19
- "tags": ["workflow", "ci"],
20
- "path": "checklists/_example-001-delete-me.md",
21
- "created": "2026-01-01"
22
- }
23
- }
1
+ {}
@@ -43,11 +43,12 @@ Review the titles in the output. Only proceed to Step 2 for entries whose titles
43
43
 
44
44
  ### Step 2 — Read specific cases (L2)
45
45
 
46
- For relevant entries only:
47
- - Project entries: read `docs/playbook/<path>`
48
- - Global entries: read `~/.playbook/repo/<path>`
46
+ For relevant entries only, run:
47
+ ```bash
48
+ playbook show <entry-id>
49
+ ```
49
50
 
50
- Do NOT read all matched entries. Only read the ones that look relevant based on title.
51
+ This outputs the full content of the case file. Do NOT read all matched entries. Only show the ones that look relevant based on title.
51
52
 
52
53
  When multiple entries have similar titles, prefer entries with higher `hits` count — they have been used more often and are likely more effective.
53
54
 
@@ -30,14 +30,15 @@ Output format (one line per match):
30
30
  (N matches: X project, Y global)
31
31
  ```
32
32
 
33
- ### Expansion (L2): Read individual case files
33
+ ### Expansion (L2): Show entry content
34
34
 
35
35
  When the user asks to see details of a specific entry, or when you need full context for a task:
36
36
 
37
- - For project entries: read `docs/playbook/<path>` where `<path>` is from the query result
38
- - For global entries: read `~/.playbook/repo/<path>`
37
+ ```bash
38
+ playbook show <entry-id>
39
+ ```
39
40
 
40
- Only expand entries that are actually needed. Do not read all matched entries at once.
41
+ This outputs the full content of the entry file. Only expand entries that are actually needed. Do not show all matched entries at once.
41
42
 
42
43
  ### Edge Cases
43
44
 
@@ -1,24 +0,0 @@
1
- # Deployment failed due to Node.js version mismatch
2
-
3
- > Delete this example file and create real cases using the playbook-case skill.
4
-
5
- ## Problem
6
-
7
- Production deployment failed silently. The build succeeded locally but crashed on startup in CI with `SyntaxError: Unexpected token ??=`.
8
-
9
- ## Context
10
-
11
- - Local dev environment: Node 20
12
- - CI runner: Node 16 (inherited from base image, not pinned)
13
- - The `??=` operator (logical nullish assignment) requires Node 15+
14
- - No `engines` field in package.json to catch this earlier
15
-
16
- ## Solution
17
-
18
- 1. Added `"engines": { "node": ">=20" }` to package.json
19
- 2. Updated CI base image to `node:20-slim`
20
- 3. Added `engine-strict=true` to `.npmrc` so mismatches fail fast on `npm install`
21
-
22
- ## Takeaway
23
-
24
- Always pin the Node.js version in `engines` and enforce it. Silent version mismatches cause the worst kind of debugging — everything works locally.
@@ -1,10 +0,0 @@
1
- # Pre-publish checklist
2
-
3
- > Delete this example file and create real checklists using the playbook-case skill.
4
-
5
- - [ ] Run `npm pack --dry-run` and verify included files
6
- - [ ] Check `files` field in package.json matches intended contents
7
- - [ ] Verify version number is correct and not already published
8
- - [ ] Run full test suite: `npm test`
9
- - [ ] Check for accidentally included secrets or credentials
10
- - [ ] Confirm README is up to date with current API/usage
@@ -1,15 +0,0 @@
1
- # Pin runtime versions before upgrading dependencies
2
-
3
- > Delete this example file and create real patterns using the playbook-case skill.
4
-
5
- ## When to Use
6
-
7
- Apply this pattern whenever you upgrade a major runtime (Node.js, Python, Ruby) or a core dependency that has runtime version requirements.
8
-
9
- ## Pattern
10
-
11
- 1. **Pin the current version first** — add `engines` field (Node), `python_requires` (Python), or equivalent before making any changes
12
- 2. **Upgrade in CI first** — update the CI base image/config before changing local dev setup
13
- 3. **Run full test suite against new version** — don't rely on local smoke tests
14
- 4. **Update lockfile** — regenerate `package-lock.json` / `yarn.lock` under the new runtime
15
- 5. **Communicate** — update README and onboarding docs with new version requirement