@nanonets/graft 0.19.0 → 0.21.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 (75) hide show
  1. package/README.md +9 -4
  2. package/dist/app/history.d.ts +36 -1
  3. package/dist/app/history.d.ts.map +1 -1
  4. package/dist/app/history.js +75 -22
  5. package/dist/app/history.js.map +1 -1
  6. package/dist/app/sources.d.ts +13 -0
  7. package/dist/app/sources.d.ts.map +1 -1
  8. package/dist/app/sources.js +129 -1
  9. package/dist/app/sources.js.map +1 -1
  10. package/dist/ask/ask.js +1 -1
  11. package/dist/brain/claude-md.d.ts +61 -0
  12. package/dist/brain/claude-md.d.ts.map +1 -0
  13. package/dist/brain/claude-md.js +171 -0
  14. package/dist/brain/claude-md.js.map +1 -0
  15. package/dist/brain/connect.d.ts +3 -3
  16. package/dist/brain/connect.js +7 -7
  17. package/dist/brain/context-files.d.ts +79 -0
  18. package/dist/brain/context-files.d.ts.map +1 -0
  19. package/dist/brain/context-files.js +245 -0
  20. package/dist/brain/context-files.js.map +1 -0
  21. package/dist/brain/link.d.ts +18 -1
  22. package/dist/brain/link.d.ts.map +1 -1
  23. package/dist/brain/link.js +22 -1
  24. package/dist/brain/link.js.map +1 -1
  25. package/dist/brain/pull.d.ts +17 -0
  26. package/dist/brain/pull.d.ts.map +1 -0
  27. package/dist/brain/pull.js +172 -0
  28. package/dist/brain/pull.js.map +1 -0
  29. package/dist/brain/push.d.ts +86 -3
  30. package/dist/brain/push.d.ts.map +1 -1
  31. package/dist/brain/push.js +149 -36
  32. package/dist/brain/push.js.map +1 -1
  33. package/dist/brain/signup.d.ts +46 -2
  34. package/dist/brain/signup.d.ts.map +1 -1
  35. package/dist/brain/signup.js +103 -13
  36. package/dist/brain/signup.js.map +1 -1
  37. package/dist/brain/upload.d.ts +61 -0
  38. package/dist/brain/upload.d.ts.map +1 -0
  39. package/dist/brain/upload.js +210 -0
  40. package/dist/brain/upload.js.map +1 -0
  41. package/dist/brain/watch.d.ts +77 -4
  42. package/dist/brain/watch.d.ts.map +1 -1
  43. package/dist/brain/watch.js +263 -39
  44. package/dist/brain/watch.js.map +1 -1
  45. package/dist/claude/build-progress.d.ts +29 -0
  46. package/dist/claude/build-progress.d.ts.map +1 -0
  47. package/dist/claude/build-progress.js +76 -0
  48. package/dist/claude/build-progress.js.map +1 -0
  49. package/dist/claude/init.d.ts.map +1 -1
  50. package/dist/claude/init.js +3 -1
  51. package/dist/claude/init.js.map +1 -1
  52. package/dist/cli-picker.d.ts +17 -2
  53. package/dist/cli-picker.d.ts.map +1 -1
  54. package/dist/cli-picker.js +103 -22
  55. package/dist/cli-picker.js.map +1 -1
  56. package/dist/cli.js +462 -123
  57. package/dist/cli.js.map +1 -1
  58. package/dist/legacy-args.d.ts +12 -0
  59. package/dist/legacy-args.d.ts.map +1 -0
  60. package/dist/legacy-args.js +31 -0
  61. package/dist/legacy-args.js.map +1 -0
  62. package/dist/telemetry/contract.d.ts +17 -4
  63. package/dist/telemetry/contract.d.ts.map +1 -1
  64. package/dist/telemetry/contract.js +25 -7
  65. package/dist/telemetry/contract.js.map +1 -1
  66. package/dist/upkeep.d.ts +1 -1
  67. package/dist/upkeep.js +1 -1
  68. package/dist/util/spinner.d.ts +15 -0
  69. package/dist/util/spinner.d.ts.map +1 -0
  70. package/dist/util/spinner.js +52 -0
  71. package/dist/util/spinner.js.map +1 -0
  72. package/dist/util/state.d.ts +8 -0
  73. package/dist/util/state.d.ts.map +1 -1
  74. package/dist/util/state.js.map +1 -1
  75. package/package.json +1 -1
