ghostrail 0.8.0 → 0.9.0

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.
@@ -1,4 +1,5 @@
1
1
  import type { ExecResult } from "../backend/types.js";
2
+ import { type ServiceManager } from "../service/index.js";
2
3
  import type { Scope } from "../vendor/skilltend/paths.js";
3
4
  import { type Asker } from "./prompt.js";
4
5
  export interface CommandStreams {
@@ -52,6 +53,26 @@ export declare function parseBoardFlags(argv: readonly string[], defaults: Board
52
53
  export declare function cmdBoard(argv: readonly string[], streams: CommandStreams, env?: CommandEnv, cwd?: string): Promise<number>;
53
54
  /** `ghostrail artifacts`: list the shared artifacts available in the global store. */
54
55
  export declare function cmdArtifacts(argv: readonly string[], streams: CommandStreams, env?: CommandEnv): Promise<number>;
56
+ /**
57
+ * How a registered ghostrail stands on disk. A stale entry is reported and
58
+ * never auto-pruned: an unmounted drive or a moved repo does not mean the
59
+ * operator stopped wanting that ghostrail, and quietly deleting their config to
60
+ * tidy a listing would be presumptuous.
61
+ */
62
+ export type GhostrailHealth = "ok" | "no config" | "missing";
63
+ /** Check one registered path without trusting the registry's own claims. */
64
+ export declare function ghostrailHealth(path: string): Promise<GhostrailHealth>;
65
+ /** `ghostrail ls`: the ghostrails this account knows about. */
66
+ export declare function cmdLs(_argv: readonly string[], streams: CommandStreams, env?: CommandEnv): Promise<number>;
67
+ /**
68
+ * `ghostrail start`: choose which ghostrails should be running, record that in
69
+ * the registry, and make the services match.
70
+ */
71
+ export declare function cmdStart(argv: readonly string[], streams: CommandStreams, env?: CommandEnv, ask?: Asker, isTty?: boolean, manager?: ServiceManager): Promise<number>;
72
+ /** `ghostrail stop`: take ghostrails down without changing what should run. */
73
+ export declare function cmdStop(argv: readonly string[], streams: CommandStreams, env?: CommandEnv, manager?: ServiceManager): Promise<number>;
74
+ /** `ghostrail status`: what each ghostrail is doing, and what it should be doing. */
75
+ export declare function cmdStatus(argv: readonly string[], streams: CommandStreams, env?: CommandEnv, manager?: ServiceManager): Promise<number>;
55
76
  /** The quickstart templates bundled with the package. */
56
77
  export declare const TEMPLATES: readonly ["code-local", "content-loop"];
57
78
  /**
@@ -62,7 +83,7 @@ export declare const TEMPLATES: readonly ["code-local", "content-loop"];
62
83
  * takes the template changes this repo never edited while naming what a
63
84
  * customized file is missing.
64
85
  */
65
- export declare function cmdInit(argv: readonly string[], streams: CommandStreams, cwd?: string): Promise<number>;
86
+ export declare function cmdInit(argv: readonly string[], streams: CommandStreams, cwd?: string, env?: CommandEnv): Promise<number>;
66
87
  interface SkillFlags {
67
88
  scope: Scope;
68
89
  agents: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { access, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
- import { dirname, isAbsolute, join, sep } from "node:path";
4
+ import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
5
5
  import process from "node:process";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { spawnCollect } from "../backend/proc.js";
@@ -11,6 +11,7 @@ import { buildProposal, writeProfile } from "../claude-profile/index.js";
11
11
  import { parseDuration } from "../config/duration.js";
12
12
  import { parseConfig, parseMergedConfig } from "../config/load.js";
13
13
  import { STORE_DIR, buildLoopDeps, buildRespondDeps, buildTriageDeps, resolveRepoSlug, } from "../factory/build.js";
14
+ import { findByPath, upsert } from "../global/registry.js";
14
15
  import { GlobalStore, resolveGlobalDir } from "../global/store.js";
15
16
  import { DEFAULT_FACTORY_IMAGE, dockerBuildArgs, dockerDir, parseImageFlags, } from "../image/build.js";
16
17
  import { extractPlaceholders, isClean, missingSignals, repoPathFor, } from "../init/drift.js";
@@ -20,6 +21,7 @@ import { runTick } from "../loop/engine.js";
20
21
  import { watchLoop } from "../loop/watch.js";
21
22
  import { respondTick } from "../respond/respond.js";
22
23
  import { GLOBAL_SECRETS_FILE, appendPlaceholders, applyOverrides, launchEditor as defaultLaunchEditor, loadEnvOverrides, missingSecrets, readSecretsFile, redactSecrets, renderFile, resolveEditor, secretValuesOf, writeSecretsFile, } from "../secrets/index.js";
24
+ import { stopTimeoutSeconds, systemdManager, unitName, } from "../service/index.js";
23
25
  import { applySkillPlan, describePlan, pathCtx, planSkillInstall, uninstallSkill, } from "../skill/install.js";
24
26
  import { FileStore } from "../store/file-store.js";
25
27
  import { triageTick } from "../triage/engine.js";
@@ -375,6 +377,250 @@ export async function cmdArtifacts(argv, streams, env = process.env) {
375
377
  streams.out(`artifacts:\n${names.map((name) => ` ${name}`).join("\n")}\n`);
376
378
  return 0;
377
379
  }
380
+ /** Check one registered path without trusting the registry's own claims. */
381
+ export async function ghostrailHealth(path) {
382
+ if (!(await pathExists(path)))
383
+ return "missing";
384
+ return (await pathExists(join(path, "ghostrail.toml"))) ? "ok" : "no config";
385
+ }
386
+ /** `ghostrail ls`: the ghostrails this account knows about. */
387
+ export async function cmdLs(_argv, streams, env = process.env) {
388
+ const store = globalStoreFor(env);
389
+ const entries = await store.readRegistry();
390
+ if (entries === undefined) {
391
+ streams.err(`cannot parse ${store.registryPath()}\n`);
392
+ streams.err("fix or remove it; a registry that does not parse is treated as none at all\n");
393
+ return 1;
394
+ }
395
+ if (entries.length === 0) {
396
+ streams.out("no ghostrails registered yet\n");
397
+ streams.out("`ghostrail init <template>` registers a new one, and\n");
398
+ streams.out("`ghostrail init <template> --update` adopts a repo set up before the registry\n");
399
+ return 0;
400
+ }
401
+ const rows = await Promise.all(entries.map(async (entry) => ({ entry, health: await ghostrailHealth(entry.path) })));
402
+ const width = Math.max(...rows.map((row) => row.entry.name.length));
403
+ streams.out(`${store.registryPath()}\n\n`);
404
+ for (const { entry, health } of rows) {
405
+ const watch = entry.watch ? "watch" : " off";
406
+ const note = health === "ok" ? "" : ` (${health})`;
407
+ streams.out(` ${watch} ${entry.name.padEnd(width)} ${entry.path}${note}\n`);
408
+ }
409
+ if (rows.some((row) => row.health !== "ok")) {
410
+ streams.out("\nsome entries no longer resolve. remove one by deleting its block from\n");
411
+ streams.out(`${store.registryPath()}\n`);
412
+ }
413
+ return 0;
414
+ }
415
+ /** Resolve the absolute path of the running ghostrail binary, for a unit file. */
416
+ function ghostrailExecPath() {
417
+ return process.argv[1] ?? "ghostrail";
418
+ }
419
+ /** A ghostrail's configured agent timeout, for the unit's stop timeout. */
420
+ async function agentTimeoutMs(repo) {
421
+ const raw = await readIfPresent(join(repo, "ghostrail.toml"));
422
+ if (raw !== undefined) {
423
+ const parsed = parseConfig(raw);
424
+ if (parsed.ok)
425
+ return parsed.value.guardrails.timeoutMs;
426
+ }
427
+ return 30 * 60_000;
428
+ }
429
+ function parseSelectFlags(argv) {
430
+ const flags = { all: false, names: [], interval: "5m", yes: false };
431
+ for (let i = 0; i < argv.length; i++) {
432
+ const arg = argv[i];
433
+ const next = argv[i + 1];
434
+ if (arg === "--all" || arg === "-a")
435
+ flags.all = true;
436
+ else if ((arg === "--name" || arg === "-n") && next !== undefined) {
437
+ flags.names.push(next);
438
+ i++;
439
+ }
440
+ else if ((arg === "--interval" || arg === "-i") && next !== undefined) {
441
+ flags.interval = next;
442
+ i++;
443
+ }
444
+ else if (arg === "--yes" || arg === "-y")
445
+ flags.yes = true;
446
+ }
447
+ return flags;
448
+ }
449
+ /** Load the registry, reporting the two failure shapes distinctly. */
450
+ async function loadRegistry(store, streams) {
451
+ const entries = await store.readRegistry();
452
+ if (entries === undefined) {
453
+ streams.err(`cannot parse ${store.registryPath()}\n`);
454
+ return undefined;
455
+ }
456
+ if (entries.length === 0) {
457
+ streams.out("no ghostrails registered yet\n");
458
+ streams.out("`ghostrail init <template>` registers one, and\n");
459
+ streams.out("`ghostrail init <template> --update` adopts an existing repo\n");
460
+ }
461
+ return entries;
462
+ }
463
+ /** Say what platform we are on when there is no service manager to use. */
464
+ function explainNoManager(kind, streams, interval) {
465
+ if (kind === "launchd") {
466
+ streams.err("this machine uses launchd, which ghostrail cannot install services for yet\n");
467
+ }
468
+ else {
469
+ streams.err("no user service manager found (looked for systemd)\n");
470
+ }
471
+ streams.err(`run a ghostrail by hand instead:\n cd <repo> && ghostrail watch --interval ${interval}\n`);
472
+ }
473
+ /**
474
+ * `ghostrail start`: choose which ghostrails should be running, record that in
475
+ * the registry, and make the services match.
476
+ */
477
+ export async function cmdStart(argv, streams, env = process.env, ask = askOnTerminal, isTty = process.stdin.isTTY === true, manager = systemdManager(env)) {
478
+ const flags = parseSelectFlags(argv);
479
+ const store = globalStoreFor(env);
480
+ const entries = await loadRegistry(store, streams);
481
+ if (entries === undefined)
482
+ return 1;
483
+ if (entries.length === 0)
484
+ return 0;
485
+ // Which ghostrails should be watched after this command.
486
+ let chosen;
487
+ if (flags.all) {
488
+ chosen = [...entries];
489
+ }
490
+ else if (flags.names.length > 0) {
491
+ const wanted = new Set(flags.names);
492
+ chosen = entries.filter((e) => wanted.has(e.name) || wanted.has(e.path));
493
+ const missing = flags.names.filter((n) => !entries.some((e) => e.name === n || e.path === n));
494
+ for (const name of missing)
495
+ streams.err(`no registered ghostrail named ${name}\n`);
496
+ if (missing.length > 0)
497
+ return 1;
498
+ }
499
+ else if (isTty && !flags.yes) {
500
+ streams.out("Which ghostrails do you want to start?\n");
501
+ chosen = [];
502
+ for (const entry of entries) {
503
+ // Pre-answered from `watch`, so the prompt reflects the standing decision
504
+ // rather than asking it fresh every time.
505
+ if (await ask(` ${entry.name} (${entry.path})`, entry.watch))
506
+ chosen.push(entry);
507
+ }
508
+ }
509
+ else {
510
+ chosen = entries.filter((e) => e.watch);
511
+ }
512
+ const chosenPaths = new Set(chosen.map((e) => e.path));
513
+ // The selection IS the watch flag. One piece of state, so the CLI, the
514
+ // dashboard, and the units cannot disagree about what should be running.
515
+ const updated = entries.map((e) => ({ ...e, watch: chosenPaths.has(e.path) }));
516
+ await store.writeRegistry(updated);
517
+ const kind = await manager.kind();
518
+ if (kind !== "systemd") {
519
+ explainNoManager(kind, streams, flags.interval);
520
+ return 1;
521
+ }
522
+ let failed = false;
523
+ for (const entry of updated) {
524
+ const unit = unitName(entry.name, entry.path);
525
+ if (!entry.watch) {
526
+ const error = await manager.disable(unit);
527
+ if (error === undefined)
528
+ streams.out(` stopped ${entry.name}\n`);
529
+ continue;
530
+ }
531
+ if ((await ghostrailHealth(entry.path)) !== "ok") {
532
+ streams.err(` skipped ${entry.name}: ${entry.path} has no ghostrail.toml\n`);
533
+ failed = true;
534
+ continue;
535
+ }
536
+ const spec = {
537
+ name: entry.name,
538
+ path: entry.path,
539
+ exec: ghostrailExecPath(),
540
+ interval: flags.interval,
541
+ stopTimeoutSec: stopTimeoutSeconds(await agentTimeoutMs(entry.path)),
542
+ };
543
+ const { outcome, file } = await manager.install(spec);
544
+ if (outcome === "hand-edited") {
545
+ streams.err(` skipped ${entry.name}: ${file} was edited; remove it to regenerate\n`);
546
+ failed = true;
547
+ continue;
548
+ }
549
+ const error = await manager.enable(unit);
550
+ if (error !== undefined) {
551
+ streams.err(` failed ${entry.name}: ${error}\n`);
552
+ failed = true;
553
+ continue;
554
+ }
555
+ streams.out(` started ${entry.name} (${outcome === "written" ? "new unit" : "unit unchanged"})\n`);
556
+ }
557
+ streams.out("\n`watch` ticks the fix queue only; `respond` and `triage` still need their own schedule\n");
558
+ return failed ? 1 : 0;
559
+ }
560
+ /** `ghostrail stop`: take ghostrails down without changing what should run. */
561
+ export async function cmdStop(argv, streams, env = process.env, manager = systemdManager(env)) {
562
+ const flags = parseSelectFlags(argv);
563
+ const store = globalStoreFor(env);
564
+ const entries = await loadRegistry(store, streams);
565
+ if (entries === undefined)
566
+ return 1;
567
+ if (entries.length === 0)
568
+ return 0;
569
+ const wanted = new Set(flags.names);
570
+ const targets = flags.names.length > 0
571
+ ? entries.filter((e) => wanted.has(e.name) || wanted.has(e.path))
572
+ : entries.filter((e) => flags.all || e.watch);
573
+ if (targets.length === 0) {
574
+ streams.out("nothing to stop\n");
575
+ return 0;
576
+ }
577
+ const kind = await manager.kind();
578
+ if (kind !== "systemd") {
579
+ explainNoManager(kind, streams, flags.interval);
580
+ return 1;
581
+ }
582
+ // Say this before the first stop, not after: an in-flight tick is not
583
+ // interrupted, so this can sit for minutes and look hung.
584
+ streams.out("stopping; a ghostrail mid-tick finishes its run first, which can take minutes\n");
585
+ let failed = false;
586
+ for (const entry of targets) {
587
+ const error = await manager.disable(unitName(entry.name, entry.path));
588
+ if (error !== undefined) {
589
+ streams.err(` failed ${entry.name}: ${error}\n`);
590
+ failed = true;
591
+ continue;
592
+ }
593
+ streams.out(` stopped ${entry.name}\n`);
594
+ }
595
+ // Deliberately does not touch `watch`: "not running now" and "should not run"
596
+ // are different statements, and only the operator makes the second one.
597
+ return failed ? 1 : 0;
598
+ }
599
+ /** `ghostrail status`: what each ghostrail is doing, and what it should be doing. */
600
+ export async function cmdStatus(argv, streams, env = process.env, manager = systemdManager(env)) {
601
+ const store = globalStoreFor(env);
602
+ const entries = await loadRegistry(store, streams);
603
+ if (entries === undefined)
604
+ return 1;
605
+ if (entries.length === 0)
606
+ return 0;
607
+ const kind = await manager.kind();
608
+ const units = entries.map((e) => unitName(e.name, e.path));
609
+ const states = kind === "systemd" ? await manager.states(units) : new Map();
610
+ const width = Math.max(...entries.map((e) => e.name.length));
611
+ for (const [i, entry] of entries.entries()) {
612
+ const unit = units[i];
613
+ const state = kind === "systemd" ? (states.get(unit) ?? "unknown") : "unknown";
614
+ const health = await ghostrailHealth(entry.path);
615
+ const wants = entry.watch ? "watch" : " off";
616
+ const note = health === "ok" ? "" : ` (${health})`;
617
+ streams.out(` ${wants} ${state.padEnd(8)} ${entry.name.padEnd(width)} ${entry.path}${note}\n`);
618
+ }
619
+ if (kind !== "systemd") {
620
+ streams.out("\nno supported service manager here, so nothing is reported as running\n");
621
+ }
622
+ return 0;
623
+ }
378
624
  /** The quickstart templates bundled with the package. */
379
625
  export const TEMPLATES = ["code-local", "content-loop"];
380
626
  function templatesDir() {
@@ -478,6 +724,54 @@ async function reportTemplateDiff(template, srcDir, repo, streams) {
478
724
  }
479
725
  return 0;
480
726
  }
727
+ /** The global store for this environment. */
728
+ function globalStoreFor(env) {
729
+ return new GlobalStore(resolveGlobalDir({ GHOSTRAIL_HOME: env.GHOSTRAIL_HOME, XDG_CONFIG_HOME: env.XDG_CONFIG_HOME }, homedir()));
730
+ }
731
+ /** A ghostrail's display name: `[factory].name`, else the directory. */
732
+ async function ghostrailName(repo) {
733
+ const raw = await readIfPresent(join(repo, "ghostrail.toml"));
734
+ if (raw !== undefined) {
735
+ const parsed = parseConfig(raw);
736
+ if (parsed.ok && parsed.value.factory.name.length > 0)
737
+ return parsed.value.factory.name;
738
+ }
739
+ return basename(repo);
740
+ }
741
+ /**
742
+ * Record this repo in the ghostrails registry, so `start` and the board have
743
+ * something to list. Adding only: an entry that already exists is left exactly
744
+ * as it is, because re-running `init --update` must not switch `watch` back on
745
+ * for a ghostrail the operator deliberately turned off.
746
+ *
747
+ * Never fails the command. Scaffolding the repo is the job; registering it is a
748
+ * convenience, and aborting a good scaffold over an unwritable config dir would
749
+ * be the worse trade.
750
+ */
751
+ async function registerGhostrail(repo, env, streams) {
752
+ const store = globalStoreFor(env);
753
+ const path = resolve(repo);
754
+ try {
755
+ const entries = await store.readRegistry();
756
+ if (entries === undefined) {
757
+ streams.err(`note: ${store.registryPath()} could not be parsed, so this ghostrail was not registered\n`);
758
+ return;
759
+ }
760
+ if (findByPath(entries, path) !== undefined)
761
+ return;
762
+ const entry = {
763
+ name: await ghostrailName(repo),
764
+ path,
765
+ added: new Date().toISOString().slice(0, 10),
766
+ watch: true,
767
+ };
768
+ await store.writeRegistry(upsert(entries, entry));
769
+ streams.out(`registered ghostrail "${entry.name}" (${path})\n`);
770
+ }
771
+ catch (error) {
772
+ streams.err(`note: could not register this ghostrail: ${error instanceof Error ? error.message : String(error)}\n`);
773
+ }
774
+ }
481
775
  /**
482
776
  * Prompt paths a config file names, by role. `undefined` when the file cannot
483
777
  * be read or parsed, which is a different thing from a config that names no
@@ -507,7 +801,7 @@ async function promptPathsAt(configPath) {
507
801
  * fields the template gained are named, because that is what every hand port has
508
802
  * actually consisted of, and the skill places them in the repo's own wording.
509
803
  */
510
- async function applyTemplateUpdate(template, srcDir, repo, streams) {
804
+ async function applyTemplateUpdate(template, srcDir, repo, streams, env) {
511
805
  const manifest = await readManifest(repo);
512
806
  const rels = await scaffoldFiles(srcDir);
513
807
  const templatePrompts = (await promptPathsAt(join(srcDir, "ghostrail.toml"))) ?? {};
@@ -570,6 +864,7 @@ async function applyTemplateUpdate(template, srcDir, repo, streams) {
570
864
  if (Object.keys(written).length > 0) {
571
865
  await writeManifest(repo, mergeManifest(manifest, template, VERSION, written));
572
866
  }
867
+ await registerGhostrail(repo, env, streams);
573
868
  streams.out("\n");
574
869
  if (took > 0)
575
870
  streams.out(`took ${took} file(s) you had not edited\n`);
@@ -588,7 +883,7 @@ async function applyTemplateUpdate(template, srcDir, repo, streams) {
588
883
  * takes the template changes this repo never edited while naming what a
589
884
  * customized file is missing.
590
885
  */
591
- export async function cmdInit(argv, streams, cwd = process.cwd()) {
886
+ export async function cmdInit(argv, streams, cwd = process.cwd(), env = process.env) {
592
887
  let template;
593
888
  let repo = cwd;
594
889
  let force = false;
@@ -631,7 +926,7 @@ export async function cmdInit(argv, streams, cwd = process.cwd()) {
631
926
  const srcDir = join(templatesDir(), template);
632
927
  if (update) {
633
928
  try {
634
- return await applyTemplateUpdate(template, srcDir, repo, streams);
929
+ return await applyTemplateUpdate(template, srcDir, repo, streams, env);
635
930
  }
636
931
  catch (error) {
637
932
  streams.err(`init --update failed: ${error instanceof Error ? error.message : String(error)}\n`);
@@ -691,7 +986,7 @@ export async function cmdInit(argv, streams, cwd = process.cwd()) {
691
986
  streams.out(`, kept ${skipped.length} existing (${skipped.join(", ")}; use --force to overwrite)`);
692
987
  }
693
988
  streams.out("\n");
694
- // A pointer, not an action: init does not write outside the repo's own files.
989
+ await registerGhostrail(repo, env, streams);
695
990
  streams.out("next: `ghostrail skill` installs the /ghostrail skill for your coding agent,\n");
696
991
  streams.out(" then run `/ghostrail customize` there to tailor the prompts to this repo\n");
697
992
  return 0;