@nanonets/graft 0.18.0 → 0.20.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.
Files changed (74) hide show
  1. package/README.md +10 -5
  2. package/dist/app/brain-build-process.d.ts +22 -0
  3. package/dist/app/brain-build-process.d.ts.map +1 -0
  4. package/dist/app/brain-build-process.js +129 -0
  5. package/dist/app/brain-build-process.js.map +1 -0
  6. package/dist/app/brain-build-worker.d.ts +35 -0
  7. package/dist/app/brain-build-worker.d.ts.map +1 -0
  8. package/dist/app/brain-build-worker.js +56 -0
  9. package/dist/app/brain-build-worker.js.map +1 -0
  10. package/dist/app/brain-build.d.ts +97 -3
  11. package/dist/app/brain-build.d.ts.map +1 -1
  12. package/dist/app/brain-build.js +148 -39
  13. package/dist/app/brain-build.js.map +1 -1
  14. package/dist/app/history.d.ts +2 -2
  15. package/dist/app/history.d.ts.map +1 -1
  16. package/dist/app/history.js +9 -6
  17. package/dist/app/history.js.map +1 -1
  18. package/dist/app/identity.d.ts +23 -0
  19. package/dist/app/identity.d.ts.map +1 -1
  20. package/dist/app/identity.js +47 -0
  21. package/dist/app/identity.js.map +1 -1
  22. package/dist/app/main.js +2 -0
  23. package/dist/app/main.js.map +1 -1
  24. package/dist/app/server.d.ts +10 -1
  25. package/dist/app/server.d.ts.map +1 -1
  26. package/dist/app/server.js +55 -2
  27. package/dist/app/server.js.map +1 -1
  28. package/dist/app/sources.d.ts +1 -1
  29. package/dist/app/sources.d.ts.map +1 -1
  30. package/dist/app/sources.js +3 -2
  31. package/dist/app/sources.js.map +1 -1
  32. package/dist/ask/ask.js +1 -1
  33. package/dist/brain/claude-md.d.ts +59 -0
  34. package/dist/brain/claude-md.d.ts.map +1 -0
  35. package/dist/brain/claude-md.js +170 -0
  36. package/dist/brain/claude-md.js.map +1 -0
  37. package/dist/brain/connect.d.ts +3 -3
  38. package/dist/brain/connect.js +7 -7
  39. package/dist/brain/link.d.ts +8 -1
  40. package/dist/brain/link.d.ts.map +1 -1
  41. package/dist/brain/link.js +11 -1
  42. package/dist/brain/link.js.map +1 -1
  43. package/dist/brain/push.d.ts +29 -1
  44. package/dist/brain/push.d.ts.map +1 -1
  45. package/dist/brain/push.js +80 -2
  46. package/dist/brain/push.js.map +1 -1
  47. package/dist/brain/signup.d.ts +73 -0
  48. package/dist/brain/signup.d.ts.map +1 -0
  49. package/dist/brain/signup.js +186 -0
  50. package/dist/brain/signup.js.map +1 -0
  51. package/dist/brain/watch.d.ts +136 -0
  52. package/dist/brain/watch.d.ts.map +1 -0
  53. package/dist/brain/watch.js +172 -0
  54. package/dist/brain/watch.js.map +1 -0
  55. package/dist/claude/init.d.ts.map +1 -1
  56. package/dist/claude/init.js +3 -1
  57. package/dist/claude/init.js.map +1 -1
  58. package/dist/cli.js +236 -33
  59. package/dist/cli.js.map +1 -1
  60. package/dist/context/node-file.d.ts +4 -10
  61. package/dist/context/node-file.d.ts.map +1 -1
  62. package/dist/context/node-file.js +6 -2
  63. package/dist/context/node-file.js.map +1 -1
  64. package/dist/legacy-args.d.ts +12 -0
  65. package/dist/legacy-args.d.ts.map +1 -0
  66. package/dist/legacy-args.js +31 -0
  67. package/dist/legacy-args.js.map +1 -0
  68. package/dist/telemetry/contract.d.ts +12 -0
  69. package/dist/telemetry/contract.d.ts.map +1 -1
  70. package/dist/telemetry/contract.js +19 -0
  71. package/dist/telemetry/contract.js.map +1 -1
  72. package/dist/upkeep.d.ts +1 -1
  73. package/dist/upkeep.js +1 -1
  74. package/package.json +1 -1
