@nanonets/graft 0.22.1 → 0.23.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 (80) hide show
  1. package/README.md +4 -4
  2. package/dist/ask/ask.d.ts +0 -3
  3. package/dist/ask/ask.d.ts.map +1 -1
  4. package/dist/ask/ask.js +1 -3
  5. package/dist/ask/ask.js.map +1 -1
  6. package/dist/brand.d.ts.map +1 -1
  7. package/dist/brand.js +4 -1
  8. package/dist/brand.js.map +1 -1
  9. package/dist/claude/format.d.ts +1 -9
  10. package/dist/claude/format.d.ts.map +1 -1
  11. package/dist/claude/format.js +2 -17
  12. package/dist/claude/format.js.map +1 -1
  13. package/dist/claude/hook-layers.d.ts.map +1 -1
  14. package/dist/claude/hook-layers.js +5 -3
  15. package/dist/claude/hook-layers.js.map +1 -1
  16. package/dist/claude/hooks.d.ts +6 -0
  17. package/dist/claude/hooks.d.ts.map +1 -1
  18. package/dist/claude/hooks.js +101 -14
  19. package/dist/claude/hooks.js.map +1 -1
  20. package/dist/claude/init.d.ts +38 -7
  21. package/dist/claude/init.d.ts.map +1 -1
  22. package/dist/claude/init.js +172 -33
  23. package/dist/claude/init.js.map +1 -1
  24. package/dist/claude/settings-merge.d.ts +23 -0
  25. package/dist/claude/settings-merge.d.ts.map +1 -1
  26. package/dist/claude/settings-merge.js +103 -7
  27. package/dist/claude/settings-merge.js.map +1 -1
  28. package/dist/claude/skill-template.d.ts +4 -3
  29. package/dist/claude/skill-template.d.ts.map +1 -1
  30. package/dist/claude/skill-template.js +7 -3
  31. package/dist/claude/skill-template.js.map +1 -1
  32. package/dist/claude/state.d.ts +1 -1
  33. package/dist/claude/state.d.ts.map +1 -1
  34. package/dist/cli.js +43 -35
  35. package/dist/cli.js.map +1 -1
  36. package/dist/context/map-dir.d.ts +13 -0
  37. package/dist/context/map-dir.d.ts.map +1 -0
  38. package/dist/context/map-dir.js +67 -0
  39. package/dist/context/map-dir.js.map +1 -0
  40. package/dist/context/node-file.d.ts +4 -3
  41. package/dist/context/node-file.d.ts.map +1 -1
  42. package/dist/context/node-file.js +18 -4
  43. package/dist/context/node-file.js.map +1 -1
  44. package/dist/hosts/claude-global.d.ts.map +1 -1
  45. package/dist/hosts/claude-global.js +31 -11
  46. package/dist/hosts/claude-global.js.map +1 -1
  47. package/dist/hosts/config-write.js +1 -1
  48. package/dist/hosts/config-write.js.map +1 -1
  49. package/dist/hosts/retract.d.ts.map +1 -1
  50. package/dist/hosts/retract.js +15 -6
  51. package/dist/hosts/retract.js.map +1 -1
  52. package/dist/notes/branches.d.ts +18 -1
  53. package/dist/notes/branches.d.ts.map +1 -1
  54. package/dist/notes/branches.js +73 -7
  55. package/dist/notes/branches.js.map +1 -1
  56. package/dist/notes/edit-overlap.d.ts +62 -0
  57. package/dist/notes/edit-overlap.d.ts.map +1 -0
  58. package/dist/notes/edit-overlap.js +202 -0
  59. package/dist/notes/edit-overlap.js.map +1 -0
  60. package/dist/notes/notes.d.ts +6 -0
  61. package/dist/notes/notes.d.ts.map +1 -1
  62. package/dist/notes/notes.js +12 -0
  63. package/dist/notes/notes.js.map +1 -1
  64. package/dist/notes/repo-trail.d.ts +3 -1
  65. package/dist/notes/repo-trail.d.ts.map +1 -1
  66. package/dist/notes/repo-trail.js +40 -3
  67. package/dist/notes/repo-trail.js.map +1 -1
  68. package/dist/notes/session-cost.d.ts +16 -4
  69. package/dist/notes/session-cost.d.ts.map +1 -1
  70. package/dist/notes/session-cost.js +120 -11
  71. package/dist/notes/session-cost.js.map +1 -1
  72. package/dist/upkeep-run.d.ts.map +1 -1
  73. package/dist/upkeep-run.js +4 -2
  74. package/dist/upkeep-run.js.map +1 -1
  75. package/dist/upkeep.js +2 -2
  76. package/dist/upkeep.js.map +1 -1
  77. package/dist/util/state.d.ts.map +1 -1
  78. package/dist/util/state.js +2 -1
  79. package/dist/util/state.js.map +1 -1
  80. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -48,7 +48,7 @@ import { normalizePathPrefix } from "./util/paths.js";
