goalbuddy 0.2.20 → 0.2.22
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 +16 -9
- package/goalbuddy/SKILL.md +77 -10
- package/goalbuddy/extend/github-projects/README.md +105 -0
- package/goalbuddy/extend/github-projects/examples/goal-board-sync/state.yaml +63 -0
- package/goalbuddy/extend/github-projects/extension.yaml +43 -0
- package/goalbuddy/extend/github-projects/scripts/lib/github-projects.mjs +728 -0
- package/goalbuddy/extend/github-projects/scripts/lib/goal-state.mjs +362 -0
- package/goalbuddy/extend/github-projects/scripts/sync-github-project.mjs +193 -0
- package/goalbuddy/extend/github-projects/test/github-projects.test.mjs +267 -0
- package/goalbuddy/extend/local-goal-board/README.md +75 -0
- package/goalbuddy/extend/local-goal-board/assets/goalbuddy-mark.png +0 -0
- package/goalbuddy/extend/local-goal-board/examples/sample-goal/notes/T001-scout.md +3 -0
- package/goalbuddy/extend/local-goal-board/examples/sample-goal/state.yaml +124 -0
- package/goalbuddy/extend/local-goal-board/extension.yaml +37 -0
- package/goalbuddy/extend/local-goal-board/scripts/lib/goal-board.mjs +1225 -0
- package/goalbuddy/extend/local-goal-board/scripts/local-goal-board.mjs +258 -0
- package/goalbuddy/extend/local-goal-board/test/local-goal-board.test.mjs +146 -0
- package/goalbuddy/scripts/check-goal-state.mjs +24 -9
- package/goalbuddy/scripts/check-update.mjs +102 -0
- package/goalbuddy/templates/goal.md +12 -8
- package/goalbuddy/templates/state.yaml +18 -3
- package/internal/assets/goalbuddy-og.png +0 -0
- package/internal/assets/goalbuddy-readme-hero.png +0 -0
- package/internal/cli/goal-maker.mjs +191 -13
- package/package.json +3 -2
- package/plugins/goalbuddy/.codex-plugin/plugin.json +3 -3
- package/plugins/goalbuddy/README.md +2 -5
- package/plugins/goalbuddy/skills/goalbuddy/SKILL.md +77 -10
- package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/README.md +105 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/examples/goal-board-sync/state.yaml +63 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/extension.yaml +43 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/scripts/lib/github-projects.mjs +728 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/scripts/lib/goal-state.mjs +362 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/scripts/sync-github-project.mjs +193 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/test/github-projects.test.mjs +267 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/local-goal-board/README.md +75 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/local-goal-board/assets/goalbuddy-mark.png +0 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/local-goal-board/examples/sample-goal/notes/T001-scout.md +3 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/local-goal-board/examples/sample-goal/state.yaml +124 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/local-goal-board/extension.yaml +37 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/local-goal-board/scripts/lib/goal-board.mjs +1225 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/local-goal-board/scripts/local-goal-board.mjs +258 -0
- package/plugins/goalbuddy/skills/goalbuddy/extend/local-goal-board/test/local-goal-board.test.mjs +146 -0
- package/plugins/goalbuddy/skills/goalbuddy/scripts/check-goal-state.mjs +24 -9
- package/plugins/goalbuddy/skills/goalbuddy/scripts/check-update.mjs +102 -0
- package/plugins/goalbuddy/skills/goalbuddy/templates/goal.md +12 -8
- package/plugins/goalbuddy/skills/goalbuddy/templates/state.yaml +18 -3
|
@@ -33,11 +33,15 @@ const requiredAgentFiles = [
|
|
|
33
33
|
"goal_scout.toml",
|
|
34
34
|
"goal_worker.toml",
|
|
35
35
|
];
|
|
36
|
+
const bundledCoreExtensionIds = new Set(["github-projects", "local-goal-board"]);
|
|
36
37
|
const optionsWithValues = new Set([
|
|
37
38
|
"--catalog",
|
|
38
39
|
"--catalog-url",
|
|
39
40
|
"--codex-home",
|
|
41
|
+
"--goal",
|
|
42
|
+
"--host",
|
|
40
43
|
"--kind",
|
|
44
|
+
"--port",
|
|
41
45
|
"--source",
|
|
42
46
|
]);
|
|
43
47
|
|
|
@@ -70,12 +74,19 @@ async function main() {
|
|
|
70
74
|
case "doctor":
|
|
71
75
|
doctor();
|
|
72
76
|
break;
|
|
77
|
+
case "check-update":
|
|
78
|
+
case "update-check":
|
|
79
|
+
checkUpdate();
|
|
80
|
+
break;
|
|
73
81
|
case "plugin":
|
|
74
82
|
plugin();
|
|
75
83
|
break;
|
|
76
84
|
case "extend":
|
|
77
85
|
await extend();
|
|
78
86
|
break;
|
|
87
|
+
case "board":
|
|
88
|
+
await board();
|
|
89
|
+
break;
|
|
79
90
|
case "help":
|
|
80
91
|
case "--help":
|
|
81
92
|
case "-h":
|
|
@@ -144,11 +155,13 @@ Usage:
|
|
|
144
155
|
${canonicalCliName} update [--codex-home <path>] [--json]
|
|
145
156
|
${canonicalCliName} agents [--codex-home <path>] [--force]
|
|
146
157
|
${canonicalCliName} doctor [--codex-home <path>] [--goal-ready]
|
|
158
|
+
${canonicalCliName} check-update [--json]
|
|
147
159
|
${canonicalCliName} extend [--catalog-url <url-or-path>] [--kind <kind>] [--json]
|
|
148
160
|
${canonicalCliName} extend <id> [--catalog-url <url-or-path>] [--json]
|
|
149
161
|
${canonicalCliName} extend install <id> [--catalog-url <url-or-path>] [--dry-run] [--force] [--json]
|
|
150
162
|
${canonicalCliName} extend install --all [--catalog-url <url-or-path>] [--dry-run] [--force] [--json]
|
|
151
163
|
${canonicalCliName} extend doctor [<id>] [--codex-home <path>] [--json]
|
|
164
|
+
${canonicalCliName} board <docs/goals/slug> [--catalog-url <url-or-path>] [--host <host>] [--port <port>] [--once] [--json]
|
|
152
165
|
|
|
153
166
|
Default:
|
|
154
167
|
${canonicalCliName} Installs and enables the native Codex plugin.
|
|
@@ -335,6 +348,46 @@ function doctor() {
|
|
|
335
348
|
process.exit(installOk && goalReadyOk ? 0 : 1);
|
|
336
349
|
}
|
|
337
350
|
|
|
351
|
+
function checkUpdate() {
|
|
352
|
+
const report = updateReport();
|
|
353
|
+
|
|
354
|
+
if (hasFlag("--json")) {
|
|
355
|
+
printJson(report);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (report.check_status !== "ok") {
|
|
360
|
+
console.log(`GoalBuddy update check unavailable: ${report.error}`);
|
|
361
|
+
} else if (report.update_available) {
|
|
362
|
+
console.log(`GoalBuddy ${report.latest_version} is available; installed version is ${report.current_version}.`);
|
|
363
|
+
console.log(`Update with: ${report.update_command}`);
|
|
364
|
+
} else {
|
|
365
|
+
console.log(`GoalBuddy is up to date (${report.current_version}).`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function updateReport() {
|
|
370
|
+
const report = {
|
|
371
|
+
package: packageInfo.name,
|
|
372
|
+
current_version: normalizeVersion(packageInfo.version),
|
|
373
|
+
latest_version: null,
|
|
374
|
+
update_available: false,
|
|
375
|
+
check_status: "unknown",
|
|
376
|
+
update_command: `npx ${canonicalCliName}`,
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
try {
|
|
380
|
+
report.latest_version = latestPublishedVersion();
|
|
381
|
+
report.update_available = compareVersions(report.current_version, report.latest_version) < 0;
|
|
382
|
+
report.check_status = "ok";
|
|
383
|
+
} catch (error) {
|
|
384
|
+
report.check_status = "unavailable";
|
|
385
|
+
report.error = error.message;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return report;
|
|
389
|
+
}
|
|
390
|
+
|
|
338
391
|
function plugin() {
|
|
339
392
|
const subcommand = positional(1) || "";
|
|
340
393
|
switch (subcommand) {
|
|
@@ -407,9 +460,9 @@ function installPlugin() {
|
|
|
407
460
|
console.log("Restart Codex, then use:");
|
|
408
461
|
console.log(` $${canonicalSkillName}`);
|
|
409
462
|
console.log("");
|
|
410
|
-
console.log("
|
|
411
|
-
console.log(` npx ${canonicalCliName}
|
|
412
|
-
console.log(` npx ${canonicalCliName} extend
|
|
463
|
+
console.log("Bundled visual boards:");
|
|
464
|
+
console.log(` npx ${canonicalCliName} board docs/goals/<slug>`);
|
|
465
|
+
console.log(` npx ${canonicalCliName} extend github-projects`);
|
|
413
466
|
}
|
|
414
467
|
|
|
415
468
|
function pluginCacheRoot(version) {
|
|
@@ -475,9 +528,12 @@ function codexGoalRuntimeStatus() {
|
|
|
475
528
|
}
|
|
476
529
|
|
|
477
530
|
function runCodex(args) {
|
|
478
|
-
const
|
|
531
|
+
const env = { ...process.env, CODEX_HOME: codexHome() };
|
|
532
|
+
const command = codexSpawnCommand(args, env);
|
|
533
|
+
const result = spawnSync(command.file, command.args, {
|
|
479
534
|
encoding: "utf8",
|
|
480
|
-
env
|
|
535
|
+
env,
|
|
536
|
+
shell: command.shell || false,
|
|
481
537
|
});
|
|
482
538
|
return {
|
|
483
539
|
ok: result.status === 0,
|
|
@@ -487,6 +543,38 @@ function runCodex(args) {
|
|
|
487
543
|
};
|
|
488
544
|
}
|
|
489
545
|
|
|
546
|
+
function codexSpawnCommand(args, env) {
|
|
547
|
+
if (process.platform !== "win32") return { file: "codex", args };
|
|
548
|
+
|
|
549
|
+
const command = resolveWindowsCommand("codex", env);
|
|
550
|
+
if (!command) return { file: "codex", args };
|
|
551
|
+
if (/\.(?:cmd|bat)$/i.test(command)) {
|
|
552
|
+
const commandLine = [quoteWindowsCommandArg(command), ...args.map(quoteWindowsCommandArg)].join(" ");
|
|
553
|
+
return {
|
|
554
|
+
file: commandLine,
|
|
555
|
+
args: [],
|
|
556
|
+
shell: true,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
return { file: command, args };
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function resolveWindowsCommand(name, env) {
|
|
563
|
+
const systemWhere = env.SystemRoot ? join(env.SystemRoot, "System32", "where.exe") : "";
|
|
564
|
+
const whereCommand = systemWhere && existsSync(systemWhere) ? systemWhere : "where.exe";
|
|
565
|
+
const where = spawnSync(whereCommand, [name], { encoding: "utf8", env });
|
|
566
|
+
if (where.status !== 0) return "";
|
|
567
|
+
const candidates = where.stdout
|
|
568
|
+
.split(/\r?\n/)
|
|
569
|
+
.map((line) => line.trim())
|
|
570
|
+
.filter(Boolean);
|
|
571
|
+
return candidates.find((candidate) => /\.(?:exe|cmd|bat)$/i.test(candidate)) || "";
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function quoteWindowsCommandArg(value) {
|
|
575
|
+
return `"${String(value).replace(/(["^&|<>()%])/g, "^$1")}"`;
|
|
576
|
+
}
|
|
577
|
+
|
|
490
578
|
function parseGoalFeature(output) {
|
|
491
579
|
const line = output.split(/\r?\n/).find((candidate) => candidate.trim().startsWith("goals"));
|
|
492
580
|
if (!line) return { enabled: false, stage: "" };
|
|
@@ -528,6 +616,56 @@ async function extend() {
|
|
|
528
616
|
}
|
|
529
617
|
}
|
|
530
618
|
|
|
619
|
+
async function board() {
|
|
620
|
+
const goal = optionValue("--goal") || positional(1);
|
|
621
|
+
if (!goal) {
|
|
622
|
+
console.error(`Missing goal directory. Usage: ${canonicalCliName} board docs/goals/<slug>`);
|
|
623
|
+
process.exit(2);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const script = await ensureLocalBoardExtension();
|
|
627
|
+
const scriptArgs = [script, "--goal", goal];
|
|
628
|
+
for (const option of ["--host", "--port"]) {
|
|
629
|
+
const value = optionValue(option);
|
|
630
|
+
if (value) scriptArgs.push(option, value);
|
|
631
|
+
}
|
|
632
|
+
if (hasFlag("--once")) scriptArgs.push("--once");
|
|
633
|
+
if (hasFlag("--json")) scriptArgs.push("--json");
|
|
634
|
+
|
|
635
|
+
const capture = hasFlag("--once") || hasFlag("--json");
|
|
636
|
+
const result = spawnSync(process.execPath, scriptArgs, {
|
|
637
|
+
cwd: packageRoot,
|
|
638
|
+
encoding: "utf8",
|
|
639
|
+
env: process.env,
|
|
640
|
+
stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit",
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
if (capture) {
|
|
644
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
645
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
646
|
+
}
|
|
647
|
+
if (result.error) throw result.error;
|
|
648
|
+
process.exit(result.status ?? 1);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
async function ensureLocalBoardExtension() {
|
|
652
|
+
const id = "local-goal-board";
|
|
653
|
+
const script = join(extensionTarget(id), "scripts", "local-goal-board.mjs");
|
|
654
|
+
if (existsSync(script)) return script;
|
|
655
|
+
|
|
656
|
+
const catalog = await loadCatalog();
|
|
657
|
+
const extension = catalog.extensions.find((candidate) => candidate.id === id);
|
|
658
|
+
if (!extension) {
|
|
659
|
+
throw new Error(`Extension ${id} is not available in ${catalog.url}.`);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
await installCatalogExtension(catalog, extension);
|
|
663
|
+
if (!existsSync(script)) {
|
|
664
|
+
throw new Error(`Extension ${id} installed, but script is missing: ${script}`);
|
|
665
|
+
}
|
|
666
|
+
return script;
|
|
667
|
+
}
|
|
668
|
+
|
|
531
669
|
function extendUsage() {
|
|
532
670
|
console.log(`${canonicalProductName} Extend
|
|
533
671
|
|
|
@@ -686,6 +824,11 @@ async function extendInstall() {
|
|
|
686
824
|
async function extendInstallAll(catalog) {
|
|
687
825
|
const results = [];
|
|
688
826
|
for (const extension of catalog.extensions) {
|
|
827
|
+
if (existsSync(extensionTarget(extension.id)) && !hasFlag("--force")) {
|
|
828
|
+
validateCatalogExtension(extension);
|
|
829
|
+
results.push({ extension, target: extensionTarget(extension.id), plan: installPlan(catalog, extension, extensionTarget(extension.id)), skipped: true });
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
689
832
|
results.push(await installCatalogExtension(catalog, extension));
|
|
690
833
|
}
|
|
691
834
|
|
|
@@ -713,11 +856,13 @@ async function extendInstallAll(catalog) {
|
|
|
713
856
|
printJson({
|
|
714
857
|
installed: true,
|
|
715
858
|
count: results.length,
|
|
716
|
-
extensions: results.map(({ extension, target }) => ({ id: extension.id, target })),
|
|
859
|
+
extensions: results.map(({ extension, target, skipped }) => ({ id: extension.id, target, skipped: Boolean(skipped) })),
|
|
717
860
|
});
|
|
718
861
|
} else {
|
|
719
|
-
|
|
720
|
-
|
|
862
|
+
const installedCount = results.filter((result) => !result.skipped).length;
|
|
863
|
+
const skippedCount = results.length - installedCount;
|
|
864
|
+
console.log(`Installed ${installedCount} extensions${skippedCount ? `, skipped ${skippedCount} already installed` : ""}`);
|
|
865
|
+
for (const { extension, target, skipped } of results) console.log(` ${extension.id} -> ${target}${skipped ? " (already installed)" : ""}`);
|
|
721
866
|
}
|
|
722
867
|
}
|
|
723
868
|
|
|
@@ -965,6 +1110,7 @@ function preserveInstalledExtensions(targets) {
|
|
|
965
1110
|
if (!existsSync(source)) continue;
|
|
966
1111
|
mkdirSync(tempPath, { recursive: true });
|
|
967
1112
|
for (const entry of readdirSync(source, { withFileTypes: true })) {
|
|
1113
|
+
if (bundledCoreExtensionIds.has(entry.name)) continue;
|
|
968
1114
|
const from = join(source, entry.name);
|
|
969
1115
|
const to = join(tempPath, entry.name);
|
|
970
1116
|
cpSync(from, to, { recursive: true, force: true });
|
|
@@ -978,9 +1124,11 @@ function preserveInstalledExtensions(targets) {
|
|
|
978
1124
|
|
|
979
1125
|
function restoreInstalledExtensions(target, tempPath) {
|
|
980
1126
|
if (!tempPath) return;
|
|
981
|
-
|
|
982
|
-
mkdirSync(
|
|
983
|
-
|
|
1127
|
+
const destinationRoot = join(target, "extend");
|
|
1128
|
+
mkdirSync(destinationRoot, { recursive: true });
|
|
1129
|
+
for (const entry of readdirSync(tempPath, { withFileTypes: true })) {
|
|
1130
|
+
cpSync(join(tempPath, entry.name), join(destinationRoot, entry.name), { recursive: true, force: true });
|
|
1131
|
+
}
|
|
984
1132
|
}
|
|
985
1133
|
|
|
986
1134
|
function cleanupPreservedExtensions(paths) {
|
|
@@ -1129,9 +1277,39 @@ function assertSkillInstalledForExtensionInstall() {
|
|
|
1129
1277
|
}
|
|
1130
1278
|
}
|
|
1131
1279
|
|
|
1280
|
+
function latestPublishedVersion() {
|
|
1281
|
+
if (process.env.GOALBUDDY_TEST_NPM_LATEST_VERSION) {
|
|
1282
|
+
return normalizeVersion(process.env.GOALBUDDY_TEST_NPM_LATEST_VERSION);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
const result = spawnSync("npm", ["view", packageInfo.name, "version"], {
|
|
1286
|
+
cwd: packageRoot,
|
|
1287
|
+
encoding: "utf8",
|
|
1288
|
+
timeout: 5000,
|
|
1289
|
+
env: {
|
|
1290
|
+
...process.env,
|
|
1291
|
+
npm_config_update_notifier: "false",
|
|
1292
|
+
},
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
if (result.error) throw result.error;
|
|
1296
|
+
if (result.status !== 0) {
|
|
1297
|
+
const output = `${result.stderr || ""}${result.stdout || ""}`.trim();
|
|
1298
|
+
throw new Error(output || `npm view exited with status ${result.status}`);
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
return normalizeVersion(result.stdout);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
function normalizeVersion(value) {
|
|
1305
|
+
const match = String(value).trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
|
|
1306
|
+
if (!match) throw new Error(`Unsupported version: ${value}`);
|
|
1307
|
+
return `${Number(match[1])}.${Number(match[2])}.${Number(match[3])}`;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1132
1310
|
function compareVersions(left, right) {
|
|
1133
|
-
const leftParts = left.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
1134
|
-
const rightParts = right.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
1311
|
+
const leftParts = normalizeVersion(left).split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
1312
|
+
const rightParts = normalizeVersion(right).split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
1135
1313
|
const length = Math.max(leftParts.length, rightParts.length);
|
|
1136
1314
|
for (let index = 0; index < length; index += 1) {
|
|
1137
1315
|
const diff = (leftParts[index] || 0) - (rightParts[index] || 0);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goalbuddy",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Turn open-ended Codex goals into
|
|
3
|
+
"version": "0.2.22",
|
|
4
|
+
"description": "Turn open-ended Codex goals into GoalBuddy Scout/Judge/Worker boards with visual board surfaces, receipts, and verification.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"goalbuddy": "internal/cli/goal-maker.mjs",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"goalbuddy/SKILL.md",
|
|
19
19
|
"goalbuddy/agents",
|
|
20
20
|
"goalbuddy/scripts",
|
|
21
|
+
"goalbuddy/extend",
|
|
21
22
|
"goalbuddy/templates"
|
|
22
23
|
],
|
|
23
24
|
"scripts": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goalbuddy",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Turn broad Codex work into verified GoalBuddy boards with Scout, Judge, Worker,
|
|
3
|
+
"version": "0.2.22",
|
|
4
|
+
"description": "Turn broad Codex work into verified GoalBuddy boards with Scout, Judge, Worker, visual boards, and receipts.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tolibear",
|
|
7
7
|
"email": "support@tolibear.com",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"interface": {
|
|
25
25
|
"displayName": "GoalBuddy",
|
|
26
26
|
"shortDescription": "Turn broad Codex work into verified Scout/Judge/Worker boards",
|
|
27
|
-
"longDescription": "GoalBuddy packages a structured Codex goal workflow for broad, long-running, or ambiguous engineering work. It creates durable goal charters, task boards, receipts, verification gates, extension handoffs, and compatibility guidance for teams moving from goal-maker.",
|
|
27
|
+
"longDescription": "GoalBuddy packages a structured Codex goal workflow for broad, long-running, or ambiguous engineering work. It creates durable goal charters, task boards, visual board surfaces, receipts, verification gates, extension handoffs, and compatibility guidance for teams moving from goal-maker.",
|
|
28
28
|
"developerName": "tolibear",
|
|
29
29
|
"category": "Coding",
|
|
30
30
|
"capabilities": [
|
|
@@ -15,6 +15,7 @@ From the repo root:
|
|
|
15
15
|
```bash
|
|
16
16
|
npm run check
|
|
17
17
|
npx goalbuddy doctor
|
|
18
|
+
npx goalbuddy check-update
|
|
18
19
|
```
|
|
19
20
|
|
|
20
21
|
## Native Codex Install
|
|
@@ -25,11 +26,7 @@ Install and enable GoalBuddy:
|
|
|
25
26
|
npx goalbuddy
|
|
26
27
|
```
|
|
27
28
|
|
|
28
|
-
Restart Codex, then use `$goal-prep`.
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npx goalbuddy extend install --all
|
|
32
|
-
```
|
|
29
|
+
Restart Codex, then use `$goal-prep`. The plugin bundles the local live board and GitHub Projects visual board backends so Goal Prep can offer a board immediately.
|
|
33
30
|
|
|
34
31
|
Or install the npm package globally:
|
|
35
32
|
|
|
@@ -24,19 +24,37 @@ There are two different modes:
|
|
|
24
24
|
|
|
25
25
|
This boundary is strict. `$goal-prep` is not a lightweight `/goal`; it is a board compiler.
|
|
26
26
|
|
|
27
|
-
During a `$goal-prep` turn, do not perform the user's requested work, even if the work looks read-only, preparatory, or obviously useful. Do not refresh or load named skills, inspect implementation files, browse reference repos, research design inspiration, generate design plans, generate images/assets, run app-specific checks,
|
|
27
|
+
During a `$goal-prep` turn, do not perform the user's requested work, even if the work looks read-only, preparatory, or obviously useful. Do not refresh or load named skills, inspect implementation files, browse reference repos, research design inspiration, generate design plans, generate images/assets, run app-specific checks, or edit product files. Put those actions into Scout, Judge, Worker, or PM tasks for the later `/goal` run.
|
|
28
28
|
|
|
29
29
|
Allowed `$goal-prep` actions:
|
|
30
30
|
|
|
31
|
+
- run the bundled GoalBuddy update checker and mention a newer version if one is available;
|
|
31
32
|
- ask diagnostic intake questions and wait when required;
|
|
32
|
-
- create or repair only `docs/goals/<slug>/goal.md`, `docs/goals/<slug>/state.yaml`,
|
|
33
|
+
- create or repair only `docs/goals/<slug>/goal.md`, `docs/goals/<slug>/state.yaml`, `docs/goals/<slug>/notes/`, and the generated `.goalbuddy-board/` visual board artifact;
|
|
34
|
+
- create and open the selected visual board surface for the goal;
|
|
33
35
|
- optionally run the GoalBuddy board checker against that `state.yaml`;
|
|
34
|
-
- verify
|
|
36
|
+
- verify GoalBuddy agent availability, if this can be done without touching implementation work, and record `installed`, `bundled_not_installed`, `missing`, or `unknown` truthfully;
|
|
35
37
|
- print exactly `/goal Follow docs/goals/<slug>/goal.md.`;
|
|
36
38
|
- ask whether to start `/goal`, refine the board, or stop.
|
|
37
39
|
|
|
38
40
|
If the prompt names another skill or tool, such as "use the taste skill", "refresh the taste skill", "look at this repo", "use browser", or "generate assets", record that requirement in the charter and seed tasks. Do not load that skill, browse that repo, or generate those assets during `$goal-prep`.
|
|
39
41
|
|
|
42
|
+
## Update Check
|
|
43
|
+
|
|
44
|
+
At the start of a `$goal-prep` turn, check whether GoalBuddy itself is stale. Run the bundled checker from the installed skill directory when available:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
node <skill-path>/scripts/check-update.mjs --json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
If the checker reports `update_available: true`, tell the user once before continuing:
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
GoalBuddy <latest_version> is available. After this turn, update with: npx goalbuddy
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Do not block intake or board creation on update checking. If the checker is missing, cannot find npm, or network access fails, continue silently unless the user asked about updates.
|
|
57
|
+
|
|
40
58
|
## Intake Compiler
|
|
41
59
|
|
|
42
60
|
Before creating, repairing, or running a board, privately translate the user's input into a Goal Intake. The input may be vague, specific, or detailed with an existing plan. Do not dump the intake to the user unless they ask for it.
|
|
@@ -55,6 +73,22 @@ Extract:
|
|
|
55
73
|
- blind spots: important risks, choices, or success dimensions the user may not have named yet;
|
|
56
74
|
- existing plan facts: user-provided steps, files, constraints, or sequencing that must be preserved but still validated.
|
|
57
75
|
|
|
76
|
+
Ask the visual-board question early, before detailed task shaping:
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
Do you want to set up a visual board for this?
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Recommended options:
|
|
83
|
+
|
|
84
|
+
1. Local live board in Codex (Recommended) - starts immediately, requires no credentials, and lets the user watch tasks populate.
|
|
85
|
+
2. GitHub Projects - best when stakeholders need a shared external board and the user can approve GitHub credentials/project details.
|
|
86
|
+
3. No visual board - best for quick or private goals where the file board is enough.
|
|
87
|
+
|
|
88
|
+
If the user chooses the local live board, create the goal directory, `notes/`, and an initial minimal `state.yaml` as soon as the slug is known, then run `npx goalbuddy board docs/goals/<slug>` and open the printed local URL in the Codex in-app Browser. In short: start the local board before filling the task list so the board pops up right away and cards populate live as `state.yaml` changes. Keep the printed URL in the final prep response as a fallback, but do not make the URL the primary experience.
|
|
89
|
+
|
|
90
|
+
If the user chooses GitHub Projects, ask for approval and the required project target before any live write. Create or sync the GitHub Project at the same early point as the local board: after the goal root and skeleton `state.yaml` exist, before the detailed task list is finished, then sync again as tasks populate. Run a dry-run sync first when possible. Missing GitHub credentials or project details should not block local board creation or goal prep; record the missing requirement in `visual_board.github_projects` and seed a PM setup task.
|
|
91
|
+
|
|
58
92
|
Ask before board creation when the request is vague, strategic, improvement-oriented, or open-ended and the user has not explicitly said to use defaults. Ask one guided question at a time with 2-3 options and a recommended default, then wait. Continue the diagnostic intake until the user's answers are sufficient to choose the board shape. Do not create or repair `docs/goals/<slug>/` until the diagnostic intake is complete or the user explicitly accepts defaults.
|
|
59
93
|
|
|
60
94
|
For vague or strategic goals, one answer is rarely enough. After each answer, reflect what it implies, name one likely blind spot, and ask the next material question. The goal is to help the user discover what they mean, not merely collect a form value.
|
|
@@ -100,10 +134,11 @@ Stop after each question. Do not create files, repair an existing board, run che
|
|
|
100
134
|
|
|
101
135
|
Minimum diagnostic ladder for vague, strategic, or improvement-oriented goals:
|
|
102
136
|
|
|
103
|
-
1.
|
|
104
|
-
2.
|
|
105
|
-
3.
|
|
106
|
-
4.
|
|
137
|
+
1. Visual board: ask "Do you want to set up a visual board for this?"
|
|
138
|
+
2. Intent target: what kind of improvement or outcome matters most?
|
|
139
|
+
3. Success proof: what evidence would convince the user this worked?
|
|
140
|
+
4. Scope and non-goals: what should remain untouched or explicitly out of scope?
|
|
141
|
+
5. Board handling: reuse an existing board, create a fresh board, or inspect first?
|
|
107
142
|
|
|
108
143
|
Ask these one at a time. Skip a step only when the user's words already answer it clearly. After the user answers one step, do not assume the remaining steps; ask the next unresolved material question.
|
|
109
144
|
|
|
@@ -131,15 +166,17 @@ When invoked directly, run intake first. For vague, strategic, improvement-orien
|
|
|
131
166
|
|
|
132
167
|
Do:
|
|
133
168
|
|
|
169
|
+
- check for a newer GoalBuddy version once at the start and mention it without blocking;
|
|
134
170
|
- clarify or infer the goal title and slug;
|
|
135
171
|
- run the Intake Compiler;
|
|
136
172
|
- ask diagnostic intake questions when clarity would materially improve the board;
|
|
137
173
|
- classify the goal as `specific`, `open_ended`, `existing_plan`, `recovery`, or `audit`;
|
|
138
174
|
- create or repair `docs/goals/<slug>/`;
|
|
139
175
|
- create `goal.md`, `state.yaml`, and `notes/`;
|
|
176
|
+
- if requested, start the local visual board immediately and open it in the Codex in-app Browser before filling the task list;
|
|
140
177
|
- seed a role-tagged task board that matches the input shape;
|
|
141
178
|
- make the first active task safe;
|
|
142
|
-
- verify Scout, Worker, and Judge
|
|
179
|
+
- verify Scout, Worker, and Judge agent availability or record an explicit truthful state;
|
|
143
180
|
- print the exact command `/goal Follow docs/goals/<slug>/goal.md.`;
|
|
144
181
|
- ask whether to start now, refine `goal.md`, or stop.
|
|
145
182
|
|
|
@@ -203,7 +240,7 @@ docs/goals/<slug>/
|
|
|
203
240
|
notes/
|
|
204
241
|
```
|
|
205
242
|
|
|
206
|
-
The goal root may contain only `goal.md`, `state.yaml`, and
|
|
243
|
+
The goal root may contain only `goal.md`, `state.yaml`, `notes/`, and generated `.goalbuddy-board/` files when the local visual board is enabled.
|
|
207
244
|
|
|
208
245
|
Most results live inline as task receipts in `state.yaml`. Only create `notes/<task-id>-<slug>.md` when Scout, Judge, or PM output is too large to fit on the task card.
|
|
209
246
|
|
|
@@ -409,6 +446,25 @@ Blocked tasks do not necessarily block the goal. The PM should keep doing safe l
|
|
|
409
446
|
|
|
410
447
|
Avoid setting `goal.status: blocked` for missing input, credentials, production access, destructive-operation permission, or policy decisions. Block the specific task instead, record the missing requirement, and continue with every safe local workaround or adjacent slice.
|
|
411
448
|
|
|
449
|
+
## Operator Escalation
|
|
450
|
+
|
|
451
|
+
When Scout, Judge, Worker, or PM discovers a problem, improvement opportunity, product suggestion, follow-up repair, or tool limitation that should not be fixed inside the current active task, do not let it disappear in chat.
|
|
452
|
+
|
|
453
|
+
The PM may create a board task to prepare a repo-native follow-up. If the user has already approved publishing and the repo/auth state supports it, the PM may create an issue or PR directly and record the link in the receipt. Otherwise, ask the operator one concise question before creating the external artifact:
|
|
454
|
+
|
|
455
|
+
```markdown
|
|
456
|
+
I found [problem or suggestion].
|
|
457
|
+
|
|
458
|
+
Should I:
|
|
459
|
+
1. Create an issue in this repo for it? (Recommended) - [why]
|
|
460
|
+
2. Prepare a PR for the fix/suggestion - [when this is better]
|
|
461
|
+
3. Keep it only in the GoalBuddy board for now - [tradeoff]
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Use an issue for follow-up work, unclear scope, missing approval, or suggestions that need discussion. Use a PR when the fix is already implemented or safely implementable within the current approved scope. If neither is appropriate, propose a different path and record the decision in `state.yaml`.
|
|
465
|
+
|
|
466
|
+
External issues and PRs are supporting artifacts, not board truth. `state.yaml` remains authoritative, and every issue/PR creation or decision must be reflected in a PM, Worker, or Judge receipt.
|
|
467
|
+
|
|
412
468
|
## Continuation Rule
|
|
413
469
|
|
|
414
470
|
After a task completes, immediately write its receipt and select the next active task unless:
|
|
@@ -435,7 +491,18 @@ If the checker and your judgment disagree, choose the more conservative state.
|
|
|
435
491
|
|
|
436
492
|
## Agents
|
|
437
493
|
|
|
438
|
-
Scout, Worker, and Judge are
|
|
494
|
+
Scout, Worker, and Judge templates are bundled with GoalBuddy. They may also be installed as user or project agent configs, but a board must not claim `installed` unless the preparer verified the matching agent files.
|
|
495
|
+
|
|
496
|
+
Use these `state.yaml` values:
|
|
497
|
+
|
|
498
|
+
| State | Meaning | Next action |
|
|
499
|
+
|---|---|---|
|
|
500
|
+
| `installed` | Matching Scout/Worker/Judge agent configs were found in the expected user or project agent location. | Continue. |
|
|
501
|
+
| `bundled_not_installed` | The bundled `goal_*.toml` template exists with the skill, but no matching installed agent config was verified. | `/goal` can proceed through PM fallback. If dedicated agents are required before `/goal`, run `npx goalbuddy agents`. |
|
|
502
|
+
| `missing` | Neither an installed config nor the bundled template was verified. | `/goal` can proceed through PM fallback. If dedicated agents are required before `/goal`, run `npx goalbuddy install`. |
|
|
503
|
+
| `unknown` | Agent availability could not be checked. | `/goal` can proceed through PM fallback. To check before `/goal`, run `npx goalbuddy doctor`. |
|
|
504
|
+
|
|
505
|
+
Non-`installed` states are warnings, not false failures, because the main `/goal` PM can perform Scout/Judge/Worker-shaped tasks directly when dedicated agents are unavailable.
|
|
439
506
|
|
|
440
507
|
| Agent | Thinking level | Write access | Use for |
|
|
441
508
|
|---|---:|---:|---|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# GitHub Projects
|
|
2
|
+
|
|
3
|
+
Mirror a GoalBuddy `state.yaml` board into GitHub Projects without making GitHub the source of truth.
|
|
4
|
+
|
|
5
|
+
This extension ports the GitHub Projects work from PR #1 into the catalog-based extension system. It keeps the package core dependency-free and optional, while giving teams a practical way to publish a GoalBuddy board into a familiar project surface.
|
|
6
|
+
|
|
7
|
+
## Use When
|
|
8
|
+
|
|
9
|
+
- A long-running GoalBuddy board needs stakeholder visibility in GitHub Projects.
|
|
10
|
+
- A team wants one-way sync from `state.yaml` into ProjectV2 draft issues.
|
|
11
|
+
- The PM needs a dry-run plan before using GitHub credentials.
|
|
12
|
+
- Existing GoalBuddy receipts, verification commands, allowed files, owners, and dependencies should be visible in a board layout.
|
|
13
|
+
|
|
14
|
+
## What It Creates
|
|
15
|
+
|
|
16
|
+
The live sync ensures a GitHub Project has:
|
|
17
|
+
|
|
18
|
+
- Draft issues keyed by `Task ID`, so reruns update existing cards instead of duplicating them.
|
|
19
|
+
- Status mapping: `queued -> Todo`, `active -> In Progress`, `blocked -> Blocked`, `done -> Done`.
|
|
20
|
+
- Single-select fields for `Status`, `Priority`, `Work Type`, and `Agent Lane`.
|
|
21
|
+
- Text fields for `Task ID`, `Owner`, `Goal Role`, `Agent Responsible`, `Credential Gate`, `Parent ID`, `Depends On`, `Receipt Summary`, `Verify`, `Allowed Files`, and `Goal Updated`.
|
|
22
|
+
- A `Goal Board` board-layout view for PM flow.
|
|
23
|
+
|
|
24
|
+
The extension must use the bundled sync script for live writes. Do not use Computer Use, browser automation, or the GitHub web UI to create or repair the board view. Do not replace the script with `gh project` commands; `gh project` does not expose the complete ProjectV2 view creation path this extension needs. The script uses GitHub GraphQL for projects, fields, items, and draft issues, plus the GitHub REST Project views endpoint for the `Goal Board` view.
|
|
25
|
+
|
|
26
|
+
The sync only creates or reuses `Goal Board`. It does not create a default Table view, an `Agent Workboard`, or extra role-specific views. GitHub may still show views that already existed on the Project.
|
|
27
|
+
|
|
28
|
+
The extension does not promise custom board grouping or sort order. GitHub's public Project views REST API currently accepts `name`, `layout`, `filter`, and `visible_fields` when creating a view, and GraphQL exposes grouping/sort fields for reading but not a public mutation for saving them. Because that display state cannot be written reliably through the public API, the sync only creates supported fields, cards, and the single `Goal Board` view.
|
|
29
|
+
|
|
30
|
+
## Inputs
|
|
31
|
+
|
|
32
|
+
- `docs/goals/<slug>/state.yaml`
|
|
33
|
+
- Optional `GITHUB_PROJECT_ID`
|
|
34
|
+
- Optional `GITHUB_PROJECT_OWNER` and `GITHUB_PROJECT_NUMBER`
|
|
35
|
+
- `GITHUB_TOKEN` or `GH_TOKEN` for live sync
|
|
36
|
+
|
|
37
|
+
## Dry Run
|
|
38
|
+
|
|
39
|
+
Dry-run mode does not call GitHub:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
node extend/github-projects/scripts/sync-github-project.mjs \
|
|
43
|
+
--state docs/goals/<slug>/state.yaml \
|
|
44
|
+
--dry-run
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For structured output:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
node extend/github-projects/scripts/sync-github-project.mjs \
|
|
51
|
+
--state docs/goals/<slug>/state.yaml \
|
|
52
|
+
--dry-run \
|
|
53
|
+
--json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Live Sync
|
|
57
|
+
|
|
58
|
+
Always run the bundled script for live sync. It creates or reuses the `Goal Board` view as part of the same run that syncs fields and draft issues.
|
|
59
|
+
|
|
60
|
+
Use a ProjectV2 node ID:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
GITHUB_TOKEN=... node extend/github-projects/scripts/sync-github-project.mjs \
|
|
64
|
+
--state docs/goals/<slug>/state.yaml \
|
|
65
|
+
--project-id <project-node-id>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or use an owner and project number:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
GITHUB_TOKEN=... node extend/github-projects/scripts/sync-github-project.mjs \
|
|
72
|
+
--state docs/goals/<slug>/state.yaml \
|
|
73
|
+
--owner <user-or-org> \
|
|
74
|
+
--project-number <number>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Environment alternatives:
|
|
78
|
+
|
|
79
|
+
- `GITHUB_PROJECT_ID`
|
|
80
|
+
- `GITHUB_PROJECT_OWNER`
|
|
81
|
+
- `GITHUB_PROJECT_NUMBER`
|
|
82
|
+
- `GITHUB_TOKEN` or `GH_TOKEN`
|
|
83
|
+
|
|
84
|
+
## Verification
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
node --test extend/github-projects/test/*.test.mjs
|
|
88
|
+
node extend/github-projects/scripts/sync-github-project.mjs \
|
|
89
|
+
--state extend/github-projects/examples/goal-board-sync/state.yaml \
|
|
90
|
+
--dry-run
|
|
91
|
+
node extend/github-projects/scripts/sync-github-project.mjs \
|
|
92
|
+
--state extend/github-projects/examples/goal-board-sync/state.yaml \
|
|
93
|
+
--dry-run \
|
|
94
|
+
--json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Boundaries
|
|
98
|
+
|
|
99
|
+
- `state.yaml` remains authoritative.
|
|
100
|
+
- The sync is one-way from GoalBuddy to GitHub Projects.
|
|
101
|
+
- Missing GitHub credentials block only live sync, not local dry-run validation.
|
|
102
|
+
- Live sync creates or updates GitHub Project draft issues, fields, and the `Goal Board` view through the bundled script.
|
|
103
|
+
- Agents must not fall back to Computer Use, browser automation, or the GitHub web UI for Project setup.
|
|
104
|
+
- Agents must not claim ProjectV2 board views are UI-only; GitHub's REST Project views API supports creating board-layout views.
|
|
105
|
+
- Native GitHub issue hierarchy and dependencies are represented as fields because ProjectV2 draft issues do not provide full issue relationship semantics.
|