@@ -0,0 +1,172 @@
1
+ import { baseUrlFor } from "./link.js";
2
+ /** The stages, in the order they happen. The wording matches Trail's build
3
+ * screen deliberately: two products describing one process differently is how
4
+ * a person ends up unsure whether they are looking at the same thing. */
5
+ export const STAGES = [
6
+ { id: "reach", label: "reaching the repository" },
7
+ { id: "read", label: "reading its history" },
8
+ { id: "mine", label: "mining the rules" },
9
+ { id: "file", label: "filing them into the brain" },
10
+ ];
11
+ const num = (v) => (typeof v === "number" && v > 0 ? v : 0);
12
+ /**
13
+ * Which stage the work is in.
14
+ *
15
+ * The rule that matters is where a failure lands. A row that never left
16
+ * `pending` failed at the access check; one that read 412 commits and then died
17
+ * failed in the miner. Reporting both as "could not reach it" would send someone
18
+ * to fix repository access for a problem that has nothing to do with it.
19
+ */
20
+ export function stagesFrom(repo) {
21
+ const commits = num(repo?.commitCount);
22
+ const threads = num(repo?.threadCount);
23
+ const rules = num(repo?.ruleCount);
24
+ const filedSoFar = num(repo?.filedSoFar);
25
+ const foundSoFar = num(repo?.foundSoFar);
26
+ const reached = !!repo && repo.status !== "pending";
27
+ const readIt = commits > 0 || threads > 0;
28
+ // Rules that exist, whether the job has finished writing its count or not.
29
+ const hasRules = filedSoFar > 0 || rules > 0;
30
+ const filed = rules > 0;
31
+ const failed = repo?.status === "failed";
32
+ const failedAt = !failed ? null : !reached || !readIt ? "reach" : !hasRules ? "mine" : "file";
33
+ const order = STAGES.map((s) => s.id);
34
+ const state = (id) => {
35
+ if (failedAt === id)
36
+ return "failed";
37
+ if (failedAt && order.indexOf(id) > order.indexOf(failedAt))
38
+ return "waiting";
39
+ switch (id) {
40
+ case "reach":
41
+ return reached ? "done" : "doing";
42
+ case "read":
43
+ return readIt ? "done" : reached ? "doing" : "waiting";
44
+ case "mine":
45
+ // Done on the first slice's rules. The remaining slices are still being
46
+ // mined at this point, behind the prompt this watcher is about to
47
+ // return — which is the whole change.
48
+ return hasRules ? "done" : readIt ? "doing" : "waiting";
49
+ case "file":
50
+ return filed ? "done" : hasRules ? "doing" : readIt ? "waiting" : "waiting";
51
+ }
52
+ };
53
+ const detail = (id) => {
54
+ if (id === "read" && (commits || threads)) {
55
+ return [commits ? `${commits} commits` : null, threads ? `${threads} discussions` : null].filter(Boolean).join(", ");
56
+ }
57
+ // "so far" because the count grows with each slice that lands. Printing it
58
+ // as a total would promise history that has not been read yet.
59
+ if (id === "mine" && foundSoFar)
60
+ return `${foundSoFar} rules so far`;
61
+ if (id === "file" && rules)
62
+ return `${rules} rules`;
63
+ if (id === "file" && filedSoFar)
64
+ return `${filedSoFar} rules so far`;
65
+ return undefined;
66
+ };
67
+ return {
68
+ stages: STAGES.map((s) => ({ id: s.id, label: s.label, state: state(s.id), detail: detail(s.id) })),
69
+ ready: hasRules && !failed,
70
+ done: repo?.status === "completed" && filed,
71
+ error: failed ? (repo?.errorMessage?.trim() || "the read stopped before it finished") : null,
72
+ };
73
+ }
74
+ /**
75
+ * Read the repo row this brain is building.
76
+ *
77
+ * The same endpoint `fetchExpectedRepo` uses, read for its counts rather than
78
+ * its slug. Null on any failure, so a watcher that cannot reach the API prints
79
+ * nothing new rather than inventing a failure the build did not have.
80
+ */
81
+ export async function fetchRepoState(link, fetchImpl = fetch) {
82
+ try {
83
+ const res = await fetchImpl(`${baseUrlFor(link)}/api/public/brains/${encodeURIComponent(link.brainId)}/repo`, {
84
+ headers: { authorization: `Bearer ${link.token}`, accept: "application/json" },
85
+ signal: AbortSignal.timeout(5000),
86
+ });
87
+ if (!res.ok)
88
+ return null;
89
+ const body = (await res.json());
90
+ if (!body.repo)
91
+ return null;
92
+ return {
93
+ status: String(body.repo.status ?? ""),
94
+ errorMessage: body.repo.error_message,
95
+ ruleCount: num(body.repo.rule_count),
96
+ commitCount: num(body.repo.commit_count),
97
+ threadCount: num(body.repo.thread_count),
98
+ filedSoFar: num(body.build?.filed_so_far),
99
+ foundSoFar: num(body.build?.found_so_far),
100
+ };
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ /** One printable line per stage that has moved since it was last printed. */
107
+ export function linesFor(view, already) {
108
+ const out = [];
109
+ for (const s of view.stages) {
110
+ // Only settled states are printed. A line per poll for a stage that is
111
+ // merely still running would bury the four that matter under a hundred
112
+ // that do not — this is a log, not a redrawn frame.
113
+ if (s.state !== "done" && s.state !== "failed")
114
+ continue;
115
+ const key = `${s.id}:${s.state}`;
116
+ if (already.has(key))
117
+ continue;
118
+ already.add(key);
119
+ const mark = s.state === "done" ? "✓" : "✗";
120
+ out.push(` ${mark} ${s.label}${s.detail ? ` — ${s.detail}` : ""}`);
121
+ }
122
+ return out;
123
+ }
124
+ /**
125
+ * Hold until the brain has rules, printing each stage as it settles.
126
+ *
127
+ * Returns on the first rules rather than on the finished job: mining is what
128
+ * fails and it is done by then, and filing the rest is minutes of work nobody
129
+ * needs to watch. The caller prints where to see the rest.
130
+ *
131
+ * Fifteen minutes by default. That is far more than the mining now takes, and it
132
+ * stays generous on purpose: a watcher that gives up before the build does would
133
+ * report a healthy build as a timeout, which is the exact confusion this is
134
+ * meant to remove. Giving up says so and says the work continues, because it
135
+ * does.
136
+ */
137
+ export async function watchBuild(link, opts = {}) {
138
+ const timeoutMs = opts.timeoutMs ?? 15 * 60 * 1000;
139
+ const pollMs = opts.pollMs ?? 4000;
140
+ const write = opts.write ?? ((l) => console.error(l));
141
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms).unref?.()));
142
+ const printed = new Set();
143
+ const startedAt = Date.now();
144
+ // A row we could never read at all, as opposed to one that answered once and
145
+ // then stopped answering: the first is a broken link or a host that is down
146
+ // and is worth saying, the second is a blip the next poll usually covers.
147
+ let everRead = false;
148
+ for (;;) {
149
+ const repo = await fetchRepoState(link, opts.fetchImpl);
150
+ if (repo) {
151
+ everRead = true;
152
+ const view = stagesFrom(repo);
153
+ for (const line of linesFor(view, printed))
154
+ write(line);
155
+ if (view.error) {
156
+ write(`✗ ${view.error}`);
157
+ return "failed";
158
+ }
159
+ if (view.done)
160
+ return "completed";
161
+ // The ordinary ending now. The job is still filing the rules the miner
162
+ // has handed over, and still mining the older slices of the history, and
163
+ // neither is worth a terminal sitting on it.
164
+ if (view.ready)
165
+ return "building";
166
+ }
167
+ if (Date.now() - startedAt >= timeoutMs)
168
+ return everRead ? "timed_out" : "unreachable";
169
+ await sleep(pollMs);
170
+ }
171
+ }
172
+ //# sourceMappingURL=watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/brain/watch.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/claude/init.ts"],"names":[],"mappings":"AAIA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAKlF,OAAO,EAA6B,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAWzD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAWrG;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,GAAG,EAAE,QAAQ,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,OAAO,CACrB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GACtG,UAAU,CAsCZ"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/claude/init.ts"],"names":[],"mappings":"AAIA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAKlF,OAAO,EAA6B,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAWzD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAarG;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,GAAG,EAAE,QAAQ,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,OAAO,CACrB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GACtG,UAAU,CAsCZ"}
@@ -38,7 +38,9 @@ export function buildGraphIfMissing(dir, opts) {
38
38
  if (opts.build === false || !opts.cliPath || hasGraftIndex(dir))
39
39
  return false;
40
40
  try {
41
- execFileSync(process.execPath, [opts.cliPath, 'build', '.'], { cwd: dir, stdio: 'inherit', timeout: 300000 });
41
+ // execArgv carries the loader graft itself was started with (tsx, when run
42
+ // from source), so the child can run a .ts entry point too.
43
+ execFileSync(process.execPath, [...process.execArgv, opts.cliPath, 'build', '.'], { cwd: dir, stdio: 'inherit', timeout: 300000 });
42
44
  return true;
43
45
  }
44
46
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/claude/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAoB,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAiB,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGjD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,IAAY,EAAE,OAA6B,QAAQ,EAAgB,EAAE,CAC5F,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,OAAO;QACL,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,EAAE,gCAAgC,CAAC;QAC1E,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,sBAAsB,CAAC,EAAE,iBAAiB,CAAC;QAC7E,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,YAAY,CAAC;QACnE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,aAAa,CAAC;QACrE,yEAAyE;QACzE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,kBAAkB,EAAE,KAAK,CAAC;KACrD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,IAA2C;IAC1F,2EAA2E;IAC3E,4EAA4E;IAC5E,oEAAoE;IACpE,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9E,IAAI,CAAC;QACH,YAAY,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9G,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAcD,MAAM,UAAU,OAAO,CACrB,GAAW,EACX,OAAqG,EAAE;IAEvG,6EAA6E;IAC7E,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAE9F,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpD,MAAM,YAAY,GAAG,QAAQ,CAAC;IAC9B,IAAI,QAAQ,GAAwB,EAAE,CAAC;IACvC,IAAI,CAAC;QAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,gCAAgC,CAAC,CAAC;IAC7G,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,kBAAkB,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IAC3F,aAAa,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAEpE,MAAM,EAAE,GAAG,UAAU,CAAC;IACtB,MAAM,EAAE,GAAG,KAAK,CAAC;IACjB,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC,CAAC,kEAAkE;IACpG,aAAa,CAAC,EAAE,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAClE,aAAa,CAAC,EAAE,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAE7D,mFAAmF;IACnF,mFAAmF;IACnF,MAAM,SAAS,GAAG,KAAK,CAAC;IACxB,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,aAAa,CAAC,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;IAE1C,0EAA0E;IAC1E,qFAAqF;IACrF,2EAA2E;IAC3E,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC;IAE3E,+EAA+E;IAC/E,gFAAgF;IAChF,8EAA8E;IAC9E,gFAAgF;IAChF,cAAc;IACd,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC;IAExF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC7C,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC3F,CAAC"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/claude/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAoB,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAiB,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGjD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,IAAY,EAAE,OAA6B,QAAQ,EAAgB,EAAE,CAC5F,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,OAAO;QACL,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,eAAe,CAAC,EAAE,gCAAgC,CAAC;QAC1E,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,sBAAsB,CAAC,EAAE,iBAAiB,CAAC;QAC7E,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,YAAY,CAAC;QACnE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,aAAa,CAAC;QACrE,yEAAyE;QACzE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,kBAAkB,EAAE,KAAK,CAAC;KACrD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,IAA2C;IAC1F,2EAA2E;IAC3E,4EAA4E;IAC5E,oEAAoE;IACpE,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9E,IAAI,CAAC;QACH,2EAA2E;QAC3E,4DAA4D;QAC5D,YAAY,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QACnI,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAcD,MAAM,UAAU,OAAO,CACrB,GAAW,EACX,OAAqG,EAAE;IAEvG,6EAA6E;IAC7E,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAE9F,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpD,MAAM,YAAY,GAAG,QAAQ,CAAC;IAC9B,IAAI,QAAQ,GAAwB,EAAE,CAAC;IACvC,IAAI,CAAC;QAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,gCAAgC,CAAC,CAAC;IAC7G,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,kBAAkB,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IAC3F,aAAa,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAEpE,MAAM,EAAE,GAAG,UAAU,CAAC;IACtB,MAAM,EAAE,GAAG,KAAK,CAAC;IACjB,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC,CAAC,kEAAkE;IACpG,aAAa,CAAC,EAAE,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAClE,aAAa,CAAC,EAAE,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAE7D,mFAAmF;IACnF,mFAAmF;IACnF,MAAM,SAAS,GAAG,KAAK,CAAC;IACxB,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,aAAa,CAAC,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;IAE1C,0EAA0E;IAC1E,qFAAqF;IACrF,2EAA2E;IAC3E,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC;IAE3E,+EAA+E;IAC/E,gFAAgF;IAChF,8EAA8E;IAC9E,gFAAgF;IAChF,cAAc;IACd,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC;IAExF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC7C,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC3F,CAAC"}
package/dist/cli.js CHANGED
@@ -19,8 +19,12 @@ import { hostIds } from "./hosts/registry.js";
19
19
  import { parseBrainArg, connectBrain, pullBrain, brainStatus } from "./brain/connect.js";
