@codeharbor/agent-playbook 0.1.1 → 0.1.3
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 -2
- package/package.json +1 -1
- package/src/cli.js +119 -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
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,102 @@ function resolveSkillsSource(candidates) {
|
|
|
339
341
|
return null;
|
|
340
342
|
}
|
|
341
343
|
|
|
342
|
-
function
|
|
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
|
+
let answer = "";
|
|
356
|
+
try {
|
|
357
|
+
answer = readLineSync().toLowerCase();
|
|
358
|
+
} catch (error) {
|
|
359
|
+
if (error && error.code === "EAGAIN") {
|
|
360
|
+
console.error("Warning: unable to read prompt input; skipping overwrite.");
|
|
361
|
+
return defaultYes;
|
|
362
|
+
}
|
|
363
|
+
throw error;
|
|
364
|
+
}
|
|
365
|
+
if (!answer) {
|
|
366
|
+
return defaultYes;
|
|
367
|
+
}
|
|
368
|
+
return answer === "y" || answer === "yes";
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function readLineSync() {
|
|
372
|
+
const buffer = Buffer.alloc(1024);
|
|
373
|
+
let input = "";
|
|
374
|
+
const ttyPath = process.platform === "win32" ? null : "/dev/tty";
|
|
375
|
+
let fd = 0;
|
|
376
|
+
let shouldClose = false;
|
|
377
|
+
|
|
378
|
+
if (ttyPath) {
|
|
379
|
+
try {
|
|
380
|
+
fd = fs.openSync(ttyPath, "r");
|
|
381
|
+
shouldClose = true;
|
|
382
|
+
} catch (error) {
|
|
383
|
+
fd = 0;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
while (true) {
|
|
388
|
+
let bytes = 0;
|
|
389
|
+
try {
|
|
390
|
+
bytes = fs.readSync(fd, buffer, 0, buffer.length, null);
|
|
391
|
+
} catch (error) {
|
|
392
|
+
if (error && error.code === "EAGAIN") {
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
397
|
+
if (bytes <= 0) {
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
input += buffer.toString("utf8", 0, bytes);
|
|
401
|
+
if (input.includes("\n")) {
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (shouldClose) {
|
|
406
|
+
try {
|
|
407
|
+
fs.closeSync(fd);
|
|
408
|
+
} catch (error) {
|
|
409
|
+
return input.trim();
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return input.trim();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function shouldOverwriteExisting(options, state, targetPath) {
|
|
416
|
+
if (options.overwrite) {
|
|
417
|
+
state.decision = true;
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
421
|
+
state.nonInteractive = true;
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
if (state.decision !== null) {
|
|
425
|
+
return state.decision;
|
|
426
|
+
}
|
|
427
|
+
state.prompted = true;
|
|
428
|
+
state.decision = promptYesNo(
|
|
429
|
+
`Existing skill found at ${targetPath}. Overwrite all existing skills?`,
|
|
430
|
+
false
|
|
431
|
+
);
|
|
432
|
+
return state.decision;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function linkSkills(sourceDir, targetDir, options, overwriteState) {
|
|
343
436
|
const created = [];
|
|
344
437
|
const skipped = [];
|
|
438
|
+
const overwritten = [];
|
|
439
|
+
const state = overwriteState || createOverwriteState(options);
|
|
345
440
|
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
|
346
441
|
|
|
347
442
|
entries.forEach((entry) => {
|
|
@@ -364,8 +459,14 @@ function linkSkills(sourceDir, targetDir, options) {
|
|
|
364
459
|
skipped.push({ source: skillDir, target: targetPath, reason: "already linked" });
|
|
365
460
|
return;
|
|
366
461
|
}
|
|
367
|
-
|
|
368
|
-
|
|
462
|
+
if (!shouldOverwriteExisting(options, state, targetPath)) {
|
|
463
|
+
skipped.push({ source: skillDir, target: targetPath, reason: "exists" });
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
overwritten.push({ source: skillDir, target: targetPath });
|
|
467
|
+
if (!options["dry-run"]) {
|
|
468
|
+
safeUnlink(targetPath);
|
|
469
|
+
}
|
|
369
470
|
}
|
|
370
471
|
|
|
371
472
|
if (options["dry-run"]) {
|
|
@@ -389,7 +490,7 @@ function linkSkills(sourceDir, targetDir, options) {
|
|
|
389
490
|
}
|
|
390
491
|
});
|
|
391
492
|
|
|
392
|
-
return { created, skipped };
|
|
493
|
+
return { created, skipped, overwritten };
|
|
393
494
|
}
|
|
394
495
|
|
|
395
496
|
function ensureLocalCli(settings, context, options) {
|
|
@@ -800,6 +901,7 @@ function buildSessionSummary(insights, sessionId, cwd) {
|
|
|
800
901
|
`**Date**: ${date}`,
|
|
801
902
|
`**Duration**: unknown`,
|
|
802
903
|
`**Context**: ${repoRoot}`,
|
|
904
|
+
`**Agent Playbook Version**: ${VERSION}`,
|
|
803
905
|
"",
|
|
804
906
|
"## Summary",
|
|
805
907
|
"Auto-generated session log.",
|
|
@@ -907,6 +1009,12 @@ function printInitSummary(settings, hooksEnabled, options, claudeLinks, codexLin
|
|
|
907
1009
|
console.log(`- Codex skills: ${settings.codexSkillsDir}`);
|
|
908
1010
|
console.log(`- Hooks: ${hooksEnabled ? "enabled" : "disabled"}`);
|
|
909
1011
|
console.log(`- Linked skills: ${claudeLinks.created.length + codexLinks.created.length}`);
|
|
1012
|
+
const overwrittenCount =
|
|
1013
|
+
(claudeLinks.overwritten ? claudeLinks.overwritten.length : 0) +
|
|
1014
|
+
(codexLinks.overwritten ? codexLinks.overwritten.length : 0);
|
|
1015
|
+
if (overwrittenCount) {
|
|
1016
|
+
console.log(`- Overwritten skills: ${overwrittenCount}`);
|
|
1017
|
+
}
|
|
910
1018
|
if (claudeLinks.skipped.length || codexLinks.skipped.length) {
|
|
911
1019
|
console.log("- Some skills were skipped due to existing paths.");
|
|
912
1020
|
}
|
|
@@ -1064,6 +1172,7 @@ function handleRepair(options, context) {
|
|
|
1064
1172
|
const settings = resolveSettings(options, context);
|
|
1065
1173
|
const status = collectStatus(settings);
|
|
1066
1174
|
const warnings = [];
|
|
1175
|
+
const overwriteState = createOverwriteState(options);
|
|
1067
1176
|
|
|
1068
1177
|
if (!settings.skillsSource) {
|
|
1069
1178
|
warnings.push("Skills directory not found; skipping skill linking.");
|
|
@@ -1094,8 +1203,8 @@ function handleRepair(options, context) {
|
|
|
1094
1203
|
}
|
|
1095
1204
|
|
|
1096
1205
|
if (settings.skillsSource) {
|
|
1097
|
-
linkSkills(settings.skillsSource, settings.claudeSkillsDir, options);
|
|
1098
|
-
linkSkills(settings.skillsSource, settings.codexSkillsDir, options);
|
|
1206
|
+
linkSkills(settings.skillsSource, settings.claudeSkillsDir, options, overwriteState);
|
|
1207
|
+
linkSkills(settings.skillsSource, settings.codexSkillsDir, options, overwriteState);
|
|
1099
1208
|
if (!options["dry-run"]) {
|
|
1100
1209
|
const manifestPath = path.join(settings.claudeSkillsDir, ".agent-playbook.json");
|
|
1101
1210
|
if (!fs.existsSync(manifestPath)) {
|