@@ -0,0 +1,171 @@
1
+ /**
2
+ * The CLAUDE.md half of `graft trail pull` (and of `graft claude-md pull`, its
3
+ * old name): write the CLAUDE.md changes accepted in Trail into this checkout's
4
+ * instruction file.
5
+ *
6
+ * Trail compares the repository's CLAUDE.md with what its history says and
7
+ * suggests edits and new sections; a person accepts some of them on the brain's
8
+ * CLAUDE.md page. This fetches the accepted ones, applies them to the local
9
+ * file, and tells Trail which it wrote so the page stops listing them.
10
+ *
11
+ * The file is edited line by line, never regenerated, so everything a change
12
+ * does not touch comes back byte for byte. An edit whose text is no longer in
13
+ * its section means the local file has moved on since Trail read it; that
14
+ * change is reported and left, never guessed at. Same rules as Trail's own
15
+ * ComposeClaudeMd, which is what the page's preview shows.
16
+ */
17
+ import { baseUrlFor } from "./link.js";
18
+ /** GET /api/public/brains/:id/claude-md — the accepted changes. */
19
+ export async function fetchAcceptedChanges(link, fetchImpl = fetch) {
20
+ const url = `${baseUrlFor(link)}/api/public/brains/${encodeURIComponent(link.brainId)}/claude-md`;
21
+ try {
22
+ const res = await fetchImpl(url, {
23
+ headers: { authorization: `Bearer ${link.token}`, accept: "application/json" },
24
+ signal: AbortSignal.timeout(15_000),
25
+ });
26
+ const body = await res.text();
27
+ if (res.status === 404 || res.status === 503) {
28
+ return { error: "this trail has no CLAUDE.md suggestions yet — its Trail may be older than the feature", unsupported: true };
29
+ }
30
+ if (!res.ok)
31
+ return { error: `Trail refused the pull: ${res.status} ${body.slice(0, 200)}` };
32
+ const parsed = JSON.parse(body);
33
+ return { path: parsed.path ?? "", head_sha: parsed.head_sha ?? "", changes: parsed.changes ?? [] };
34
+ }
35
+ catch (e) {
36
+ return { error: `could not reach Trail: ${e instanceof Error ? e.message : e}` };
37
+ }
38
+ }
39
+ /** POST /api/public/brains/:id/claude-md/applied — what this pull wrote. */
40
+ export async function markApplied(link, ids, fetchImpl = fetch) {
41
+ if (ids.length === 0)
42
+ return true;
43
+ const url = `${baseUrlFor(link)}/api/public/brains/${encodeURIComponent(link.brainId)}/claude-md/applied`;
44
+ try {
45
+ // Trail takes at most 100 per request.
46
+ for (let i = 0; i < ids.length; i += 100) {
47
+ const res = await fetchImpl(url, {
48
+ method: "POST",
49
+ headers: {
50
+ authorization: `Bearer ${link.token}`,
51
+ "content-type": "application/json",
52
+ accept: "application/json",
53
+ },
54
+ body: JSON.stringify({ ids: ids.slice(i, i + 100) }),
55
+ signal: AbortSignal.timeout(15_000),
56
+ });
57
+ if (!res.ok)
58
+ return false;
59
+ }
60
+ return true;
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ }
66
+ const HEADING = /^(#{1,6})\s+(.+?)\s*#*\s*$/;
67
+ /** Every ATX heading outside code fences, with the lines it owns. */
68
+ function sectionsOf(lines) {
69
+ const heads = [];
70
+ let fence = false;
71
+ lines.forEach((ln, i) => {
72
+ const t = ln.trim();
73
+ if (t.startsWith("```") || t.startsWith("~~~")) {
74
+ fence = !fence;
75
+ return;
76
+ }
77
+ if (fence)
78
+ return;
79
+ const m = HEADING.exec(ln);
80
+ if (m)
81
+ heads.push({ line: i, level: m[1].length, title: m[2].trim() });
82
+ });
83
+ return heads.map((h, i) => {
84
+ let end = lines.length;
85
+ for (let j = i + 1; j < heads.length; j++) {
86
+ if (heads[j].level <= h.level) {
87
+ end = heads[j].line;
88
+ break;
89
+ }
90
+ }
91
+ return { title: h.title, level: h.level, head: h.line, end };
92
+ });
93
+ }
94
+ const norm = (s) => s.trim().toLowerCase();
95
+ /** Trim the blank lines a section ends with, so appended text sits under its content. */
96
+ function lastContentLine(lines, s) {
97
+ let i = s.end;
98
+ while (i > s.head + 1 && lines[i - 1].trim() === "")
99
+ i--;
100
+ return i;
101
+ }
102
+ /**
103
+ * Apply accepted changes to the file's text. Pure: reads nothing, writes
104
+ * nothing, so the command and the tests share it.
105
+ */
106
+ export function applyChanges(original, changes) {
107
+ const crlf = original.includes("\r\n");
108
+ let lines = original.replace(/\r\n/g, "\n").split("\n");
109
+ const out = { text: original, written: [], present: [], skipped: [] };
110
+ // Edits first, against headings that exist; then additions, which may go
111
+ // after a section an edit just touched.
112
+ const ordered = [...changes.filter((c) => c.kind === "edit"), ...changes.filter((c) => c.kind === "add")];
113
+ for (const c of ordered) {
114
+ const sections = sectionsOf(lines);
115
+ if (c.kind === "edit") {
116
+ const s = sections.find((x) => norm(x.title) === norm(c.heading));
117
+ if (!s) {
118
+ out.skipped.push({ change: c, why: `no “${c.heading}” section in the file` });
119
+ continue;
120
+ }
121
+ const body = lines.slice(s.head + 1, s.end).join("\n");
122
+ const text = c.text.replace(/\r\n/g, "\n");
123
+ const find = (c.find ?? "").replace(/\r\n/g, "\n");
124
+ if (text.trim() && body.includes(text.trim())) {
125
+ out.present.push(c);
126
+ continue;
127
+ }
128
+ if (!find) {
129
+ const at = lastContentLine(lines, s);
130
+ lines = [...lines.slice(0, at), ...text.trimEnd().split("\n"), ...lines.slice(at)];
131
+ out.written.push(c);
132
+ continue;
133
+ }
134
+ const idx = body.indexOf(find);
135
+ if (idx < 0) {
136
+ out.skipped.push({ change: c, why: `the text it rewrites is no longer in “${c.heading}”` });
137
+ continue;
138
+ }
139
+ const next = body.slice(0, idx) + text + body.slice(idx + find.length);
140
+ lines = [...lines.slice(0, s.head + 1), ...next.split("\n"), ...lines.slice(s.end)];
141
+ out.written.push(c);
142
+ continue;
143
+ }
144
+ // add
145
+ if (sections.some((x) => norm(x.title) === norm(c.heading))) {
146
+ out.present.push(c);
147
+ continue;
148
+ }
149
+ const block = [`## ${c.heading.trim()}`, "", ...c.text.replace(/\r\n/g, "\n").trim().split("\n")];
150
+ const after = c.after_heading ? sections.find((x) => norm(x.title) === norm(c.after_heading)) : undefined;
151
+ if (after) {
152
+ const at = lastContentLine(lines, after);
153
+ // A blank line either side, whatever the file had there.
154
+ const tail = lines.slice(at);
155
+ lines = [...lines.slice(0, at), "", ...block, ...(tail.length > 0 && tail[0].trim() !== "" ? [""] : []), ...tail];
156
+ }
157
+ else {
158
+ while (lines.length > 0 && lines[lines.length - 1].trim() === "")
159
+ lines.pop();
160
+ lines = lines.length > 0 ? [...lines, "", ...block] : block;
161
+ lines.push("");
162
+ }
163
+ out.written.push(c);
164
+ }
165
+ let text = lines.join("\n");
166
+ if (out.written.length > 0 && !text.endsWith("\n"))
167
+ text += "\n";
168
+ out.text = out.written.length > 0 ? (crlf ? text.replace(/\n/g, "\r\n") : text) : original;
169
+ return out;
170
+ }
171
+ //# sourceMappingURL=claude-md.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-md.js","sourceRoot":"","sources":["../../src/brain/claude-md.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@
5
5
  import { type BrainLink, type BrainRule } from './link.js';
6
6
  import { type BrainWrite } from './wire.js';
7
7
  /**
8
- * Parse the `--brain` value.
8
+ * Parse the `--trail` value.
9
9
  *
10
10
  * The onboarding page hands over one string so there is one thing to copy. Two
11
11
  * accepted shapes:
@@ -33,7 +33,7 @@ export interface ConnectResult {
33
33
  *
34
34
  * The link is stored even when the pull fails. That is deliberate: a token
35
35
  * typed correctly against a brain that is momentarily unreachable should not
36
- * have to be pasted again — `graft brain pull` retries, and `ask` degrades to
36
+ * have to be pasted again — `graft trail pull` retries, and `ask` degrades to
37
37
  * the code-only answer meanwhile.
38
38
  */
39
39
  export declare function connectBrain(repo: string, link: BrainLink, opts?: {
@@ -50,7 +50,7 @@ export declare function pullBrain(repo: string, opts: {
50
50
  ids?: string[];
51
51
  fetchImpl?: typeof fetch;
52
52
  }): Promise<ConnectResult | null>;
53
- /** The linked brain and its cached rule set, for `graft brain status`. */
53
+ /** The linked brain and its cached rule set, for `graft trail status`. */
54
54
  export declare function brainStatus(repo: string): {
55
55
  link: BrainLink | null;
56
56
  rules: BrainRule[];
@@ -6,7 +6,7 @@ import { readLink, writeLink, fetchRules, writeRulesCache, readRulesCache, } fro
6
6
  import { writeBrainSections } from './wire.js';
7
7
  import { basename } from 'node:path';
8
8
  /**
9
- * Parse the `--brain` value.
9
+ * Parse the `--trail` value.
10
10
  *
11
11
  * The onboarding page hands over one string so there is one thing to copy. Two
12
12
  * accepted shapes:
@@ -21,19 +21,19 @@ import { basename } from 'node:path';
21
21
  export function parseBrainArg(value) {
22
22
  const raw = value.trim();
23
23
  if (!raw)
24
- return { error: 'empty --brain value' };
24
+ return { error: 'empty --trail value' };
25
25
  const cut = raw.indexOf(':');
26
26
  if (cut > 0) {
27
27
  const brainId = raw.slice(0, cut).trim();
28
28
  const token = raw.slice(cut + 1).trim();
29
29
  if (!brainId || !token)
30
- return { error: 'expected --brain <brainId>:<token>' };
30
+ return { error: 'expected --trail <brainId>:<token>' };
31
31
  return { brainId, token };
32
32
  }
33
33
  const envToken = process.env.GRAFT_BRAIN_TOKEN;
34
34
  if (!envToken) {
35
35
  return {
36
- error: 'expected --brain <brainId>:<token>, or a bare brain id with the token in GRAFT_BRAIN_TOKEN',
36
+ error: 'expected --trail <brainId>:<token>, or a bare brain id with the token in GRAFT_BRAIN_TOKEN',
37
37
  };
38
38
  }
39
39
  return { brainId: raw, token: envToken };
@@ -43,7 +43,7 @@ export function parseBrainArg(value) {
43
43
  *
44
44
  * The link is stored even when the pull fails. That is deliberate: a token
45
45
  * typed correctly against a brain that is momentarily unreachable should not
46
- * have to be pasted again — `graft brain pull` retries, and `ask` degrades to
46
+ * have to be pasted again — `graft trail pull` retries, and `ask` degrades to
47
47
  * the code-only answer meanwhile.
48
48
  */
49
49
  export async function connectBrain(repo, link, opts = { home: '' }) {
@@ -54,7 +54,7 @@ export async function connectBrain(repo, link, opts = { home: '' }) {
54
54
  linked: true,
55
55
  ruleCount: 0,
56
56
  writes: [],
57
- warning: 'could not reach the brain to pull its rules — the link is saved; run `graft brain pull` to retry',
57
+ warning: 'could not reach the brain to pull its rules — the link is saved; run `graft trail pull` to retry',
58
58
  };
59
59
  }
60
60
  writeRulesCache(repo, { brainId: link.brainId, fetchedAt: Date.now(), rules });
@@ -71,7 +71,7 @@ export async function pullBrain(repo, opts) {
71
71
  return null;
72
72
  return connectBrain(repo, link, opts);
73
73
  }
74
- /** The linked brain and its cached rule set, for `graft brain status`. */
74
+ /** The linked brain and its cached rule set, for `graft trail status`. */
75
75
  export function brainStatus(repo) {
76
76
  const link = readLink(repo);
77
77
  const cache = readRulesCache(repo);
@@ -0,0 +1,79 @@
1
+ import { type BrainLink } from "./link.js";
2
+ export type ContextFileKind = "agents_md" | "folder_claude_md" | "cursor_rule" | "skill";
3
+ /** One accepted change to one context file. */
4
+ export interface ContextFileChange {
5
+ id: string;
6
+ kind: "edit" | "add" | "create" | "replace";
7
+ heading?: string;
8
+ after_heading?: string;
9
+ find?: string;
10
+ /** edit/add: as in claude-md.ts. create/replace: the whole file. For
11
+ * replace, `find` is the whole file the suggestion was computed against. */
12
+ text: string;
13
+ reason?: string;
14
+ }
15
+ export interface ContextFile {
16
+ kind: ContextFileKind | string;
17
+ path: string;
18
+ exists?: boolean;
19
+ managed_by_graft?: boolean;
20
+ changes: ContextFileChange[];
21
+ }
22
+ export interface ContextFilesPull {
23
+ head_sha: string;
24
+ files: ContextFile[];
25
+ }
26
+ /** The kind of a context file, from its path — for the root CLAUDE.md pull,
27
+ * which names a path but no kind. */
28
+ export declare function kindForPath(path: string): string;
29
+ /**
30
+ * Whether a file of `kind` is for one of the `wired` agents. Everything passes
31
+ * when nothing is wired — there is no choice to respect — and so does a kind
32
+ * graft does not know, rather than dropping what Trail sent.
33
+ */
34
+ export declare function readByWired(kind: string, wired: readonly string[]): boolean;
35
+ /** The agents among `wired` that read a file of `kind`. */
36
+ export declare function readersOf(kind: string, wired: readonly string[]): string[];
37
+ /** GET /api/public/brains/:id/context-files. `unsupported` on a 404: an older Trail. */
38
+ export declare function fetchContextFiles(link: BrainLink, fetchImpl?: typeof fetch): Promise<ContextFilesPull | {
39
+ unsupported: true;
40
+ } | {
41
+ error: string;
42
+ }>;
43
+ /** POST /api/public/brains/:id/context-files/applied, 100 ids per request. */
44
+ export declare function markContextFilesApplied(link: BrainLink, ids: string[], fetchImpl?: typeof fetch): Promise<boolean>;
45
+ /** What a pull will do (or did) to one file. */
46
+ export interface FilePlan {
47
+ /** Repo-relative, forward slashes. */
48
+ path: string;
49
+ /** M: an existing file changes. A: a new file. "": nothing to write. */
50
+ status: "M" | "A" | "";
51
+ /** The file's new text, when status is set. */
52
+ text?: string;
53
+ written: ContextFileChange[];
54
+ /** Already there: counts as applied, nothing to write. */
55
+ present: ContextFileChange[];
56
+ skipped: Array<{
57
+ change: ContextFileChange;
58
+ why: string;
59
+ }>;
60
+ }
61
+ /**
62
+ * The repo-relative form of a path Trail sent, or null when it points
63
+ * anywhere but a file inside this checkout. Trail is trusted to suggest text,
64
+ * not to choose where on this machine it lands.
65
+ */
66
+ export declare function safeRelPath(root: string, path: string): string | null;
67
+ /**
68
+ * Work out one file's changes against what is on disk. Pure but for reading
69
+ * the file, so the command, the dry run and the tests share it.
70
+ */
71
+ export declare function planContextFile(root: string, file: ContextFile): FilePlan;
72
+ /** Write a planned file, making its folder when it is new. */
73
+ export declare function writePlannedFile(root: string, plan: FilePlan): void;
74
+ /** `+ Money · ~ Commands` — a new section is +, an edited one ~, a whole file nothing. */
75
+ export declare function changeSummary(changes: Array<{
76
+ kind: string;
77
+ heading?: string;
78
+ }>): string;
79
+ //# sourceMappingURL=context-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-files.d.ts","sourceRoot":"","sources":["../../src/brain/context-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,245 @@
1
+ /**
2
+ * `graft trail pull`, for every context file rather than only the root CLAUDE.md.
3
+ *
4
+ * Trail suggests changes to the files a repository hands its coding agents —
5
+ * AGENTS.md, a folder's own CLAUDE.md, Cursor rules, Claude skills — and a
6
+ * person accepts some of them. This fetches the accepted ones and writes them.
7
+ *
8
+ * Edits and new sections go through the same line-by-line patcher as the root
9
+ * CLAUDE.md (see claude-md.ts), so everything a change does not name comes back
10
+ * byte for byte. A `create` carries a whole file, and it is never allowed to
11
+ * overwrite a file someone wrote here: an existing file with other content is a
12
+ * conflict to report, unless graft itself owns that file. A `replace` (how a
13
+ * Cursor rule is edited) carries the whole new file and the whole file it was
14
+ * computed against, and is written only while the file here is still that one.
15
+ */
16
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
17
+ import { dirname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
18
+ import { applyChanges } from "./claude-md.js";
19
+ import { baseUrlFor } from "./link.js";
20
+ /**
21
+ * Which picked agents read a file of this kind. A file whose readers were not
22
+ * picked is neither listed nor written: `graft init` is where someone said which
23
+ * agents this repo is for, and a pull must not quietly add files for others.
24
+ */
25
+ const READERS = {
26
+ claude_md: ["claude"],
27
+ folder_claude_md: ["claude"],
28
+ skill: ["claude"],
29
+ agents_md: ["agents", "hermes", "antigravity"],
30
+ cursor_rule: ["cursor"],
31
+ };
32
+ /** The kind of a context file, from its path — for the root CLAUDE.md pull,
33
+ * which names a path but no kind. */
34
+ export function kindForPath(path) {
35
+ const p = path.replace(/\\/g, "/");
36
+ const name = p.split("/").pop() ?? "";
37
+ if (name === "CLAUDE.md")
38
+ return p.includes("/") ? "folder_claude_md" : "claude_md";
39
+ if (name === "AGENTS.md")
40
+ return "agents_md";
41
+ if (p.startsWith(".cursor/rules/"))
42
+ return "cursor_rule";
43
+ if (p.startsWith(".claude/skills/"))
44
+ return "skill";
45
+ return "";
46
+ }
47
+ /**
48
+ * Whether a file of `kind` is for one of the `wired` agents. Everything passes
49
+ * when nothing is wired — there is no choice to respect — and so does a kind
50
+ * graft does not know, rather than dropping what Trail sent.
51
+ */
52
+ export function readByWired(kind, wired) {
53
+ if (wired.length === 0)
54
+ return true;
55
+ const readers = READERS[kind];
56
+ if (!readers)
57
+ return true;
58
+ return readers.some((r) => wired.includes(r));
59
+ }
60
+ /** The agents among `wired` that read a file of `kind`. */
61
+ export function readersOf(kind, wired) {
62
+ const readers = READERS[kind] ?? [];
63
+ return wired.filter((w) => readers.includes(w));
64
+ }
65
+ /** GET /api/public/brains/:id/context-files. `unsupported` on a 404: an older Trail. */
66
+ export async function fetchContextFiles(link, fetchImpl = fetch) {
67
+ const url = `${baseUrlFor(link)}/api/public/brains/${encodeURIComponent(link.brainId)}/context-files`;
68
+ try {
69
+ const res = await fetchImpl(url, {
70
+ headers: { authorization: `Bearer ${link.token}`, accept: "application/json" },
71
+ signal: AbortSignal.timeout(15_000),
72
+ });
73
+ const body = await res.text();
74
+ if (res.status === 404)
75
+ return { unsupported: true };
76
+ if (!res.ok)
77
+ return { error: `Trail refused the pull: ${res.status} ${body.slice(0, 200)}` };
78
+ const parsed = JSON.parse(body);
79
+ const files = Array.isArray(parsed.files) ? parsed.files : [];
80
+ return {
81
+ head_sha: String(parsed.head_sha ?? ""),
82
+ files: files
83
+ .filter((f) => f && typeof f.path === "string")
84
+ .map((f) => ({ ...f, changes: Array.isArray(f.changes) ? f.changes : [] })),
85
+ };
86
+ }
87
+ catch (e) {
88
+ return { error: `could not reach Trail: ${e instanceof Error ? e.message : e}` };
89
+ }
90
+ }
91
+ /** POST /api/public/brains/:id/context-files/applied, 100 ids per request. */
92
+ export async function markContextFilesApplied(link, ids, fetchImpl = fetch) {
93
+ if (ids.length === 0)
94
+ return true;
95
+ const url = `${baseUrlFor(link)}/api/public/brains/${encodeURIComponent(link.brainId)}/context-files/applied`;
96
+ try {
97
+ for (let i = 0; i < ids.length; i += 100) {
98
+ const res = await fetchImpl(url, {
99
+ method: "POST",
100
+ headers: {
101
+ authorization: `Bearer ${link.token}`,
102
+ "content-type": "application/json",
103
+ accept: "application/json",
104
+ },
105
+ body: JSON.stringify({ ids: ids.slice(i, i + 100) }),
106
+ signal: AbortSignal.timeout(15_000),
107
+ });
108
+ if (!res.ok)
109
+ return false;
110
+ }
111
+ return true;
112
+ }
113
+ catch {
114
+ return false;
115
+ }
116
+ }
117
+ /**
118
+ * The repo-relative form of a path Trail sent, or null when it points
119
+ * anywhere but a file inside this checkout. Trail is trusted to suggest text,
120
+ * not to choose where on this machine it lands.
121
+ */
122
+ export function safeRelPath(root, path) {
123
+ const raw = path.replace(/\\/g, "/").trim();
124
+ if (!raw || isAbsolute(raw) || /^[a-zA-Z]:/.test(raw))
125
+ return null;
126
+ const parts = raw.split("/");
127
+ if (parts.some((p) => p === ".." || p === ""))
128
+ return null;
129
+ if (parts[0] === ".git")
130
+ return null;
131
+ const abs = resolve(root, normalize(raw));
132
+ const rel = relative(resolve(root), abs);
133
+ if (!rel || rel.startsWith("..") || isAbsolute(rel))
134
+ return null;
135
+ return rel.split(sep).join("/");
136
+ }
137
+ /**
138
+ * Work out one file's changes against what is on disk. Pure but for reading
139
+ * the file, so the command, the dry run and the tests share it.
140
+ */
141
+ export function planContextFile(root, file) {
142
+ const rel = safeRelPath(root, file.path);
143
+ const plan = { path: rel ?? file.path, status: "", written: [], present: [], skipped: [] };
144
+ if (!rel) {
145
+ for (const c of file.changes)
146
+ plan.skipped.push({ change: c, why: "the path points outside this repo" });
147
+ return plan;
148
+ }
149
+ const abs = join(root, rel);
150
+ const existed = existsSync(abs);
151
+ let original = "";
152
+ if (existed) {
153
+ try {
154
+ original = readFileSync(abs, "utf8");
155
+ }
156
+ catch {
157
+ for (const c of file.changes)
158
+ plan.skipped.push({ change: c, why: "the file here could not be read" });
159
+ return plan;
160
+ }
161
+ }
162
+ let text = original;
163
+ let changed = false;
164
+ // Whole files first; the edits and sections after them may be relative to one.
165
+ for (const c of file.changes.filter((x) => x.kind === "create")) {
166
+ const want = c.text.replace(/\r\n/g, "\n");
167
+ const have = text.replace(/\r\n/g, "\n");
168
+ if (!existed && !changed) {
169
+ text = c.text.endsWith("\n") ? c.text : `${c.text}\n`;
170
+ changed = true;
171
+ plan.written.push(c);
172
+ }
173
+ else if (have.trimEnd() === want.trimEnd()) {
174
+ plan.present.push(c);
175
+ }
176
+ else if (file.managed_by_graft) {
177
+ text = c.text.endsWith("\n") ? c.text : `${c.text}\n`;
178
+ changed = true;
179
+ plan.written.push(c);
180
+ }
181
+ else {
182
+ plan.skipped.push({ change: c, why: "a file with other content is already there" });
183
+ }
184
+ }
185
+ // Whole-file replacements, guarded by the text they were computed against.
186
+ const same = (a, b) => a.replace(/\r\n/g, "\n").trimEnd() === b.replace(/\r\n/g, "\n").trimEnd();
187
+ for (const c of file.changes.filter((x) => x.kind === "replace")) {
188
+ if (same(text, c.text)) {
189
+ plan.present.push(c);
190
+ }
191
+ else if (same(text, c.find ?? "") && (existed || changed || (c.find ?? "").trim() === "")) {
192
+ text = c.text.endsWith("\n") ? c.text : `${c.text}\n`;
193
+ changed = true;
194
+ plan.written.push(c);
195
+ }
196
+ else {
197
+ plan.skipped.push({ change: c, why: "the file changed here since Trail read it" });
198
+ }
199
+ }
200
+ const patches = file.changes.filter((x) => x.kind === "edit" || x.kind === "add");
201
+ if (patches.length > 0) {
202
+ // A file that is not here yet is an empty one: an edit to a section of it
203
+ // becomes that section, so the change lands rather than being skipped for
204
+ // a heading nobody could have written.
205
+ const fresh = !existed && !changed;
206
+ const r = applyChanges(text, patches.map((c) => ({
207
+ ...c,
208
+ heading: c.heading ?? "",
209
+ kind: fresh && c.kind === "edit" ? "add" : c.kind,
210
+ ...(fresh && c.kind === "edit" ? { after_heading: "" } : {}),
211
+ })));
212
+ const byId = new Map(patches.map((c) => [c.id, c]));
213
+ for (const w of r.written)
214
+ plan.written.push(byId.get(w.id));
215
+ for (const p of r.present)
216
+ plan.present.push(byId.get(p.id));
217
+ for (const s of r.skipped)
218
+ plan.skipped.push({ change: byId.get(s.change.id), why: s.why });
219
+ if (r.written.length > 0) {
220
+ text = r.text;
221
+ changed = true;
222
+ }
223
+ }
224
+ if (changed && text !== original) {
225
+ plan.status = existed ? "M" : "A";
226
+ plan.text = text;
227
+ }
228
+ return plan;
229
+ }
230
+ /** Write a planned file, making its folder when it is new. */
231
+ export function writePlannedFile(root, plan) {
232
+ if (!plan.status || plan.text === undefined)
233
+ return;
234
+ const abs = join(root, plan.path);
235
+ mkdirSync(dirname(abs), { recursive: true });
236
+ writeFileSync(abs, plan.text);
237
+ }
238
+ /** `+ Money · ~ Commands` — a new section is +, an edited one ~, a whole file nothing. */
239
+ export function changeSummary(changes) {
240
+ return changes
241
+ .filter((c) => c.kind === "add" || c.kind === "edit")
242
+ .map((c) => `${c.kind === "add" ? "+" : "~"} ${c.heading ?? ""}`.trim())
243
+ .join(" · ");
244
+ }
245
+ //# sourceMappingURL=context-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-files.js","sourceRoot":"","sources":["../../src/brain/context-files.ts"],"names":[],"mappings":"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"}
@@ -54,7 +54,7 @@ export declare function readLink(dir: string): BrainLink | null;
54
54
  * token — which is exactly why this is also where it gets ignored. The comment
55
55
  * on {@link BrainLink} has claimed `.graft/` was "git-ignored" since the day it
56
56
  * was written, and nothing ever made it true: `ensureGitignored` only ever ran
57
- * for the graph cache. Every repository anyone ran `graft brain connect` in was
57
+ * for the graph cache. Every repository anyone ran `graft trail connect` in was
58
58
  * therefore one `git add -A` away from publishing a credential. */
59
59
  export declare function writeLink(dir: string, link: BrainLink): void;
60
60
  /** Forget the link for repo `dir`. Leaves the cached rules for `uninstall` to remove. */
@@ -78,6 +78,23 @@ export declare function markRulesChecked(dir: string, now?: number): void;
78
78
  * stored value can always be corrected without editing a file.
79
79
  */
80
80
  export declare function baseUrlFor(link: BrainLink): string;
81
+ /** The API host before there is a link to read one from: signing up. */
82
+ export declare function apiBaseUrl(): string;
83
+ /** A sign-up an agent-run push started and has not collected yet. */
84
+ export interface PendingSignup {
85
+ /** The state in the link, which is what claims the trail. */
86
+ state: string;
87
+ /** The repository the link was for, so a changed remote starts over. */
88
+ repo: string;
89
+ createdAt: number;
90
+ }
91
+ /** The sign-up waiting on repo `dir`, or null when there is none or it is too
92
+ * old to claim, or it was for a different repository. */
93
+ export declare function readPendingSignup(dir: string, repo: string, maxAgeMs: number, now?: number): PendingSignup | null;
94
+ /** Save the sign-up an agent-run push just started. Git-ignored like the link,
95
+ * because the state claims a read token. */
96
+ export declare function writePendingSignup(dir: string, pending: PendingSignup): void;
97
+ export declare function clearPendingSignup(dir: string): void;
81
98
  /**
82
99
  * Fetch the brain's symbol-anchored rules.
83
100
  *