20
20
  import { rulesForPointers } from "./brain/attach.js";
21
21
  import { clearLink } from "./brain/link.js";
22
- import { buildLocalDigest, fetchExpectedRepo, pushDigest, repoSlugFromGit, sameRepo } from "./brain/push.js";
23
- import { readLink } from "./brain/link.js";
22
+ import { buildEarlyDigest, buildLocalDigest, fetchExpectedRepo, pushDigest, pushEarlyDigest, repoSlugFromGit, sameRepo, } from "./brain/push.js";
23
+ import { readLink, writeLink } from "./brain/link.js";
24
+ import { withLegacyNames } from "./legacy-args.js";
25
+ import { applyChanges, fetchAcceptedChanges, markApplied } from "./brain/claude-md.js";
26
+ import { watchBuild } from "./brain/watch.js";
27
+ import { openBrowser, signupUrl, startHandoff } from "./brain/signup.js";
24
28
  import { contextDirFor } from "./context/node-file.js";
25
29
  import { loadGraphCached } from "./graph/load.js";
26
30
  import { ensureFreshChildren, ensureFreshGraph, refreshNote } from "./graph/refresh.js";
@@ -39,7 +43,7 @@ import { patchBuildConfig } from "./util/state.js";
39
43
  import { normalizePathPrefix } from "./util/paths.js";
