@codeharbor/agent-playbook 0.1.1 → 0.1.2

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 (3) hide show
  1. package/README.md +4 -2
  2. package/package.json +1 -1
  3. package/src/cli.js +82 -10
package/README.md CHANGED
@@ -23,16 +23,18 @@ pnpm dlx @codeharbor/agent-playbook init --project
23
23
  - Records a metadata block in `~/.codex/config.toml`.
24
24
 
25
25
  ## Commands
26
- - `agent-playbook init [--project] [--copy] [--hooks] [--no-hooks] [--session-dir <path>] [--dry-run] [--repo <path>]`
26
+ - `agent-playbook init [--project] [--copy] [--overwrite] [--hooks] [--no-hooks] [--session-dir <path>] [--dry-run] [--repo <path>]`
27
27
  - `agent-playbook status`
28
28
  - `agent-playbook doctor`
29
- - `agent-playbook repair`
29
+ - `agent-playbook repair [--overwrite]`
30
30
  - `agent-playbook uninstall`
31
31
  - `agent-playbook session-log [--session-dir <path>]`
32
32
  - `agent-playbook self-improve`
33
33
 
34
34
  ## Notes
35
35
  - Default session logs go to repo `sessions/` if a Git root is found; otherwise `~/.claude/sessions/`.
36
+ - If skill folders already exist, you will be prompted before overwriting. Use `--overwrite` to skip the prompt.
37
+ - Session logs and self-improve entries record the agent-playbook version.
36
38
  - Hooks are merged without overwriting existing user hooks.
37
39
  - Requires Node.js 18+.
