goalbuddy 0.2.13 → 0.2.14

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
@@ -30,6 +30,12 @@ $goalbuddy
30
30
 
31
31
  `$goalbuddy` prepares the board and prints the `/goal` command to run next. It does not start `/goal` automatically.
32
32
 
33
+ For native Codex plugin install instead of the skill-only installer:
34
+
35
+ ```bash
36
+ npx goalbuddy plugin install
37
+ ```
38
+
33
39
  ## Why GoalBuddy Exists
34
40
 
35
41
  Long Codex goals drift. A request like "improve this project" can turn into unbounded edits, stale verification, and premature completion claims.
@@ -79,6 +85,12 @@ npx goalbuddy
79
85
  npx goalbuddy update
80
86
  ```
81
87
 
88
+ Install and enable the native Codex plugin:
89
+
90
+ ```bash
91
+ npx goalbuddy plugin install
92
+ ```
93
+
82
94
  Native Codex `/goal` is still an under-development Codex feature. Before relying on the printed command, confirm your local Codex runtime is logged in and has goals enabled:
83
95
 
84
96
  ```bash
@@ -36,6 +36,7 @@ const optionsWithValues = new Set([
36
36
  "--catalog-url",
37
37
  "--codex-home",
38
38
  "--kind",
39
+ "--source",
39
40
  ]);
40
41
 
41
42
  const args = process.argv.slice(2);
