@nanonets/graft 0.22.0 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- package/dist/app/checkout.d.ts +0 -32
- package/dist/app/checkout.d.ts.map +1 -1
- package/dist/app/checkout.js +0 -45
- package/dist/app/checkout.js.map +1 -1
- package/dist/app/identity.d.ts +0 -56
- package/dist/app/identity.d.ts.map +1 -1
- package/dist/app/identity.js +0 -119
- package/dist/app/identity.js.map +1 -1
- package/dist/app/main.js +0 -8
- package/dist/app/main.js.map +1 -1
- package/dist/app/server.d.ts +0 -20
- package/dist/app/server.d.ts.map +1 -1
- package/dist/app/server.js +0 -121
- package/dist/app/server.js.map +1 -1
- package/dist/check/learning-edits.d.ts +9 -0
- package/dist/check/learning-edits.d.ts.map +1 -0
- package/dist/check/learning-edits.js +62 -0
- package/dist/check/learning-edits.js.map +1 -0
- package/dist/claude/format.d.ts.map +1 -1
- package/dist/claude/format.js +4 -4
- package/dist/claude/format.js.map +1 -1
- package/dist/claude/shim-template.d.ts.map +1 -1
- package/dist/claude/shim-template.js +39 -3
- package/dist/claude/shim-template.js.map +1 -1
- package/dist/claude/skill-template.d.ts.map +1 -1
- package/dist/claude/skill-template.js +18 -7
- package/dist/claude/skill-template.js.map +1 -1
- package/dist/cli-picker.d.ts +15 -0
- package/dist/cli-picker.d.ts.map +1 -1
- package/dist/cli-picker.js +33 -3
- package/dist/cli-picker.js.map +1 -1
- package/dist/cli.js +100 -27
- package/dist/cli.js.map +1 -1
- package/dist/cloud/diff.d.ts +2 -0
- package/dist/cloud/diff.d.ts.map +1 -1
- package/dist/cloud/diff.js +1 -0
- package/dist/cloud/diff.js.map +1 -1
- package/dist/notes/cold-start.d.ts +54 -4
- package/dist/notes/cold-start.d.ts.map +1 -1
- package/dist/notes/cold-start.js +97 -36
- package/dist/notes/cold-start.js.map +1 -1
- package/dist/notes/notes.d.ts +19 -4
- package/dist/notes/notes.d.ts.map +1 -1
- package/dist/notes/notes.js +53 -15
- package/dist/notes/notes.js.map +1 -1
- package/dist/notes/pr-reviews.d.ts +108 -0
- package/dist/notes/pr-reviews.d.ts.map +1 -0
- package/dist/notes/pr-reviews.js +325 -0
- package/dist/notes/pr-reviews.js.map +1 -0
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +6 -5
- package/dist/telemetry/contract.js.map +1 -1
- package/package.json +1 -1
- package/dist/app/brain-build-process.d.ts +0 -22
- package/dist/app/brain-build-process.d.ts.map +0 -1
- package/dist/app/brain-build-process.js +0 -129
- package/dist/app/brain-build-process.js.map +0 -1
- package/dist/app/brain-build-worker.d.ts +0 -35
- package/dist/app/brain-build-worker.d.ts.map +0 -1
- package/dist/app/brain-build-worker.js +0 -56
- package/dist/app/brain-build-worker.js.map +0 -1
- package/dist/app/brain-build.d.ts +0 -160
- package/dist/app/brain-build.d.ts.map +0 -1
- package/dist/app/brain-build.js +0 -278
- package/dist/app/brain-build.js.map +0 -1
- package/dist/app/history.d.ts +0 -163
- package/dist/app/history.d.ts.map +0 -1
- package/dist/app/history.js +0 -319
- package/dist/app/history.js.map +0 -1
- package/dist/app/sources.d.ts +0 -81
- package/dist/app/sources.d.ts.map +0 -1
- package/dist/app/sources.js +0 -470
- package/dist/app/sources.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -26,6 +26,7 @@ import { checkoutRoot, ensureRepoHome, keepsNotes, rememberRepo, repoName, repoP
|
|
|
26
26
|
import { brand, cmd, graftNotice, strandedRepoNotice, tag } from "./brand.js";
|
|
27
27
|
import { branchDiff } from "./cloud/diff.js";
|
|
28
28
|
import { openBrowser } from "./util/browser.js";
|
|
29
|
+
import { learningsAtBase, teammateEdits, withBaseLearnings } from "./check/learning-edits.js";
|
|
29
30
|
import { contextDirFor } from "./context/node-file.js";
|
|
30
31
|
import { loadGraphCached } from "./graph/load.js";
|
|
31
32
|
import { ensureFreshChildren, ensureFreshGraph, refreshNote } from "./graph/refresh.js";
|
|
@@ -37,7 +38,7 @@ import { runWorkspaceAsk, runWorkspaceBuild, runWorkspaceCallers, runWorkspaceCh
|
|
|
37
38
|
import { formatInitEpilogue } from "./cli-epilogue.js";
|
|
38
39
|
import { planInit, selectedWrites } from "./hosts/plan.js";
|
|
39
40
|
import { planRetract, runRetract, changed } from "./hosts/retract.js";
|
|
40
|
-
import { compactWrites, formatNonInteractiveHelp, formatPlan, runPicker } from "./cli-picker.js";
|
|
41
|
+
import { compactWrites, formatNonInteractiveHelp, formatPlan, pickLearnings, runPicker } from "./cli-picker.js";
|
|
41
42
|
import { buildGraphWithProgress } from "./claude/build-progress.js";
|
|
42
43
|
import { homedir } from "node:os";
|
|
43
44
|
import { formatUpgradeReport, formatVersionReport, getNpmViewVersion, readCurrentVersion, readPackageName, runUpgrade } from "./cli-meta.js";
|
|
@@ -46,9 +47,10 @@ import { patchBuildConfig } from "./util/state.js";
|
|
|
46
47
|
import { normalizePathPrefix } from "./util/paths.js";
|
|
47
48
|
import { latestSession, formatSessionStats, sessionInputRate } from "./claude/session-metrics.js";
|
|
48
49
|
import { listSessionIds, readSession, sessionDir } from "./claude/state.js";
|
|
49
|
-
import { costLabel, listNotes, noteName, notePlace, noteTargets, readingTokens, savedByNote, commitLines, PERSONAL_SAVED_LINE, SECTIONS, sectionLead, shortDate, tokensLabel, writeNote, mentionedFiles } from "./notes/notes.js";
|
|
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";
|
|
50
51
|
import { currentSessionCost } from "./notes/session-cost.js";
|
|
51
52
|
import { beginPastRead, finishPastRead } from "./notes/cold-start.js";
|
|
53
|
+
import { beginReviewRead, finishReviewRead } from "./notes/pr-reviews.js";
|
|
52
54
|
import { currentBranch, noteAuthor, teammatesOn } from "./notes/git-facts.js";
|
|
53
55
|
import { confirm, fold, getSkill, learn, listSkills, publish, skillForAgent, skillName, skillStatus } from "./skills/skills.js";
|
|
54
56
|
import { clearPending, cloudLines, loginLink, sessionKey, waitForConnect } from "./cloud/nudge.js";
|
|
@@ -267,15 +269,18 @@ function spelling(action) {
|
|
|
267
269
|
// Hidden: spawned detached by maybeSyncInBackground when a workspace repo's
|
|
268
270
|
// learnings changed, or uses are waiting to go. Running it by hand syncs now.
|
|
269
271
|
// A repo that moved from graft to trail at session start reads its past
|
|
270
|
-
// sessions here, detached, so the session doesn't wait on it.
|
|
272
|
+
// sessions and PR reviews here, detached, so the session doesn't wait on it.
|
|
271
273
|
program
|
|
272
274
|
.command("_trail-past-read", { hidden: true })
|
|
273
275
|
.argument("<dir>")
|
|
274
276
|
.action(async (dir) => {
|
|
275
277
|
const repo = resolve(dir);
|
|
276
278
|
const read = beginPastRead(repo);
|
|
279
|
+
const reviews = beginReviewRead(repo);
|
|
277
280
|
if (read)
|
|
278
281
|
await finishPastRead(repo, read);
|
|
282
|
+
if (reviews)
|
|
283
|
+
await finishReviewRead(repo, reviews);
|
|
279
284
|
});
|
|
280
285
|
program
|
|
281
286
|
.command("_trail-sync", { hidden: true })
|
|
@@ -1220,14 +1225,17 @@ async function runNoteCommand(repo, opts) {
|
|
|
1220
1225
|
for (const n of saved)
|
|
1221
1226
|
console.log(`✓ ${n.scope === "personal" ? "saved just for you" : "saved for your team"} · ${notePlace(n)}`);
|
|
1222
1227
|
const note = saved[0];
|
|
1228
|
+
// Name a code file if the note touches one; docs are rarely what it's about.
|
|
1229
|
+
const about = touches.find((t) => !/\.(md|mdx|txt)$/i.test(t.split("#")[0])) ?? touches[0];
|
|
1230
|
+
const first = about ? ` that touches ${basename(about.split("#")[0])}` : "";
|
|
1223
1231
|
const took = costLabel(note.cost);
|
|
1224
|
-
if (took) {
|
|
1225
|
-
// Name a code file if the note touches one; docs are rarely what it's about.
|
|
1226
|
-
const about = touches.find((t) => !/\.(md|mdx|txt)$/i.test(t.split("#")[0])) ?? touches[0];
|
|
1227
|
-
const first = about ? ` that touches ${basename(about.split("#")[0])}` : "";
|
|
1232
|
+
if (took && tokensWorthSaying(note.cost?.tokens)) {
|
|
1228
1233
|
const reading = tokensLabel(readingTokens(note)).replace(" tokens", "");
|
|
1229
1234
|
console.log(`· this ${took.replace(" to work out", " to figure out")}. the next session${first} gets it for ${reading}`);
|
|
1230
1235
|
}
|
|
1236
|
+
else if (note.scope !== "personal") {
|
|
1237
|
+
console.log(`· the next session${first} starts from this, and gets to a PR ${PR_SPEEDUP}`);
|
|
1238
|
+
}
|
|
1231
1239
|
// Learnings about the same files, from someone else: this one builds on theirs.
|
|
1232
1240
|
const files = new Set(touches.map((t) => t.split("#")[0]));
|
|
1233
1241
|
const prior = earlier.find((n) => n.author !== note.author && n.touches.some((t) => files.has(t.split("#")[0])));
|
|
@@ -1347,11 +1355,15 @@ async function runInitCommand(dir, opts) {
|
|
|
1347
1355
|
const wantClaude = ids.includes("claude");
|
|
1348
1356
|
const cliPath = fileURLToPath(import.meta.url);
|
|
1349
1357
|
// Under trail, with Claude Code picked: what the person kept telling Claude
|
|
1350
|
-
// in this repo's past sessions,
|
|
1351
|
-
//
|
|
1358
|
+
// in this repo's past sessions, and what reviewers kept flagging on its
|
|
1359
|
+
// merged PRs, read while the rest of init runs and saved as learnings at
|
|
1360
|
+
// the end. Once per repo, and never asked about first.
|
|
1352
1361
|
const pastRead = TRAIL && wantClaude ? beginPastRead(repo) : null;
|
|
1353
1362
|
if (pastRead)
|
|
1354
1363
|
console.error(`· reading your ${pastRead.sessions} past Claude sessions here for what you keep telling Claude`);
|
|
1364
|
+
const reviewRead = TRAIL && wantClaude ? beginReviewRead(repo) : null;
|
|
1365
|
+
if (reviewRead)
|
|
1366
|
+
console.error("· reading review comments on this repo's merged PRs for what reviewers keep flagging");
|
|
1355
1367
|
if (children.length)
|
|
1356
1368
|
console.error(`· workspace: wiring ${repo} and ${children.length} child repo(s) — ${children.join(", ")}`);
|
|
1357
1369
|
// Today's output, file by file, behind --verbose. Everything else gets one
|
|
@@ -1418,26 +1430,82 @@ async function runInitCommand(dir, opts) {
|
|
|
1418
1430
|
console.error(`✓ ${id.padEnd(width)} ${summary}`.trimEnd());
|
|
1419
1431
|
}
|
|
1420
1432
|
}
|
|
1421
|
-
// Under trail: what init learned from past sessions, where
|
|
1422
|
-
// learnings are, and how many there already are.
|
|
1433
|
+
// Under trail: what init learned from past sessions and PR reviews, where
|
|
1434
|
+
// this repo's learnings are, and how many there already are.
|
|
1423
1435
|
let learnedNow = false;
|
|
1424
|
-
if (TRAIL && pastRead) {
|
|
1425
|
-
if (!pastRead.done)
|
|
1436
|
+
if (TRAIL && (pastRead || reviewRead)) {
|
|
1437
|
+
if (pastRead && !pastRead.done)
|
|
1426
1438
|
console.error("· still reading your past sessions…");
|
|
1427
|
-
const
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1439
|
+
const fromSessions = pastRead ? (await pastRead.result).findings : [];
|
|
1440
|
+
if (reviewRead && !reviewRead.done)
|
|
1441
|
+
console.error("· still reading PR reviews…");
|
|
1442
|
+
const fromReviews = reviewRead ? (await reviewRead.result).findings : [];
|
|
1443
|
+
// On a terminal, the person ticks which to keep in one list for both
|
|
1444
|
+
// sources, all ticked to start; a scripted run, or one with --yes, keeps
|
|
1445
|
+
// them all. Cancelling saves none, and the next init asks again.
|
|
1446
|
+
const picked = process.stdin.isTTY && process.stderr.isTTY && !opts.yes && fromSessions.length + fromReviews.length
|
|
1447
|
+
? await pickLearnings([
|
|
1448
|
+
...fromSessions.map((f) => ({ title: f.title, forTeam: f.for === "team", sessions: f.sessions.length })),
|
|
1449
|
+
...fromReviews.map((f) => ({ title: f.title, forTeam: true, prs: f.prs.length })),
|
|
1450
|
+
])
|
|
1451
|
+
: undefined;
|
|
1452
|
+
// The picked rows that are `found`'s, which start at `from` in the list.
|
|
1453
|
+
const keepFrom = (from) => picked === undefined
|
|
1454
|
+
? undefined
|
|
1455
|
+
: async (found) => picked && picked.filter((i) => i >= from && i < from + found.length).map((i) => found[i - from]);
|
|
1456
|
+
const learned = [];
|
|
1457
|
+
if (pastRead) {
|
|
1458
|
+
const { result, saved, kept, cancelled } = await finishPastRead(repo, pastRead, keepFrom(0));
|
|
1459
|
+
const offered = result.findings.length;
|
|
1460
|
+
if (saved.length) {
|
|
1461
|
+
const n = saved.length;
|
|
1462
|
+
const what = n < offered ? `kept ${n} of the ${offered} things` : `learned ${n} thing${n === 1 ? "" : "s"}`;
|
|
1463
|
+
console.error(`✓ ${what} you told Claude more than once, from your last ${result.read} sessions here`);
|
|
1464
|
+
// Each with how many sessions it was said in: saveFindings keeps the findings' order.
|
|
1465
|
+
saved.forEach((note, i) => {
|
|
1466
|
+
const said = kept[i]?.sessions.length ?? 0;
|
|
1467
|
+
const count = said > 1 ? `told Claude in ${said} sessions: ` : "";
|
|
1468
|
+
console.error(` ${note.scope === "personal" ? "just for you " : "for your team"} · ${count}${note.title}`);
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
else if (cancelled) {
|
|
1472
|
+
console.error(`· saved none of the ${offered} things you told Claude more than once; ${cmd("graft init")} again asks again`);
|
|
1473
|
+
}
|
|
1474
|
+
else if (offered) {
|
|
1475
|
+
console.error(`· kept none of the ${offered} things you told Claude more than once`);
|
|
1476
|
+
}
|
|
1477
|
+
else if (result.reason === "failed") {
|
|
1478
|
+
console.error(`· couldn't read your past sessions this time; ${cmd("graft init")} again tries again`);
|
|
1479
|
+
}
|
|
1480
|
+
learned.push(...saved);
|
|
1481
|
+
}
|
|
1482
|
+
if (reviewRead) {
|
|
1483
|
+
const { result, saved, kept, cancelled } = await finishReviewRead(repo, reviewRead, keepFrom(fromSessions.length));
|
|
1484
|
+
const offered = result.findings.length;
|
|
1485
|
+
if (saved.length) {
|
|
1486
|
+
const n = saved.length;
|
|
1487
|
+
const what = n < offered ? `kept ${n} of the ${offered} things` : `learned ${n} thing${n === 1 ? "" : "s"}`;
|
|
1488
|
+
console.error(`✓ ${what} reviewers flagged on more than one PR, from the last ${result.prs} merged PRs here`);
|
|
1489
|
+
// saveReviewFindings keeps the findings' order.
|
|
1490
|
+
saved.forEach((note, i) => console.error(` for your team · flagged in review on ${kept[i]?.prs.length ?? 2} PRs: ${note.title}`));
|
|
1491
|
+
}
|
|
1492
|
+
else if (cancelled) {
|
|
1493
|
+
console.error(`· saved none of the ${offered} things reviewers flagged; ${cmd("graft init")} again asks again`);
|
|
1494
|
+
}
|
|
1495
|
+
else if (offered) {
|
|
1496
|
+
console.error(`· kept none of the ${offered} things reviewers flagged`);
|
|
1497
|
+
}
|
|
1498
|
+
else if (result.reason === "failed") {
|
|
1499
|
+
console.error(`· couldn't read PR reviews this time; ${cmd("graft init")} again tries again`);
|
|
1500
|
+
}
|
|
1501
|
+
learned.push(...saved);
|
|
1502
|
+
}
|
|
1503
|
+
learnedNow = learned.length > 0;
|
|
1504
|
+
if (learnedNow) {
|
|
1434
1505
|
console.error("· Claude follows them from your next session");
|
|
1435
|
-
if (
|
|
1506
|
+
if (learned.some((s) => s.scope === "repo"))
|
|
1436
1507
|
console.error(commitLines({}, teammatesOn(repo), "repo").uncommitted);
|
|
1437
1508
|
}
|
|
1438
|
-
else if (result.reason === "failed") {
|
|
1439
|
-
console.error(`· couldn't read your past sessions this time; ${cmd("graft init")} again tries again`);
|
|
1440
|
-
}
|
|
1441
1509
|
}
|
|
1442
1510
|
if (TRAIL && !learnedNow) {
|
|
1443
1511
|
const home = notesHome.get(repo);
|
|
@@ -1931,15 +1999,20 @@ async function runTeamCheck(repo, opts) {
|
|
|
1931
1999
|
return;
|
|
1932
2000
|
}
|
|
1933
2001
|
const files = d.files.filter((f) => !f.startsWith(".trail/"));
|
|
1934
|
-
|
|
2002
|
+
const me = noteAuthor(repo);
|
|
2003
|
+
// Learnings this branch changed, as they were before it: the check reads those,
|
|
2004
|
+
// and changing a teammate's is a finding of its own.
|
|
2005
|
+
const atBase = learningsAtBase(repo, d.from, d.files);
|
|
2006
|
+
const edits = teammateEdits(repo, atBase, me);
|
|
2007
|
+
if (files.length === 0 && edits.length === 0) {
|
|
1935
2008
|
console.log(`· nothing to check: no changes against ${d.base}`);
|
|
1936
2009
|
return;
|
|
1937
2010
|
}
|
|
1938
2011
|
maybeRefreshBranches(repo);
|
|
1939
2012
|
if (d.truncated)
|
|
1940
2013
|
console.error("⚠ the diff is over 400 KB; only the first 400 KB is checked");
|
|
1941
|
-
const
|
|
1942
|
-
|
|
2014
|
+
const r = checkLocally({ diff: withoutTrailFiles(d.diff), files, notes: withBaseLearnings(listNotes(repo), atBase), skills: listSkills(repo), asker: me });
|
|
2015
|
+
r.findings.unshift(...edits);
|
|
1943
2016
|
const sources = r.findings.map((f) => f.source).filter((s) => s.path);
|
|
1944
2017
|
recordActivity({
|
|
1945
2018
|
repo: repoPlace(repo).key,
|