40
44
  import { latestSession, formatSessionStats, sessionInputRate } from "./claude/session-metrics.js";
41
45
  import { setInputRate } from "./context/savings.js";
42
- import { formatUpdateNudge, maybeRefreshInBackground, readUpdateCache, refreshUpdateCache, writeStamp } from "./upkeep.js";
46
+ import { formatUpdateNudge, maybeRefreshInBackground, readUpdateCache, refreshUpdateCache, wiredHostIds, writeStamp } from "./upkeep.js";
43
47
  import { errorCode, filesBucket, durationBucket, formatDebug, formatStatus, firstRunNotice, isTrackedCommand, langsValue, offReason, maybeFlushInBackground, patchState, runFlush, track, trackFirstRunIfNew, } from "./telemetry/index.js";
44
48
  const program = new Command();
45
49
  const currentVersion = readCurrentVersion(import.meta.url);
@@ -815,8 +819,15 @@ program
815
819
  .option("--dry-run", "print every file init would touch, then exit without writing")
816
820
  .option("-y, --yes", "skip the picker and wire every detected agent (the pre-0.8 default)")
817
821
  .option("--no-global", "skip writes outside this repo (the ~/.codex/ config + hooks)")
818
- .option("--brain <handoff>", "attach a Trail brain: <brainId>:<token> (or a bare brain id with GRAFT_BRAIN_TOKEN set)")
819
- .action(async (dir, opts) => {
822
+ .option("--trail <handoff>", "attach a Trail: <brainId>:<token> (or a bare id with GRAFT_BRAIN_TOKEN set)")
823
+ .action((dir, opts) => runInitCommand(dir, opts));
824
+ /**
825
+ * `graft init`. Also run by `graft trail push` on a repo graft has not been set
826
+ * up in — the Trail pages start people at push, and a trail whose rules no
827
+ * coding agent reads is only half set up. `epilogue: false` leaves out the
828
+ * closing banner when push is going to keep talking.
829
+ */
830
+ async function runInitCommand(dir, opts, how = {}) {
820
831
  if (opts.listAgents) {
821
832
  for (const id of [...hostIds(), "claude"])
822
833
  console.log(id);
@@ -825,10 +836,10 @@ program
825
836
  // Parsed before anything is written: a mistyped handoff should cost the user
826
837
  // an error, not a half-wired repo they have to `graft uninstall` out of.
827
838
  let brainLink;
828
- if (opts.brain) {
829
- const parsed = parseBrainArg(opts.brain);
839
+ if (opts.trail) {
840
+ const parsed = parseBrainArg(opts.trail);
830
841
  if ("error" in parsed) {
831
- console.error(`✗ --brain: ${parsed.error}`);
842
+ console.error(`✗ --trail: ${parsed.error}`);
832
843
  process.exitCode = 1;
833
844
  return;
834
845
  }
@@ -917,7 +928,7 @@ program
917
928
  wireTarget(target, ids, { home, cliPath, plan, opts, wantClaude });
918
929
  }
919
930
  // The brain comes last, after the graph exists: its rules are anchored to
920
- // symbols, and `graft brain status` can only report how many of them resolve
931
+ // symbols, and `graft trail status` can only report how many of them resolve
921
932
  // once there is a graph to resolve them against.
922
933
  if (brainLink) {
923
934
  const res = await connectBrain(repo, brainLink, { home, ids });
@@ -936,15 +947,16 @@ program
936
947
  const graphs = (children.length ? children.map((c) => join(repo, c)) : [repo])
937
948
  .map((d) => loadGraphCached(contextDirFor(d, children.length ? undefined : globalDir)))
938
949
  .filter((g) => g !== null);
939
- console.error("\n" +
940
- formatInitEpilogue({
941
- graphBuilt: graphs.length > 0,
942
- nodes: graphs.reduce((n, g) => n + g.meta.nodeCount, 0),
943
- edges: graphs.reduce((n, g) => n + g.meta.edgeCount, 0),
944
- }));
950
+ if (how.epilogue !== false)
951
+ console.error("\n" +
952
+ formatInitEpilogue({
953
+ graphBuilt: graphs.length > 0,
954
+ nodes: graphs.reduce((n, g) => n + g.meta.nodeCount, 0),
955
+ edges: graphs.reduce((n, g) => n + g.meta.edgeCount, 0),
956
+ }));
945
957
  // Sorted so `claude,cursor` and `cursor,claude` aggregate as one value.
946
958
  track("init_completed", { agents: [...ids].sort().join(","), consent: consent === undefined ? "unasked" : String(consent) }, { repo });
947
- });
959
+ }
948
960
  /** One repo's worth of `init` writes — the parent, then each workspace child. */
949
961
  function wireTarget(repo, ids, ctx) {
950
962
  const { home, cliPath, plan, wantClaude, opts } = ctx;
@@ -1076,9 +1088,57 @@ program
1076
1088
  ? `\n⚠ ${bad.length} file(s) could not be parsed and were left as-is — see above.`
1077
1089
  : "\n✓ graft fully removed. `graft init` re-wires from scratch.");
1078
1090
  });
1091
+ // `graft trail …` still works: see legacy-args.ts.
1079
1092
  const brain = program
1080
- .command("brain")
1081
- .description("The Trail brain attached to this repo: the rules mined from its own history");
1093
+ .command("trail")
1094
+ .description("The Trail attached to this repo: the rules mined from its own history");
1095
+ /**
1096
+ * Get this repo a brain from the terminal, by sending the user through signup
1097
+ * in their browser and catching the handoff on loopback.
1098
+ *
1099
+ * Returns the link, already saved, or null when the user should be left alone —
1100
+ * every failure prints its own reason first, because the caller only needs to
1101
+ * know whether to carry on.
1102
+ */
1103
+ async function signUpForBrain(repo, slug) {
1104
+ const handoff = await startHandoff();
1105
+ const url = signupUrl({ repo: slug, port: handoff.port, state: handoff.state });
1106
+ const startedAt = Date.now();
1107
+ // Queued before the link is printed rather than after the outcome, because
1108
+ // the outcome is the one thing a terminal handoff can lose: a user who reads
1109
+ // the URL and walks away kills the process, and only an event already on disk
1110
+ // survives that. This is the denominator; `brain_signup_settled` is not.
1111
+ track("brain_signup_opened", {}, { repo });
1112
+ // Printed before the browser opens, and printed whether or not it opens: on a
1113
+ // remote shell nothing can open, and on a desktop the window sometimes lands
1114
+ // behind the terminal. The URL is the thing that always works.
1115
+ console.error(`· ${slug} has no brain yet. Opening your browser to make one:`);
1116
+ console.error(` ${url}`);
1117
+ // A non-interactive shell has nobody to click anything, so waiting five
1118
+ // minutes for a browser that will never come is worse than saying so now.
1119
+ if (!process.stderr.isTTY) {
1120
+ console.error("· not a terminal — open that link, then run `graft trail connect <brainId>:<token>` here");
1121
+ // Its own outcome, not a timeout: nothing here could have opened a browser,
1122
+ // so folding the two together would read as people abandoning signup when
1123
+ // it is only a remote shell doing what it has to.
1124
+ track("brain_signup_settled", { outcome: "no_tty", duration_bucket: durationBucket(Date.now() - startedAt) }, { repo });
1125
+ handoff.close();
1126
+ return null;
1127
+ }
1128
+ openBrowser(url);
1129
+ console.error("· waiting for you to finish signing up…");
1130
+ const got = await handoff.wait();
1131
+ if ("error" in got) {
1132
+ console.error(`✗ ${got.error}`);
1133
+ // The category, never the sentence: `got.error` names the repo and the link.
1134
+ track("brain_signup_settled", { outcome: got.reason, duration_bucket: durationBucket(Date.now() - startedAt) }, { repo });
1135
+ return null;
1136
+ }
1137
+ writeLink(repo, got.link);
1138
+ console.error(`✓ brain connected to ${slug}`);
1139
+ track("brain_signup_settled", { outcome: "linked", duration_bucket: durationBucket(Date.now() - startedAt) }, { repo });
1140
+ return got.link;
1141
+ }
1082
1142
  brain
1083
1143
  .command("connect")
1084
1144
  .description("Attach a brain to this repo and pull its rules")
@@ -1102,7 +1162,7 @@ brain
1102
1162
  // read the repo yet. Saying "0 rules" without saying why reads as a
1103
1163
  // failure, and the next step is the whole point.
1104
1164
  console.error("✓ attached this repo to the brain — it has no rules yet");
1105
- console.error("· run `graft brain push` to read this repository into it");
1165
+ console.error("· run `graft trail push` to read this repository into it");
1106
1166
  return;
1107
1167
  }