38
40
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeharbor/agent-playbook",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "One-click installer and workflow fixer for agent-playbook across Claude Code and Codex.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
package/src/cli.js CHANGED
@@ -48,10 +48,10 @@ function printHelp() {
48
48
  `${APP_NAME} ${VERSION}`,
49
49
  "",
50
50
  "Usage:",
51
- ` ${APP_NAME} init [--project] [--copy] [--hooks] [--no-hooks] [--session-dir <path>] [--dry-run] [--repo <path>]`,
51
+ ` ${APP_NAME} init [--project] [--copy] [--overwrite] [--hooks] [--no-hooks] [--session-dir <path>] [--dry-run] [--repo <path>]`,
52
52
  ` ${APP_NAME} status [--project] [--repo <path>]`,
53
53
  ` ${APP_NAME} doctor [--project] [--repo <path>]`,
54
- ` ${APP_NAME} repair [--project] [--repo <path>]`,
54
+ ` ${APP_NAME} repair [--project] [--overwrite] [--repo <path>]`,
55
55
  ` ${APP_NAME} uninstall [--project] [--repo <path>]`,
56
56
  "",
57
57
  "Hook commands:",
@@ -116,6 +116,7 @@ function handleInit(options, context) {
116
116
  const hooksEnabled = options.hooks !== false;
117
117
  const repoRoot = settings.repoRoot;
118
118
  const warnings = [];
119
+ const overwriteState = createOverwriteState(options);
119
120
 
120
121
  if (!settings.skillsSource) {
121
122
  if (options.repair) {
@@ -143,8 +144,8 @@ function handleInit(options, context) {
143
144
  let claudeLinks = { created: [], skipped: [] };
144
145
  let codexLinks = { created: [], skipped: [] };
145
146
  if (settings.skillsSource) {
146
- claudeLinks = linkSkills(settings.skillsSource, settings.claudeSkillsDir, options);
147
- codexLinks = linkSkills(settings.skillsSource, settings.codexSkillsDir, options);
147
+ claudeLinks = linkSkills(settings.skillsSource, settings.claudeSkillsDir, options, overwriteState);
148
+ codexLinks = linkSkills(settings.skillsSource, settings.codexSkillsDir, options, overwriteState);
148
149
  manifest.links.claude = claudeLinks.created;
149
150
  manifest.links.codex = codexLinks.created;
150
151
 
@@ -251,6 +252,7 @@ async function handleSelfImprove(options) {
251
252
  session_id: sessionId,
252
253
  cwd,
253
254
  transcript_path: transcriptPath,
255
+ agent_playbook_version: VERSION,
254
256
  hook_event: input.hook_event_name || "PostToolUse",
255
257
  tool_name: input.tool_name || "",
256
258
  tool_input: input.tool_input || "",
@@ -339,9 +341,65 @@ function resolveSkillsSource(candidates) {
339
341
  return null;
340
342
  }
341
343
 
342
- function linkSkills(sourceDir, targetDir, options) {
344
+ function createOverwriteState(options) {
345
+ return {
346
+ decision: options.overwrite === true ? true : null,
347
+ prompted: false,
348
+ nonInteractive: false,
349
+ };
350
+ }
351
+
352
+ function promptYesNo(question, defaultYes) {
353
+ const suffix = defaultYes ? "[Y/n]" : "[y/N]";
354
+ process.stdout.write(`${question} ${suffix} `);
355
+ const answer = readLineSync().toLowerCase();
356
+ if (!answer) {
357
+ return defaultYes;
358
+ }
359
+ return answer === "y" || answer === "yes";
360
+ }
361
+
362
+ function readLineSync() {
363
+ const buffer = Buffer.alloc(1024);
364
+ let input = "";
365
+ while (true) {
366
+ const bytes = fs.readSync(0, buffer, 0, buffer.length, null);
367
+ if (bytes <= 0) {
368
+ break;
369
+ }
370
+ input += buffer.toString("utf8", 0, bytes);
371
+ if (input.includes("\n")) {
372
+ break;
373
+ }
374
+ }
375
+ return input.trim();
376
+ }
377
+
378
+ function shouldOverwriteExisting(options, state, targetPath) {
379
+ if (options.overwrite) {
380
+ state.decision = true;
381
+ return true;
382
+ }
383
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
384
+ state.nonInteractive = true;
385
+ return false;
386
+ }
387
+ if (state.decision !== null) {
388
+ return state.decision;
389
+ }
390
+ state.prompted = true;
391
+ state.decision = promptYesNo(
392
+ `Existing skill found at ${targetPath}. Overwrite all existing skills?`,
393
+ false
394
+ );
395
+ return state.decision;
396
+ }
397
+
398
+ function linkSkills(sourceDir, targetDir, options, overwriteState) {
343
399
  const created = [];
344
400
  const skipped = [];
401
+ const overwritten = [];
402
+ const state = overwriteState || createOverwriteState(options);
345
403
  const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
346
404
 
347
405
  entries.forEach((entry) => {
@@ -364,8 +422,14 @@ function linkSkills(sourceDir, targetDir, options) {
364
422
  skipped.push({ source: skillDir, target: targetPath, reason: "already linked" });
365
423
  return;
366
424
  }
367
- skipped.push({ source: skillDir, target: targetPath, reason: "exists" });
368
- return;
425
+ if (!shouldOverwriteExisting(options, state, targetPath)) {
426
+ skipped.push({ source: skillDir, target: targetPath, reason: "exists" });
427
+ return;
428
+ }
429
+ overwritten.push({ source: skillDir, target: targetPath });
430
+ if (!options["dry-run"]) {
431
+ safeUnlink(targetPath);
432
+ }
369
433
  }
370
434
 
371
435
  if (options["dry-run"]) {
@@ -389,7 +453,7 @@ function linkSkills(sourceDir, targetDir, options) {
389
453
  }
390
454
  });
391
455
 
392
- return { created, skipped };
456
+ return { created, skipped, overwritten };
393
457
  }
394
458
 
395
459
  function ensureLocalCli(settings, context, options) {
@@ -800,6 +864,7 @@ function buildSessionSummary(insights, sessionId, cwd) {
800
864
  `**Date**: ${date}`,
801
865
  `**Duration**: unknown`,
802
866
  `**Context**: ${repoRoot}`,
867
+ `**Agent Playbook Version**: ${VERSION}`,
803
868
  "",
804
869
  "## Summary",
805
870
  "Auto-generated session log.",
@@ -907,6 +972,12 @@ function printInitSummary(settings, hooksEnabled, options, claudeLinks, codexLin
907
972
  console.log(`- Codex skills: ${settings.codexSkillsDir}`);
908
973
  console.log(`- Hooks: ${hooksEnabled ? "enabled" : "disabled"}`);
909
974
  console.log(`- Linked skills: ${claudeLinks.created.length + codexLinks.created.length}`);
975
+ const overwrittenCount =
976
+ (claudeLinks.overwritten ? claudeLinks.overwritten.length : 0) +
977
+ (codexLinks.overwritten ? codexLinks.overwritten.length : 0);
978
+ if (overwrittenCount) {
979
+ console.log(`- Overwritten skills: ${overwrittenCount}`);
980
+ }
910
981
  if (claudeLinks.skipped.length || codexLinks.skipped.length) {
911
982
  console.log("- Some skills were skipped due to existing paths.");
912
983
  }
@@ -1064,6 +1135,7 @@ function handleRepair(options, context) {
1064
1135
  const settings = resolveSettings(options, context);
1065
1136
  const status = collectStatus(settings);
1066
1137
  const warnings = [];
1138
+ const overwriteState = createOverwriteState(options);
1067
1139
 
1068
1140
  if (!settings.skillsSource) {
1069
1141
  warnings.push("Skills directory not found; skipping skill linking.");
@@ -1094,8 +1166,8 @@ function handleRepair(options, context) {
1094
1166
  }
1095
1167
 
1096
1168
  if (settings.skillsSource) {
1097
- linkSkills(settings.skillsSource, settings.claudeSkillsDir, options);
1098
- linkSkills(settings.skillsSource, settings.codexSkillsDir, options);
1169
+ linkSkills(settings.skillsSource, settings.claudeSkillsDir, options, overwriteState);
1170
+ linkSkills(settings.skillsSource, settings.codexSkillsDir, options, overwriteState);
1099
1171
  if (!options["dry-run"]) {
1100
1172
  const manifestPath = path.join(settings.claudeSkillsDir, ".agent-playbook.json");
1101
1173
  if (!fs.existsSync(manifestPath)) {