@nanonets/graft 0.23.1 → 0.24.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 +4 -4
- package/dist/ai/crux.d.ts +31 -3
- package/dist/ai/crux.d.ts.map +1 -1
- package/dist/ai/crux.js +88 -10
- package/dist/ai/crux.js.map +1 -1
- package/dist/ai/llm/openai.d.ts +101 -0
- package/dist/ai/llm/openai.d.ts.map +1 -1
- package/dist/ai/llm/openai.js +396 -6
- package/dist/ai/llm/openai.js.map +1 -1
- package/dist/ai/llm/recover-tool.d.ts +2 -0
- package/dist/ai/llm/recover-tool.d.ts.map +1 -1
- package/dist/ai/llm/recover-tool.js +1 -1
- package/dist/ai/llm/recover-tool.js.map +1 -1
- package/dist/ai/synthesize.d.ts.map +1 -1
- package/dist/ai/synthesize.js +100 -1
- package/dist/ai/synthesize.js.map +1 -1
- package/dist/ask/ask.d.ts +8 -0
- package/dist/ask/ask.d.ts.map +1 -1
- package/dist/ask/ask.js +33 -12
- package/dist/ask/ask.js.map +1 -1
- package/dist/claude/format.js +1 -1
- package/dist/claude/format.js.map +1 -1
- package/dist/claude/hook-io.d.ts +44 -0
- package/dist/claude/hook-io.d.ts.map +1 -0
- package/dist/claude/hook-io.js +144 -0
- package/dist/claude/hook-io.js.map +1 -0
- package/dist/claude/hooks.d.ts +0 -18
- package/dist/claude/hooks.d.ts.map +1 -1
- package/dist/claude/hooks.js +24 -77
- package/dist/claude/hooks.js.map +1 -1
- package/dist/claude/init.js +3 -3
- package/dist/claude/init.js.map +1 -1
- package/dist/claude/note-signals.d.ts +28 -0
- package/dist/claude/note-signals.d.ts.map +1 -0
- package/dist/claude/note-signals.js +325 -0
- package/dist/claude/note-signals.js.map +1 -0
- package/dist/claude/shim-template.d.ts.map +1 -1
- package/dist/claude/shim-template.js +10 -4
- 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 +49 -18
- package/dist/claude/skill-template.js.map +1 -1
- package/dist/claude/skills-hook-util.d.ts +25 -0
- package/dist/claude/skills-hook-util.d.ts.map +1 -0
- package/dist/claude/skills-hook-util.js +71 -0
- package/dist/claude/skills-hook-util.js.map +1 -0
- package/dist/claude/state.d.ts +4 -3
- package/dist/claude/state.d.ts.map +1 -1
- package/dist/claude/state.js.map +1 -1
- package/dist/claude/sync-run.js +1 -1
- package/dist/claude/sync-run.js.map +1 -1
- package/dist/cli-picker.d.ts +36 -5
- package/dist/cli-picker.d.ts.map +1 -1
- package/dist/cli-picker.js +60 -13
- package/dist/cli-picker.js.map +1 -1
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +406 -64
- package/dist/cli.js.map +1 -1
- package/dist/cloud/lookup-activity.d.ts +16 -0
- package/dist/cloud/lookup-activity.d.ts.map +1 -0
- package/dist/cloud/lookup-activity.js +56 -0
- package/dist/cloud/lookup-activity.js.map +1 -0
- package/dist/cloud/nudge.d.ts.map +1 -1
- package/dist/cloud/nudge.js +3 -0
- package/dist/cloud/nudge.js.map +1 -1
- package/dist/cloud/onboarding.d.ts +237 -0
- package/dist/cloud/onboarding.d.ts.map +1 -0
- package/dist/cloud/onboarding.js +536 -0
- package/dist/cloud/onboarding.js.map +1 -0
- package/dist/cloud/pair.d.ts +76 -0
- package/dist/cloud/pair.d.ts.map +1 -0
- package/dist/cloud/pair.js +249 -0
- package/dist/cloud/pair.js.map +1 -0
- package/dist/cloud/workspace-sync.d.ts +10 -2
- package/dist/cloud/workspace-sync.d.ts.map +1 -1
- package/dist/cloud/workspace-sync.js +119 -7
- package/dist/cloud/workspace-sync.js.map +1 -1
- package/dist/cloud/workspace.d.ts +22 -1
- package/dist/cloud/workspace.d.ts.map +1 -1
- package/dist/cloud/workspace.js +16 -1
- package/dist/cloud/workspace.js.map +1 -1
- package/dist/context/batches.d.ts +29 -0
- package/dist/context/batches.d.ts.map +1 -0
- package/dist/context/batches.js +130 -0
- package/dist/context/batches.js.map +1 -0
- package/dist/context/build.d.ts +7 -2
- package/dist/context/build.d.ts.map +1 -1
- package/dist/context/build.js +84 -50
- package/dist/context/build.js.map +1 -1
- package/dist/context/node-file.d.ts.map +1 -1
- package/dist/context/node-file.js +41 -4
- package/dist/context/node-file.js.map +1 -1
- package/dist/engine.d.ts +2 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1 -0
- package/dist/engine.js.map +1 -1
- package/dist/graph/cards.d.ts.map +1 -1
- package/dist/graph/cards.js +9 -4
- package/dist/graph/cards.js.map +1 -1
- package/dist/graph/enrich.d.ts.map +1 -1
- package/dist/graph/enrich.js +8 -3
- package/dist/graph/enrich.js.map +1 -1
- package/dist/graph/generic.d.ts.map +1 -1
- package/dist/graph/generic.js +207 -6
- package/dist/graph/generic.js.map +1 -1
- package/dist/graph/lsp/client.d.ts +18 -0
- package/dist/graph/lsp/client.d.ts.map +1 -1
- package/dist/graph/lsp/client.js +40 -2
- package/dist/graph/lsp/client.js.map +1 -1
- package/dist/graph/lsp/enrich.d.ts.map +1 -1
- package/dist/graph/lsp/enrich.js +9 -1
- package/dist/graph/lsp/enrich.js.map +1 -1
- package/dist/graph/lsp/registry.d.ts +7 -0
- package/dist/graph/lsp/registry.d.ts.map +1 -1
- package/dist/graph/lsp/registry.js +27 -2
- package/dist/graph/lsp/registry.js.map +1 -1
- package/dist/graph/native.d.ts +2 -0
- package/dist/graph/native.d.ts.map +1 -1
- package/dist/graph/native.js +29 -4
- package/dist/graph/native.js.map +1 -1
- package/dist/graph/queries/gleam.scm +51 -0
- package/dist/graph/resolve.d.ts.map +1 -1
- package/dist/graph/resolve.js +65 -3
- package/dist/graph/resolve.js.map +1 -1
- package/dist/graph/workspace-cli.d.ts +2 -0
- package/dist/graph/workspace-cli.d.ts.map +1 -1
- package/dist/graph/workspace-cli.js +1 -1
- package/dist/graph/workspace-cli.js.map +1 -1
- package/dist/hosts/claude-global.d.ts +14 -0
- package/dist/hosts/claude-global.d.ts.map +1 -1
- package/dist/hosts/claude-global.js +38 -3
- package/dist/hosts/claude-global.js.map +1 -1
- package/dist/init/install-record.d.ts +21 -0
- package/dist/init/install-record.d.ts.map +1 -0
- package/dist/init/install-record.js +144 -0
- package/dist/init/install-record.js.map +1 -0
- package/dist/init/plain-file.d.ts +11 -0
- package/dist/init/plain-file.d.ts.map +1 -0
- package/dist/init/plain-file.js +80 -0
- package/dist/init/plain-file.js.map +1 -0
- package/dist/mcp/brains.d.ts +72 -0
- package/dist/mcp/brains.d.ts.map +1 -0
- package/dist/mcp/brains.js +220 -0
- package/dist/mcp/brains.js.map +1 -0
- package/dist/mcp/server.d.ts +3 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +40 -5
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +7 -2
- package/dist/mcp/tools.js.map +1 -1
- package/dist/notes/branches.d.ts +7 -1
- package/dist/notes/branches.d.ts.map +1 -1
- package/dist/notes/branches.js +31 -3
- package/dist/notes/branches.js.map +1 -1
- package/dist/notes/cold-start.d.ts +42 -3
- package/dist/notes/cold-start.d.ts.map +1 -1
- package/dist/notes/cold-start.js +144 -24
- package/dist/notes/cold-start.js.map +1 -1
- package/dist/notes/credit.d.ts +37 -0
- package/dist/notes/credit.d.ts.map +1 -0
- package/dist/notes/credit.js +78 -0
- package/dist/notes/credit.js.map +1 -0
- package/dist/notes/every-repo.d.ts +18 -0
- package/dist/notes/every-repo.d.ts.map +1 -0
- package/dist/notes/every-repo.js +86 -0
- package/dist/notes/every-repo.js.map +1 -0
- package/dist/notes/find-notes.d.ts +17 -0
- package/dist/notes/find-notes.d.ts.map +1 -0
- package/dist/notes/find-notes.js +48 -0
- package/dist/notes/find-notes.js.map +1 -0
- package/dist/notes/flatten.d.ts +31 -0
- package/dist/notes/flatten.d.ts.map +1 -0
- package/dist/notes/flatten.js +73 -0
- package/dist/notes/flatten.js.map +1 -0
- package/dist/notes/git-facts.d.ts +2 -0
- package/dist/notes/git-facts.d.ts.map +1 -1
- package/dist/notes/git-facts.js +6 -0
- package/dist/notes/git-facts.js.map +1 -1
- package/dist/notes/guardrails.d.ts +8 -0
- package/dist/notes/guardrails.d.ts.map +1 -0
- package/dist/notes/guardrails.js +38 -0
- package/dist/notes/guardrails.js.map +1 -0
- package/dist/notes/home.d.ts +24 -6
- package/dist/notes/home.d.ts.map +1 -1
- package/dist/notes/home.js +82 -15
- package/dist/notes/home.js.map +1 -1
- package/dist/notes/learning-fields.d.ts +102 -0
- package/dist/notes/learning-fields.d.ts.map +1 -0
- package/dist/notes/learning-fields.js +214 -0
- package/dist/notes/learning-fields.js.map +1 -0
- package/dist/notes/lesson-index.d.ts +13 -0
- package/dist/notes/lesson-index.d.ts.map +1 -0
- package/dist/notes/lesson-index.js +135 -0
- package/dist/notes/lesson-index.js.map +1 -0
- package/dist/notes/linked-learnings.d.ts +8 -0
- package/dist/notes/linked-learnings.d.ts.map +1 -0
- package/dist/notes/linked-learnings.js +113 -0
- package/dist/notes/linked-learnings.js.map +1 -0
- package/dist/notes/memory-notes.d.ts +71 -0
- package/dist/notes/memory-notes.d.ts.map +1 -0
- package/dist/notes/memory-notes.js +239 -0
- package/dist/notes/memory-notes.js.map +1 -0
- package/dist/notes/note-gate.d.ts +42 -0
- package/dist/notes/note-gate.d.ts.map +1 -0
- package/dist/notes/note-gate.js +181 -0
- package/dist/notes/note-gate.js.map +1 -0
- package/dist/notes/notes.d.ts +29 -85
- package/dist/notes/notes.d.ts.map +1 -1
- package/dist/notes/notes.js +69 -254
- package/dist/notes/notes.js.map +1 -1
- package/dist/notes/pr-reviews.d.ts +35 -7
- package/dist/notes/pr-reviews.d.ts.map +1 -1
- package/dist/notes/pr-reviews.js +156 -46
- package/dist/notes/pr-reviews.js.map +1 -1
- package/dist/notes/replace.d.ts +14 -0
- package/dist/notes/replace.d.ts.map +1 -0
- package/dist/notes/replace.js +38 -0
- package/dist/notes/replace.js.map +1 -0
- package/dist/notes/repo-notes-dir.d.ts +2 -0
- package/dist/notes/repo-notes-dir.d.ts.map +1 -0
- package/dist/notes/repo-notes-dir.js +16 -0
- package/dist/notes/repo-notes-dir.js.map +1 -0
- package/dist/notes/secrets.d.ts +28 -0
- package/dist/notes/secrets.d.ts.map +1 -0
- package/dist/notes/secrets.js +362 -0
- package/dist/notes/secrets.js.map +1 -0
- package/dist/notes/targets.d.ts +37 -0
- package/dist/notes/targets.d.ts.map +1 -0
- package/dist/notes/targets.js +108 -0
- package/dist/notes/targets.js.map +1 -0
- package/dist/notes/team-check.d.ts +22 -0
- package/dist/notes/team-check.d.ts.map +1 -0
- package/dist/notes/team-check.js +130 -0
- package/dist/notes/team-check.js.map +1 -0
- package/dist/notes/trail-home.d.ts +3 -0
- package/dist/notes/trail-home.d.ts.map +1 -0
- package/dist/notes/trail-home.js +12 -0
- package/dist/notes/trail-home.js.map +1 -0
- package/dist/skills/anchors-cli.d.ts +33 -0
- package/dist/skills/anchors-cli.d.ts.map +1 -0
- package/dist/skills/anchors-cli.js +274 -0
- package/dist/skills/anchors-cli.js.map +1 -0
- package/dist/skills/anchors-git.d.ts +29 -0
- package/dist/skills/anchors-git.d.ts.map +1 -0
- package/dist/skills/anchors-git.js +241 -0
- package/dist/skills/anchors-git.js.map +1 -0
- package/dist/skills/anchors-wide.d.ts +25 -0
- package/dist/skills/anchors-wide.d.ts.map +1 -0
- package/dist/skills/anchors-wide.js +481 -0
- package/dist/skills/anchors-wide.js.map +1 -0
- package/dist/skills/anchors.d.ts +32 -0
- package/dist/skills/anchors.d.ts.map +1 -0
- package/dist/skills/anchors.js +485 -0
- package/dist/skills/anchors.js.map +1 -0
- package/dist/skills/answers-prompt.d.ts +13 -0
- package/dist/skills/answers-prompt.d.ts.map +1 -0
- package/dist/skills/answers-prompt.js +83 -0
- package/dist/skills/answers-prompt.js.map +1 -0
- package/dist/skills/check-result.d.ts +26 -0
- package/dist/skills/check-result.d.ts.map +1 -0
- package/dist/skills/check-result.js +46 -0
- package/dist/skills/check-result.js.map +1 -0
- package/dist/skills/codex-sandbox.d.ts +6 -0
- package/dist/skills/codex-sandbox.d.ts.map +1 -0
- package/dist/skills/codex-sandbox.js +55 -0
- package/dist/skills/codex-sandbox.js.map +1 -0
- package/dist/skills/compile-split.d.ts +57 -0
- package/dist/skills/compile-split.d.ts.map +1 -0
- package/dist/skills/compile-split.js +532 -0
- package/dist/skills/compile-split.js.map +1 -0
- package/dist/skills/compile-version.d.ts +37 -0
- package/dist/skills/compile-version.d.ts.map +1 -0
- package/dist/skills/compile-version.js +56 -0
- package/dist/skills/compile-version.js.map +1 -0
- package/dist/skills/compile-waits.d.ts +12 -0
- package/dist/skills/compile-waits.d.ts.map +1 -0
- package/dist/skills/compile-waits.js +52 -0
- package/dist/skills/compile-waits.js.map +1 -0
- package/dist/skills/compile-words.d.ts +82 -0
- package/dist/skills/compile-words.d.ts.map +1 -0
- package/dist/skills/compile-words.js +385 -0
- package/dist/skills/compile-words.js.map +1 -0
- package/dist/skills/compile.d.ts +35 -0
- package/dist/skills/compile.d.ts.map +1 -0
- package/dist/skills/compile.js +488 -0
- package/dist/skills/compile.js.map +1 -0
- package/dist/skills/credit-facts.d.ts +8 -0
- package/dist/skills/credit-facts.d.ts.map +1 -0
- package/dist/skills/credit-facts.js +32 -0
- package/dist/skills/credit-facts.js.map +1 -0
- package/dist/skills/directive-words.d.ts +11 -0
- package/dist/skills/directive-words.d.ts.map +1 -0
- package/dist/skills/directive-words.js +33 -0
- package/dist/skills/directive-words.js.map +1 -0
- package/dist/skills/fidelity.d.ts +42 -0
- package/dist/skills/fidelity.d.ts.map +1 -0
- package/dist/skills/fidelity.js +138 -0
- package/dist/skills/fidelity.js.map +1 -0
- package/dist/skills/forget-notice.d.ts +44 -0
- package/dist/skills/forget-notice.d.ts.map +1 -0
- package/dist/skills/forget-notice.js +131 -0
- package/dist/skills/forget-notice.js.map +1 -0
- package/dist/skills/found-skill.d.ts +35 -0
- package/dist/skills/found-skill.d.ts.map +1 -0
- package/dist/skills/found-skill.js +66 -0
- package/dist/skills/found-skill.js.map +1 -0
- package/dist/skills/front-version.d.ts +12 -0
- package/dist/skills/front-version.d.ts.map +1 -0
- package/dist/skills/front-version.js +87 -0
- package/dist/skills/front-version.js.map +1 -0
- package/dist/skills/host-facts.d.ts +51 -0
- package/dist/skills/host-facts.d.ts.map +1 -0
- package/dist/skills/host-facts.js +134 -0
- package/dist/skills/host-facts.js.map +1 -0
- package/dist/skills/ledger.d.ts +43 -0
- package/dist/skills/ledger.d.ts.map +1 -0
- package/dist/skills/ledger.js +336 -0
- package/dist/skills/ledger.js.map +1 -0
- package/dist/skills/lessons.d.ts +60 -0
- package/dist/skills/lessons.d.ts.map +1 -0
- package/dist/skills/lessons.js +396 -0
- package/dist/skills/lessons.js.map +1 -0
- package/dist/skills/never-save.d.ts +5 -0
- package/dist/skills/never-save.d.ts.map +1 -0
- package/dist/skills/never-save.js +158 -0
- package/dist/skills/never-save.js.map +1 -0
- package/dist/skills/offer-answers.d.ts +20 -0
- package/dist/skills/offer-answers.d.ts.map +1 -0
- package/dist/skills/offer-answers.js +51 -0
- package/dist/skills/offer-answers.js.map +1 -0
- package/dist/skills/own-folder.d.ts +9 -0
- package/dist/skills/own-folder.d.ts.map +1 -0
- package/dist/skills/own-folder.js +36 -0
- package/dist/skills/own-folder.js.map +1 -0
- package/dist/skills/params.d.ts +39 -0
- package/dist/skills/params.d.ts.map +1 -0
- package/dist/skills/params.js +275 -0
- package/dist/skills/params.js.map +1 -0
- package/dist/skills/person.d.ts +34 -0
- package/dist/skills/person.d.ts.map +1 -0
- package/dist/skills/person.js +102 -0
- package/dist/skills/person.js.map +1 -0
- package/dist/skills/probe-clean.d.ts +9 -0
- package/dist/skills/probe-clean.d.ts.map +1 -0
- package/dist/skills/probe-clean.js +48 -0
- package/dist/skills/probe-clean.js.map +1 -0
- package/dist/skills/probes.d.ts +26 -0
- package/dist/skills/probes.d.ts.map +1 -0
- package/dist/skills/probes.js +225 -0
- package/dist/skills/probes.js.map +1 -0
- package/dist/skills/ran-less.d.ts +5 -0
- package/dist/skills/ran-less.d.ts.map +1 -0
- package/dist/skills/ran-less.js +51 -0
- package/dist/skills/ran-less.js.map +1 -0
- package/dist/skills/recall.d.ts +119 -0
- package/dist/skills/recall.d.ts.map +1 -0
- package/dist/skills/recall.js +370 -0
- package/dist/skills/recall.js.map +1 -0
- package/dist/skills/receipts.d.ts +44 -0
- package/dist/skills/receipts.d.ts.map +1 -0
- package/dist/skills/receipts.js +223 -0
- package/dist/skills/receipts.js.map +1 -0
- package/dist/skills/risk-delta.d.ts +32 -0
- package/dist/skills/risk-delta.d.ts.map +1 -0
- package/dist/skills/risk-delta.js +56 -0
- package/dist/skills/risk-delta.js.map +1 -0
- package/dist/skills/risk.d.ts +73 -0
- package/dist/skills/risk.d.ts.map +1 -0
- package/dist/skills/risk.js +396 -0
- package/dist/skills/risk.js.map +1 -0
- package/dist/skills/run-checkout.d.ts +4 -0
- package/dist/skills/run-checkout.d.ts.map +1 -0
- package/dist/skills/run-checkout.js +37 -0
- package/dist/skills/run-checkout.js.map +1 -0
- package/dist/skills/runblock.d.ts +45 -0
- package/dist/skills/runblock.d.ts.map +1 -0
- package/dist/skills/runblock.js +222 -0
- package/dist/skills/runblock.js.map +1 -0
- package/dist/skills/save-parse.d.ts +42 -0
- package/dist/skills/save-parse.d.ts.map +1 -0
- package/dist/skills/save-parse.js +172 -0
- package/dist/skills/save-parse.js.map +1 -0
- package/dist/skills/save-store.d.ts +12 -0
- package/dist/skills/save-store.d.ts.map +1 -0
- package/dist/skills/save-store.js +22 -0
- package/dist/skills/save-store.js.map +1 -0
- package/dist/skills/settings.d.ts +38 -0
- package/dist/skills/settings.d.ts.map +1 -0
- package/dist/skills/settings.js +133 -0
- package/dist/skills/settings.js.map +1 -0
- package/dist/skills/skill-diff.d.ts +40 -0
- package/dist/skills/skill-diff.d.ts.map +1 -0
- package/dist/skills/skill-diff.js +191 -0
- package/dist/skills/skill-diff.js.map +1 -0
- package/dist/skills/skill-file.d.ts +24 -0
- package/dist/skills/skill-file.d.ts.map +1 -0
- package/dist/skills/skill-file.js +97 -0
- package/dist/skills/skill-file.js.map +1 -0
- package/dist/skills/skill-rebuild.d.ts +27 -0
- package/dist/skills/skill-rebuild.d.ts.map +1 -0
- package/dist/skills/skill-rebuild.js +85 -0
- package/dist/skills/skill-rebuild.js.map +1 -0
- package/dist/skills/skillmd.d.ts +17 -0
- package/dist/skills/skillmd.d.ts.map +1 -0
- package/dist/skills/skillmd.js +219 -0
- package/dist/skills/skillmd.js.map +1 -0
- package/dist/skills/skills.d.ts +2 -0
- package/dist/skills/skills.d.ts.map +1 -1
- package/dist/skills/skills.js +1 -1
- package/dist/skills/skills.js.map +1 -1
- package/dist/skills/staleness.d.ts +27 -0
- package/dist/skills/staleness.d.ts.map +1 -0
- package/dist/skills/staleness.js +178 -0
- package/dist/skills/staleness.js.map +1 -0
- package/dist/skills/state.d.ts +36 -0
- package/dist/skills/state.d.ts.map +1 -0
- package/dist/skills/state.js +259 -0
- package/dist/skills/state.js.map +1 -0
- package/dist/skills/store-fs.d.ts +77 -0
- package/dist/skills/store-fs.d.ts.map +1 -0
- package/dist/skills/store-fs.js +194 -0
- package/dist/skills/store-fs.js.map +1 -0
- package/dist/skills/store-held.d.ts +44 -0
- package/dist/skills/store-held.d.ts.map +1 -0
- package/dist/skills/store-held.js +147 -0
- package/dist/skills/store-held.js.map +1 -0
- package/dist/skills/store.d.ts +73 -0
- package/dist/skills/store.d.ts.map +1 -0
- package/dist/skills/store.js +279 -0
- package/dist/skills/store.js.map +1 -0
- package/dist/skills/types.d.ts +422 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/skills/types.js +2 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/skills/validate.d.ts +11 -0
- package/dist/skills/validate.d.ts.map +1 -0
- package/dist/skills/validate.js +112 -0
- package/dist/skills/validate.js.map +1 -0
- package/dist/skills/versions-fs.d.ts +45 -0
- package/dist/skills/versions-fs.d.ts.map +1 -0
- package/dist/skills/versions-fs.js +234 -0
- package/dist/skills/versions-fs.js.map +1 -0
- package/dist/skills/versions.d.ts +56 -0
- package/dist/skills/versions.d.ts.map +1 -0
- package/dist/skills/versions.js +351 -0
- package/dist/skills/versions.js.map +1 -0
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +8 -6
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/telemetry/notice.d.ts.map +1 -1
- package/dist/telemetry/notice.js +3 -1
- package/dist/telemetry/notice.js.map +1 -1
- package/dist/trace/check-reads.d.ts +36 -0
- package/dist/trace/check-reads.d.ts.map +1 -0
- package/dist/trace/check-reads.js +57 -0
- package/dist/trace/check-reads.js.map +1 -0
- package/dist/trace/checks.d.ts +63 -0
- package/dist/trace/checks.d.ts.map +1 -0
- package/dist/trace/checks.js +487 -0
- package/dist/trace/checks.js.map +1 -0
- package/dist/trace/claude.d.ts +124 -0
- package/dist/trace/claude.d.ts.map +1 -0
- package/dist/trace/claude.js +303 -0
- package/dist/trace/claude.js.map +1 -0
- package/dist/trace/codex-exec.d.ts +22 -0
- package/dist/trace/codex-exec.d.ts.map +1 -0
- package/dist/trace/codex-exec.js +129 -0
- package/dist/trace/codex-exec.js.map +1 -0
- package/dist/trace/codex-meta.d.ts +23 -0
- package/dist/trace/codex-meta.d.ts.map +1 -0
- package/dist/trace/codex-meta.js +112 -0
- package/dist/trace/codex-meta.js.map +1 -0
- package/dist/trace/codex.d.ts +33 -0
- package/dist/trace/codex.d.ts.map +1 -0
- package/dist/trace/codex.js +485 -0
- package/dist/trace/codex.js.map +1 -0
- package/dist/trace/cursor.d.ts +16 -0
- package/dist/trace/cursor.d.ts.map +1 -0
- package/dist/trace/cursor.js +133 -0
- package/dist/trace/cursor.js.map +1 -0
- package/dist/trace/dialect.d.ts +43 -0
- package/dist/trace/dialect.d.ts.map +1 -0
- package/dist/trace/dialect.js +95 -0
- package/dist/trace/dialect.js.map +1 -0
- package/dist/trace/epochs.d.ts +10 -0
- package/dist/trace/epochs.d.ts.map +1 -0
- package/dist/trace/epochs.js +16 -0
- package/dist/trace/epochs.js.map +1 -0
- package/dist/trace/failure.d.ts +15 -0
- package/dist/trace/failure.d.ts.map +1 -0
- package/dist/trace/failure.js +69 -0
- package/dist/trace/failure.js.map +1 -0
- package/dist/trace/gate-file.d.ts +71 -0
- package/dist/trace/gate-file.d.ts.map +1 -0
- package/dist/trace/gate-file.js +329 -0
- package/dist/trace/gate-file.js.map +1 -0
- package/dist/trace/guard-fast.d.ts +22 -0
- package/dist/trace/guard-fast.d.ts.map +1 -0
- package/dist/trace/guard-fast.js +213 -0
- package/dist/trace/guard-fast.js.map +1 -0
- package/dist/trace/host.d.ts +40 -0
- package/dist/trace/host.d.ts.map +1 -0
- package/dist/trace/host.js +84 -0
- package/dist/trace/host.js.map +1 -0
- package/dist/trace/housekeeping.d.ts +5 -0
- package/dist/trace/housekeeping.d.ts.map +1 -0
- package/dist/trace/housekeeping.js +93 -0
- package/dist/trace/housekeeping.js.map +1 -0
- package/dist/trace/jobs.d.ts +52 -0
- package/dist/trace/jobs.d.ts.map +1 -0
- package/dist/trace/jobs.js +182 -0
- package/dist/trace/jobs.js.map +1 -0
- package/dist/trace/known-repo.d.ts +3 -0
- package/dist/trace/known-repo.d.ts.map +1 -0
- package/dist/trace/known-repo.js +55 -0
- package/dist/trace/known-repo.js.map +1 -0
- package/dist/trace/pool.d.ts +40 -0
- package/dist/trace/pool.d.ts.map +1 -0
- package/dist/trace/pool.js +154 -0
- package/dist/trace/pool.js.map +1 -0
- package/dist/trace/redact.d.ts +8 -0
- package/dist/trace/redact.d.ts.map +1 -0
- package/dist/trace/redact.js +63 -0
- package/dist/trace/redact.js.map +1 -0
- package/dist/trace/repairs.d.ts +27 -0
- package/dist/trace/repairs.d.ts.map +1 -0
- package/dist/trace/repairs.js +303 -0
- package/dist/trace/repairs.js.map +1 -0
- package/dist/trace/run-calls.d.ts +82 -0
- package/dist/trace/run-calls.d.ts.map +1 -0
- package/dist/trace/run-calls.js +331 -0
- package/dist/trace/run-calls.js.map +1 -0
- package/dist/trace/run-join.d.ts +20 -0
- package/dist/trace/run-join.d.ts.map +1 -0
- package/dist/trace/run-join.js +99 -0
- package/dist/trace/run-join.js.map +1 -0
- package/dist/trace/run-sheets.d.ts +26 -0
- package/dist/trace/run-sheets.d.ts.map +1 -0
- package/dist/trace/run-sheets.js +145 -0
- package/dist/trace/run-sheets.js.map +1 -0
- package/dist/trace/run-turns.d.ts +24 -0
- package/dist/trace/run-turns.d.ts.map +1 -0
- package/dist/trace/run-turns.js +67 -0
- package/dist/trace/run-turns.js.map +1 -0
- package/dist/trace/run-verdict.d.ts +24 -0
- package/dist/trace/run-verdict.d.ts.map +1 -0
- package/dist/trace/run-verdict.js +176 -0
- package/dist/trace/run-verdict.js.map +1 -0
- package/dist/trace/runs.d.ts +54 -0
- package/dist/trace/runs.d.ts.map +1 -0
- package/dist/trace/runs.js +501 -0
- package/dist/trace/runs.js.map +1 -0
- package/dist/trace/seen-cache.d.ts +9 -0
- package/dist/trace/seen-cache.d.ts.map +1 -0
- package/dist/trace/seen-cache.js +46 -0
- package/dist/trace/seen-cache.js.map +1 -0
- package/dist/trace/shell-scan.d.ts +52 -0
- package/dist/trace/shell-scan.d.ts.map +1 -0
- package/dist/trace/shell-scan.js +318 -0
- package/dist/trace/shell-scan.js.map +1 -0
- package/dist/trace/shell-words.d.ts +91 -0
- package/dist/trace/shell-words.d.ts.map +1 -0
- package/dist/trace/shell-words.js +558 -0
- package/dist/trace/shell-words.js.map +1 -0
- package/dist/trace/shell.d.ts +91 -0
- package/dist/trace/shell.d.ts.map +1 -0
- package/dist/trace/shell.js +435 -0
- package/dist/trace/shell.js.map +1 -0
- package/dist/trace/skill-session.d.ts +64 -0
- package/dist/trace/skill-session.d.ts.map +1 -0
- package/dist/trace/skill-session.js +232 -0
- package/dist/trace/skill-session.js.map +1 -0
- package/dist/trace/state.d.ts +27 -0
- package/dist/trace/state.d.ts.map +1 -0
- package/dist/trace/state.js +98 -0
- package/dist/trace/state.js.map +1 -0
- package/dist/trace/tamper-code.d.ts +9 -0
- package/dist/trace/tamper-code.d.ts.map +1 -0
- package/dist/trace/tamper-code.js +174 -0
- package/dist/trace/tamper-code.js.map +1 -0
- package/dist/trace/tamper-git.d.ts +12 -0
- package/dist/trace/tamper-git.d.ts.map +1 -0
- package/dist/trace/tamper-git.js +77 -0
- package/dist/trace/tamper-git.js.map +1 -0
- package/dist/trace/tamper.d.ts +12 -0
- package/dist/trace/tamper.d.ts.map +1 -0
- package/dist/trace/tamper.js +405 -0
- package/dist/trace/tamper.js.map +1 -0
- package/dist/trace/typed.d.ts +30 -0
- package/dist/trace/typed.d.ts.map +1 -0
- package/dist/trace/typed.js +132 -0
- package/dist/trace/typed.js.map +1 -0
- package/dist/trace/types.d.ts +477 -0
- package/dist/trace/types.d.ts.map +1 -0
- package/dist/trace/types.js +12 -0
- package/dist/trace/types.js.map +1 -0
- package/dist/trace/wall-state.d.ts +86 -0
- package/dist/trace/wall-state.d.ts.map +1 -0
- package/dist/trace/wall-state.js +279 -0
- package/dist/trace/wall-state.js.map +1 -0
- package/dist/trace/walls-glob.d.ts +11 -0
- package/dist/trace/walls-glob.d.ts.map +1 -0
- package/dist/trace/walls-glob.js +264 -0
- package/dist/trace/walls-glob.js.map +1 -0
- package/dist/trace/walls-quoted.d.ts +25 -0
- package/dist/trace/walls-quoted.d.ts.map +1 -0
- package/dist/trace/walls-quoted.js +81 -0
- package/dist/trace/walls-quoted.js.map +1 -0
- package/dist/trace/walls.d.ts +40 -0
- package/dist/trace/walls.d.ts.map +1 -0
- package/dist/trace/walls.js +318 -0
- package/dist/trace/walls.js.map +1 -0
- package/dist/upkeep.d.ts +1 -1
- package/dist/upkeep.d.ts.map +1 -1
- package/dist/upkeep.js +4 -1
- package/dist/upkeep.js.map +1 -1
- package/dist/util/actions-folder.d.ts +2 -0
- package/dist/util/actions-folder.d.ts.map +1 -0
- package/dist/util/actions-folder.js +9 -0
- package/dist/util/actions-folder.js.map +1 -0
- package/dist/util/front-matter.d.ts +31 -4
- package/dist/util/front-matter.d.ts.map +1 -1
- package/dist/util/front-matter.js +95 -3
- package/dist/util/front-matter.js.map +1 -1
- package/dist/util/private-file.d.ts +14 -0
- package/dist/util/private-file.d.ts.map +1 -0
- package/dist/util/private-file.js +82 -0
- package/dist/util/private-file.js.map +1 -0
- package/dist/util/private-fs.d.ts +25 -0
- package/dist/util/private-fs.d.ts.map +1 -0
- package/dist/util/private-fs.js +253 -0
- package/dist/util/private-fs.js.map +1 -0
- package/dist/util/state.d.ts.map +1 -1
- package/dist/util/state.js +3 -0
- package/dist/util/state.js.map +1 -1
- package/dist/vendor/tree-sitter-swift/LICENSE +21 -0
- package/dist/vendor/tree-sitter-swift/node-types.json +30694 -0
- package/dist/vendor/tree-sitter-swift/prebuilds/darwin-arm64/tree-sitter-swift.node +0 -0
- package/dist/vendor/tree-sitter-swift/prebuilds/darwin-x64/tree-sitter-swift.node +0 -0
- package/dist/vendor/tree-sitter-swift/prebuilds/linux-arm64/tree-sitter-swift.node +0 -0
- package/dist/vendor/tree-sitter-swift/prebuilds/linux-x64/tree-sitter-swift.node +0 -0
- package/dist/vendor/tree-sitter-swift/prebuilds/win32-arm64/tree-sitter-swift.node +0 -0
- package/dist/vendor/tree-sitter-swift/prebuilds/win32-x64/tree-sitter-swift.node +0 -0
- package/dist/viewer/app.js +2 -2
- package/package.json +10 -7
- package/scripts/run-tests.mjs +14 -2
- package/scripts/stamp-telemetry-key.mjs +8 -5
- package/scripts/vendor-grammars.mjs +35 -0
package/dist/cli.js
CHANGED
|
@@ -41,6 +41,7 @@ import { planInit, selectedWrites } from "./hosts/plan.js";
|
|
|
41
41
|
import { planRetract, runRetract, changed } from "./hosts/retract.js";
|
|
42
42
|
import { compactWrites, formatNonInteractiveHelp, formatPlan, pickLearnings, runPicker } from "./cli-picker.js";
|
|
43
43
|
import { buildGraphWithProgress } from "./claude/build-progress.js";
|
|
44
|
+
import { installBrainsServer, removeBrainsServer } from "./hosts/claude-global.js";
|
|
44
45
|
import { homedir } from "node:os";
|
|
45
46
|
import { formatUpgradeReport, formatVersionReport, getNpmViewVersion, readCurrentVersion, readPackageName, runUpgrade } from "./cli-meta.js";
|
|
46
47
|
import { nativeFallbackNotice } from "./graph/native.js";
|
|
@@ -49,13 +50,20 @@ import { normalizePathPrefix } from "./util/paths.js";
|
|
|
49
50
|
import { latestSession, formatSessionStats, sessionInputRate } from "./claude/session-metrics.js";
|
|
50
51
|
import { listSessionIds, readSession, sessionDir } from "./claude/state.js";
|
|
51
52
|
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";
|
|
53
|
+
import { findLearning, liveLearnings } from "./notes/learning-fields.js";
|
|
52
54
|
import { aboutTerms, currentSessionCost } from "./notes/session-cost.js";
|
|
53
55
|
import { beginPastRead, finishPastRead } from "./notes/cold-start.js";
|
|
54
|
-
import {
|
|
56
|
+
import { skippedSkillLines } from "./skills/found-skill.js";
|
|
57
|
+
import { anchoredReviews, beginReviewRead, finishReviewRead } from "./notes/pr-reviews.js";
|
|
58
|
+
import { finishMemoryRead, memoryCandidates, offeredBefore } from "./notes/memory-notes.js";
|
|
59
|
+
import { finishPair, PairRun, validPairCode } from "./cloud/pair.js";
|
|
55
60
|
import { currentBranch, noteAuthor, teammatesOn } from "./notes/git-facts.js";
|
|
61
|
+
import { gateNote, namesRepoSymbol, symbolCandidates, trackedFiles } from "./notes/note-gate.js";
|
|
56
62
|
import { confirm, fold, getSkill, learn, listSkills, publish, skillForAgent, skillName, skillStatus } from "./skills/skills.js";
|
|
63
|
+
import { recordLookup } from "./cloud/lookup-activity.js";
|
|
57
64
|
import { clearPending, cloudLines, loginLink, sessionKey, waitForConnect } from "./cloud/nudge.js";
|
|
58
|
-
import { addRepo, clearCloud, cloudEnabled, fetchMe, fetchTeam, isError, readCloud, writeCloud } from "./cloud/workspace.js";
|
|
65
|
+
import { addRepo, clearCloud, cloudEnabled, fetchMe, fetchTeam, isError, readCloud, trailUrl, writeCloud } from "./cloud/workspace.js";
|
|
66
|
+
import { claimConnectCode, connectAgain, offlineLine, learningsSummary, machineName, normalizeCode, OnboardingReporter, repoRef, teammatesChecked, teammatesFromGit, tokensOnOneLookup, } from "./cloud/onboarding.js";
|
|
59
67
|
import { inWorkspace, maybeSyncInBackground, recordActivity, runSyncChild, syncRepo } from "./cloud/workspace-sync.js";
|
|
60
68
|
import { checkLocally } from "./check/local-check.js";
|
|
61
69
|
import { formatOverlap, maybeRefreshBranches, overlapsWith, pushedWhen, readBranchCache, refreshBranches } from "./notes/branches.js";
|
|
@@ -304,6 +312,14 @@ program
|
|
|
304
312
|
.action(async () => {
|
|
305
313
|
await waitForConnect();
|
|
306
314
|
});
|
|
315
|
+
program
|
|
316
|
+
.command("_trail-pair", { hidden: true })
|
|
317
|
+
.description("internal: after `init --pair`, wait for the connect and the review in the browser, then upload the repo's learnings")
|
|
318
|
+
.argument("<state>")
|
|
319
|
+
.argument("<dir>")
|
|
320
|
+
.action(async (state, dir) => {
|
|
321
|
+
await finishPair(state, resolve(dir));
|
|
322
|
+
});
|
|
307
323
|
program
|
|
308
324
|
.command("_update-check", { hidden: true })
|
|
309
325
|
.description("internal: refresh the cached latest-version answer")
|
|
@@ -390,6 +406,8 @@ grouped(program.command("build"), GROUP.setup)
|
|
|
390
406
|
.option("--deep", "run the LLM pass: concept nodes (graft/*.md) + per-symbol summary/crux")
|
|
391
407
|
.option("-e, --extensions <exts...>", 'code extensions to include (e.g. ".ts" ".py"); an extension with no parser is ignored with a warning that lists the supported set')
|
|
392
408
|
.option("-j, --concurrency <n>", "files summarized in parallel during --deep (default 5)")
|
|
409
|
+
.option("--synth-concurrency <n>", "concept synthesis batches in flight at once during --deep (default 4). Separate from -j: " +
|
|
410
|
+
"each synthesis call carries a whole batch of summaries, not one file")
|
|
393
411
|
.option("--no-reuse", "re-parse every file instead of replaying unchanged ones from the extraction cache")
|
|
394
412
|
.option("--lsp", "add compiler-grade call edges via a language server if one is installed (opt-in, slower; e.g. rust-analyzer, clangd)")
|
|
395
413
|
.option("--allow-partial", "with --deep: exit 0 even when some files' summaries failed (default: a degraded meaning tier exits 1)")
|
|
@@ -423,6 +441,11 @@ grouped(program.command("build"), GROUP.setup)
|
|
|
423
441
|
console.error(`✗ --concurrency must be a number, got "${opts.concurrency}"`);
|
|
424
442
|
process.exit(1);
|
|
425
443
|
}
|
|
444
|
+
const synthConcurrency = opts.synthConcurrency ? Math.max(1, Number(opts.synthConcurrency)) : undefined;
|
|
445
|
+
if (opts.synthConcurrency && !Number.isFinite(synthConcurrency)) {
|
|
446
|
+
console.error(`✗ --synth-concurrency must be a number, got "${opts.synthConcurrency}"`);
|
|
447
|
+
process.exit(1);
|
|
448
|
+
}
|
|
426
449
|
warnUnsupportedExtensions(opts.extensions);
|
|
427
450
|
// Persisted BEFORE the build itself runs, so this invocation's walks (and
|
|
428
451
|
// every later no-flag build / hooks refresh) see it identically — the
|
|
@@ -499,6 +522,7 @@ grouped(program.command("build"), GROUP.setup)
|
|
|
499
522
|
deep: !!deep,
|
|
500
523
|
extensions: opts.extensions,
|
|
501
524
|
concurrency,
|
|
525
|
+
synthConcurrency,
|
|
502
526
|
childConfig: cliConfig(),
|
|
503
527
|
override: buildGlobalDir,
|
|
504
528
|
includeDirs: opts.includeDir,
|
|
@@ -515,6 +539,7 @@ grouped(program.command("build"), GROUP.setup)
|
|
|
515
539
|
const c = await engine.init(dir, {
|
|
516
540
|
extensions: opts.extensions,
|
|
517
541
|
onlyDirs,
|
|
542
|
+
synthConcurrency,
|
|
518
543
|
onProgress: ({ phase, index, total, file }) => process.stderr.write(`\r${phase === "summarize" ? "reading" : "writing"} concepts ${index + 1}/${total}: ${file.slice(0, 40).padEnd(40)}`),
|
|
519
544
|
}).catch((err) => {
|
|
520
545
|
// Only the stage and a code enum; the message stays on this machine.
|
|
@@ -522,7 +547,11 @@ grouped(program.command("build"), GROUP.setup)
|
|
|
522
547
|
throw err;
|
|
523
548
|
});
|
|
524
549
|
process.stderr.write("\n");
|
|
525
|
-
|
|
550
|
+
const conceptLine = `concepts: ${c.nodes} nodes, ${c.links} links from ${c.files} files (${c.summarized} read, ${c.cached} cached)`;
|
|
551
|
+
if (c.nodes === 0)
|
|
552
|
+
console.error(`⚠ ${conceptLine}`);
|
|
553
|
+
else
|
|
554
|
+
console.log(`✓ ${conceptLine}`);
|
|
526
555
|
for (const e of c.errors)
|
|
527
556
|
console.error(`✗ ${e}`);
|
|
528
557
|
conceptErrors = c.errors;
|
|
@@ -606,39 +635,27 @@ grouped(program.command("build"), GROUP.setup)
|
|
|
606
635
|
}
|
|
607
636
|
}
|
|
608
637
|
});
|
|
609
|
-
/** The file part of a hit's `file:Lx-Ly` pointer. */
|
|
610
|
-
function pointerFile(pointer) {
|
|
611
|
-
return pointer.replace(/:L\d+(-L?\d+)?$/, "");
|
|
612
|
-
}
|
|
613
638
|
/**
|
|
614
639
|
* After `ask`, under trail: which shown learnings are a teammate's, and the
|
|
615
|
-
*
|
|
616
|
-
* reuse
|
|
617
|
-
* current. Spawns no git itself. Teammates' branches come up after an edit
|
|
640
|
+
* lookup recorded (the learnings shown are a use, which is what the team page
|
|
641
|
+
* counts as reuse; the tokens saved ride along). Then the background upkeep
|
|
642
|
+
* that keeps branches and the workspace current. Spawns no git itself. Teammates' branches come up after an edit
|
|
618
643
|
* instead, once there's a function to compare (notes/edit-overlap.ts).
|
|
619
644
|
*/
|
|
620
645
|
function afterAsk(r, repo) {
|
|
646
|
+
// Every lookup is reported, not only ones that showed a learning: the tokens
|
|
647
|
+
// the map saved are what the team page's running total counts. A no-op unless
|
|
648
|
+
// this machine is connected and the repo is the workspace's.
|
|
649
|
+
recordLookup(r, repo);
|
|
621
650
|
if (!r.notesChecked)
|
|
622
651
|
return;
|
|
623
652
|
rememberRepo(repo);
|
|
624
653
|
maybeRefreshBranches(repo);
|
|
625
654
|
const me = noteAuthor(repo);
|
|
626
|
-
const files = [...new Set(r.hits.map((h) => pointerFile(h.pointer)))];
|
|
627
655
|
const shown = r.notes ?? [];
|
|
628
656
|
if (shown.length) {
|
|
629
657
|
for (const h of shown)
|
|
630
658
|
h.note.teammate = !!h.note.author && !!me && h.note.author !== me;
|
|
631
|
-
recordActivity({
|
|
632
|
-
repo: repoPlace(repo).key,
|
|
633
|
-
kind: "search",
|
|
634
|
-
asker: me,
|
|
635
|
-
session: sessionKey(repo) ?? `${me}-${new Date().toISOString().slice(0, 10)}`,
|
|
636
|
-
query: r.query,
|
|
637
|
-
files,
|
|
638
|
-
shown_paths: shown.map((h) => noteName(h.note)),
|
|
639
|
-
shown_authors: shown.map((h) => h.note.author),
|
|
640
|
-
at: new Date().toISOString(),
|
|
641
|
-
});
|
|
642
659
|
const saved = shown.reduce((sum, h) => sum + savedByNote(h.note), 0);
|
|
643
660
|
track("learning_reused", { teammate: String(shown.some((h) => h.note.teammate)), saved_tokens_bucket: savedTokensBucket(saved) }, { repo });
|
|
644
661
|
}
|
|
@@ -837,9 +854,14 @@ program
|
|
|
837
854
|
.command("mcp", { hidden: TRAIL })
|
|
838
855
|
.description("Serve the graph over MCP (stdio) — exposes graft_find_code, graft_trace_calls, graft_find_all, graft_file_api, graft_repo_map and graft_check_freshness as tools")
|
|
839
856
|
.argument(...DIR_ARG)
|
|
840
|
-
.
|
|
841
|
-
|
|
857
|
+
.option("--brains", "serve only the connected Trail workspace's brain tools (what Claude Code gets after init --connect)")
|
|
858
|
+
.action(async (dirArg, opts) => {
|
|
842
859
|
const { startMcpServer } = await import("./mcp/server.js");
|
|
860
|
+
if (opts.brains) {
|
|
861
|
+
startMcpServer(process.cwd(), undefined, currentVersion, { brainsOnly: true });
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
const dir = noteQuery(queryRoot(dirArg));
|
|
843
865
|
const globalOpts = program.opts();
|
|
844
866
|
startMcpServer(dir, globalOpts.dir, currentVersion);
|
|
845
867
|
});
|
|
@@ -984,6 +1006,8 @@ grouped(program.command("note", { hidden: !TRAIL }), GROUP.memory)
|
|
|
984
1006
|
.option("--author <name>", "who wrote it (default: the first word of git config user.name)")
|
|
985
1007
|
.option("--minutes <n>", "what it took to work out, when the agent's transcript can't say")
|
|
986
1008
|
.option("--tokens <n>", "the same, in tokens")
|
|
1009
|
+
.option("--replaces <file>", "an earlier learning this one takes over from (its file name in .trail/learnings/); the old file stays")
|
|
1010
|
+
.option("--anyway", "save it even if trail's checks (names code, says why, not status, not a repeat) turn it away")
|
|
987
1011
|
.option("--json", "print the saved learning as JSON")
|
|
988
1012
|
.argument("[dir]", "repository root", ".")
|
|
989
1013
|
.action(async (dir, opts) => {
|
|
@@ -1154,8 +1178,19 @@ grouped(program.command("init"), GROUP.setup)
|
|
|
1154
1178
|
.option("-y, --yes", "skip the picker and wire every detected agent (the pre-0.8 default)")
|
|
1155
1179
|
.option("--no-global", "skip writes outside this repo (the ~/.codex/ config + hooks)")
|
|
1156
1180
|
.option("--verbose", "print every file written, the graph build's own output and the closing banner")
|
|
1181
|
+
.option("--connect <code>", "connect this machine to your Trail workspace with the one-time code from team.trailhq.com, and send it a summary of this repo (titles, counts and names; never code)")
|
|
1182
|
+
.option("--pair <code>", "the pair code from Trail's onboarding page: that page shows this run live, then you review what it found")
|
|
1183
|
+
.option("--server <url>", "the Trail that gave the code (default: Trail cloud, https://team.trailhq.com)")
|
|
1157
1184
|
.action(async (dir, opts) => {
|
|
1158
|
-
|
|
1185
|
+
if (opts.connect !== undefined && opts.pair !== undefined) {
|
|
1186
|
+
console.error("✗ --connect and --pair are two ways in: use the one your Trail page gave you");
|
|
1187
|
+
process.exitCode = 1;
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
if (opts.connect !== undefined)
|
|
1191
|
+
await runConnectInit(dir, opts);
|
|
1192
|
+
else
|
|
1193
|
+
await runInitCommand(dir, opts);
|
|
1159
1194
|
});
|
|
1160
1195
|
/** Everything on stdin, or "" when nothing is piped in. */
|
|
1161
1196
|
async function readStdin() {
|
|
@@ -1204,14 +1239,55 @@ async function runNoteCommand(repo, opts) {
|
|
|
1204
1239
|
// Then the files the note names, then the ones the session only read.
|
|
1205
1240
|
touches: [...new Set([...t.touches, ...mentionedFiles(t.dir, body), ...(t.read ?? [])])].slice(0, 10),
|
|
1206
1241
|
}));
|
|
1242
|
+
// A touch is a file git tracks or one this session edited: never a file
|
|
1243
|
+
// that was just lying around (a token, a scratch file).
|
|
1244
|
+
const real = (dir, files) => {
|
|
1245
|
+
const tracked = trackedFiles(dir, files.map((f) => f.split("#")[0]));
|
|
1246
|
+
const edited = new Set((spent?.edited ?? []).map((f) => relative(dir, f).split("\\").join("/")));
|
|
1247
|
+
return files.filter((f) => tracked.has(f.split("#")[0]) || edited.has(f.split("#")[0]));
|
|
1248
|
+
};
|
|
1249
|
+
for (const t of targets)
|
|
1250
|
+
t.touches = real(t.dir, t.touches);
|
|
1251
|
+
const scope = opts.personal ? "personal" : "repo";
|
|
1252
|
+
const primary = targets[0];
|
|
1253
|
+
const existing = listNotes(primary.dir, { branches: false });
|
|
1254
|
+
let old;
|
|
1255
|
+
if (opts.replaces) {
|
|
1256
|
+
const want = basename(opts.replaces.trim());
|
|
1257
|
+
old = findLearning(existing, want);
|
|
1258
|
+
if (!old) {
|
|
1259
|
+
console.error(`✗ --replaces: no learning named ${want} here. The names are the files in .trail/learnings/.`);
|
|
1260
|
+
process.exitCode = 1;
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
// What `replaces:` holds is the old learning's id (learning-fields.ts), not its file name.
|
|
1265
|
+
const replaces = old?.id;
|
|
1266
|
+
const text = `${opts.title}\n${body}`;
|
|
1267
|
+
const gate = gateNote({
|
|
1268
|
+
title: opts.title,
|
|
1269
|
+
body,
|
|
1270
|
+
personal: scope === "personal",
|
|
1271
|
+
files: real(primary.dir, [...new Set([...mentionedFiles(primary.dir, text), ...(opts.touches ? primary.touches : [])])]),
|
|
1272
|
+
namesSymbol: namesRepoSymbol(primary.dir, symbolCandidates(text)),
|
|
1273
|
+
touches: primary.touches,
|
|
1274
|
+
replaces,
|
|
1275
|
+
}, liveLearnings(existing).filter((n) => (n.scope ?? "repo") === scope));
|
|
1276
|
+
if (gate.problems.length && !opts.anyway) {
|
|
1277
|
+
console.error("✗ not saved yet:");
|
|
1278
|
+
for (const p of gate.problems)
|
|
1279
|
+
console.error(` · ${p}`);
|
|
1280
|
+
console.error(`Fix it and run ${cmd("graft note")} again, or add --anyway if a check is wrong.`);
|
|
1281
|
+
process.exitCode = 1;
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1207
1284
|
const author = opts.author?.trim() || noteAuthor(repo);
|
|
1208
1285
|
const date = new Date().toISOString().slice(0, 10);
|
|
1209
|
-
const scope = opts.personal ? "personal" : "repo";
|
|
1210
1286
|
const earlier = [];
|
|
1211
1287
|
const saved = [];
|
|
1212
1288
|
for (const t of targets) {
|
|
1213
1289
|
earlier.push(...listNotes(t.dir));
|
|
1214
|
-
saved.push(writeNote(t.dir, { title: opts.title, body, author, date, branch: currentBranch(t.dir), cost, touches: t.touches }, scope));
|
|
1290
|
+
saved.push(writeNote(t.dir, { title: opts.title, body, author, date, branch: currentBranch(t.dir), cost, touches: t.touches, replaces }, scope));
|
|
1215
1291
|
}
|
|
1216
1292
|
const touches = targets.flatMap((t) => t.touches);
|
|
1217
1293
|
track("note_saved", { has_cost: String(cost?.tokens !== undefined || cost?.minutes !== undefined), touches_bucket: countBucket(touches.length), scope: saved[0]?.scope ?? scope }, { repo });
|
|
@@ -1240,6 +1316,10 @@ async function runNoteCommand(repo, opts) {
|
|
|
1240
1316
|
console.log(`· builds on ${prior.author ? `${prior.author}'s` : "a"} learning from ${shortDate(prior.date)}`);
|
|
1241
1317
|
if (!SECTIONS.some((s) => sectionLead(body, s.heading) !== null))
|
|
1242
1318
|
console.log("· tip: learnings read best under ## Decided, ## Tried and ruled out and ## Watch out");
|
|
1319
|
+
for (const w of gate.warnings)
|
|
1320
|
+
console.log(`· ${w}`);
|
|
1321
|
+
if (old)
|
|
1322
|
+
console.log(`· takes over from ${noteName(old)}, which stays as the record`);
|
|
1243
1323
|
const repoSaved = saved.filter((n) => n.scope === "repo");
|
|
1244
1324
|
if (repoSaved.length) {
|
|
1245
1325
|
// Who else works on these files, for the case for committing it.
|
|
@@ -1247,7 +1327,8 @@ async function runNoteCommand(repo, opts) {
|
|
|
1247
1327
|
console.log(`${lines.uncommitted} · git add ${repoSaved.map(notePlace).join(" ")}`);
|
|
1248
1328
|
if (!process.stdout.isTTY && TRAIL)
|
|
1249
1329
|
console.log(`[trail] Close your reply with one line, word for word. If this learning is committed by the end of your turn: ${lines.committed}\n` +
|
|
1250
|
-
` If it isn't: ${
|
|
1330
|
+
` If it isn't: ${commitAsk(note.title, about)}\n` +
|
|
1331
|
+
" On a yes, commit it with the change: in the change's own commit when that isn't pushed yet.");
|
|
1251
1332
|
}
|
|
1252
1333
|
else {
|
|
1253
1334
|
console.log("· just for you: kept on this machine, out of the repo");
|
|
@@ -1260,24 +1341,68 @@ async function runNoteCommand(repo, opts) {
|
|
|
1260
1341
|
}
|
|
1261
1342
|
printLines(cloudLines(repo, { command: "note" }));
|
|
1262
1343
|
}
|
|
1344
|
+
/** What the agent asks after saving a team learning it hasn't committed: who reads it, and when. */
|
|
1345
|
+
export function commitAsk(title, about) {
|
|
1346
|
+
const when = about ? `when they open ${basename(about.split("#")[0])}` : "on their next session here";
|
|
1347
|
+
return `📝 Learning: "${title.replace(/"/g, "'")}" · your teammates' Claude reads it ${when}. Commit it with this change? (y/n)`;
|
|
1348
|
+
}
|
|
1263
1349
|
/** Lines that close a command's output, with a blank line before them. */
|
|
1264
1350
|
function printLines(lines) {
|
|
1265
1351
|
if (lines.length)
|
|
1266
1352
|
console.log(`\n${lines.join("\n")}`);
|
|
1267
1353
|
}
|
|
1354
|
+
/**
|
|
1355
|
+
* `--server <url>` on `init --connect` and `--pair`: the Trail that gave the
|
|
1356
|
+
* code, kept for the rest of this run as TRAIL_URL. False, after saying why,
|
|
1357
|
+
* when it isn't a URL.
|
|
1358
|
+
*/
|
|
1359
|
+
function useServerOption(raw) {
|
|
1360
|
+
const server = raw?.trim().replace(/\/+$/, "");
|
|
1361
|
+
if (!server)
|
|
1362
|
+
return true;
|
|
1363
|
+
if (!/^https?:\/\//.test(server)) {
|
|
1364
|
+
console.error("✗ --server takes a URL, like https://dev.trailhq.com");
|
|
1365
|
+
process.exitCode = 1;
|
|
1366
|
+
return false;
|
|
1367
|
+
}
|
|
1368
|
+
process.env.TRAIL_URL = server;
|
|
1369
|
+
return true;
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* What `init --pair` hears from init, as the onboarding page's steps: the map
|
|
1373
|
+
* is done when `mapped` fires, the past sessions when `learned` does. Nothing
|
|
1374
|
+
* to say for a run that read no sessions (that step is already marked).
|
|
1375
|
+
*/
|
|
1376
|
+
function pairProgress(pair) {
|
|
1377
|
+
return {
|
|
1378
|
+
mapped: (m) => pair.phase("files", "done", m.files ? `${m.files.toLocaleString("en-US")} files` : "", m.files ? `Mapped ${m.files.toLocaleString("en-US")} files` : "Mapped the code"),
|
|
1379
|
+
learned: (l) => {
|
|
1380
|
+
if (l.sessions <= 0)
|
|
1381
|
+
return;
|
|
1382
|
+
// The PR reviews' learnings come with `sessions: 0`; the rest were said in past sessions.
|
|
1383
|
+
const fromSessions = l.notes.filter((n) => n.sessions !== 0).length;
|
|
1384
|
+
pair.phase("decisions", "done", `${l.sessions} sessions`, `Read ${l.sessions} past sessions: ${fromSessions} thing${fromSessions === 1 ? "" : "s"} you told Claude more than once`);
|
|
1385
|
+
},
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1268
1388
|
/** Repo-relative with forward slashes, for printing. */
|
|
1269
1389
|
function shown(repo, path) {
|
|
1270
1390
|
return relative(repo, path).split("\\").join("/");
|
|
1271
1391
|
}
|
|
1392
|
+
/** Agents init knows by another id: both read AGENTS.md. */
|
|
1393
|
+
const AGENT_ALIASES = { codex: "agents", opencode: "agents" };
|
|
1272
1394
|
/** `graft init`. */
|
|
1273
|
-
async function runInitCommand(dir, opts) {
|
|
1395
|
+
async function runInitCommand(dir, opts, progress) {
|
|
1274
1396
|
if (opts.listAgents) {
|
|
1275
1397
|
for (const id of [...hostIds(), "claude"])
|
|
1276
1398
|
console.log(id);
|
|
1277
1399
|
return null;
|
|
1278
1400
|
}
|
|
1279
1401
|
const repo = resolve(dir);
|
|
1280
|
-
|
|
1402
|
+
// Codex and OpenCode read AGENTS.md: `--agents codex` means that wiring.
|
|
1403
|
+
// The name as given is kept for Trail's onboarding page.
|
|
1404
|
+
const asked = Array.isArray(opts.agents) ? opts.agents : undefined;
|
|
1405
|
+
const explicit = asked ? [...new Set(asked.map((id) => AGENT_ALIASES[id] ?? id))] : undefined;
|
|
1281
1406
|
// Checked before anything is written: run from the home folder, init wired
|
|
1282
1407
|
// it, mapped every code file under it, then said to commit.
|
|
1283
1408
|
const refusal = homeFolderRefusal(repo);
|
|
@@ -1357,6 +1482,26 @@ async function runInitCommand(dir, opts) {
|
|
|
1357
1482
|
console.error("· no agents selected — nothing written");
|
|
1358
1483
|
return null;
|
|
1359
1484
|
}
|
|
1485
|
+
// With --pair, Trail's onboarding page is waiting on this run: tell it
|
|
1486
|
+
// first, so the person can sign up while the rest of init runs.
|
|
1487
|
+
let pair = null;
|
|
1488
|
+
if (opts.pair !== undefined) {
|
|
1489
|
+
if (!useServerOption(opts.server))
|
|
1490
|
+
return null;
|
|
1491
|
+
if (!validPairCode(opts.pair)) {
|
|
1492
|
+
console.error(`✗ --pair takes the code from Trail's onboarding page, got "${opts.pair}"`);
|
|
1493
|
+
process.exitCode = 1;
|
|
1494
|
+
return null;
|
|
1495
|
+
}
|
|
1496
|
+
const started = await PairRun.start(repo, opts.pair, { agent: asked?.[0] ?? ids[0], version: currentVersion });
|
|
1497
|
+
if ("error" in started) {
|
|
1498
|
+
console.error(`⚠ couldn't tell Trail about this run (${started.error}); setting up here anyway`);
|
|
1499
|
+
}
|
|
1500
|
+
else {
|
|
1501
|
+
pair = started;
|
|
1502
|
+
console.error(`· Trail's page is following along · ${started.url}`);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1360
1505
|
const wantClaude = ids.includes("claude");
|
|
1361
1506
|
const cliPath = fileURLToPath(import.meta.url);
|
|
1362
1507
|
// Under trail, each repo gets its `.trail/learnings/`, committed with the
|
|
@@ -1383,6 +1528,16 @@ async function runInitCommand(dir, opts) {
|
|
|
1383
1528
|
const reviewRead = TRAIL && wantClaude ? beginReviewRead(repo) : null;
|
|
1384
1529
|
if (reviewRead)
|
|
1385
1530
|
console.error("· reading review comments on this repo's merged PRs for what reviewers keep flagging");
|
|
1531
|
+
if (pair) {
|
|
1532
|
+
if (pastRead)
|
|
1533
|
+
pair.phase("decisions", "running", "", `Reading ${pastRead.sessions} past Claude sessions`);
|
|
1534
|
+
else
|
|
1535
|
+
pair.phase("decisions", "done", "none here");
|
|
1536
|
+
if (reviewRead)
|
|
1537
|
+
pair.phase("prs", "running", "", "Reading review comments on merged PRs");
|
|
1538
|
+
else
|
|
1539
|
+
pair.phase("prs", "done", "none here");
|
|
1540
|
+
}
|
|
1386
1541
|
if (children.length)
|
|
1387
1542
|
console.error(`· workspace: wiring ${repo} and ${children.length} child repo(s) — ${children.join(", ")}`);
|
|
1388
1543
|
// Today's output, file by file, behind --verbose. Everything else gets one
|
|
@@ -1398,6 +1553,7 @@ async function runInitCommand(dir, opts) {
|
|
|
1398
1553
|
// the workspace build, so it covers every child.
|
|
1399
1554
|
let graphLine = null;
|
|
1400
1555
|
if (!verbose) {
|
|
1556
|
+
pair?.phase("files", "running", "", "Mapping the code");
|
|
1401
1557
|
const res = await buildGraphWithProgress(repo, { build: opts.build, cliPath });
|
|
1402
1558
|
for (const m of res.messages)
|
|
1403
1559
|
console.error(m);
|
|
@@ -1422,6 +1578,25 @@ async function runInitCommand(dir, opts) {
|
|
|
1422
1578
|
graphLine = `· skipped the graph build — run ${cmd("graft build")}`;
|
|
1423
1579
|
}
|
|
1424
1580
|
}
|
|
1581
|
+
// --pair hears the same stages --connect does; only the ones InitProgress
|
|
1582
|
+
// has no slot for (a read starting, the PR read, nothing to read) stay inline.
|
|
1583
|
+
const report = pair ? pairProgress(pair) : progress;
|
|
1584
|
+
if (report?.mapped) {
|
|
1585
|
+
const g = loadGraphCached(contextDirFor(repo, children.length ? undefined : program.opts().dir));
|
|
1586
|
+
report.mapped({
|
|
1587
|
+
nodes: g?.meta.nodeCount ?? 0,
|
|
1588
|
+
edges: g?.meta.edgeCount ?? 0,
|
|
1589
|
+
files: g ? g.nodes.filter((n) => n.kind === "file").length : 0,
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
// With --pair, one lookup measured against reading the files whole: the
|
|
1593
|
+
// page's token step. It needs the map, so it follows the build.
|
|
1594
|
+
let tokens = null;
|
|
1595
|
+
if (pair) {
|
|
1596
|
+
pair.phase("tokens", "running", "", "Measuring one lookup against reading the files");
|
|
1597
|
+
tokens = tokensOnOneLookup(repo, { contextDir: program.opts().dir });
|
|
1598
|
+
pair.tokensDone(tokens);
|
|
1599
|
+
}
|
|
1425
1600
|
if (!verbose) {
|
|
1426
1601
|
// Warnings and removals in full, whatever else is folded.
|
|
1427
1602
|
const removed = [...new Set(reports.flatMap((r) => r.retracted))];
|
|
@@ -1438,23 +1613,30 @@ async function runInitCommand(dir, opts) {
|
|
|
1438
1613
|
console.error(`✓ ${id.padEnd(width)} ${summary}`.trimEnd());
|
|
1439
1614
|
}
|
|
1440
1615
|
}
|
|
1441
|
-
// Under trail: what init learned
|
|
1442
|
-
//
|
|
1616
|
+
// Under trail: what init learned, where this repo's learnings are, and how
|
|
1617
|
+
// many there already are. The team's come from what is tied to the code:
|
|
1618
|
+
// what reviewers kept flagging on merged PRs, and Claude Code's notes about
|
|
1619
|
+
// the repo on this machine. What the person kept telling Claude is theirs.
|
|
1443
1620
|
let learnedNow = false;
|
|
1444
|
-
|
|
1621
|
+
let skillsWritten = [];
|
|
1622
|
+
let teamSaved = 0;
|
|
1623
|
+
const memory = TRAIL && wantClaude ? memoryCandidates(repo, { offered: offeredBefore(repo) }) : [];
|
|
1624
|
+
if (TRAIL && (pastRead || reviewRead || memory.length)) {
|
|
1445
1625
|
if (pastRead && !pastRead.done)
|
|
1446
1626
|
console.error("· still reading your past sessions…");
|
|
1447
1627
|
const fromSessions = pastRead ? (await pastRead.result).findings : [];
|
|
1448
1628
|
if (reviewRead && !reviewRead.done)
|
|
1449
1629
|
console.error("· still reading PR reviews…");
|
|
1450
|
-
const fromReviews = reviewRead ? (await reviewRead.result).findings : [];
|
|
1451
|
-
// On a terminal, the person ticks which to keep in one list
|
|
1452
|
-
//
|
|
1453
|
-
//
|
|
1454
|
-
|
|
1630
|
+
const fromReviews = reviewRead ? anchoredReviews(repo, (await reviewRead.result).findings) : [];
|
|
1631
|
+
// On a terminal, the person ticks which to keep in one list, the team's
|
|
1632
|
+
// first and ticked, their own unticked. A scripted run, or one with
|
|
1633
|
+
// --yes, keeps the team's and what they told Claude. Cancelling saves
|
|
1634
|
+
// none, and the next init asks again.
|
|
1635
|
+
const picked = process.stdin.isTTY && process.stderr.isTTY && !opts.yes && !pair && fromReviews.length + memory.length + fromSessions.length
|
|
1455
1636
|
? await pickLearnings([
|
|
1456
|
-
...
|
|
1457
|
-
...
|
|
1637
|
+
...fromReviews.map((f) => ({ title: f.title, forTeam: true, prs: f.prs })),
|
|
1638
|
+
...memory.map((c) => ({ title: c.title, forTeam: c.forTeam, memory: true })),
|
|
1639
|
+
...fromSessions.map((f) => ({ title: f.title, forTeam: false, sessions: f.sessions.length })),
|
|
1458
1640
|
])
|
|
1459
1641
|
: undefined;
|
|
1460
1642
|
// The picked rows that are `found`'s, which start at `from` in the list.
|
|
@@ -1462,65 +1644,101 @@ async function runInitCommand(dir, opts) {
|
|
|
1462
1644
|
? undefined
|
|
1463
1645
|
: async (found) => picked && picked.filter((i) => i >= from && i < from + found.length).map((i) => found[i - from]);
|
|
1464
1646
|
const learned = [];
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1647
|
+
const learnedFrom = [];
|
|
1648
|
+
let sessionsRead = 0;
|
|
1649
|
+
// Reviews first: which of them are offered depends on the learnings already saved.
|
|
1650
|
+
if (reviewRead) {
|
|
1651
|
+
const { result, saved, kept, cancelled } = await finishReviewRead(repo, reviewRead, keepFrom(0));
|
|
1652
|
+
const offered = fromReviews.length;
|
|
1653
|
+
pair?.phase("prs", "done", `${result.prs} PRs`, `Read ${result.prs} merged PRs: ${saved.length} thing${saved.length === 1 ? "" : "s"} reviewers made authors change`);
|
|
1468
1654
|
if (saved.length) {
|
|
1469
1655
|
const n = saved.length;
|
|
1470
1656
|
const what = n < offered ? `kept ${n} of the ${offered} things` : `learned ${n} thing${n === 1 ? "" : "s"}`;
|
|
1471
|
-
console.error(`✓ ${what}
|
|
1472
|
-
//
|
|
1473
|
-
saved.forEach((note, i) => {
|
|
1474
|
-
const said = kept[i]?.sessions.length ?? 0;
|
|
1475
|
-
const count = said > 1 ? `told Claude in ${said} sessions: ` : "";
|
|
1476
|
-
console.error(` ${note.scope === "personal" ? "just for you " : "for your team"} · ${count}${note.title}`);
|
|
1477
|
-
});
|
|
1657
|
+
console.error(`✓ ${what} reviewers made authors change, from the last ${result.prs} merged PRs here`);
|
|
1658
|
+
// saveReviewFindings keeps the findings' order.
|
|
1659
|
+
saved.forEach((note, i) => console.error(` for your team · from review on ${(kept[i]?.prs ?? []).map((n) => `#${n}`).join(", ")}: ${note.title}`));
|
|
1478
1660
|
}
|
|
1479
1661
|
else if (cancelled) {
|
|
1480
|
-
console.error(`· saved none of the ${offered} things
|
|
1662
|
+
console.error(`· saved none of the ${offered} things reviewers made authors change; ${cmd("graft init")} again asks again`);
|
|
1481
1663
|
}
|
|
1482
1664
|
else if (offered) {
|
|
1483
|
-
console.error(`· kept none of the ${offered} things
|
|
1665
|
+
console.error(`· kept none of the ${offered} things reviewers made authors change`);
|
|
1484
1666
|
}
|
|
1485
1667
|
else if (result.reason === "failed") {
|
|
1486
|
-
console.error(`· couldn't read
|
|
1668
|
+
console.error(`· couldn't read PR reviews this time; ${cmd("graft init")} again tries again`);
|
|
1487
1669
|
}
|
|
1488
1670
|
learned.push(...saved);
|
|
1671
|
+
// Not said in past sessions: `sessions: 0` keeps them out of the page's session count.
|
|
1672
|
+
for (const note of saved)
|
|
1673
|
+
learnedFrom.push({ note, sessions: 0 });
|
|
1489
1674
|
}
|
|
1490
|
-
if (
|
|
1491
|
-
const {
|
|
1675
|
+
if (memory.length) {
|
|
1676
|
+
const { saved, cancelled } = await finishMemoryRead(repo, memory, keepFrom(fromReviews.length));
|
|
1677
|
+
if (saved.length) {
|
|
1678
|
+
console.error(`✓ kept ${saved.length} of the ${memory.length} notes your Claude keeps about this repo`);
|
|
1679
|
+
for (const note of saved)
|
|
1680
|
+
console.error(` ${note.scope === "personal" ? "just for you " : "for your team"} · ${note.title}`);
|
|
1681
|
+
}
|
|
1682
|
+
else if (cancelled) {
|
|
1683
|
+
console.error(`· saved none of the ${memory.length} notes your Claude keeps about this repo; ${cmd("graft init")} again asks again`);
|
|
1684
|
+
}
|
|
1685
|
+
learned.push(...saved);
|
|
1686
|
+
for (const note of saved)
|
|
1687
|
+
learnedFrom.push({ note, sessions: 0 });
|
|
1688
|
+
}
|
|
1689
|
+
if (pastRead) {
|
|
1690
|
+
const { result, saved, kept, skills, cancelled } = await finishPastRead(repo, pastRead, keepFrom(fromReviews.length + memory.length));
|
|
1691
|
+
sessionsRead = result.read ?? 0;
|
|
1692
|
+
skillsWritten = skills;
|
|
1693
|
+
for (const { skill, path } of skills) {
|
|
1694
|
+
console.error(`✓ wrote a skill from a procedure you walked Claude through more than once: ${skill.title}`);
|
|
1695
|
+
console.error(` ${skill.for === "team" ? "for your team" : "just for you "} · ${path}`);
|
|
1696
|
+
}
|
|
1697
|
+
if (!cancelled)
|
|
1698
|
+
for (const line of skippedSkillLines(repo, result.skills, skills))
|
|
1699
|
+
console.error(line);
|
|
1700
|
+
saved.forEach((note, i) => learnedFrom.push({ note, sessions: kept[i]?.sessions.length }));
|
|
1492
1701
|
const offered = result.findings.length;
|
|
1493
1702
|
if (saved.length) {
|
|
1494
1703
|
const n = saved.length;
|
|
1495
1704
|
const what = n < offered ? `kept ${n} of the ${offered} things` : `learned ${n} thing${n === 1 ? "" : "s"}`;
|
|
1496
|
-
console.error(`✓ ${what}
|
|
1497
|
-
//
|
|
1498
|
-
saved.forEach((note, i) =>
|
|
1705
|
+
console.error(`✓ ${what} you told Claude more than once, from your last ${result.read} sessions here, just for you`);
|
|
1706
|
+
// Each with how many sessions it was said in: saveFindings keeps the findings' order.
|
|
1707
|
+
saved.forEach((note, i) => {
|
|
1708
|
+
const said = kept[i]?.sessions.length ?? 0;
|
|
1709
|
+
const count = said > 1 ? `told Claude in ${said} sessions: ` : "";
|
|
1710
|
+
console.error(` just for you · ${count}${note.title}`);
|
|
1711
|
+
});
|
|
1499
1712
|
}
|
|
1500
1713
|
else if (cancelled) {
|
|
1501
|
-
console.error(`· saved none of the ${offered} things
|
|
1714
|
+
console.error(`· saved none of the ${offered} things you told Claude more than once; ${cmd("graft init")} again asks again`);
|
|
1502
1715
|
}
|
|
1503
1716
|
else if (offered) {
|
|
1504
|
-
console.error(`· kept none of the ${offered} things
|
|
1717
|
+
console.error(`· kept none of the ${offered} things you told Claude more than once`);
|
|
1505
1718
|
}
|
|
1506
1719
|
else if (result.reason === "failed") {
|
|
1507
|
-
console.error(`· couldn't read
|
|
1720
|
+
console.error(`· couldn't read your past sessions this time; ${cmd("graft init")} again tries again`);
|
|
1508
1721
|
}
|
|
1509
1722
|
learned.push(...saved);
|
|
1510
1723
|
}
|
|
1511
1724
|
learnedNow = learned.length > 0;
|
|
1725
|
+
teamSaved = learned.filter((s) => s.scope === "repo").length;
|
|
1726
|
+
report?.learned?.({ notes: learnedFrom, sessions: sessionsRead });
|
|
1512
1727
|
if (learnedNow) {
|
|
1513
1728
|
console.error("· Claude follows them from your next session");
|
|
1514
|
-
if (
|
|
1729
|
+
if (teamSaved)
|
|
1515
1730
|
console.error(commitLines({}, teammatesOn(repo), "repo").uncommitted);
|
|
1516
1731
|
}
|
|
1517
1732
|
}
|
|
1733
|
+
// Nothing read this run (already read once, or not Claude Code): the page still gets its stage.
|
|
1734
|
+
if (!(TRAIL && (pastRead || reviewRead || memory.length)))
|
|
1735
|
+
report?.learned?.({ notes: [], sessions: 0 });
|
|
1518
1736
|
if (TRAIL && !learnedNow) {
|
|
1519
1737
|
const home = notesHome.get(repo);
|
|
1520
1738
|
if (home) {
|
|
1521
1739
|
const learnings = listNotes(repo, { branches: false }).filter((n) => n.scope === "repo");
|
|
1522
1740
|
if (learnings.length === 0)
|
|
1523
|
-
console.error(`✓ learnings ${LEARNINGS_DIR}/ ·
|
|
1741
|
+
console.error(`✓ learnings ${LEARNINGS_DIR}/ · a session that fixes a bug, takes a correction or opens a PR leaves one there, for your whole team`);
|
|
1524
1742
|
else
|
|
1525
1743
|
console.error(`✓ learnings ${learnings.length} in ${LEARNINGS_DIR}/, from ${new Set(learnings.map((n) => n.author || "someone")).size} ${new Set(learnings.map((n) => n.author)).size === 1 ? "person" : "people"}`);
|
|
1526
1744
|
}
|
|
@@ -1571,10 +1789,130 @@ async function runInitCommand(dir, opts) {
|
|
|
1571
1789
|
nodes: graphs.reduce((n, g) => n + g.meta.nodeCount, 0),
|
|
1572
1790
|
edges: graphs.reduce((n, g) => n + g.meta.edgeCount, 0),
|
|
1573
1791
|
}));
|
|
1792
|
+
// Hand what was found to the review page, and leave a child waiting for
|
|
1793
|
+
// the person to connect and review there.
|
|
1794
|
+
if (pair) {
|
|
1795
|
+
// Teammates' branches on this person's files, checked against the
|
|
1796
|
+
// learnings just saved and the repo's own: synchronous, so the finding
|
|
1797
|
+
// exists before the page is told the read is done.
|
|
1798
|
+
pair.phase("team", "running", "", "Checking your team's branches");
|
|
1799
|
+
const teamNotes = listNotes(repo, { branches: false }).filter((n) => n.scope === "repo" && !n.ref);
|
|
1800
|
+
// No remote (or no default branch) means nothing was checked: `teammates`
|
|
1801
|
+
// is left out, since an empty list would say "checked, none".
|
|
1802
|
+
const teammates = teammatesChecked(repo, { learnings: teamNotes });
|
|
1803
|
+
pair.teamDone(teammates);
|
|
1804
|
+
pair.findings({ tokens, sessions: pastRead?.sessions, teammates });
|
|
1805
|
+
await pair.finish(repo, skillsWritten);
|
|
1806
|
+
pair.handOff(repo);
|
|
1807
|
+
console.error(`✓ Trail has what it found · review it in the browser: ${pair.url}`);
|
|
1808
|
+
if (!process.stdout.isTTY)
|
|
1809
|
+
console.error(`[trail] Tell the user, in one line: their Trail page has moved on; review what Trail found there. If it isn't open, the link is ${pair.url}`);
|
|
1810
|
+
}
|
|
1811
|
+
// Run by an agent, not a person: the last line tells it to ask about the
|
|
1812
|
+
// commit, since the person never saw the picker and Claude Code folds the rest.
|
|
1813
|
+
// With --pair the person reviews them on the Trail page first, so no ask here.
|
|
1814
|
+
if (teamSaved && !pair && !process.stderr.isTTY)
|
|
1815
|
+
console.error(`→ ${teamSaved} team learning${teamSaved === 1 ? "" : "s"} in ${LEARNINGS_DIR}/ ${teamSaved === 1 ? "isn't" : "aren't"} committed yet. Ask the user: "Commit ${teamSaved === 1 ? "it" : `the ${teamSaved}`} so your teammates' Claude starts from ${teamSaved === 1 ? "it" : "them"}? (yes / no)"`);
|
|
1574
1816
|
// Sorted so `claude,cursor` and `cursor,claude` aggregate as one value.
|
|
1575
1817
|
track("init_completed", { agents: [...ids].sort().join(","), consent: consent === undefined ? "unasked" : String(consent) }, { repo });
|
|
1576
1818
|
return { ids };
|
|
1577
1819
|
}
|
|
1820
|
+
/**
|
|
1821
|
+
* `trail init --connect <CODE>`: the onboarding on team.trailhq.com. Claims the
|
|
1822
|
+
* one-time code (a wrong or spent one stops here, before anything is written),
|
|
1823
|
+
* saves the workspace token where `trail login` keeps it, then runs init as
|
|
1824
|
+
* usual while the page ticks off connected, mapped and learned. At the end,
|
|
1825
|
+
* one summary of the repo goes up (a lookup's tokens saved, the learnings'
|
|
1826
|
+
* titles, teammates' branches from git: never code, diffs or session text),
|
|
1827
|
+
* then `done`. Offline, init still sets the repo up and says how to connect.
|
|
1828
|
+
*/
|
|
1829
|
+
async function runConnectInit(dir, opts) {
|
|
1830
|
+
if (opts.listAgents || opts.dryRun) {
|
|
1831
|
+
if (opts.dryRun)
|
|
1832
|
+
console.error("· --dry-run: not connecting");
|
|
1833
|
+
await runInitCommand(dir, opts);
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
const repo = resolve(dir);
|
|
1837
|
+
if (!useServerOption(opts.server))
|
|
1838
|
+
return;
|
|
1839
|
+
const base = trailUrl();
|
|
1840
|
+
const code = normalizeCode(opts.connect ?? "");
|
|
1841
|
+
if (!code) {
|
|
1842
|
+
console.error(`✗ "${opts.connect ?? ""}" isn't a Trail code · it looks like TRL-7F2A-K9QD, from ${base}/welcome/code`);
|
|
1843
|
+
process.exitCode = 1;
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
const ref = repoRef(repo);
|
|
1847
|
+
const machine = machineName();
|
|
1848
|
+
const got = await claimConnectCode(base, code, { machine, repo: ref });
|
|
1849
|
+
const cloud = "cloud" in got ? got.cloud : null;
|
|
1850
|
+
if ("cloud" in got) {
|
|
1851
|
+
const cloud = got.cloud;
|
|
1852
|
+
writeCloud(cloud);
|
|
1853
|
+
// A sign-in link out from before is moot now.
|
|
1854
|
+
clearPending();
|
|
1855
|
+
console.error(`✓ connected to workspace ${cloud.workspace.name}`);
|
|
1856
|
+
track("brain_signup_settled", { outcome: "linked", mode: "connect", duration_bucket: durationBucket(0) }, { repo });
|
|
1857
|
+
}
|
|
1858
|
+
else if (got.reason === "invalid") {
|
|
1859
|
+
console.error(`✗ ${got.error} · get a new code at ${base}/welcome/code`);
|
|
1860
|
+
process.exitCode = 1;
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
else {
|
|
1864
|
+
console.error(offlineLine(got.error, base, code, cmd("graft init")));
|
|
1865
|
+
}
|
|
1866
|
+
const reporter = cloud ? new OnboardingReporter(cloud, ref, { machine }) : null;
|
|
1867
|
+
void reporter?.stage("connected");
|
|
1868
|
+
// What init learned this run, kept for the summary.
|
|
1869
|
+
const heard = { notes: [], sessions: 0 };
|
|
1870
|
+
const progress = reporter
|
|
1871
|
+
? {
|
|
1872
|
+
mapped: (m) => void reporter.stage("mapped", m),
|
|
1873
|
+
learned: (l) => {
|
|
1874
|
+
Object.assign(heard, l);
|
|
1875
|
+
const team = learningsSummary(repo, l.notes);
|
|
1876
|
+
void reporter.stage("learned", { learnings: team.length, sessions: l.sessions || Math.max(0, ...team.map((t) => t.sessions)) });
|
|
1877
|
+
},
|
|
1878
|
+
}
|
|
1879
|
+
: undefined;
|
|
1880
|
+
// The page's command is pasted into a terminal, but an agent may run it for
|
|
1881
|
+
// them: with no terminal to pick in, wire the agents this machine has.
|
|
1882
|
+
const interactive = process.stdin.isTTY && process.stderr.isTTY;
|
|
1883
|
+
const res = await runInitCommand(dir, interactive ? opts : { ...opts, yes: true }, progress);
|
|
1884
|
+
if (!reporter || !cloud)
|
|
1885
|
+
return;
|
|
1886
|
+
if (!res) {
|
|
1887
|
+
await reporter.flush();
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
// What Trail found, for the page: route B, summaries only.
|
|
1891
|
+
const learnings = learningsSummary(repo, heard.notes);
|
|
1892
|
+
const sessions = heard.sessions || Math.max(0, ...learnings.map((l) => l.sessions));
|
|
1893
|
+
const tokens = tokensOnOneLookup(repo, { contextDir: program.opts().dir });
|
|
1894
|
+
const teamNotes = listNotes(repo, { branches: false }).filter((n) => n.scope === "repo" && !n.ref);
|
|
1895
|
+
const teammates = teammatesFromGit(repo, { learnings: teamNotes });
|
|
1896
|
+
const sent = await reporter.summary({ tokens, ...(sessions ? { sessions } : {}), learnings, teammates });
|
|
1897
|
+
await reporter.stage("done");
|
|
1898
|
+
await reporter.flush();
|
|
1899
|
+
// Claude Code gets the workspace's brains through one user-level `trail` entry.
|
|
1900
|
+
const brainsEntry = res.ids.includes("claude") && opts.global !== false ? installBrainsServer(homedir()) : null;
|
|
1901
|
+
if (sent) {
|
|
1902
|
+
const parts = [
|
|
1903
|
+
tokens ? `one lookup saved ${tokens.pct}% of the tokens` : null,
|
|
1904
|
+
`${learnings.length} learning${learnings.length === 1 ? "" : "s"}`,
|
|
1905
|
+
`${teammates.length} teammate branch${teammates.length === 1 ? "" : "es"} on your files`,
|
|
1906
|
+
].filter(Boolean);
|
|
1907
|
+
console.error(`✓ sent ${cloud.workspace.name} a summary: ${parts.join(", ")} · titles, counts and names only, no code`);
|
|
1908
|
+
console.error(`· see what Trail found: ${base}/welcome/code/found`);
|
|
1909
|
+
}
|
|
1910
|
+
else {
|
|
1911
|
+
console.error(`· couldn't send the summary to Trail this time · with a new code from ${base}/welcome/code, ${connectAgain(cmd("graft init"), "<code>", base)} sends it again`);
|
|
1912
|
+
}
|
|
1913
|
+
if (brainsEntry?.action === "created" || brainsEntry?.action === "updated")
|
|
1914
|
+
console.error(`✓ Claude Code gets your workspace's brains in every project · restart it to pick them up`);
|
|
1915
|
+
}
|
|
1578
1916
|
/** One repo's worth of `init` writes — the parent, then each workspace child. */
|
|
1579
1917
|
function wireTarget(repo, ids, ctx) {
|
|
1580
1918
|
const { home, cliPath, plan, wantClaude } = ctx;
|
|
@@ -1736,7 +2074,7 @@ function loginCommand(name = "login") {
|
|
|
1736
2074
|
return new Command(name)
|
|
1737
2075
|
.description("Connect this machine to a Trail workspace: sign in, pick the repos it gets, invite your team")
|
|
1738
2076
|
.argument("[dir]", "the repo you're in, ticked first in the picker", ".")
|
|
1739
|
-
.option("--server <url>", "a self-hosted Trail, e.g. https://trail.acme.internal (default: Trail cloud)")
|
|
2077
|
+
.option("--server <url>", "a self-hosted Trail, e.g. https://trail.acme.internal (default: Trail cloud, https://team.trailhq.com)")
|
|
1740
2078
|
.action(async (dir, opts) => {
|
|
1741
2079
|
const repo = resolve(dir);
|
|
1742
2080
|
const server = opts.server?.trim().replace(/\/+$/, "");
|
|
@@ -1776,6 +2114,7 @@ function logoutCommand(name = "logout") {
|
|
|
1776
2114
|
.action(() => {
|
|
1777
2115
|
const cloud = readCloud();
|
|
1778
2116
|
clearPending();
|
|
2117
|
+
removeBrainsServer(homedir());
|
|
1779
2118
|
if (!clearCloud()) {
|
|
1780
2119
|
console.error("· not connected to a workspace");
|
|
1781
2120
|
return;
|
|
@@ -2157,6 +2496,9 @@ function statusCommand(name = "status") {
|
|
|
2157
2496
|
if (cloudEnabled())
|
|
2158
2497
|
row("workspace", "not connected · Claude offers a link once there are learnings to share");
|
|
2159
2498
|
}
|
|
2499
|
+
else if (cloud.via === "connect") {
|
|
2500
|
+
row("workspace", `${cloud.workspace.name} · gets a summary of the repos you run ${cmd("graft init")} --connect in · ${cloud.workspace.url}`);
|
|
2501
|
+
}
|
|
2160
2502
|
else
|
|
2161
2503
|
row("workspace", `${cloud.workspace.name} · ${cloud.repos.length} repo${cloud.repos.length === 1 ? "" : "s"}${shared ? ", this one included" : `, not this one (${cmd("graft workspace add")})`} · ${cloud.workspace.url}`);
|
|
2162
2504
|
row("this week", saved > 0 ? `~${shortCount(saved)} tokens saved` : "nothing saved yet");
|