1108
1168
  console.error(`✓ pulled ${res.ruleCount} rule(s) from ${parsed.brainId}`);
@@ -1117,7 +1177,7 @@ brain
1117
1177
  const repo = resolve(dir);
1118
1178
  const res = await pullBrain(repo, { home: homedir() });
1119
1179
  if (!res) {
1120
- console.error("· no brain attached — run `graft brain connect <brainId>:<token>`");
1180
+ console.error("· no brain attached — run `graft trail connect <brainId>:<token>`");
1121
1181
  return;
1122
1182
  }
1123
1183
  if (res.warning) {
@@ -1134,19 +1194,32 @@ brain
1134
1194
  .description("Read THIS repo on your machine and build its brain — no GitHub App, works on private repos")
1135
1195
  .argument("[dir]", "target repo directory", ".")
1136
1196
  .option("--no-approve", "leave the mined rules as drafts for review")
1197
+ .option("--no-watch", "return as soon as the push is sent, without following the build")
1137
1198
  .action(async (dir, opts) => {
1138
1199
  const repo = resolve(dir);
1139
- const link = readLink(repo);
1200
+ // Resolved before the link, because an unlinked repo now signs up for a
1201
+ // brain and Trail creates that brain FOR a named repository. Without a slug
1202
+ // there is nothing to name it after — and the digest builder would fail on
1203
+ // the same missing remote a moment later regardless.
1204
+ const here = repoSlugFromGit(repo);
1205
+ let link = readLink(repo);
1140
1206
  if (!link) {
1141
- console.error("· no brain attached — run `graft brain connect <brainId>:<token>` first");
1142
- process.exitCode = 1;
1143
- return;
1207
+ if (!here) {
1208
+ console.error("✗ this directory has no GitHub `origin` remote — graft can only push a GitHub repository today");
1209
+ process.exitCode = 1;
1210
+ return;
1211
+ }
1212
+ const signedUp = await signUpForBrain(repo, `${here.owner}/${here.name}`);
1213
+ if (!signedUp) {
1214
+ process.exitCode = 1;
1215
+ return;
1216
+ }
1217
+ link = signedUp;
1144
1218
  }
1145
1219
  // What the website said this brain is for. Checked BEFORE any reading, so
1146
1220
  // standing in the wrong checkout costs a message rather than a brain full
1147
1221
  // of another repository's rules — a mistake that is silent afterwards,
1148
1222
  // because the rules look perfectly plausible, just not about your code.
1149
- const here = repoSlugFromGit(repo);
1150
1223
  const expected = await fetchExpectedRepo(link);
1151
1224
  if (expected && here && !sameRepo(expected.slug, `${here.owner}/${here.name}`)) {
1152
1225
  console.error(`✗ this brain is for ${expected.slug}, but you are in ${here.owner}/${here.name}`);
@@ -1154,16 +1227,48 @@ brain
1154
1227
  process.exitCode = 1;
1155
1228
  return;
1156
1229
  }
1230
+ const label = expected?.slug ?? (here ? `${here.owner}/${here.name}` : "this repository");
1231
+ console.error(`· reading ${label} — commit messages, pull-request discussion and the docs in the tree.`);
1232
+ console.error(" No file contents leave this machine.");
1233
+ // The instruction file and the newest pull requests go first, when the brain
1234
+ // takes them: its CLAUDE.md suggestions start from those while the rest of
1235
+ // the history is still being read here. Not awaited — the full read starts
1236
+ // at once, reusing the threads the early one already fetched.
1237
+ let early = Promise.resolve();
1238
+ let knownThreads = [];
1239
+ if (expected?.earlyUpload) {
1240
+ const first = await buildEarlyDigest(repo);
1241
+ if (first) {
1242
+ knownThreads = first.threads;
1243
+ early = pushEarlyDigest(link, first.digest).then((ok) => {
1244
+ if (ok)
1245
+ console.error("· CLAUDE.md suggestions are on their way in your browser, from the newest pull requests");
1246
+ });
1247
+ }
1248
+ }
1249
+ // A repo graft was never set up in gets `graft init` first — after the early
1250
+ // upload has gone, so the CLAUDE.md suggestions are not held up by the graph
1251
+ // build. Same picker as init: it is how someone agrees to what gets written,
1252
+ // so a non-interactive push only says what to run.
1253
+ if (wiredHostIds(repo).length === 0) {
1254
+ if (process.stdin.isTTY && process.stderr.isTTY) {
1255
+ // Its one line of output lands before the picker, not across it.
1256
+ await early;
1257
+ console.error("· graft is not set up in this repo yet — setting it up first, the same as `graft init`");
1258
+ await runInitCommand(repo, { build: true, mcp: true, hooks: true, statusline: true, global: true }, { epilogue: false });
1259
+ }
1260
+ else {
1261
+ console.error("· graft is not set up in this repo — run `graft init` so your coding agent reads these rules");
1262
+ }
1263
+ }
1157
1264
  // The graph is what symbol anchors are resolved against, so a rule mined
1158
1265
  // here can later go stale on its own. Without one the ingest still works;
1159
1266
  // its rules simply govern the repo rather than a symbol in it.
1160
1267
  const graph = loadGraphCached(contextDirFor(repo, program.opts().dir));
1161
1268
  if (!graph)
1162
1269
  console.error("· no graph yet — run `graft build` first so rules can be anchored to symbols");
1163
- const label = expected?.slug ?? (here ? `${here.owner}/${here.name}` : "this repository");
1164
- console.error(`· reading ${label} — commit messages, pull-request discussion and the docs in the tree.`);
1165
- console.error(" No file contents leave this machine.");
1166
- const built = await buildLocalDigest(repo, graph, { autoApprove: opts.approve !== false });
1270
+ const built = await buildLocalDigest(repo, graph, { autoApprove: opts.approve !== false, knownThreads });
1271
+ await early;
1167
1272
  if ("error" in built) {
1168
1273
  console.error(`✗ ${built.error}`);
1169
1274
  process.exitCode = 1;
@@ -1181,8 +1286,106 @@ brain
1181
1286
  }
1182
1287
  const brainLabel = expected?.brainName ? `“${expected.brainName}”` : link.brainId;
1183
1288
  console.error(`✓ sent ${d.owner}/${d.name} to ${brainLabel}`);
1184
- console.error("· it is being mined into rules now — a few minutes. Watch it finish in your browser.");
1185
- console.error(" The rules reach this repo on their own; nothing else to run.");
1289
+ // Held rather than handed back. Everything above this line succeeded even
1290
+ // in the runs that end badly: the push lands, and then the miner fails —
1291
+ // which is where roughly a third of production's repo brains die. Returning
1292
+ // the prompt here is what made that invisible from this side, on CI and
1293
+ // over SSH permanently so. `--no-watch` is for a caller that genuinely
1294
+ // wants fire-and-forget, and it prints the old two lines instead.
1295
+ if (opts.watch === false) {
1296
+ console.error("· it is being mined into rules now — a few minutes. Watch it finish in your browser.");
1297
+ console.error(" The rules reach this repo on their own; nothing else to run.");
1298
+ return;
1299
+ }
1300
+ console.error("· building the brain — Ctrl-C detaches, it keeps going without you");
1301
+ const outcome = await watchBuild(link);
1302
+ if (outcome === "completed") {
1303
+ console.error("✓ the brain is built — its rules reach this repo on their own; nothing else to run");
1304
+ return;
1305
+ }
1306
+ // The ordinary ending for anything but a small repository. The history is
1307
+ // mined in slices, and the prompt comes back on the first of them: mining is
1308
+ // the part that fails and it has now succeeded, and the rest is filing,
1309
+ // which is minutes of reliable work nobody gains by watching. The rules
1310
+ // already in the brain are already being served.
1311
+ if (outcome === "building") {
1312
+ console.error("✓ the brain has its first rules — they reach this repo on their own; nothing else to run");
1313
+ console.error(" The rest of the history is still being read. Watch it fill in your browser.");
1314
+ return;
1315
+ }
1316
+ if (outcome === "failed") {
1317
+ // A non-zero exit, unlike every other ending here: this is the one case
1318
+ // where the work did not produce a brain, and a CI step that ran the push
1319
+ // should hear about it the way it hears about any other failure.
1320
+ console.error(" Nothing was lost — the brain is still there. Run `graft trail push` again to retry the read.");
1321
+ process.exitCode = 1;
1322
+ return;
1323
+ }
1324
+ if (outcome === "unreachable") {
1325
+ console.error("· could not reach Trail to follow the build — it is still running. Watch it finish in your browser.");
1326
+ return;
1327
+ }
1328
+ console.error("· still building after 15 minutes — it has not failed, it is just long. Watch it finish in your browser.");
1329
+ });
1330
+ const claudeMd = program
1331
+ .command("claude-md")
1332
+ .description("The CLAUDE.md changes this repo's brain suggested and you accepted in Trail");
1333
+ claudeMd
1334
+ .command("pull")
1335
+ .description("Write the CLAUDE.md changes you accepted in Trail into this repo's instruction file")
1336
+ .argument("[dir]", "target repo directory", ".")
1337
+ .option("--dry-run", "show what would change without writing the file or telling Trail")
1338
+ .action(async (dir, opts) => {
1339
+ const repo = resolve(dir);
1340
+ const link = readLink(repo);
1341
+ if (!link) {
1342
+ console.error("✗ this repo has no brain attached — run `graft trail push` first");
1343
+ process.exitCode = 1;
1344
+ return;
1345
+ }
1346
+ const pulled = await fetchAcceptedChanges(link);
1347
+ if ("error" in pulled) {
1348
+ console.error(`✗ ${pulled.error}`);
1349
+ process.exitCode = 1;
1350
+ return;
1351
+ }
1352
+ if (pulled.changes.length === 0) {
1353
+ console.error("· nothing accepted yet — accept suggestions on the brain's CLAUDE.md page, then pull again");
1354
+ return;
1355
+ }
1356
+ // The file Trail read, or a new CLAUDE.md when the repo had none.
1357
+ const rel = pulled.path || "CLAUDE.md";
1358
+ const file = join(repo, rel);
1359
+ const { existsSync, readFileSync, writeFileSync } = await import("node:fs");
1360
+ const before = existsSync(file) ? readFileSync(file, "utf8") : "";
1361
+ const result = applyChanges(before, pulled.changes);
1362
+ for (const c of result.written)
1363
+ console.error(` + ${c.kind === "add" ? "new section" : "edit"} · ${c.heading}`);
1364
+ for (const c of result.present)
1365
+ console.error(` = already in the file · ${c.heading}`);
1366
+ for (const s of result.skipped)
1367
+ console.error(` ! skipped · ${s.change.heading} — ${s.why}`);
1368
+ if (opts.dryRun) {
1369
+ console.error(`· dry run: ${result.written.length} change(s) would be written to ${rel}; nothing was written`);
1370
+ return;
1371
+ }
1372
+ if (result.written.length > 0)
1373
+ writeFileSync(file, result.text);
1374
+ // Written now or already there: either way the page should stop listing it.
1375
+ const done = [...result.written, ...result.present].map((c) => c.id);
1376
+ const told = await markApplied(link, done);
1377
+ if (result.written.length > 0) {
1378
+ console.error(`✓ wrote ${result.written.length} change(s) to ${rel} — review and commit it like any other edit`);
1379
+ }
1380
+ else if (result.present.length > 0) {
1381
+ console.error(`✓ ${rel} already has every accepted change`);
1382
+ }
1383
+ if (!told)
1384
+ console.error("⚠ could not tell Trail which changes were written; the page may still list them");
1385
+ if (result.skipped.length > 0) {
1386
+ console.error(`· ${result.skipped.length} change(s) no longer match the local file — edit them on the CLAUDE.md page, or apply them by hand`);
1387
+ process.exitCode = 1;
1388
+ }
1186
1389
  });
1187
1390
  brain
1188
1391
  .command("status")
@@ -1203,7 +1406,7 @@ brain
1203
1406
  return;
1204
1407
  }
1205
1408
  if (!link) {
1206
- console.error("· no brain attached — run `graft brain connect <brainId>:<token>`");
1409
+ console.error("· no brain attached — run `graft trail connect <brainId>:<token>`");
1207
1410
  return;
1208
1411
  }
1209
1412
  console.error(`brain ${link.brainId}`);
@@ -1229,7 +1432,7 @@ brain
1229
1432
  clearLink(resolve(dir));
1230
1433
  console.error(`✓ detached the brain from ${repo}`);
1231
1434
  });
1232
- program.parseAsync().catch((err) => {
1435
+ program.parseAsync(withLegacyNames(process.argv)).catch((err) => {
1233
1436
  console.error(err instanceof Error ? err.message : err);
1234
1437
  process.exit(1);
1235
1438
  });