@@ -64,6 +65,9 @@ async function main() {
64
65
  case "doctor":
65
66
  doctor();
66
67
  break;
68
+ case "plugin":
69
+ plugin();
70
+ break;
67
71
  case "extend":
68
72
  await extend();
69
73
  break;
@@ -133,6 +137,7 @@ Usage:
133
137
  ${canonicalCliName} update [--codex-home <path>] [--json]
134
138
  ${canonicalCliName} agents [--codex-home <path>] [--force]
135
139
  ${canonicalCliName} doctor [--codex-home <path>] [--goal-ready]
140
+ ${canonicalCliName} plugin install [--source <marketplace-source>] [--codex-home <path>] [--json]
136
141
  ${canonicalCliName} extend [--catalog-url <url-or-path>] [--kind <kind>] [--json]
137
142
  ${canonicalCliName} extend <id> [--catalog-url <url-or-path>] [--json]
138
143
  ${canonicalCliName} extend install <id> [--catalog-url <url-or-path>] [--dry-run] [--force] [--json]
@@ -321,6 +326,123 @@ function doctor() {
321
326
  process.exit(installOk && goalReadyOk ? 0 : 1);
322
327
  }
323
328
 
329
+ function plugin() {
330
+ const subcommand = positional(1) || "";
331
+ switch (subcommand) {
332
+ case "install":
333
+ installPlugin();
334
+ break;
335
+ case "help":
336
+ case "--help":
337
+ case "-h":
338
+ pluginUsage();
339
+ break;
340
+ default:
341
+ console.error(`Unknown plugin command: ${subcommand || "<missing>"}`);
342
+ pluginUsage();
343
+ process.exit(2);
344
+ }
345
+ }
346
+
347
+ function pluginUsage() {
348
+ console.log(`${canonicalProductName} Plugin
349
+
350
+ Usage:
351
+ ${canonicalCliName} plugin install [--source <marketplace-source>] [--codex-home <path>] [--json]
352
+
353
+ Default source:
354
+ tolibear/goalbuddy
355
+ `);
356
+ }
357
+
358
+ function installPlugin() {
359
+ const source = optionValue("--source") || "tolibear/goalbuddy";
360
+ const pluginSource = join(packageRoot, "plugins", canonicalSkillName);
361
+ const pluginManifestPath = join(pluginSource, ".codex-plugin", "plugin.json");
362
+ if (!existsSync(pluginManifestPath)) {
363
+ throw new Error(`Plugin manifest not found: ${pluginManifestPath}`);
364
+ }
365
+
366
+ const pluginManifest = JSON.parse(readFileSync(pluginManifestPath, "utf8"));
367
+ const pluginCachePath = pluginCacheRoot(pluginManifest.version);
368
+ const marketplace = runCodex(["plugin", "marketplace", "add", source]);
369
+ if (!marketplace.ok) {
370
+ throw new Error(`Failed to add Codex plugin marketplace: ${firstLine(marketplace.stderr || marketplace.stdout)}`);
371
+ }
372
+
373
+ mkdirSync(dirname(pluginCachePath), { recursive: true });
374
+ rmSync(pluginCachePath, { recursive: true, force: true });
375
+ cpSync(pluginSource, pluginCachePath, { recursive: true });
376
+ const configPath = enablePluginConfig();
377
+
378
+ const report = {
379
+ installed: true,
380
+ plugin: `${canonicalSkillName}@${canonicalSkillName}`,
381
+ version: pluginManifest.version,
382
+ codex_home: codexHome(),
383
+ marketplace_source: source,
384
+ cache_path: pluginCachePath,
385
+ config_path: configPath,
386
+ };
387
+
388
+ if (hasFlag("--json")) {
389
+ printJson(report);
390
+ return;
391
+ }
392
+
393
+ console.log(`Installed ${canonicalProductName} Codex plugin ${pluginManifest.version}`);
394
+ console.log(`Marketplace: ${source}`);
395
+ console.log(`Cache: ${pluginCachePath}`);
396
+ console.log(`Config: ${configPath}`);
397
+ console.log("");
398
+ console.log("Restart Codex, then use:");
399
+ console.log(` $${canonicalSkillName}`);
400
+ }
401
+
402
+ function pluginCacheRoot(version) {
403
+ return join(codexHome(), "plugins", "cache", canonicalSkillName, canonicalSkillName, version);
404
+ }
405
+
406
+ function enablePluginConfig() {
407
+ const configPath = join(codexHome(), "config.toml");
408
+ mkdirSync(dirname(configPath), { recursive: true });
409
+ const header = `[plugins."${canonicalSkillName}@${canonicalSkillName}"]`;
410
+ const existing = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
411
+ const updated = upsertTomlEnabled(existing, header);
412
+ writeFileSync(configPath, updated);
413
+ return configPath;
414
+ }
415
+
416
+ function upsertTomlEnabled(text, header) {
417
+ const normalized = text.endsWith("\n") || text.length === 0 ? text : `${text}\n`;
418
+ const lines = normalized.split("\n");
419
+ const start = lines.findIndex((line) => line.trim() === header);
420
+ if (start === -1) {
421
+ const prefix = normalized.trim() ? `${normalized}\n` : "";
422
+ return `${prefix}${header}\nenabled = true\n`;
423
+ }
424
+
425
+ let end = lines.length;
426
+ for (let index = start + 1; index < lines.length; index += 1) {
427
+ if (/^\s*\[/.test(lines[index])) {
428
+ end = index;
429
+ break;
430
+ }
431
+ }
432
+
433
+ let sawEnabled = false;
434
+ for (let index = start + 1; index < end; index += 1) {
435
+ if (/^\s*enabled\s*=/.test(lines[index])) {
436
+ lines[index] = "enabled = true";
437
+ sawEnabled = true;
438
+ break;
439
+ }
440
+ }
441
+ if (!sawEnabled) lines.splice(start + 1, 0, "enabled = true");
442
+
443
+ return lines.join("\n").replace(/\n*$/, "\n");
444
+ }
445
+
324
446
  function codexGoalRuntimeStatus() {
325
447
  const version = runCodex(["--version"]);
326
448
  const login = version.ok ? runCodex(["login", "status"]) : { ok: false, stdout: "", stderr: "codex CLI unavailable" };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goalbuddy",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "Turn open-ended Codex goals into a GoalBuddy Scout/Judge/Worker board with receipts, verification, and optional extensions.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goalbuddy",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "Turn broad Codex work into verified GoalBuddy boards with Scout, Judge, Worker, receipts, and optional extensions.",
5
5
  "author": {
6
6
  "name": "tolibear",
@@ -19,13 +19,13 @@ npx goalbuddy doctor
19
19
 
20
20
  ## Native Codex Install
21
21
 
22
- Add the repository as a Codex plugin marketplace:
22
+ Install and enable the plugin with the GoalBuddy CLI:
23
23
 
24
24
  ```bash
25
- codex plugin marketplace add tolibear/goalbuddy
25
+ npx goalbuddy plugin install
26
26
  ```
27
27
 
28
- Then enable the `goalbuddy` plugin from that marketplace in Codex. The npm CLI remains useful for `doctor`, project-local agent setup, and optional GoalBuddy extension management.
28
+ This adds the `tolibear/goalbuddy` marketplace, caches the packaged plugin, and enables `goalbuddy@goalbuddy` for Codex. The npm CLI remains useful for `doctor`, project-local agent setup, and optional GoalBuddy extension management.
29
29
 
30
30
  For local CLI testing before npm publish:
31
31