48
48
  import { latestSession, formatSessionStats, sessionInputRate } from "./claude/session-metrics.js";
49
49
  import { listSessionIds, readSession, sessionDir } from "./claude/state.js";
50
50
  import { costLabel, listNotes, noteName, notePlace, noteTargets, readingTokens, savedByNote, commitLines, PERSONAL_SAVED_LINE, PR_SPEEDUP, SECTIONS, tokensWorthSaying, sectionLead, shortDate, tokensLabel, writeNote, mentionedFiles } from "./notes/notes.js";
51
- import { currentSessionCost } from "./notes/session-cost.js";
51
+ import { aboutTerms, currentSessionCost } from "./notes/session-cost.js";
52
52
  import { beginPastRead, finishPastRead } from "./notes/cold-start.js";
53
53
  import { beginReviewRead, finishReviewRead } from "./notes/pr-reviews.js";
54
54
  import { currentBranch, noteAuthor, teammatesOn } from "./notes/git-facts.js";
@@ -165,7 +165,7 @@ function queryRoot(dir) {
165
165
  return resolve(dir);
166
166
  const { root, levels } = nearestGraftRoot(process.cwd(), program.opts().dir);
167
167
  if (levels > 0)
168
- console.error(`${tag()} no graft/ here — answering from ${root}/graft`);
168
+ console.error(`${tag()} no code map here — answering from ${contextDirFor(root, program.opts().dir)}`);
169
169
  return root;
170
170
  }
171
171
  /**
@@ -292,10 +292,10 @@ program
292
292
  // Hidden: spawned detached by maybeRefreshBranches.
293
293
  program
294
294
  .command("_trail-branches", { hidden: true })
295
- .description("internal: fetch, then note which files teammates' pushed branches change and the learnings they carry")
295
+ .description("internal: fetch, then note which files teammates' open branches change and the learnings they carry")
296
296
  .argument("[dir]", "target repo directory", ".")
297
297
  .action((dir) => {
298
- refreshBranches(resolve(dir));
298
+ refreshBranches(resolve(dir), { prs: true });
299
299
  });
300
300
  program
301
301
  .command("_trail-claim", { hidden: true })
@@ -380,7 +380,7 @@ function moveHereToTrail(dir) {
380
380
  }
381
381
  grouped(program.command("build"), GROUP.setup)
382
382
  .description(TRAIL
383
- ? "Rebuild the code map in graft/ ($0, no key). --check fails if it's stale; --deep adds LLM summaries."
383
+ ? "Rebuild the code map in .trail/cache/ ($0, no key). --check fails if it's stale; --deep adds LLM summaries."
384
384
  : "Build graft/ from your code — wiring graph + per-file cards ($0, no key). " +
385
385
  "Add --deep for the LLM concept map + per-symbol summaries/crux.")
386
386
  .argument("[dir]", "repository root", ".")
@@ -610,11 +610,11 @@ function pointerFile(pointer) {
610
610
  return pointer.replace(/:L\d+(-L?\d+)?$/, "");
611
611
  }
612
612
  /**
613
- * After `ask`, under trail: which shown learnings are a teammate's, which
614
- * teammates' pushed branches change the files the answer points into, and the
613
+ * After `ask`, under trail: which shown learnings are a teammate's, and the
615
614
  * shown learnings recorded as a use, which is what the team page counts as
616
615
  * reuse. Then the background upkeep that keeps branches and the workspace
617
- * current. Spawns no git itself.
616
+ * current. Spawns no git itself. Teammates' branches come up after an edit
617
+ * instead, once there's a function to compare (notes/edit-overlap.ts).
618
618
  */
