@punk6529/playbook 0.2.4 → 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.4",
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/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
  };
@@ -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