619
619
  function afterAsk(r, repo) {
620
620
  if (!r.notesChecked)
@@ -623,12 +623,6 @@ function afterAsk(r, repo) {
623
623
  maybeRefreshBranches(repo);
624
624
  const me = noteAuthor(repo);
625
625
  const files = [...new Set(r.hits.map((h) => pointerFile(h.pointer)))];
626
- const overlaps = overlapsWith(repo, files, me).slice(0, 2);
627
- if (overlaps.length) {
628
- r.overlaps = overlaps.map((o) => formatOverlap(o));
629
- if (!process.stdout.isTTY)
630
- r.overlaps.push(`${tag()} Start your reply with the overlap line${overlaps.length === 1 ? "" : "s"} above, word for word, so the user knows.`);
631
- }
632
626
  const shown = r.notes ?? [];
633
627
  if (shown.length) {
634
628
  for (const h of shown)
@@ -750,7 +744,7 @@ async function runCheckCommand(dirArg, opts, label) {
750
744
  console.log(JSON.stringify({ context: r, graph: g.missing ? null : g }, null, 2));
751
745
  }
752
746
  else if (bothMissing) {
753
- console.log(`${label}: NO GRAPH\n\nNo graft/ graph found. Run \`${cmd("graft build")}\` first.`);
747
+ console.log(`${label}: NO GRAPH\n\nNo code map found. Run \`${cmd("graft build")}\` first.`);
754
748
  }
755
749
  else {
756
750
  if (r.missing) {
@@ -1192,9 +1186,11 @@ async function runNoteCommand(repo, opts) {
1192
1186
  };
1193
1187
  const minutes = wholeNumber(opts.minutes, "--minutes");
1194
1188
  const tokens = wholeNumber(opts.tokens, "--tokens");
1195
- // What the session spent, from the agent's own transcript when there is one;
1196
- // the flags win, since only the agent knows where its task began.
1197
- const spent = currentSessionCost(repo);
1189
+ // What the session spent, from the agent's own transcript when there is one,
1190
+ // counted from where the work the note names first came up; the flags win,
1191
+ // since only the agent knows for sure where its task began.
1192
+ const named = [...(opts.touches?.split(",").map((t) => t.trim()).filter(Boolean) ?? []), ...mentionedFiles(repo, body)];
1193
+ const spent = currentSessionCost(repo, undefined, Date.now(), aboutTerms(body, named));
1198
1194
  const cost = minutes !== undefined || tokens !== undefined || spent
1199
1195
  ? { minutes: minutes ?? spent?.minutes, tokens: tokens ?? spent?.tokens }
1200
1196
  : undefined;
@@ -1354,6 +1350,20 @@ async function runInitCommand(dir, opts) {
1354
1350
  }
1355
1351
  const wantClaude = ids.includes("claude");
1356
1352
  const cliPath = fileURLToPath(import.meta.url);
1353
+ // Under trail, each repo gets its `.trail/learnings/`, committed with the
1354
+ // code, and its folder in ~/.trail for personal learnings and corrections.
1355
+ // First, before the reads below send their first event: that makes the
1356
+ // repo one on trail, so the event's repo id lands in `.trail/cache/` and
1357
+ // not in a `graft/` that nothing ignores.
1358
+ const notesHome = new Map();
1359
+ if (TRAIL)
1360
+ for (const target of targets) {
1361
+ const { place } = ensureRepoHome(target);
1362
+ const created = place.git ? ensureRepoTrail(place.checkout) : false;
1363
+ notesHome.set(target, { created, dir: place.git ? place.checkout : place.dir });
1364
+ if (place.git)
1365
+ maybeRefreshBranches(place.checkout);
1366
+ }
1357
1367
  // Under trail, with Claude Code picked: what the person kept telling Claude
1358
1368
  // in this repo's past sessions, and what reviewers kept flagging on its
1359
1369
  // merged PRs, read while the rest of init runs and saved as learnings at
@@ -1369,17 +1379,6 @@ async function runInitCommand(dir, opts) {
1369
1379
  // Today's output, file by file, behind --verbose. Everything else gets one
1370
1380
  // line per step: the graph, then one per agent.
1371
1381
  const verbose = opts.verbose === true;
1372
- // Under trail, each repo gets its `.trail/learnings/`, committed with the
1373
- // code, and its folder in ~/.trail for personal learnings and corrections.
1374
- const notesHome = new Map();
1375
- if (TRAIL)
1376
- for (const target of targets) {
1377
- const { place } = ensureRepoHome(target);
1378
- const created = place.git ? ensureRepoTrail(place.checkout) : false;
1379
- notesHome.set(target, { created, dir: place.git ? place.checkout : place.dir });
1380
- if (place.git)
1381
- maybeRefreshBranches(place.checkout);
1382
- }
1383
1382
  const reports = [];
1384
1383
  for (const target of targets) {
1385
1384
  if (verbose && target !== repo)
@@ -1523,8 +1522,12 @@ async function runInitCommand(dir, opts) {
1523
1522
  for (const w of selectedWrites(plan, ids)) {
1524
1523
  if (w.scope !== "repo")
1525
1524
  continue;
1526
- const parts = shown(repo, w.path).split("/");
1527
- const top = parts.length > 1 ? `${parts[0]}/` : parts[0];
1525
+ const rel = shown(repo, w.path);
1526
+ // trail's Claude Code setup shares one file in .claude/; the rest there is personal.
1527
+ if (TRAIL && rel.startsWith(".claude/") && rel !== ".claude/settings.json")
1528
+ continue;
1529
+ const parts = rel.split("/");
1530
+ const top = TRAIL && rel === ".claude/settings.json" ? rel : parts.length > 1 ? `${parts[0]}/` : parts[0];
1528
1531
  if (!tops.includes(top))
1529
1532
  tops.push(top);
1530
1533
  }
@@ -1538,8 +1541,9 @@ async function runInitCommand(dir, opts) {
1538
1541
  });
1539
1542
  tops.splice(0, tops.length, ...changedTops);
1540
1543
  if (tops.length)
1541
- console.error(`· commit ${tops.slice(0, 5).join(" ")}${tops.length > 5 ? ` +${tops.length - 5} more` : ""} to share it — ` +
1542
- (TRAIL ? "teammates' Claude reads the learnings even without trail installed; the code map in graft/ stays on this machine" : "graft/ stays local and git-ignored"));
1544
+ console.error(TRAIL
1545
+ ? `· commit ${tops.slice(0, 5).join(" and ")}${tops.length > 5 ? ` +${tops.length - 5} more` : ""} once — after that, teammates' Claude reads your learnings even before they install trail`
1546
+ : `· commit ${tops.slice(0, 5).join(" ")}${tops.length > 5 ? ` +${tops.length - 5} more` : ""} to share it — graft/ stays local and git-ignored`);
1543
1547
  }
1544
1548
  // One epilogue for the whole run. A workspace parent holds no nodes of its
1545
1549
  // own, so the totals come from the children — the graph the user actually got.
@@ -1590,8 +1594,12 @@ function wireTarget(repo, ids, ctx) {
1590
1594
  say(`✓ wrote ${res.settingsPath}`);
1591
1595
  for (const s of res.shims)
1592
1596
  say(`✓ wrote ${s}`);
1593
- say(`✓ wrote ${res.skill}`);
1594
- if (res.mcp.action === "skipped-unparseable")
1597
+ if (res.skill)
1598
+ say(`✓ wrote ${res.skill}`);
1599
+ if (!res.mcp) {
1600
+ // trail registers no server for Claude Code: the hooks and the `trail` command cover it.
1601
+ }
1602
+ else if (res.mcp.action === "skipped-unparseable")
1595
1603
  say(`⚠ .mcp.json: ${res.mcp.path} left unchanged (not valid JSON) — add the graft server manually`);
1596
1604
  else if (res.mcp.action === "unchanged")
1597
1605
  say(`· mcp claude: ${res.mcp.path} (already registered)`);