@nanonets/graft 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -21
- 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/blast/render.js +1 -1
- package/dist/blast/render.js.map +1 -1
- package/dist/brand.js +1 -1
- package/dist/brand.js.map +1 -1
- package/dist/check/learning-edits.d.ts +18 -3
- package/dist/check/learning-edits.d.ts.map +1 -1
- package/dist/check/learning-edits.js +54 -10
- package/dist/check/learning-edits.js.map +1 -1
- package/dist/check/local-check.d.ts +7 -3
- package/dist/check/local-check.d.ts.map +1 -1
- package/dist/check/local-check.js +55 -11
- package/dist/check/local-check.js.map +1 -1
- package/dist/check/types.d.ts +11 -2
- package/dist/check/types.d.ts.map +1 -1
- package/dist/claude/format.d.ts +11 -7
- package/dist/claude/format.d.ts.map +1 -1
- package/dist/claude/format.js +50 -28
- 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/hook-layers.d.ts +4 -2
- package/dist/claude/hook-layers.d.ts.map +1 -1
- package/dist/claude/hook-layers.js +21 -4
- package/dist/claude/hook-layers.js.map +1 -1
- package/dist/claude/hooks.d.ts +9 -32
- package/dist/claude/hooks.d.ts.map +1 -1
- package/dist/claude/hooks.js +154 -350
- package/dist/claude/hooks.js.map +1 -1
- package/dist/claude/init.d.ts.map +1 -1
- package/dist/claude/init.js +56 -18
- package/dist/claude/init.js.map +1 -1
- package/dist/claude/note-signals.d.ts +6 -0
- package/dist/claude/note-signals.d.ts.map +1 -1
- package/dist/claude/note-signals.js +14 -3
- package/dist/claude/note-signals.js.map +1 -1
- package/dist/claude/post-bash.d.ts +26 -0
- package/dist/claude/post-bash.d.ts.map +1 -0
- package/dist/claude/post-bash.js +191 -0
- package/dist/claude/post-bash.js.map +1 -0
- package/dist/claude/pre-bash-skills.d.ts +21 -0
- package/dist/claude/pre-bash-skills.d.ts.map +1 -0
- package/dist/claude/pre-bash-skills.js +253 -0
- package/dist/claude/pre-bash-skills.js.map +1 -0
- package/dist/claude/pre-bash.d.ts +50 -0
- package/dist/claude/pre-bash.d.ts.map +1 -0
- package/dist/claude/pre-bash.js +245 -0
- package/dist/claude/pre-bash.js.map +1 -0
- package/dist/claude/prompt-consent.d.ts +19 -0
- package/dist/claude/prompt-consent.d.ts.map +1 -0
- package/dist/claude/prompt-consent.js +14 -0
- package/dist/claude/prompt-consent.js.map +1 -0
- package/dist/claude/prompt-hook.d.ts +76 -0
- package/dist/claude/prompt-hook.d.ts.map +1 -0
- package/dist/claude/prompt-hook.js +303 -0
- package/dist/claude/prompt-hook.js.map +1 -0
- package/dist/claude/prompt-skills.d.ts +19 -0
- package/dist/claude/prompt-skills.d.ts.map +1 -0
- package/dist/claude/prompt-skills.js +305 -0
- package/dist/claude/prompt-skills.js.map +1 -0
- package/dist/claude/session-metrics.js +4 -4
- package/dist/claude/session-metrics.js.map +1 -1
- package/dist/claude/settings-merge.d.ts +10 -0
- package/dist/claude/settings-merge.d.ts.map +1 -1
- package/dist/claude/settings-merge.js +46 -17
- package/dist/claude/settings-merge.js.map +1 -1
- package/dist/claude/shim-template.d.ts.map +1 -1
- package/dist/claude/shim-template.js +86 -28
- package/dist/claude/shim-template.js.map +1 -1
- package/dist/claude/skill-template.d.ts +2 -0
- package/dist/claude/skill-template.d.ts.map +1 -1
- package/dist/claude/skill-template.js +53 -34
- package/dist/claude/skill-template.js.map +1 -1
- package/dist/claude/skill-use.d.ts +16 -0
- package/dist/claude/skill-use.d.ts.map +1 -0
- package/dist/claude/skill-use.js +78 -0
- package/dist/claude/skill-use.js.map +1 -0
- 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/skills-wiring.d.ts +26 -0
- package/dist/claude/skills-wiring.d.ts.map +1 -0
- package/dist/claude/skills-wiring.js +110 -0
- package/dist/claude/skills-wiring.js.map +1 -0
- package/dist/claude/start-init.d.ts +13 -0
- package/dist/claude/start-init.d.ts.map +1 -0
- package/dist/claude/start-init.js +75 -0
- package/dist/claude/start-init.js.map +1 -0
- package/dist/claude/start-skills.d.ts +31 -0
- package/dist/claude/start-skills.d.ts.map +1 -0
- package/dist/claude/start-skills.js +274 -0
- package/dist/claude/start-skills.js.map +1 -0
- package/dist/claude/state.d.ts +19 -7
- package/dist/claude/state.d.ts.map +1 -1
- package/dist/claude/state.js +59 -4
- package/dist/claude/state.js.map +1 -1
- package/dist/claude/stop-skills.d.ts +23 -0
- package/dist/claude/stop-skills.d.ts.map +1 -0
- package/dist/claude/stop-skills.js +233 -0
- package/dist/claude/stop-skills.js.map +1 -0
- package/dist/claude/stop.d.ts +23 -0
- package/dist/claude/stop.d.ts.map +1 -0
- package/dist/claude/stop.js +219 -0
- package/dist/claude/stop.js.map +1 -0
- package/dist/claude/tally.d.ts +29 -41
- package/dist/claude/tally.d.ts.map +1 -1
- package/dist/claude/tally.js +111 -165
- package/dist/claude/tally.js.map +1 -1
- package/dist/claude/walls-hook.d.ts +52 -0
- package/dist/claude/walls-hook.d.ts.map +1 -0
- package/dist/claude/walls-hook.js +279 -0
- package/dist/claude/walls-hook.js.map +1 -0
- package/dist/cli-epilogue.d.ts.map +1 -1
- package/dist/cli-epilogue.js +24 -14
- package/dist/cli-epilogue.js.map +1 -1
- package/dist/cli-meta.js +3 -3
- package/dist/cli-meta.js.map +1 -1
- package/dist/cli-picker.d.ts +24 -0
- package/dist/cli-picker.d.ts.map +1 -1
- package/dist/cli-picker.js +44 -11
- package/dist/cli-picker.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +475 -390
- package/dist/cli.js.map +1 -1
- package/dist/cloud/head-learnings.d.ts +13 -0
- package/dist/cloud/head-learnings.d.ts.map +1 -0
- package/dist/cloud/head-learnings.js +84 -0
- package/dist/cloud/head-learnings.js.map +1 -0
- package/dist/cloud/lookup-activity.d.ts +18 -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 +15 -7
- package/dist/cloud/nudge.d.ts.map +1 -1
- package/dist/cloud/nudge.js +45 -33
- package/dist/cloud/nudge.js.map +1 -1
- package/dist/cloud/onboarding.d.ts +273 -0
- package/dist/cloud/onboarding.d.ts.map +1 -0
- package/dist/cloud/onboarding.js +612 -0
- package/dist/cloud/onboarding.js.map +1 -0
- package/dist/cloud/other-repos.d.ts +39 -0
- package/dist/cloud/other-repos.d.ts.map +1 -0
- package/dist/cloud/other-repos.js +137 -0
- package/dist/cloud/other-repos.js.map +1 -0
- package/dist/cloud/pair.d.ts +86 -0
- package/dist/cloud/pair.d.ts.map +1 -0
- package/dist/cloud/pair.js +270 -0
- package/dist/cloud/pair.js.map +1 -0
- package/dist/cloud/workspace-sync.d.ts +75 -11
- package/dist/cloud/workspace-sync.d.ts.map +1 -1
- package/dist/cloud/workspace-sync.js +409 -53
- package/dist/cloud/workspace-sync.js.map +1 -1
- package/dist/cloud/workspace.d.ts +95 -10
- package/dist/cloud/workspace.d.ts.map +1 -1
- package/dist/cloud/workspace.js +112 -9
- package/dist/cloud/workspace.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/hosts/claude-global.d.ts +14 -0
- package/dist/hosts/claude-global.d.ts.map +1 -1
- package/dist/hosts/claude-global.js +39 -4
- package/dist/hosts/claude-global.js.map +1 -1
- package/dist/hosts/codex-hooks.d.ts +6 -0
- package/dist/hosts/codex-hooks.d.ts.map +1 -1
- package/dist/hosts/codex-hooks.js +89 -16
- package/dist/hosts/codex-hooks.js.map +1 -1
- package/dist/hosts/codex-trust.d.ts +28 -0
- package/dist/hosts/codex-trust.d.ts.map +1 -0
- package/dist/hosts/codex-trust.js +280 -0
- package/dist/hosts/codex-trust.js.map +1 -0
- package/dist/hosts/cursor-hooks.d.ts.map +1 -1
- package/dist/hosts/cursor-hooks.js +3 -1
- package/dist/hosts/cursor-hooks.js.map +1 -1
- package/dist/init/candidates.d.ts +60 -0
- package/dist/init/candidates.d.ts.map +1 -0
- package/dist/init/candidates.js +202 -0
- package/dist/init/candidates.js.map +1 -0
- package/dist/init/claude-call.d.ts +51 -0
- package/dist/init/claude-call.d.ts.map +1 -0
- package/dist/init/claude-call.js +199 -0
- package/dist/init/claude-call.js.map +1 -0
- package/dist/init/discover-worker.d.ts +18 -0
- package/dist/init/discover-worker.d.ts.map +1 -0
- package/dist/init/discover-worker.js +111 -0
- package/dist/init/discover-worker.js.map +1 -0
- package/dist/init/handover.d.ts +16 -0
- package/dist/init/handover.d.ts.map +1 -0
- package/dist/init/handover.js +92 -0
- package/dist/init/handover.js.map +1 -0
- package/dist/init/howtos.d.ts +20 -0
- package/dist/init/howtos.d.ts.map +1 -0
- package/dist/init/howtos.js +90 -0
- package/dist/init/howtos.js.map +1 -0
- package/dist/init/init-record.d.ts +134 -0
- package/dist/init/init-record.d.ts.map +1 -0
- package/dist/init/init-record.js +339 -0
- package/dist/init/init-record.js.map +1 -0
- package/dist/init/install-paths.d.ts +6 -0
- package/dist/init/install-paths.d.ts.map +1 -0
- package/dist/init/install-paths.js +87 -0
- package/dist/init/install-paths.js.map +1 -0
- 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/keep-answer.d.ts +43 -0
- package/dist/init/keep-answer.d.ts.map +1 -0
- package/dist/init/keep-answer.js +118 -0
- package/dist/init/keep-answer.js.map +1 -0
- package/dist/init/keep-gate.d.ts +30 -0
- package/dist/init/keep-gate.d.ts.map +1 -0
- package/dist/init/keep-gate.js +178 -0
- package/dist/init/keep-gate.js.map +1 -0
- package/dist/init/keep-list.d.ts +67 -0
- package/dist/init/keep-list.d.ts.map +1 -0
- package/dist/init/keep-list.js +232 -0
- package/dist/init/keep-list.js.map +1 -0
- package/dist/init/keep.d.ts +52 -0
- package/dist/init/keep.d.ts.map +1 -0
- package/dist/init/keep.js +393 -0
- package/dist/init/keep.js.map +1 -0
- package/dist/init/look-back-mine.d.ts +37 -0
- package/dist/init/look-back-mine.d.ts.map +1 -0
- package/dist/init/look-back-mine.js +215 -0
- package/dist/init/look-back-mine.js.map +1 -0
- package/dist/init/look-back.d.ts +81 -0
- package/dist/init/look-back.d.ts.map +1 -0
- package/dist/init/look-back.js +170 -0
- package/dist/init/look-back.js.map +1 -0
- package/dist/init/miners.d.ts +46 -0
- package/dist/init/miners.d.ts.map +1 -0
- package/dist/init/miners.js +141 -0
- package/dist/init/miners.js.map +1 -0
- package/dist/init/name-call.d.ts +37 -0
- package/dist/init/name-call.d.ts.map +1 -0
- package/dist/init/name-call.js +151 -0
- package/dist/init/name-call.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 +83 -0
- package/dist/init/plain-file.js.map +1 -0
- package/dist/init/prefs.d.ts +70 -0
- package/dist/init/prefs.d.ts.map +1 -0
- package/dist/init/prefs.js +282 -0
- package/dist/init/prefs.js.map +1 -0
- package/dist/init/relay.d.ts +15 -0
- package/dist/init/relay.d.ts.map +1 -0
- package/dist/init/relay.js +15 -0
- package/dist/init/relay.js.map +1 -0
- package/dist/init/replay-runs.d.ts +121 -0
- package/dist/init/replay-runs.d.ts.map +1 -0
- package/dist/init/replay-runs.js +457 -0
- package/dist/init/replay-runs.js.map +1 -0
- package/dist/init/replay-tamper.d.ts +18 -0
- package/dist/init/replay-tamper.d.ts.map +1 -0
- package/dist/init/replay-tamper.js +87 -0
- package/dist/init/replay-tamper.js.map +1 -0
- package/dist/init/review-screen.d.ts +74 -0
- package/dist/init/review-screen.d.ts.map +1 -0
- package/dist/init/review-screen.js +258 -0
- package/dist/init/review-screen.js.map +1 -0
- package/dist/init/review-tty.d.ts +29 -0
- package/dist/init/review-tty.d.ts.map +1 -0
- package/dist/init/review-tty.js +218 -0
- package/dist/init/review-tty.js.map +1 -0
- package/dist/init/review.d.ts +12 -0
- package/dist/init/review.d.ts.map +1 -0
- package/dist/init/review.js +12 -0
- package/dist/init/review.js.map +1 -0
- package/dist/init/run.d.ts +87 -0
- package/dist/init/run.d.ts.map +1 -0
- package/dist/init/run.js +347 -0
- package/dist/init/run.js.map +1 -0
- package/dist/init/sessions.d.ts +63 -0
- package/dist/init/sessions.d.ts.map +1 -0
- package/dist/init/sessions.js +377 -0
- package/dist/init/sessions.js.map +1 -0
- package/dist/init/transcript.d.ts +81 -0
- package/dist/init/transcript.d.ts.map +1 -0
- package/dist/init/transcript.js +216 -0
- package/dist/init/transcript.js.map +1 -0
- package/dist/init/types.d.ts +139 -0
- package/dist/init/types.d.ts.map +1 -0
- package/dist/init/types.js +2 -0
- package/dist/init/types.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/cold-start.d.ts +5 -184
- package/dist/notes/cold-start.d.ts.map +1 -1
- package/dist/notes/cold-start.js +5 -575
- package/dist/notes/cold-start.js.map +1 -1
- package/dist/notes/edit-overlap.d.ts.map +1 -1
- package/dist/notes/edit-overlap.js +4 -1
- package/dist/notes/edit-overlap.js.map +1 -1
- 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/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/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/notes.d.ts +8 -4
- package/dist/notes/notes.d.ts.map +1 -1
- package/dist/notes/notes.js +9 -7
- package/dist/notes/notes.js.map +1 -1
- package/dist/notes/pr-reviews.d.ts +4 -0
- package/dist/notes/pr-reviews.d.ts.map +1 -1
- package/dist/notes/pr-reviews.js +6 -5
- 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/repo-trail.d.ts +1 -1
- package/dist/notes/repo-trail.d.ts.map +1 -1
- package/dist/notes/repo-trail.js +24 -4
- package/dist/notes/repo-trail.js.map +1 -1
- package/dist/search/grep.d.ts.map +1 -1
- package/dist/search/grep.js +4 -2
- package/dist/search/grep.js.map +1 -1
- package/dist/search/user-regexp.d.ts +26 -0
- package/dist/search/user-regexp.d.ts.map +1 -0
- package/dist/search/user-regexp.js +150 -0
- package/dist/search/user-regexp.js.map +1 -0
- package/dist/skills/anchors-cli.d.ts +38 -0
- package/dist/skills/anchors-cli.d.ts.map +1 -0
- package/dist/skills/anchors-cli.js +279 -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/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/cli-skills-1b.d.ts +17 -0
- package/dist/skills/cli-skills-1b.d.ts.map +1 -0
- package/dist/skills/cli-skills-1b.js +166 -0
- package/dist/skills/cli-skills-1b.js.map +1 -0
- package/dist/skills/cli-skills-2.d.ts +35 -0
- package/dist/skills/cli-skills-2.d.ts.map +1 -0
- package/dist/skills/cli-skills-2.js +129 -0
- package/dist/skills/cli-skills-2.js.map +1 -0
- package/dist/skills/cli-skills-report-1b.d.ts +49 -0
- package/dist/skills/cli-skills-report-1b.d.ts.map +1 -0
- package/dist/skills/cli-skills-report-1b.js +191 -0
- package/dist/skills/cli-skills-report-1b.js.map +1 -0
- package/dist/skills/cli-skills-report.d.ts +86 -0
- package/dist/skills/cli-skills-report.d.ts.map +1 -0
- package/dist/skills/cli-skills-report.js +121 -0
- package/dist/skills/cli-skills-report.js.map +1 -0
- package/dist/skills/cli-skills.d.ts +30 -0
- package/dist/skills/cli-skills.d.ts.map +1 -0
- package/dist/skills/cli-skills.js +379 -0
- package/dist/skills/cli-skills.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 +550 -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 +85 -0
- package/dist/skills/compile-words.d.ts.map +1 -0
- package/dist/skills/compile-words.js +445 -0
- package/dist/skills/compile-words.js.map +1 -0
- package/dist/skills/compile.d.ts +42 -0
- package/dist/skills/compile.d.ts.map +1 -0
- package/dist/skills/compile.js +505 -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/credit.d.ts +86 -0
- package/dist/skills/credit.d.ts.map +1 -0
- package/dist/skills/credit.js +259 -0
- package/dist/skills/credit.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/fold-gated.d.ts +3 -0
- package/dist/skills/fold-gated.d.ts.map +1 -0
- package/dist/skills/fold-gated.js +78 -0
- package/dist/skills/fold-gated.js.map +1 -0
- package/dist/skills/fold.d.ts +87 -0
- package/dist/skills/fold.d.ts.map +1 -0
- package/dist/skills/fold.js +348 -0
- package/dist/skills/fold.js.map +1 -0
- package/dist/skills/follow-learnings.d.ts +128 -0
- package/dist/skills/follow-learnings.d.ts.map +1 -0
- package/dist/skills/follow-learnings.js +278 -0
- package/dist/skills/follow-learnings.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/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/learning-steps.d.ts +43 -0
- package/dist/skills/learning-steps.d.ts.map +1 -0
- package/dist/skills/learning-steps.js +294 -0
- package/dist/skills/learning-steps.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/lesson-gate.d.ts +25 -0
- package/dist/skills/lesson-gate.d.ts.map +1 -0
- package/dist/skills/lesson-gate.js +316 -0
- package/dist/skills/lesson-gate.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/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/offer-drift.d.ts +35 -0
- package/dist/skills/offer-drift.d.ts.map +1 -0
- package/dist/skills/offer-drift.js +98 -0
- package/dist/skills/offer-drift.js.map +1 -0
- package/dist/skills/offer-lesson.d.ts +31 -0
- package/dist/skills/offer-lesson.d.ts.map +1 -0
- package/dist/skills/offer-lesson.js +123 -0
- package/dist/skills/offer-lesson.js.map +1 -0
- package/dist/skills/offer-lines.d.ts +40 -0
- package/dist/skills/offer-lines.d.ts.map +1 -0
- package/dist/skills/offer-lines.js +80 -0
- package/dist/skills/offer-lines.js.map +1 -0
- package/dist/skills/offer-moment.d.ts +102 -0
- package/dist/skills/offer-moment.d.ts.map +1 -0
- package/dist/skills/offer-moment.js +260 -0
- package/dist/skills/offer-moment.js.map +1 -0
- package/dist/skills/offer-repair.d.ts +36 -0
- package/dist/skills/offer-repair.d.ts.map +1 -0
- package/dist/skills/offer-repair.js +212 -0
- package/dist/skills/offer-repair.js.map +1 -0
- package/dist/skills/offer-retire.d.ts +53 -0
- package/dist/skills/offer-retire.d.ts.map +1 -0
- package/dist/skills/offer-retire.js +149 -0
- package/dist/skills/offer-retire.js.map +1 -0
- package/dist/skills/offer-update.d.ts +54 -0
- package/dist/skills/offer-update.d.ts.map +1 -0
- package/dist/skills/offer-update.js +115 -0
- package/dist/skills/offer-update.js.map +1 -0
- package/dist/skills/offer-words-1b.d.ts +32 -0
- package/dist/skills/offer-words-1b.d.ts.map +1 -0
- package/dist/skills/offer-words-1b.js +80 -0
- package/dist/skills/offer-words-1b.js.map +1 -0
- package/dist/skills/offer-words-2.d.ts +123 -0
- package/dist/skills/offer-words-2.d.ts.map +1 -0
- package/dist/skills/offer-words-2.js +194 -0
- package/dist/skills/offer-words-2.js.map +1 -0
- package/dist/skills/offer-words.d.ts +65 -0
- package/dist/skills/offer-words.d.ts.map +1 -0
- package/dist/skills/offer-words.js +205 -0
- package/dist/skills/offer-words.js.map +1 -0
- package/dist/skills/offers.d.ts +66 -0
- package/dist/skills/offers.d.ts.map +1 -0
- package/dist/skills/offers.js +284 -0
- package/dist/skills/offers.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/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/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/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 +47 -0
- package/dist/skills/runblock.d.ts.map +1 -0
- package/dist/skills/runblock.js +227 -0
- package/dist/skills/runblock.js.map +1 -0
- package/dist/skills/save-gate.d.ts +64 -0
- package/dist/skills/save-gate.d.ts.map +1 -0
- package/dist/skills/save-gate.js +354 -0
- package/dist/skills/save-gate.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/save-verify.d.ts +59 -0
- package/dist/skills/save-verify.d.ts.map +1 -0
- package/dist/skills/save-verify.js +390 -0
- package/dist/skills/save-verify.js.map +1 -0
- package/dist/skills/settings.d.ts +48 -0
- package/dist/skills/settings.d.ts.map +1 -0
- package/dist/skills/settings.js +155 -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 +28 -8
- package/dist/skills/skills.d.ts.map +1 -1
- package/dist/skills/skills.js +106 -53
- 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 +8 -0
- package/dist/skills/types.d.ts.map +1 -1
- package/dist/skills/update-gate.d.ts +36 -0
- package/dist/skills/update-gate.d.ts.map +1 -0
- package/dist/skills/update-gate.js +357 -0
- package/dist/skills/update-gate.js.map +1 -0
- package/dist/skills/update-plan.d.ts +40 -0
- package/dist/skills/update-plan.d.ts.map +1 -0
- package/dist/skills/update-plan.js +196 -0
- package/dist/skills/update-plan.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/value-line.d.ts +94 -0
- package/dist/skills/value-line.d.ts.map +1 -0
- package/dist/skills/value-line.js +355 -0
- package/dist/skills/value-line.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 +15 -1
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +80 -11
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/telemetry/gate.js +4 -4
- package/dist/telemetry/gate.js.map +1 -1
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +1 -1
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/notice.js +5 -5
- package/dist/telemetry/notice.js.map +1 -1
- package/dist/telemetry/queue.d.ts.map +1 -1
- package/dist/telemetry/queue.js +7 -7
- package/dist/telemetry/queue.js.map +1 -1
- package/dist/telemetry/track.d.ts.map +1 -1
- package/dist/telemetry/track.js +4 -3
- package/dist/telemetry/track.js.map +1 -1
- package/dist/trace/checks.d.ts +63 -0
- package/dist/trace/checks.d.ts.map +1 -0
- package/dist/trace/checks.js +495 -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/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/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/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/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/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.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-run.d.ts.map +1 -1
- package/dist/upkeep-run.js +2 -23
- package/dist/upkeep-run.js.map +1 -1
- package/dist/upkeep.d.ts.map +1 -1
- package/dist/upkeep.js +2 -3
- package/dist/upkeep.js.map +1 -1
- package/dist/util/private-fs.d.ts +6 -0
- package/dist/util/private-fs.d.ts.map +1 -1
- package/dist/util/private-fs.js +9 -1
- package/dist/util/private-fs.js.map +1 -1
- package/dist/util/state.d.ts.map +1 -1
- package/dist/util/state.js +17 -3
- 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/package.json +6 -3
- package/scripts/stamp-telemetry-key.mjs +8 -5
- package/scripts/vendor-grammars.mjs +35 -0
package/dist/cli.js
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
import { spawnSync } from "node:child_process";
|
|
12
12
|
import "dotenv/config";
|
|
13
13
|
import { Command } from "commander";
|
|
14
|
-
import { statSync } from "node:fs";
|
|
15
|
-
import { basename,
|
|
14
|
+
import { readFileSync, statSync } from "node:fs";
|
|
15
|
+
import { basename, join, relative, resolve } from "node:path";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
17
|
import { Graft } from "./engine.js";
|
|
18
18
|
import { resolveConfig } from "./ai/providers.js";
|
|
@@ -21,12 +21,13 @@ import { formatGraphCheckReport } from "./graph/check.js";
|
|
|
21
21
|
import { buildGraphIfMissing, runInit } from "./claude/init.js";
|
|
22
22
|
import { statuslineWanted } from "./claude/settings-merge.js";
|
|
23
23
|
import { runHostsInit } from "./hosts/init.js";
|
|
24
|
+
import { codexStatusLines, codexTrustLine } from "./hosts/codex-trust.js";
|
|
24
25
|
import { hostIds } from "./hosts/registry.js";
|
|
25
|
-
import { checkoutRoot, ensureRepoHome, keepsNotes, rememberRepo, repoName, repoPlace
|
|
26
|
+
import { checkoutRoot, ensureRepoHome, keepsNotes, rememberRepo, repoName, repoPlace } from "./notes/home.js";
|
|
26
27
|
import { brand, cmd, graftNotice, strandedRepoNotice, tag } from "./brand.js";
|
|
27
28
|
import { branchDiff } from "./cloud/diff.js";
|
|
28
29
|
import { openBrowser } from "./util/browser.js";
|
|
29
|
-
import { learningsAtBase, teammateEdits, withBaseLearnings } from "./check/learning-edits.js";
|
|
30
|
+
import { committedAtBase, learningsAtBase, teammateEdits, withBaseLearnings } from "./check/learning-edits.js";
|
|
30
31
|
import { contextDirFor } from "./context/node-file.js";
|
|
31
32
|
import { loadGraphCached } from "./graph/load.js";
|
|
32
33
|
import { ensureFreshChildren, ensureFreshGraph, refreshNote } from "./graph/refresh.js";
|
|
@@ -39,8 +40,9 @@ import { runWorkspaceAsk, runWorkspaceBuild, runWorkspaceCallers, runWorkspaceCh
|
|
|
39
40
|
import { formatInitEpilogue } from "./cli-epilogue.js";
|
|
40
41
|
import { planInit, selectedWrites } from "./hosts/plan.js";
|
|
41
42
|
import { planRetract, runRetract, changed } from "./hosts/retract.js";
|
|
42
|
-
import { compactWrites, formatNonInteractiveHelp, formatPlan,
|
|
43
|
+
import { compactWrites, formatNonInteractiveHelp, formatPlan, runPicker } from "./cli-picker.js";
|
|
43
44
|
import { buildGraphWithProgress } from "./claude/build-progress.js";
|
|
45
|
+
import { installBrainsServer, removeBrainsServer } from "./hosts/claude-global.js";
|
|
44
46
|
import { homedir } from "node:os";
|
|
45
47
|
import { formatUpgradeReport, formatVersionReport, getNpmViewVersion, readCurrentVersion, readPackageName, runUpgrade } from "./cli-meta.js";
|
|
46
48
|
import { nativeFallbackNotice } from "./graph/native.js";
|
|
@@ -48,25 +50,31 @@ import { patchBuildConfig } from "./util/state.js";
|
|
|
48
50
|
import { normalizePathPrefix } from "./util/paths.js";
|
|
49
51
|
import { latestSession, formatSessionStats, sessionInputRate } from "./claude/session-metrics.js";
|
|
50
52
|
import { listSessionIds, readSession, sessionDir } from "./claude/state.js";
|
|
51
|
-
import { costLabel, listNotes, noteName, notePlace, noteTargets, readingTokens,
|
|
52
|
-
import { findLearning, liveLearnings } from "./notes/learning-fields.js";
|
|
53
|
+
import { costLabel, listNotes, noteName, notePlace, noteTargets, readingTokens, commitLines, PERSONAL_SAVED_LINE, PR_SPEEDUP, SECTIONS, tokensWorthSaying, sectionLead, shortDate, tokensLabel, writeNote, mentionedFiles } from "./notes/notes.js";
|
|
53
54
|
import { aboutTerms, currentSessionCost } from "./notes/session-cost.js";
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
import {
|
|
55
|
+
import { flatLine, unmarkedWords } from "./notes/flatten.js";
|
|
56
|
+
import { beginLookBack, finishLookBack } from "./init/run.js";
|
|
57
|
+
import { keepCommand } from "./init/keep.js";
|
|
58
|
+
import { recordInit, snapInit } from "./init/install-paths.js";
|
|
59
|
+
import { finishPair, PairRun, validPairCode } from "./cloud/pair.js";
|
|
57
60
|
import { currentBranch, noteAuthor, teammatesOn } from "./notes/git-facts.js";
|
|
61
|
+
import { listSkills } from "./skills/skills.js";
|
|
62
|
+
import { registerSkillCommands } from "./skills/cli-skills.js";
|
|
58
63
|
import { gateNote, namesRepoSymbol, symbolCandidates, trackedFiles } from "./notes/note-gate.js";
|
|
59
|
-
import {
|
|
64
|
+
import { recordLookup } from "./cloud/lookup-activity.js";
|
|
60
65
|
import { clearPending, cloudLines, loginLink, sessionKey, waitForConnect } from "./cloud/nudge.js";
|
|
61
|
-
import { addRepo, clearCloud, cloudEnabled, fetchMe, fetchTeam, isError, readCloud, writeCloud } from "./cloud/workspace.js";
|
|
62
|
-
import {
|
|
66
|
+
import { addRepo, clearCloud, cloudEnabled, fetchMe, fetchTeam, isError, readCloud, serverUrl, trailUrl, useServer, writeCloud } from "./cloud/workspace.js";
|
|
67
|
+
import { claimConnectCode, connectAgain, offlineLine, learningsSummary, machineName, normalizeCode, OnboardingReporter, repoRef, teammatesChecked, teammatesFromGit, tokensOnOneLookup, } from "./cloud/onboarding.js";
|
|
68
|
+
import { inWorkspace, maybeSyncInBackground, recordActivity, runSyncChild, syncRepo, workspaceLearnings } from "./cloud/workspace-sync.js";
|
|
63
69
|
import { checkLocally } from "./check/local-check.js";
|
|
70
|
+
import { findLearning, learningFileName, liveLearnings } from "./notes/learning-fields.js";
|
|
71
|
+
import { savedIdLine } from "./notes/replace.js";
|
|
64
72
|
import { formatOverlap, maybeRefreshBranches, overlapsWith, pushedWhen, readBranchCache, refreshBranches } from "./notes/branches.js";
|
|
65
73
|
import { ensureRepoTrail, LEARNINGS_DIR } from "./notes/repo-trail.js";
|
|
66
74
|
import { setInputRate } from "./context/savings.js";
|
|
67
75
|
import { formatUpdateNudge, maybeRefreshInBackground, readStamp, readUpdateCache, refreshUpdateCache, wiredHostIds, wiringOpts, writeStamp } from "./upkeep.js";
|
|
68
76
|
import { shouldMoveToTrail } from "./upkeep-run.js";
|
|
69
|
-
import { countBucket,
|
|
77
|
+
import { countBucket, errorCode, filesBucket, durationBucket, formatDebug, formatStatus, firstRunNotice, isTrackedCommand, langsValue, offReason, maybeFlushInBackground, patchState, runFlush, track, trackFirstRunIfNew, } from "./telemetry/index.js";
|
|
70
78
|
const program = new Command();
|
|
71
79
|
const currentVersion = readCurrentVersion(import.meta.url);
|
|
72
80
|
/**
|
|
@@ -80,7 +88,7 @@ const TRAIL = brand() === "trail";
|
|
|
80
88
|
/** Help-screen groups under trail; graft keeps its one flat list. */
|
|
81
89
|
const GROUP = {
|
|
82
90
|
find: "Find code · your agent runs these:",
|
|
83
|
-
memory: "
|
|
91
|
+
memory: "Learnings:",
|
|
84
92
|
setup: "Setup:",
|
|
85
93
|
cloud: "Workspace · your team's view, across repos:",
|
|
86
94
|
ci: "CI:",
|
|
@@ -118,8 +126,8 @@ function noteHit(found) {
|
|
|
118
126
|
program
|
|
119
127
|
.name(brand())
|
|
120
128
|
.description(TRAIL
|
|
121
|
-
? "
|
|
122
|
-
"
|
|
129
|
+
? "A code map for your coding agents, and the learnings each session leaves\n" +
|
|
130
|
+
"in your repo's .trail/, so everyone's Claude in the repo starts from them."
|
|
123
131
|
: "Build a repo's context graph as linked markdown, and keep it in sync with the code.")
|
|
124
132
|
.version(currentVersion, "-v, --version")
|
|
125
133
|
.option("--dir <path>", "context graph directory (default: <repo>/graft)")
|
|
@@ -152,7 +160,7 @@ function warnUnsupportedExtensions(exts) {
|
|
|
152
160
|
return;
|
|
153
161
|
for (const e of bad) {
|
|
154
162
|
const shown = e.trim().startsWith(".") ? e.trim() : `.${e.trim()}`;
|
|
155
|
-
console.error(`⚠ -e "${shown}": no parser registered for this extension
|
|
163
|
+
console.error(`⚠ -e "${shown}": no parser registered for this extension, so it is ignored.`);
|
|
156
164
|
}
|
|
157
165
|
console.error(` supported: ${supportedExtensions().join(" ")}`);
|
|
158
166
|
}
|
|
@@ -169,7 +177,7 @@ function queryRoot(dir) {
|
|
|
169
177
|
return resolve(dir);
|
|
170
178
|
const { root, levels } = nearestGraftRoot(process.cwd(), program.opts().dir);
|
|
171
179
|
if (levels > 0)
|
|
172
|
-
console.error(`${tag()} no code map here
|
|
180
|
+
console.error(`${tag()} no code map here: answering from ${contextDirFor(root, program.opts().dir)}`);
|
|
173
181
|
return root;
|
|
174
182
|
}
|
|
175
183
|
/**
|
|
@@ -192,7 +200,7 @@ async function refreshBefore(dir, opts) {
|
|
|
192
200
|
}
|
|
193
201
|
/** Attached to every query command: `--no-refresh` answers from the graph exactly
|
|
194
202
|
* as it is on disk, no rebuild. */
|
|
195
|
-
const NO_REFRESH_FLAG = ["--no-refresh", "skip the freshness check
|
|
203
|
+
const NO_REFRESH_FLAG = ["--no-refresh", "skip the freshness check: answer from the graph as-is"];
|
|
196
204
|
const VIZ_TABS = ["context", "code", "outline"];
|
|
197
205
|
/**
|
|
198
206
|
* `--tabs context,code` → the tabs to write into an exported page.
|
|
@@ -208,7 +216,7 @@ function parseTabs(raw) {
|
|
|
208
216
|
const want = raw.split(",").map((t) => t.trim()).filter(Boolean);
|
|
209
217
|
const bad = want.filter((t) => !VIZ_TABS.includes(t));
|
|
210
218
|
if (bad.length > 0 || want.length === 0) {
|
|
211
|
-
console.error(`✗ --tabs takes a comma-separated subset of ${VIZ_TABS.join(", ")}${bad.length ?
|
|
219
|
+
console.error(`✗ --tabs takes a comma-separated subset of ${VIZ_TABS.join(", ")}${bad.length ? `, not "${bad.join('", "')}"` : ""}`);
|
|
212
220
|
process.exit(1);
|
|
213
221
|
}
|
|
214
222
|
return want;
|
|
@@ -218,7 +226,7 @@ function parseTabs(raw) {
|
|
|
218
226
|
* not editorialize on stderr at startup (`mcp` runs its own upkeep at boot, and
|
|
219
227
|
* `_update-check` IS the fetch).
|
|
220
228
|
*/
|
|
221
|
-
const UPKEEP_SKIP = new Set(["version", "upgrade", "_update-check", "
|
|
229
|
+
const UPKEEP_SKIP = new Set(["version", "upgrade", "_update-check", "mcp"]);
|
|
222
230
|
/**
|
|
223
231
|
* Every other command: top up the cached registry answer in the background and,
|
|
224
232
|
* if a newer graft is out, say so once on stderr. This is what makes the CLI the
|
|
@@ -272,20 +280,6 @@ function spelling(action) {
|
|
|
272
280
|
}
|
|
273
281
|
// Hidden: spawned detached by maybeSyncInBackground when a workspace repo's
|
|
274
282
|
// learnings changed, or uses are waiting to go. Running it by hand syncs now.
|
|
275
|
-
// A repo that moved from graft to trail at session start reads its past
|
|
276
|
-
// sessions and PR reviews here, detached, so the session doesn't wait on it.
|
|
277
|
-
program
|
|
278
|
-
.command("_trail-past-read", { hidden: true })
|
|
279
|
-
.argument("<dir>")
|
|
280
|
-
.action(async (dir) => {
|
|
281
|
-
const repo = resolve(dir);
|
|
282
|
-
const read = beginPastRead(repo);
|
|
283
|
-
const reviews = beginReviewRead(repo);
|
|
284
|
-
if (read)
|
|
285
|
-
await finishPastRead(repo, read);
|
|
286
|
-
if (reviews)
|
|
287
|
-
await finishReviewRead(repo, reviews);
|
|
288
|
-
});
|
|
289
283
|
program
|
|
290
284
|
.command("_trail-sync", { hidden: true })
|
|
291
285
|
.description("internal: send this repo's learnings and the waiting uses to the Trail workspace")
|
|
@@ -307,6 +301,18 @@ program
|
|
|
307
301
|
.action(async () => {
|
|
308
302
|
await waitForConnect();
|
|
309
303
|
});
|
|
304
|
+
program
|
|
305
|
+
.command("_trail-pair", { hidden: true })
|
|
306
|
+
.description("internal: after `init --pair`, wait for the connect and the review in the browser, then upload the repo's learnings")
|
|
307
|
+
.argument("<state>")
|
|
308
|
+
.argument("<dir>")
|
|
309
|
+
.argument("[server]", "the Trail the run reported to (init's --server)")
|
|
310
|
+
.action(async (state, dir, server) => {
|
|
311
|
+
const base = serverUrl(server);
|
|
312
|
+
if (base)
|
|
313
|
+
useServer(base);
|
|
314
|
+
await finishPair(state, resolve(dir));
|
|
315
|
+
});
|
|
310
316
|
program
|
|
311
317
|
.command("_update-check", { hidden: true })
|
|
312
318
|
.description("internal: refresh the cached latest-version answer")
|
|
@@ -331,7 +337,7 @@ grouped(program.command("telemetry"), GROUP.setup)
|
|
|
331
337
|
return;
|
|
332
338
|
case "enable":
|
|
333
339
|
patchState({ enabled: true });
|
|
334
|
-
console.log(`telemetry: on
|
|
340
|
+
console.log(`telemetry: on, anonymous and aggregate-only. \`${cmd("graft telemetry status")}\` for details.`);
|
|
335
341
|
return;
|
|
336
342
|
case "disable":
|
|
337
343
|
// Also stamp the notice as shown: someone who has just opted out should
|
|
@@ -343,7 +349,7 @@ grouped(program.command("telemetry"), GROUP.setup)
|
|
|
343
349
|
console.log(formatDebug());
|
|
344
350
|
return;
|
|
345
351
|
default:
|
|
346
|
-
console.error(`✗ unknown action "${action}"
|
|
352
|
+
console.error(`✗ unknown action "${action}": expected status, enable, disable, or debug`);
|
|
347
353
|
process.exit(1);
|
|
348
354
|
}
|
|
349
355
|
});
|
|
@@ -385,7 +391,7 @@ function moveHereToTrail(dir) {
|
|
|
385
391
|
grouped(program.command("build"), GROUP.setup)
|
|
386
392
|
.description(TRAIL
|
|
387
393
|
? "Rebuild the code map in .trail/cache/ ($0, no key). --check fails if it's stale; --deep adds LLM summaries."
|
|
388
|
-
: "Build graft/ from your code
|
|
394
|
+
: "Build graft/ from your code: wiring graph + per-file cards ($0, no key). " +
|
|
389
395
|
"Add --deep for the LLM concept map + per-symbol summaries/crux.")
|
|
390
396
|
.argument("[dir]", "repository root", ".")
|
|
391
397
|
.option("--check", "build nothing: fail if graft/ is stale relative to the code (for CI)")
|
|
@@ -403,9 +409,9 @@ grouped(program.command("build"), GROUP.setup)
|
|
|
403
409
|
.option("--follow-nested-repos", "include nested Git clones the index does not track (a multi-repo manifest checkout, or any repo cloned into the tree) " +
|
|
404
410
|
"as ONE graph, so imports across them resolve; persisted for later builds and automatic refreshes")
|
|
405
411
|
.option("--no-follow-nested-repos", "exclude untracked nested Git clones; persisted for later builds and automatic refreshes (default)")
|
|
406
|
-
.option("--include-dir <name>", "override SKIP_DIRS for this repo's walks
|
|
412
|
+
.option("--include-dir <name>", "override SKIP_DIRS for this repo's walks, repeatable (e.g. --include-dir build --include-dir tools); " +
|
|
407
413
|
"persisted, so a later build (and the hooks/refresh path) include it without the flag; dot-dirs are never overridable", (val, prev) => [...prev, val], [])
|
|
408
|
-
.option("--only-dir <path>", "only index files under this repo-relative path
|
|
414
|
+
.option("--only-dir <path>", "only index files under this repo-relative path, repeatable; the wiring walk and the --deep " +
|
|
409
415
|
"concept pass both honor it. Recorded in the graph fingerprint so a later build " +
|
|
410
416
|
"(and the hooks/refresh path) walks the same set; everything outside the list is skipped", (val, prev) => [...prev, val], [])
|
|
411
417
|
.option("--no-gitignore", "skip writing graft/ into .gitignore (same as GRAFT_NO_GITIGNORE=1)")
|
|
@@ -494,12 +500,12 @@ grouped(program.command("build"), GROUP.setup)
|
|
|
494
500
|
const resolved = resolveConfig(cliConfig());
|
|
495
501
|
if (deep && !resolved.apiKey) {
|
|
496
502
|
deep = false;
|
|
497
|
-
console.error("⚠ no API key set
|
|
503
|
+
console.error("⚠ no API key set, so falling back to the structural build (no LLM summaries).\n" +
|
|
498
504
|
" Set GRAFT_API_KEY (and GRAFT_PROVIDER / GRAFT_BASE_URL / GRAFT_MODEL for your\n" +
|
|
499
505
|
` provider) and re-run \`${cmd("graft build --deep")}\` to add concept nodes and summaries.`);
|
|
500
506
|
}
|
|
501
507
|
if (deep && resolved.usedLegacyEnv) {
|
|
502
|
-
console.error("⚠ using OPENROUTER_API_KEY (deprecated)
|
|
508
|
+
console.error("⚠ using OPENROUTER_API_KEY (deprecated): prefer GRAFT_API_KEY + GRAFT_BASE_URL.");
|
|
503
509
|
}
|
|
504
510
|
// Workspace parent: build each child into its OWN graft/ + a workspace index.
|
|
505
511
|
const buildRoot = resolve(dir);
|
|
@@ -585,10 +591,10 @@ grouped(program.command("build"), GROUP.setup)
|
|
|
585
591
|
console.error(fallbackNotice);
|
|
586
592
|
const rel = relative(process.cwd(), g.contextDir) || "graft";
|
|
587
593
|
if (process.env.GRAFT_NO_GITIGNORE) {
|
|
588
|
-
console.log(` ${rel}/ is a local cache
|
|
594
|
+
console.log(` ${rel}/ is a local cache: add it to your gitignore if you want it untracked.`);
|
|
589
595
|
}
|
|
590
596
|
else {
|
|
591
|
-
console.log(` ${rel}/ is git-ignored (added automatically)
|
|
597
|
+
console.log(` ${rel}/ is git-ignored (added automatically), a local cache; teammates run \`${cmd("graft build")}\` to get their own.`);
|
|
592
598
|
}
|
|
593
599
|
// #127: a --deep run whose LLM calls failed used to print the same success
|
|
594
600
|
// footer and exit 0, so a quota-exhausted build looked identical to a clean
|
|
@@ -603,7 +609,7 @@ grouped(program.command("build"), GROUP.setup)
|
|
|
603
609
|
const total = ready + m.stale + m.pending;
|
|
604
610
|
const pct = total > 0 ? Math.round((ready / total) * 100) : 0;
|
|
605
611
|
console.error("");
|
|
606
|
-
console.error(`✗ the deep pass did not complete
|
|
612
|
+
console.error(`✗ the deep pass did not complete, so the meaning tier is incomplete.`);
|
|
607
613
|
if (conceptFatal)
|
|
608
614
|
console.error(` concepts: ${conceptFatal}`);
|
|
609
615
|
if (m.fatal)
|
|
@@ -622,56 +628,45 @@ grouped(program.command("build"), GROUP.setup)
|
|
|
622
628
|
}
|
|
623
629
|
}
|
|
624
630
|
});
|
|
625
|
-
/** The file part of a hit's `file:Lx-Ly` pointer. */
|
|
626
|
-
function pointerFile(pointer) {
|
|
627
|
-
return pointer.replace(/:L\d+(-L?\d+)?$/, "");
|
|
628
|
-
}
|
|
629
631
|
/**
|
|
630
632
|
* After `ask`, under trail: which shown learnings are a teammate's, and the
|
|
631
|
-
*
|
|
632
|
-
* reuse
|
|
633
|
-
* current. Spawns no git itself. Teammates' branches come up after an edit
|
|
633
|
+
* lookup recorded (the learnings shown are a use, which is what the team page
|
|
634
|
+
* counts as reuse; the tokens saved ride along). Then the background upkeep
|
|
635
|
+
* that keeps branches and the workspace current. Spawns no git itself. Teammates' branches come up after an edit
|
|
634
636
|
* instead, once there's a function to compare (notes/edit-overlap.ts).
|
|
635
637
|
*/
|
|
636
638
|
function afterAsk(r, repo) {
|
|
639
|
+
// Every lookup is reported, not only ones that showed a learning: the tokens
|
|
640
|
+
// the map saved are what the team page's running total counts. A no-op unless
|
|
641
|
+
// this machine is connected and the repo is the workspace's.
|
|
642
|
+
recordLookup(r, repo);
|
|
637
643
|
if (!r.notesChecked)
|
|
638
644
|
return;
|
|
639
645
|
rememberRepo(repo);
|
|
640
646
|
maybeRefreshBranches(repo);
|
|
641
647
|
const me = noteAuthor(repo);
|
|
642
|
-
const files = [...new Set(r.hits.map((h) => pointerFile(h.pointer)))];
|
|
643
648
|
const shown = r.notes ?? [];
|
|
644
649
|
if (shown.length) {
|
|
645
650
|
for (const h of shown)
|
|
646
651
|
h.note.teammate = !!h.note.author && !!me && h.note.author !== me;
|
|
647
|
-
recordActivity({
|
|
648
|
-
repo: repoPlace(repo).key,
|
|
649
|
-
kind: "search",
|
|
650
|
-
asker: me,
|
|
651
|
-
session: sessionKey(repo) ?? `${me}-${new Date().toISOString().slice(0, 10)}`,
|
|
652
|
-
query: r.query,
|
|
653
|
-
files,
|
|
654
|
-
shown_paths: shown.map((h) => noteName(h.note)),
|
|
655
|
-
shown_authors: shown.map((h) => h.note.author),
|
|
656
|
-
at: new Date().toISOString(),
|
|
657
|
-
});
|
|
658
|
-
const saved = shown.reduce((sum, h) => sum + savedByNote(h.note), 0);
|
|
659
|
-
track("learning_reused", { teammate: String(shown.some((h) => h.note.teammate)), saved_tokens_bucket: savedTokensBucket(saved) }, { repo });
|
|
660
652
|
}
|
|
661
653
|
maybeSyncInBackground(repo);
|
|
662
654
|
}
|
|
663
655
|
grouped(program.command("ask"), GROUP.find)
|
|
664
|
-
.description("Query the graft/ graph
|
|
665
|
-
.argument("<query>", "what you want to understand, in plain words")
|
|
656
|
+
.description("Query the graft/ graph: returns ranked nodes + exact file:line, routed to prose or wiring ($0, no key)")
|
|
657
|
+
.argument("<query>", "what you want to understand, in plain words (- reads it from stdin)")
|
|
666
658
|
.argument(...DIR_ARG)
|
|
667
659
|
.option("-n, --limit <n>", "max results", "8")
|
|
668
|
-
.option("--source", "inline the source at each file:line hit (retriever mode
|
|
660
|
+
.option("--source", "inline the source at each file:line hit (retriever mode: the pack IS the answer, no need to re-open files)")
|
|
669
661
|
.option("--full", "with --source: inline whole definition spans instead of the default ≤8-line crux excerpts")
|
|
670
662
|
.option("--in <path>", "narrow to nodes under this path prefix, filtered before scoring (segment-aware, like scopeOf)")
|
|
671
663
|
.option("--json", "output the result as JSON")
|
|
672
664
|
.option("--no-graph-rank", "rank by lexical relevance only, without the graph-connectivity re-rank (ablation/eval)")
|
|
673
665
|
.option(...NO_REFRESH_FLAG)
|
|
674
666
|
.action(async (query, dirArg, opts) => {
|
|
667
|
+
// The prompt hook passes what the person typed on stdin, out of sight of `ps`.
|
|
668
|
+
if (query === "-")
|
|
669
|
+
query = readFileSync(0, "utf8").trim();
|
|
675
670
|
const dir = noteQuery(queryRoot(dirArg));
|
|
676
671
|
await refreshBefore(dir, opts);
|
|
677
672
|
const askGlobalDir = program.opts().dir;
|
|
@@ -708,7 +703,7 @@ grouped(program.command("ask"), GROUP.find)
|
|
|
708
703
|
}
|
|
709
704
|
});
|
|
710
705
|
grouped(program.command("skeleton"), GROUP.find)
|
|
711
|
-
.description("Signatures-only view of one file from the wiring graph
|
|
706
|
+
.description("Signatures-only view of one file from the wiring graph: the cheapest way to see a file's API surface")
|
|
712
707
|
.argument("<file>", "repo-relative path (or unique basename) of the file")
|
|
713
708
|
.argument(...DIR_ARG)
|
|
714
709
|
.option("--json", "output the result as JSON")
|
|
@@ -765,7 +760,7 @@ async function runCheckCommand(dirArg, opts, label) {
|
|
|
765
760
|
}
|
|
766
761
|
else {
|
|
767
762
|
if (r.missing) {
|
|
768
|
-
console.log(`deep layer: not built (run \`${cmd("graft build --deep")}\` for concept nodes)
|
|
763
|
+
console.log(`deep layer: not built (run \`${cmd("graft build --deep")}\` for concept nodes); the wiring graph is the source of truth`);
|
|
769
764
|
}
|
|
770
765
|
else {
|
|
771
766
|
console.log(formatCheckReport(r, label));
|
|
@@ -819,7 +814,7 @@ program
|
|
|
819
814
|
const globalOpts = program.opts();
|
|
820
815
|
const contextDir = contextDirFor(root, globalOpts.dir);
|
|
821
816
|
if (!existsSync(contextDir)) {
|
|
822
|
-
console.error(`✗ no context graph at ${contextDir}
|
|
817
|
+
console.error(`✗ no context graph at ${contextDir}: run \`${cmd("graft build --deep")}\` first`);
|
|
823
818
|
process.exit(1);
|
|
824
819
|
}
|
|
825
820
|
const viewerDir = fileURLToPath(new URL("./viewer/", import.meta.url)); // prebuilt
|
|
@@ -851,11 +846,16 @@ program
|
|
|
851
846
|
});
|
|
852
847
|
program
|
|
853
848
|
.command("mcp", { hidden: TRAIL })
|
|
854
|
-
.description("Serve the graph over MCP (stdio)
|
|
849
|
+
.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")
|
|
855
850
|
.argument(...DIR_ARG)
|
|
856
|
-
.
|
|
857
|
-
|
|
851
|
+
.option("--brains", "serve only the connected Trail workspace's brain tools (what Claude Code gets after init --connect)")
|
|
852
|
+
.action(async (dirArg, opts) => {
|
|
858
853
|
const { startMcpServer } = await import("./mcp/server.js");
|
|
854
|
+
if (opts.brains) {
|
|
855
|
+
startMcpServer(process.cwd(), undefined, currentVersion, { brainsOnly: true });
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
const dir = noteQuery(queryRoot(dirArg));
|
|
859
859
|
const globalOpts = program.opts();
|
|
860
860
|
startMcpServer(dir, globalOpts.dir, currentVersion);
|
|
861
861
|
});
|
|
@@ -893,13 +893,13 @@ grouped(program.command("callers"), GROUP.find)
|
|
|
893
893
|
});
|
|
894
894
|
grouped(program.command("blast"), GROUP.ci)
|
|
895
895
|
.description("Blast radius of a diff: what depends on the lines this change touched ($0, no LLM). " +
|
|
896
|
-
"Built for CI
|
|
896
|
+
"Built for CI: `--format markdown` is a PR comment with a Mermaid diagram.")
|
|
897
897
|
.argument(...DIR_ARG)
|
|
898
898
|
.option("--base <ref>", "diff against this ref's merge base with HEAD (e.g. origin/main); default: the working tree vs HEAD")
|
|
899
899
|
.option("-d, --depth <n>", 'hops to walk over incoming edges, or "all" for the full closure (default 2)')
|
|
900
900
|
.option("--format <fmt>", "text (default) | markdown | mermaid | json")
|
|
901
901
|
.option("--name", "name the affected areas with one cached LLM call (needs GRAFT_API_KEY); without it, areas are named after their hub symbol")
|
|
902
|
-
.option("--export-viz <dir>", "also write the interactive page for this radius (one self-contained index.html
|
|
902
|
+
.option("--export-viz <dir>", "also write the interactive page for this radius (one self-contained index.html, for CI, GitHub Pages, or an artifact)")
|
|
903
903
|
.option("--title <text>", "subtitle beside the repo name on the exported page (e.g. \"PR #171\")")
|
|
904
904
|
.option("--no-owners", "do not suggest who to tag (by default, git history names the people behind each affected area)")
|
|
905
905
|
.option("--pr-author <who...>", "GitHub login, git name or email of the PR author, so they are left out of their own suggestions")
|
|
@@ -949,7 +949,7 @@ grouped(program.command("grep"), GROUP.find)
|
|
|
949
949
|
});
|
|
950
950
|
});
|
|
951
951
|
grouped(program.command("map"), GROUP.find)
|
|
952
|
-
.description("Token-budgeted repo orientation
|
|
952
|
+
.description("Token-budgeted repo orientation: directory clusters, per-directory hubs, and global hotspots from the wiring graph ($0, no LLM)")
|
|
953
953
|
.argument(...DIR_ARG)
|
|
954
954
|
.option("--max-dirs <n>", "max directory entries shown, rest counted into dropped (default 16)")
|
|
955
955
|
.option("--json", "output as JSON")
|
|
@@ -977,7 +977,7 @@ grouped(program.command("map"), GROUP.find)
|
|
|
977
977
|
const contextDir = contextDirFor(root, globalOpts.dir);
|
|
978
978
|
const graph = loadGraphCached(contextDir);
|
|
979
979
|
if (!graph) {
|
|
980
|
-
console.error(`✗ no graph
|
|
980
|
+
console.error(`✗ no graph: run ${cmd("graft build")} first`);
|
|
981
981
|
process.exit(1);
|
|
982
982
|
return;
|
|
983
983
|
}
|
|
@@ -1007,154 +1007,12 @@ grouped(program.command("note", { hidden: !TRAIL }), GROUP.memory)
|
|
|
1007
1007
|
.action(async (dir, opts) => {
|
|
1008
1008
|
await runNoteCommand(resolve(dir), opts);
|
|
1009
1009
|
});
|
|
1010
|
-
// `trail
|
|
1011
|
-
//
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
.argument("<skill>", "the skill it belongs to, e.g. pdf-coords; a new name makes a new skill")
|
|
1017
|
-
.argument("[dir]", "repository root", ".")
|
|
1018
|
-
.option("-m, --text <text>", "the takeaway itself, instead of stdin")
|
|
1019
|
-
.option("--description <text>", "for a new skill: what it covers")
|
|
1020
|
-
.option("--section <heading>", "the SKILL.md heading it belongs under when folded")
|
|
1021
|
-
.option("--author <name>", "who taught it (default: the first word of git config user.name)")
|
|
1022
|
-
.action(async (skill, dir, opts) => {
|
|
1023
|
-
const repo = resolve(dir);
|
|
1024
|
-
const text = (opts.text ?? (await readStdin())).trim();
|
|
1025
|
-
if (!text) {
|
|
1026
|
-
console.error(`✗ a takeaway needs its text, on stdin or with -m:`);
|
|
1027
|
-
console.error(` ${cmd("graft learn")} ${skill} <<'EOF'\n Read rotation with page.Rotation(), never pdfcpu: it drops /Rotate on linearized files.\n EOF`);
|
|
1028
|
-
process.exitCode = 1;
|
|
1029
|
-
return;
|
|
1030
|
-
}
|
|
1031
|
-
const author = opts.author?.trim() || noteAuthor(repo);
|
|
1032
|
-
let r;
|
|
1033
|
-
try {
|
|
1034
|
-
r = learn(repo, { skill, text, author, date: new Date().toISOString().slice(0, 10), description: opts.description, section: opts.section });
|
|
1035
|
-
}
|
|
1036
|
-
catch (err) {
|
|
1037
|
-
console.error(`✗ ${err instanceof Error ? err.message : String(err)}`);
|
|
1038
|
-
process.exitCode = 1;
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
track("takeaway_saved", { new_skill: String(r.created) }, { repo });
|
|
1042
|
-
const kind = r.created ? "new skill" : r.skill.where === "repo" ? "the repo's skill" : "skill";
|
|
1043
|
-
console.log(`✓ takeaway saved to ${kind} ${r.skill.name}${author ? ` · taught by ${author}` : ""}`);
|
|
1044
|
-
console.log(`· your agent follows it from now on · kept on this machine, in ${shownPath(dirname(dirname(r.takeaway.path)))}`);
|
|
1045
|
-
console.log(`· ${cmd("graft skills")} fold ${r.skill.name} writes it into SKILL.md`);
|
|
1046
|
-
});
|
|
1047
|
-
const skillsCmd = grouped(program.command("skills", { hidden: true }), GROUP.memory)
|
|
1048
|
-
.description("List this repo's skills, and the takeaways waiting to fold into each")
|
|
1049
|
-
.argument("[dir]", "repository root", ".")
|
|
1050
|
-
.option("--json", "output as JSON")
|
|
1051
|
-
.action((dir, opts) => {
|
|
1052
|
-
const repo = resolve(dir);
|
|
1053
|
-
const all = listSkills(repo);
|
|
1054
|
-
if (opts.json) {
|
|
1055
|
-
console.log(JSON.stringify(all, null, 2));
|
|
1056
|
-
return;
|
|
1057
|
-
}
|
|
1058
|
-
if (all.length === 0) {
|
|
1059
|
-
console.log(`no skills yet · ${cmd("graft learn")} <skill> saves the first takeaway and makes the skill`);
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
const width = Math.max(...all.map((s) => s.name.length)) + 2;
|
|
1063
|
-
for (const s of all)
|
|
1064
|
-
console.log(`${s.name.padEnd(width)}${skillStatus(s)}`);
|
|
1065
|
-
});
|
|
1066
|
-
/** `✗ no skill called x` and the exit code, for a name that isn't one. */
|
|
1067
|
-
function noSuchSkill(name) {
|
|
1068
|
-
console.error(`✗ no skill called ${skillName(name)} · ${cmd("graft skills")} lists them`);
|
|
1069
|
-
process.exitCode = 1;
|
|
1070
|
-
}
|
|
1071
|
-
skillsCmd
|
|
1072
|
-
.command("show")
|
|
1073
|
-
.description("Print a skill with every takeaway not yet folded in: what an agent reads before working in its area")
|
|
1074
|
-
.argument("<skill>", "the skill")
|
|
1075
|
-
.option("-C, --dir <path>", "repository root", ".")
|
|
1076
|
-
.action((skill, opts) => {
|
|
1077
|
-
const s = getSkill(resolve(opts.dir), skill);
|
|
1078
|
-
if (!s)
|
|
1079
|
-
return noSuchSkill(skill);
|
|
1080
|
-
process.stdout.write(skillForAgent(s));
|
|
1081
|
-
});
|
|
1082
|
-
skillsCmd
|
|
1083
|
-
.command("fold")
|
|
1084
|
-
.description("Write a skill's waiting takeaways into its SKILL.md and bump its version")
|
|
1085
|
-
.argument("[skill]", "one skill; leave out to fold every skill with takeaways waiting")
|
|
1086
|
-
.option("--confirmed", "only the takeaways someone other than their teacher confirmed")
|
|
1087
|
-
.option("-C, --dir <path>", "repository root", ".")
|
|
1088
|
-
.action((skill, opts) => {
|
|
1089
|
-
const repo = resolve(opts.dir);
|
|
1090
|
-
if (skill && !getSkill(repo, skill))
|
|
1091
|
-
return noSuchSkill(skill);
|
|
1092
|
-
const names = skill ? [skill] : listSkills(repo).map((s) => s.name);
|
|
1093
|
-
let inRepo = false;
|
|
1094
|
-
let any = false;
|
|
1095
|
-
for (const n of names) {
|
|
1096
|
-
const r = fold(repo, n, { confirmed: opts.confirmed });
|
|
1097
|
-
if (!r)
|
|
1098
|
-
continue;
|
|
1099
|
-
if (r.folded.length === 0) {
|
|
1100
|
-
if (skill)
|
|
1101
|
-
console.log(`· ${r.skill} v${r.from} · nothing to fold${r.stillWaiting ? `, ${r.stillWaiting} waiting for a second person` : ""}`);
|
|
1102
|
-
continue;
|
|
1103
|
-
}
|
|
1104
|
-
any = true;
|
|
1105
|
-
inRepo ||= r.where === "repo";
|
|
1106
|
-
track("skills_folded", { takeaways_bucket: countBucket(r.folded.length) }, { repo });
|
|
1107
|
-
console.log(`✓ ${r.skill} v${r.from} → v${r.to} · folded ${r.folded.length} takeaway${r.folded.length === 1 ? "" : "s"}${r.stillWaiting ? `, ${r.stillWaiting} still waiting` : ""}`);
|
|
1108
|
-
console.log(` M ${r.where === "repo" ? shown(repo, r.file) : shownPath(r.file)} ${r.sections.map((h) => `+ ${h}`).join(" ")}`);
|
|
1109
|
-
}
|
|
1110
|
-
if (!any) {
|
|
1111
|
-
if (!skill)
|
|
1112
|
-
console.log("· nothing to fold");
|
|
1113
|
-
return;
|
|
1114
|
-
}
|
|
1115
|
-
if (inRepo)
|
|
1116
|
-
console.log("· review with git diff, then commit");
|
|
1117
|
-
else
|
|
1118
|
-
console.log(`· ${cmd("graft skills")} publish <skill> moves a skill into the repo's .claude/skills/, to commit for your team`);
|
|
1119
|
-
});
|
|
1120
|
-
skillsCmd
|
|
1121
|
-
.command("publish")
|
|
1122
|
-
.description("Move a skill kept on this machine into the repo's .claude/skills/, ready to commit")
|
|
1123
|
-
.argument("<skill>", "the skill")
|
|
1124
|
-
.option("-C, --dir <path>", "repository root", ".")
|
|
1125
|
-
.action((skill, opts) => {
|
|
1126
|
-
const repo = resolve(opts.dir);
|
|
1127
|
-
const r = publish(repo, skill);
|
|
1128
|
-
if (!r.ok) {
|
|
1129
|
-
if (r.reason === "unknown")
|
|
1130
|
-
return noSuchSkill(skill);
|
|
1131
|
-
console.log(`· ${skillName(skill)} is already in the repo, in .claude/skills/${skillName(skill)}/`);
|
|
1132
|
-
return;
|
|
1133
|
-
}
|
|
1134
|
-
console.log(`✓ ${skillName(skill)} → ${shown(repo, r.file)}`);
|
|
1135
|
-
console.log("· commit it to share it: Claude Code loads it for everyone who pulls. its takeaways stay on this machine");
|
|
1136
|
-
});
|
|
1137
|
-
skillsCmd
|
|
1138
|
-
.command("confirm")
|
|
1139
|
-
.description("Say a teammate's takeaway is right, so `fold --confirmed` takes it")
|
|
1140
|
-
.argument("<skill>", "the skill")
|
|
1141
|
-
.argument("[takeaway]", "one takeaway file (default: every one waiting that someone else taught)")
|
|
1142
|
-
.option("--author <name>", "who is confirming (default: the first word of git config user.name)")
|
|
1143
|
-
.option("-C, --dir <path>", "repository root", ".")
|
|
1144
|
-
.action((skill, takeaway, opts) => {
|
|
1145
|
-
const repo = resolve(opts.dir);
|
|
1146
|
-
if (!getSkill(repo, skill))
|
|
1147
|
-
return noSuchSkill(skill);
|
|
1148
|
-
const who = opts.author?.trim() || noteAuthor(repo);
|
|
1149
|
-
const done = confirm(repo, skill, who, takeaway);
|
|
1150
|
-
if (done.length === 0) {
|
|
1151
|
-
console.log(`· nothing to confirm on ${skillName(skill)} — a takeaway needs someone other than its teacher`);
|
|
1152
|
-
return;
|
|
1153
|
-
}
|
|
1154
|
-
for (const t of done)
|
|
1155
|
-
console.log(`✓ confirmed ${t.taughtBy || "a"}'s takeaway from ${shortDate(t.date)} · ${shownPath(t.path)}`);
|
|
1156
|
-
console.log(`· ${cmd("graft skills")} fold ${skillName(skill)} writes it into SKILL.md`);
|
|
1157
|
-
});
|
|
1010
|
+
// `trail skills` (design 1.7): the skills Trail kept for you, held ones to keep, offers and the switch; and
|
|
1011
|
+
// `trail forget <name>`, the undo every save line names. The person-only ones refuse an agent (cli-skills.ts).
|
|
1012
|
+
registerSkillCommands(program);
|
|
1013
|
+
for (const c of program.commands)
|
|
1014
|
+
if (c.name() === "skills")
|
|
1015
|
+
grouped(c, GROUP.memory);
|
|
1158
1016
|
grouped(program.command("init"), GROUP.setup)
|
|
1159
1017
|
.description(TRAIL
|
|
1160
1018
|
? "Wire trail into your agents: instruction files and MCP for each, plus hooks and a statusline for Claude Code"
|
|
@@ -1172,8 +1030,36 @@ grouped(program.command("init"), GROUP.setup)
|
|
|
1172
1030
|
.option("-y, --yes", "skip the picker and wire every detected agent (the pre-0.8 default)")
|
|
1173
1031
|
.option("--no-global", "skip writes outside this repo (the ~/.codex/ config + hooks)")
|
|
1174
1032
|
.option("--verbose", "print every file written, the graph build's own output and the closing banner")
|
|
1033
|
+
.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)")
|
|
1034
|
+
.option("--pair <code>", "the pair code from Trail's onboarding page: that page shows this run live, then you review what it found")
|
|
1035
|
+
.option("--server <url>", "the Trail that gave the code (default: Trail cloud, https://team.trailhq.com)")
|
|
1036
|
+
.option("--plain", "look back without asking Claude to name what it finds")
|
|
1037
|
+
.option("--look-back", "look back at your past sessions again, for what isn't kept or closed yet")
|
|
1175
1038
|
.action(async (dir, opts) => {
|
|
1176
|
-
|
|
1039
|
+
if (opts.connect !== undefined && opts.pair !== undefined) {
|
|
1040
|
+
console.error("✗ --connect and --pair are two ways in: use the one your Trail page gave you");
|
|
1041
|
+
process.exitCode = 1;
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
if (opts.connect !== undefined)
|
|
1045
|
+
await runConnectInit(dir, opts);
|
|
1046
|
+
else
|
|
1047
|
+
await runInitCommand(dir, opts);
|
|
1048
|
+
});
|
|
1049
|
+
// `trail init` prints this when there was no terminal to ask on (an agent ran
|
|
1050
|
+
// it) or with --yes: the person picks what to keep from what the look-back
|
|
1051
|
+
// found. An agent may run it only after the person answered (pre-bash checks).
|
|
1052
|
+
program
|
|
1053
|
+
.command("keep", { hidden: true })
|
|
1054
|
+
.description("keep learnings `trail init` found: trail keep 1 3, or trail keep none")
|
|
1055
|
+
.argument("<numbers...>", "the numbers from the list init printed, or none")
|
|
1056
|
+
.action((numbers) => {
|
|
1057
|
+
// The CLI half of the keep check (src/init/keep.ts): it needs the receipt the PreToolUse half wrote.
|
|
1058
|
+
const r = keepCommand(numbers, process.env);
|
|
1059
|
+
for (const line of r.lines)
|
|
1060
|
+
console.error(line);
|
|
1061
|
+
if (r.code)
|
|
1062
|
+
process.exitCode = r.code;
|
|
1177
1063
|
});
|
|
1178
1064
|
/** Everything on stdin, or "" when nothing is piped in. */
|
|
1179
1065
|
async function readStdin() {
|
|
@@ -1266,11 +1152,12 @@ async function runNoteCommand(repo, opts) {
|
|
|
1266
1152
|
}
|
|
1267
1153
|
const author = opts.author?.trim() || noteAuthor(repo);
|
|
1268
1154
|
const date = new Date().toISOString().slice(0, 10);
|
|
1155
|
+
// Saved by `trail note`, which is how an agent saves: src says so, and no flag changes it.
|
|
1269
1156
|
const earlier = [];
|
|
1270
1157
|
const saved = [];
|
|
1271
1158
|
for (const t of targets) {
|
|
1272
1159
|
earlier.push(...listNotes(t.dir));
|
|
1273
|
-
saved.push(writeNote(t.dir, { title: opts.title, body, author, date, branch: currentBranch(t.dir), cost, touches: t.touches, replaces }, scope));
|
|
1160
|
+
saved.push(writeNote(t.dir, { title: opts.title, body, author, date, branch: currentBranch(t.dir), cost, touches: t.touches, replaces, src: "agent" }, scope));
|
|
1274
1161
|
}
|
|
1275
1162
|
const touches = targets.flatMap((t) => t.touches);
|
|
1276
1163
|
track("note_saved", { has_cost: String(cost?.tokens !== undefined || cost?.minutes !== undefined), touches_bucket: countBucket(touches.length), scope: saved[0]?.scope ?? scope }, { repo });
|
|
@@ -1279,7 +1166,8 @@ async function runNoteCommand(repo, opts) {
|
|
|
1279
1166
|
return;
|
|
1280
1167
|
}
|
|
1281
1168
|
for (const n of saved)
|
|
1282
|
-
console.log(`✓ ${n.scope === "personal" ? "saved just for you" : "
|
|
1169
|
+
console.log(`✓ ${n.scope === "personal" ? "saved just for you" : "added to this repo"} · ${notePlace(n)}`);
|
|
1170
|
+
console.log(savedIdLine(saved));
|
|
1283
1171
|
const note = saved[0];
|
|
1284
1172
|
// Name a code file if the note touches one; docs are rarely what it's about.
|
|
1285
1173
|
const about = touches.find((t) => !/\.(md|mdx|txt)$/i.test(t.split("#")[0])) ?? touches[0];
|
|
@@ -1294,7 +1182,7 @@ async function runNoteCommand(repo, opts) {
|
|
|
1294
1182
|
}
|
|
1295
1183
|
// Learnings about the same files, from someone else: this one builds on theirs.
|
|
1296
1184
|
const files = new Set(touches.map((t) => t.split("#")[0]));
|
|
1297
|
-
const prior = earlier.find((n) => n.author !== note.author && n.touches.some((t) => files.has(t.split("#")[0])));
|
|
1185
|
+
const prior = liveLearnings(earlier).find((n) => n !== old && n.author !== note.author && n.touches.some((t) => files.has(t.split("#")[0])));
|
|
1298
1186
|
if (prior)
|
|
1299
1187
|
console.log(`· builds on ${prior.author ? `${prior.author}'s` : "a"} learning from ${shortDate(prior.date)}`);
|
|
1300
1188
|
if (!SECTIONS.some((s) => sectionLead(body, s.heading) !== null))
|
|
@@ -1334,19 +1222,56 @@ function printLines(lines) {
|
|
|
1334
1222
|
if (lines.length)
|
|
1335
1223
|
console.log(`\n${lines.join("\n")}`);
|
|
1336
1224
|
}
|
|
1225
|
+
/**
|
|
1226
|
+
* `--server <url>` on `init --connect` and `--pair`: the Trail that gave the
|
|
1227
|
+
* code, for the rest of this run (`useServer`, never the environment). False,
|
|
1228
|
+
* after saying why, when it isn't a URL.
|
|
1229
|
+
*/
|
|
1230
|
+
function useServerOption(raw) {
|
|
1231
|
+
if (raw === undefined || !raw.trim())
|
|
1232
|
+
return true;
|
|
1233
|
+
const server = serverUrl(raw);
|
|
1234
|
+
if (!server) {
|
|
1235
|
+
console.error("✗ --server takes a URL, like https://dev.trailhq.com");
|
|
1236
|
+
process.exitCode = 1;
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
useServer(server);
|
|
1240
|
+
return true;
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* What `init --pair` hears from init, as the onboarding page's steps: the map
|
|
1244
|
+
* is done when `mapped` fires, the past sessions when `learned` does. Nothing
|
|
1245
|
+
* to say for a run that read no sessions (that step is already marked).
|
|
1246
|
+
*/
|
|
1247
|
+
function pairProgress(pair) {
|
|
1248
|
+
return {
|
|
1249
|
+
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"),
|
|
1250
|
+
learned: (l) => {
|
|
1251
|
+
if (l.sessions <= 0)
|
|
1252
|
+
return;
|
|
1253
|
+
pair.phase("decisions", "done", `${l.sessions} sessions`, `Read ${l.sessions} past Claude sessions`);
|
|
1254
|
+
},
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1337
1257
|
/** Repo-relative with forward slashes, for printing. */
|
|
1338
1258
|
function shown(repo, path) {
|
|
1339
1259
|
return relative(repo, path).split("\\").join("/");
|
|
1340
1260
|
}
|
|
1261
|
+
/** Agents init knows by another id: both read AGENTS.md. */
|
|
1262
|
+
const AGENT_ALIASES = { codex: "agents", opencode: "agents" };
|
|
1341
1263
|
/** `graft init`. */
|
|
1342
|
-
async function runInitCommand(dir, opts) {
|
|
1264
|
+
async function runInitCommand(dir, opts, progress) {
|
|
1343
1265
|
if (opts.listAgents) {
|
|
1344
1266
|
for (const id of [...hostIds(), "claude"])
|
|
1345
1267
|
console.log(id);
|
|
1346
1268
|
return null;
|
|
1347
1269
|
}
|
|
1348
1270
|
const repo = resolve(dir);
|
|
1349
|
-
|
|
1271
|
+
// Codex and OpenCode read AGENTS.md: `--agents codex` means that wiring.
|
|
1272
|
+
// The name as given is kept for Trail's onboarding page.
|
|
1273
|
+
const asked = Array.isArray(opts.agents) ? opts.agents : undefined;
|
|
1274
|
+
const explicit = asked ? [...new Set(asked.map((id) => AGENT_ALIASES[id] ?? id))] : undefined;
|
|
1350
1275
|
// Checked before anything is written: run from the home folder, init wired
|
|
1351
1276
|
// it, mapped every code file under it, then said to commit.
|
|
1352
1277
|
const refusal = homeFolderRefusal(repo);
|
|
@@ -1359,7 +1284,7 @@ async function runInitCommand(dir, opts) {
|
|
|
1359
1284
|
const validIds = [...hostIds(), "claude"];
|
|
1360
1285
|
const unknown = explicit.filter((id) => !validIds.includes(id));
|
|
1361
1286
|
if (unknown.length) {
|
|
1362
|
-
console.error(`✗ unknown agent id(s): ${unknown.join(", ")}
|
|
1287
|
+
console.error(`✗ unknown agent id(s): ${unknown.join(", ")}; valid: ${validIds.join(", ")}`);
|
|
1363
1288
|
process.exit(1);
|
|
1364
1289
|
}
|
|
1365
1290
|
}
|
|
@@ -1391,7 +1316,7 @@ async function runInitCommand(dir, opts) {
|
|
|
1391
1316
|
offerTelemetry: reason === null || reason === "disabled",
|
|
1392
1317
|
});
|
|
1393
1318
|
if (picked === null) {
|
|
1394
|
-
console.error("· cancelled
|
|
1319
|
+
console.error("· cancelled, nothing written");
|
|
1395
1320
|
return null;
|
|
1396
1321
|
}
|
|
1397
1322
|
ids = picked.hosts;
|
|
@@ -1419,20 +1344,42 @@ async function runInitCommand(dir, opts) {
|
|
|
1419
1344
|
if (opts.dryRun) {
|
|
1420
1345
|
console.error(formatPlan(plan, ids, repo, home));
|
|
1421
1346
|
for (const child of children)
|
|
1422
|
-
console.error(`\n
|
|
1347
|
+
console.error(`\n· ${child}/ (workspace child)\n` + formatPlan(planInit(join(repo, child), { home }), ids, join(repo, child), home));
|
|
1423
1348
|
return null;
|
|
1424
1349
|
}
|
|
1425
1350
|
if (ids.length === 0) {
|
|
1426
|
-
console.error("· no agents selected
|
|
1351
|
+
console.error("· no agents selected, nothing written");
|
|
1427
1352
|
return null;
|
|
1428
1353
|
}
|
|
1354
|
+
// With --pair, Trail's onboarding page is waiting on this run: tell it
|
|
1355
|
+
// first, so the person can sign up while the rest of init runs.
|
|
1356
|
+
let pair = null;
|
|
1357
|
+
if (opts.pair !== undefined) {
|
|
1358
|
+
if (!useServerOption(opts.server))
|
|
1359
|
+
return null;
|
|
1360
|
+
if (!validPairCode(opts.pair)) {
|
|
1361
|
+
console.error(`✗ --pair takes the code from Trail's onboarding page, got "${opts.pair}"`);
|
|
1362
|
+
process.exitCode = 1;
|
|
1363
|
+
return null;
|
|
1364
|
+
}
|
|
1365
|
+
const started = await PairRun.start(repo, opts.pair, { agent: asked?.[0] ?? ids[0], version: currentVersion });
|
|
1366
|
+
if ("error" in started) {
|
|
1367
|
+
console.error(`⚠ couldn't tell Trail about this run (${started.error}); setting up here anyway`);
|
|
1368
|
+
}
|
|
1369
|
+
else {
|
|
1370
|
+
pair = started;
|
|
1371
|
+
console.error(`· Trail's page is following along · ${started.url}`);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1429
1374
|
const wantClaude = ids.includes("claude");
|
|
1430
1375
|
const cliPath = fileURLToPath(import.meta.url);
|
|
1376
|
+
// D120 (2): what each checkout holds before init writes, so its install record names only init's own changes.
|
|
1377
|
+
const installSnaps = TRAIL ? snapInit(targets, home) : [];
|
|
1431
1378
|
// Under trail, each repo gets its `.trail/learnings/`, committed with the
|
|
1432
1379
|
// code, and its folder in ~/.trail for personal learnings and corrections.
|
|
1433
|
-
//
|
|
1434
|
-
//
|
|
1435
|
-
//
|
|
1380
|
+
// Before the look-back below sends its first event: that makes the repo one
|
|
1381
|
+
// on trail, so the event's repo id lands in `.trail/cache/` and not in a
|
|
1382
|
+
// `graft/` that nothing ignores.
|
|
1436
1383
|
const notesHome = new Map();
|
|
1437
1384
|
if (TRAIL)
|
|
1438
1385
|
for (const target of targets) {
|
|
@@ -1442,31 +1389,39 @@ async function runInitCommand(dir, opts) {
|
|
|
1442
1389
|
if (place.git)
|
|
1443
1390
|
maybeRefreshBranches(place.checkout);
|
|
1444
1391
|
}
|
|
1445
|
-
// Under trail, with Claude Code picked:
|
|
1446
|
-
//
|
|
1447
|
-
//
|
|
1448
|
-
|
|
1449
|
-
const
|
|
1450
|
-
|
|
1451
|
-
console.error(
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1392
|
+
// Under trail, with Claude Code picked: look back at this repo's past
|
|
1393
|
+
// sessions and PR reviews while the rest of init runs, and offer what came
|
|
1394
|
+
// up at the end. Nothing is kept without a tick (see src/init/review.ts).
|
|
1395
|
+
const tty = Boolean(process.stdin.isTTY && process.stderr.isTTY);
|
|
1396
|
+
const lookBack = TRAIL && wantClaude ? beginLookBack(repo, { plain: opts.plain === true, again: opts.lookBack === true }) : null;
|
|
1397
|
+
for (const line of lookBack?.lines ?? [])
|
|
1398
|
+
console.error(line);
|
|
1399
|
+
if (pair) {
|
|
1400
|
+
if (lookBack?.reads.lb)
|
|
1401
|
+
pair.phase("decisions", "running", "", "Reading past Claude sessions");
|
|
1402
|
+
else
|
|
1403
|
+
pair.phase("decisions", "done", "none here");
|
|
1404
|
+
if (lookBack?.reads.reviews)
|
|
1405
|
+
pair.phase("prs", "running", "", "Reading review comments on merged PRs");
|
|
1406
|
+
else
|
|
1407
|
+
pair.phase("prs", "done", "none here");
|
|
1408
|
+
}
|
|
1455
1409
|
if (children.length)
|
|
1456
|
-
console.error(`· workspace: wiring ${repo} and ${children.length} child repo(s)
|
|
1410
|
+
console.error(`· workspace: wiring ${repo} and ${children.length} child repo(s): ${children.join(", ")}`);
|
|
1457
1411
|
// Today's output, file by file, behind --verbose. Everything else gets one
|
|
1458
1412
|
// line per step: the graph, then one per agent.
|
|
1459
1413
|
const verbose = opts.verbose === true;
|
|
1460
1414
|
const reports = [];
|
|
1461
1415
|
for (const target of targets) {
|
|
1462
1416
|
if (verbose && target !== repo)
|
|
1463
|
-
console.error(`\n
|
|
1417
|
+
console.error(`\n· ${relative(repo, target)}/`);
|
|
1464
1418
|
reports.push(wireTarget(target, ids, { home, cliPath, plan, opts, wantClaude, quiet: !verbose }));
|
|
1465
1419
|
}
|
|
1466
1420
|
// The graph, built once after the wiring: a workspace parent's build is
|
|
1467
1421
|
// the workspace build, so it covers every child.
|
|
1468
1422
|
let graphLine = null;
|
|
1469
1423
|
if (!verbose) {
|
|
1424
|
+
pair?.phase("files", "running", "", "Mapping the code");
|
|
1470
1425
|
const res = await buildGraphWithProgress(repo, { build: opts.build, cliPath });
|
|
1471
1426
|
for (const m of res.messages)
|
|
1472
1427
|
console.error(m);
|
|
@@ -1479,7 +1434,7 @@ async function runInitCommand(dir, opts) {
|
|
|
1479
1434
|
graphLine = "✓ graph built";
|
|
1480
1435
|
}
|
|
1481
1436
|
else if (res.failed) {
|
|
1482
|
-
graphLine = `⚠ the graph build failed
|
|
1437
|
+
graphLine = `⚠ the graph build failed: run ${cmd("graft build")} to see why`;
|
|
1483
1438
|
}
|
|
1484
1439
|
else if (existing) {
|
|
1485
1440
|
graphLine = `✓ graph ready · ${fmt(existing.meta.nodeCount)} nodes, ${fmt(existing.meta.edgeCount)} edges`;
|
|
@@ -1488,14 +1443,36 @@ async function runInitCommand(dir, opts) {
|
|
|
1488
1443
|
graphLine = "✓ graph ready";
|
|
1489
1444
|
}
|
|
1490
1445
|
else {
|
|
1491
|
-
graphLine = `· skipped the graph build
|
|
1446
|
+
graphLine = `· skipped the graph build: run ${cmd("graft build")}`;
|
|
1492
1447
|
}
|
|
1493
1448
|
}
|
|
1449
|
+
// After the graph build too: it adds the map's ignore lines (`.trail/.gitignore` under trail, `.gitignore` and `.ignore`
|
|
1450
|
+
// under graft), and those are init's writes.
|
|
1451
|
+
recordInit(installSnaps);
|
|
1452
|
+
// --pair hears the same stages --connect does; only the ones InitProgress
|
|
1453
|
+
// has no slot for (a read starting, the PR read, nothing to read) stay inline.
|
|
1454
|
+
const report = pair ? pairProgress(pair) : progress;
|
|
1455
|
+
if (report?.mapped) {
|
|
1456
|
+
const g = loadGraphCached(contextDirFor(repo, children.length ? undefined : program.opts().dir));
|
|
1457
|
+
report.mapped({
|
|
1458
|
+
nodes: g?.meta.nodeCount ?? 0,
|
|
1459
|
+
edges: g?.meta.edgeCount ?? 0,
|
|
1460
|
+
files: g ? g.nodes.filter((n) => n.kind === "file").length : 0,
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
// With --pair, one lookup measured against reading the files whole: the
|
|
1464
|
+
// page's token step. It needs the map, so it follows the build.
|
|
1465
|
+
let tokens = null;
|
|
1466
|
+
if (pair) {
|
|
1467
|
+
pair.phase("tokens", "running", "", "Measuring one lookup against reading the files");
|
|
1468
|
+
tokens = tokensOnOneLookup(repo, { contextDir: program.opts().dir });
|
|
1469
|
+
pair.tokensDone(tokens);
|
|
1470
|
+
}
|
|
1494
1471
|
if (!verbose) {
|
|
1495
1472
|
// Warnings and removals in full, whatever else is folded.
|
|
1496
1473
|
const removed = [...new Set(reports.flatMap((r) => r.retracted))];
|
|
1497
1474
|
for (const id of removed)
|
|
1498
|
-
console.error(`- removed ${id}
|
|
1475
|
+
console.error(`- removed ${id} (agent not selected)`);
|
|
1499
1476
|
for (const w of reports.flatMap((r) => r.warnings))
|
|
1500
1477
|
console.error(w);
|
|
1501
1478
|
const writesFor = (id) => (plan.find((p) => p.id === id)?.writes ?? []).filter((w) => opts.global !== false || w.scope !== "global");
|
|
@@ -1507,107 +1484,58 @@ async function runInitCommand(dir, opts) {
|
|
|
1507
1484
|
console.error(`✓ ${id.padEnd(width)} ${summary}`.trimEnd());
|
|
1508
1485
|
}
|
|
1509
1486
|
}
|
|
1510
|
-
// Under trail: what
|
|
1511
|
-
//
|
|
1512
|
-
//
|
|
1513
|
-
//
|
|
1487
|
+
// Under trail: what the look-back found, and #662's team rows (what
|
|
1488
|
+
// reviewers kept flagging on merged PRs, and Claude Code's notes about the
|
|
1489
|
+
// repo on this machine), what the person kept, where this repo's learnings
|
|
1490
|
+
// are, and how many there already are.
|
|
1514
1491
|
let learnedNow = false;
|
|
1515
1492
|
let teamSaved = 0;
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
...memory.map((c) => ({ title: c.title, forTeam: c.forTeam, memory: true })),
|
|
1532
|
-
...fromSessions.map((f) => ({ title: f.title, forTeam: false, sessions: f.sessions.length })),
|
|
1533
|
-
])
|
|
1534
|
-
: undefined;
|
|
1535
|
-
// The picked rows that are `found`'s, which start at `from` in the list.
|
|
1536
|
-
const keepFrom = (from) => picked === undefined
|
|
1537
|
-
? undefined
|
|
1538
|
-
: async (found) => picked && picked.filter((i) => i >= from && i < from + found.length).map((i) => found[i - from]);
|
|
1539
|
-
const learned = [];
|
|
1540
|
-
// Reviews first: which of them are offered depends on the learnings already saved.
|
|
1541
|
-
if (reviewRead) {
|
|
1542
|
-
const { result, saved, kept, cancelled } = await finishReviewRead(repo, reviewRead, keepFrom(0));
|
|
1543
|
-
const offered = fromReviews.length;
|
|
1544
|
-
if (saved.length) {
|
|
1545
|
-
const n = saved.length;
|
|
1546
|
-
const what = n < offered ? `kept ${n} of the ${offered} things` : `learned ${n} thing${n === 1 ? "" : "s"}`;
|
|
1547
|
-
console.error(`✓ ${what} reviewers made authors change, from the last ${result.prs} merged PRs here`);
|
|
1548
|
-
// saveReviewFindings keeps the findings' order.
|
|
1549
|
-
saved.forEach((note, i) => console.error(` for your team · from review on ${(kept[i]?.prs ?? []).map((n) => `#${n}`).join(", ")}: ${note.title}`));
|
|
1550
|
-
}
|
|
1551
|
-
else if (cancelled) {
|
|
1552
|
-
console.error(`· saved none of the ${offered} things reviewers made authors change; ${cmd("graft init")} again asks again`);
|
|
1553
|
-
}
|
|
1554
|
-
else if (offered) {
|
|
1555
|
-
console.error(`· kept none of the ${offered} things reviewers made authors change`);
|
|
1556
|
-
}
|
|
1557
|
-
else if (result.reason === "failed") {
|
|
1558
|
-
console.error(`· couldn't read PR reviews this time; ${cmd("graft init")} again tries again`);
|
|
1559
|
-
}
|
|
1560
|
-
learned.push(...saved);
|
|
1493
|
+
// What the look-back read, for the --pair and --connect pages, and the skills it found (--pair names them).
|
|
1494
|
+
let sessionsRead = 0;
|
|
1495
|
+
let skillsFound = [];
|
|
1496
|
+
if (lookBack) {
|
|
1497
|
+
// With --pair the person reviews what was found on Trail's page, so no screen here, as with no terminal.
|
|
1498
|
+
const screen = tty && !pair;
|
|
1499
|
+
const learned = await finishLookBack(repo, lookBack.reads, { yes: opts.yes === true, verbose: opts.verbose === true, say: (l) => console.error(l), ...(screen ? { io: { stdin: process.stdin, stdout: process.stderr } } : {}) });
|
|
1500
|
+
// #662's lines for the team rows it kept with no screen, in its words.
|
|
1501
|
+
const { fromReviews, fromMemory } = learned.teamKept ?? { fromReviews: null, fromMemory: null };
|
|
1502
|
+
if (fromReviews?.saved.length) {
|
|
1503
|
+
const n = fromReviews.saved.length;
|
|
1504
|
+
const what = n < fromReviews.offered ? `kept ${n} of the ${fromReviews.offered} things` : `learned ${n} thing${n === 1 ? "" : "s"}`;
|
|
1505
|
+
console.error(`✓ ${what} reviewers made authors change, from the last ${fromReviews.result.prs} merged PRs here`);
|
|
1506
|
+
// saveReviewFindings keeps the findings' order.
|
|
1507
|
+
fromReviews.saved.forEach((note, i) => console.error(` for your team · from review on ${(fromReviews.kept[i]?.prs ?? []).map((n) => `#${n}`).join(", ")}: ${note.title}`));
|
|
1561
1508
|
}
|
|
1562
|
-
if (
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
console.error(
|
|
1566
|
-
for (const note of saved)
|
|
1567
|
-
console.error(` ${note.scope === "personal" ? "just for you " : "for your team"} · ${note.title}`);
|
|
1568
|
-
}
|
|
1569
|
-
else if (cancelled) {
|
|
1570
|
-
console.error(`· saved none of the ${memory.length} notes your Claude keeps about this repo; ${cmd("graft init")} again asks again`);
|
|
1571
|
-
}
|
|
1572
|
-
learned.push(...saved);
|
|
1509
|
+
if (fromMemory?.saved.length) {
|
|
1510
|
+
console.error(`✓ kept ${fromMemory.saved.length} of the ${fromMemory.offered} notes your Claude keeps about this repo`);
|
|
1511
|
+
for (const note of fromMemory.saved)
|
|
1512
|
+
console.error(` ${note.scope === "personal" ? "just for you " : "for your team"} · ${note.title}`);
|
|
1573
1513
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
console.error(`✓ ${what} you told Claude more than once, from your last ${result.read} sessions here, just for you`);
|
|
1581
|
-
// Each with how many sessions it was said in: saveFindings keeps the findings' order.
|
|
1582
|
-
saved.forEach((note, i) => {
|
|
1583
|
-
const said = kept[i]?.sessions.length ?? 0;
|
|
1584
|
-
const count = said > 1 ? `told Claude in ${said} sessions: ` : "";
|
|
1585
|
-
console.error(` just for you · ${count}${note.title}`);
|
|
1586
|
-
});
|
|
1587
|
-
}
|
|
1588
|
-
else if (cancelled) {
|
|
1589
|
-
console.error(`· saved none of the ${offered} things you told Claude more than once; ${cmd("graft init")} again asks again`);
|
|
1590
|
-
}
|
|
1591
|
-
else if (offered) {
|
|
1592
|
-
console.error(`· kept none of the ${offered} things you told Claude more than once`);
|
|
1593
|
-
}
|
|
1594
|
-
else if (result.reason === "failed") {
|
|
1595
|
-
console.error(`· couldn't read your past sessions this time; ${cmd("graft init")} again tries again`);
|
|
1596
|
-
}
|
|
1597
|
-
learned.push(...saved);
|
|
1514
|
+
sessionsRead = learned.read?.sessions ?? 0;
|
|
1515
|
+
skillsFound = learned.skills ?? [];
|
|
1516
|
+
const prs = learned.read?.prs;
|
|
1517
|
+
if (pair && prs !== undefined && prs !== null) {
|
|
1518
|
+
const n = fromReviews?.saved.length ?? 0;
|
|
1519
|
+
pair.phase("prs", "done", `${prs} PRs`, `Read ${prs} merged PRs: ${n} thing${n === 1 ? "" : "s"} reviewers made authors change`);
|
|
1598
1520
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1521
|
+
// The team learnings kept with no screen, said in no past session; after a screen the page lists the repo's own.
|
|
1522
|
+
const notes = [...(fromReviews?.saved ?? []), ...(fromMemory?.saved ?? [])].map((note) => ({ note, sessions: 0 }));
|
|
1523
|
+
report?.learned?.({ notes, sessions: sessionsRead });
|
|
1524
|
+
learnedNow = learned.kept > 0;
|
|
1525
|
+
teamSaved = learned.team;
|
|
1601
1526
|
if (learnedNow) {
|
|
1602
1527
|
console.error("· Claude follows them from your next session");
|
|
1603
|
-
if (
|
|
1528
|
+
if (learned.inRepo)
|
|
1604
1529
|
console.error(commitLines({}, teammatesOn(repo), "repo").uncommitted);
|
|
1605
1530
|
}
|
|
1606
1531
|
}
|
|
1532
|
+
// Not looked back (not trail, or not Claude Code): the page still gets its stage.
|
|
1533
|
+
if (!lookBack)
|
|
1534
|
+
report?.learned?.({ notes: [], sessions: 0 });
|
|
1607
1535
|
if (TRAIL && !learnedNow) {
|
|
1608
1536
|
const home = notesHome.get(repo);
|
|
1609
1537
|
if (home) {
|
|
1610
|
-
const learnings = listNotes(repo, { branches: false }).filter((n) => n.scope === "repo");
|
|
1538
|
+
const learnings = liveLearnings(listNotes(repo, { branches: false })).filter((n) => n.scope === "repo");
|
|
1611
1539
|
if (learnings.length === 0)
|
|
1612
1540
|
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`);
|
|
1613
1541
|
else
|
|
@@ -1644,8 +1572,8 @@ async function runInitCommand(dir, opts) {
|
|
|
1644
1572
|
console.error(NOT_GIT_LINE);
|
|
1645
1573
|
else if (tops.length)
|
|
1646
1574
|
console.error(TRAIL
|
|
1647
|
-
? `· commit ${tops.slice(0, 5).join(" and ")}${tops.length > 5 ? ` +${tops.length - 5} more` : ""} once
|
|
1648
|
-
: `· commit ${tops.slice(0, 5).join(" ")}${tops.length > 5 ? ` +${tops.length - 5} more` : ""} to share it
|
|
1575
|
+
? `· commit ${tops.slice(0, 5).join(" and ")}${tops.length > 5 ? ` +${tops.length - 5} more` : ""} once: after that, teammates' Claude reads your learnings even before they install trail`
|
|
1576
|
+
: `· commit ${tops.slice(0, 5).join(" ")}${tops.length > 5 ? ` +${tops.length - 5} more` : ""} to share it: graft/ stays local and git-ignored`);
|
|
1649
1577
|
}
|
|
1650
1578
|
// One epilogue for the whole run. A workspace parent holds no nodes of its
|
|
1651
1579
|
// own, so the totals come from the children — the graph the user actually got.
|
|
@@ -1660,14 +1588,130 @@ async function runInitCommand(dir, opts) {
|
|
|
1660
1588
|
nodes: graphs.reduce((n, g) => n + g.meta.nodeCount, 0),
|
|
1661
1589
|
edges: graphs.reduce((n, g) => n + g.meta.edgeCount, 0),
|
|
1662
1590
|
}));
|
|
1591
|
+
// Hand what was found to the review page, and leave a child waiting for
|
|
1592
|
+
// the person to connect and review there.
|
|
1593
|
+
if (pair) {
|
|
1594
|
+
// Teammates' branches on this person's files, checked against the
|
|
1595
|
+
// learnings just saved and the repo's own: synchronous, so the finding
|
|
1596
|
+
// exists before the page is told the read is done.
|
|
1597
|
+
pair.phase("team", "running", "", "Checking your team's branches");
|
|
1598
|
+
const teamNotes = listNotes(repo, { branches: false }).filter((n) => n.scope === "repo" && !n.ref);
|
|
1599
|
+
// No remote (or no default branch) means nothing was checked: `teammates`
|
|
1600
|
+
// is left out, since an empty list would say "checked, none".
|
|
1601
|
+
const teammates = teammatesChecked(repo, { learnings: teamNotes });
|
|
1602
|
+
pair.teamDone(teammates);
|
|
1603
|
+
pair.findings({ tokens, sessions: lookBack?.reads.lb ? sessionsRead : undefined, teammates });
|
|
1604
|
+
await pair.finish(repo, skillsFound);
|
|
1605
|
+
pair.handOff(repo);
|
|
1606
|
+
console.error(`✓ Trail has what it found · review it in the browser: ${pair.url}`);
|
|
1607
|
+
if (!process.stdout.isTTY)
|
|
1608
|
+
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}`);
|
|
1609
|
+
}
|
|
1663
1610
|
// Run by an agent, not a person: the last line tells it to ask about the
|
|
1664
1611
|
// commit, since the person never saw the picker and Claude Code folds the rest.
|
|
1665
|
-
|
|
1612
|
+
// With --pair the person reviews them on the Trail page first, so no ask here.
|
|
1613
|
+
if (teamSaved && !pair && !process.stderr.isTTY)
|
|
1666
1614
|
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)"`);
|
|
1667
1615
|
// Sorted so `claude,cursor` and `cursor,claude` aggregate as one value.
|
|
1668
1616
|
track("init_completed", { agents: [...ids].sort().join(","), consent: consent === undefined ? "unasked" : String(consent) }, { repo });
|
|
1669
1617
|
return { ids };
|
|
1670
1618
|
}
|
|
1619
|
+
/**
|
|
1620
|
+
* `trail init --connect <CODE>`: the onboarding on team.trailhq.com. Claims the
|
|
1621
|
+
* one-time code (a wrong or spent one stops here, before anything is written),
|
|
1622
|
+
* saves the workspace token where `trail login` keeps it, then runs init as
|
|
1623
|
+
* usual while the page ticks off connected, mapped and learned. At the end,
|
|
1624
|
+
* one summary of the repo goes up (a lookup's tokens saved, the learnings'
|
|
1625
|
+
* titles, teammates' branches from git: never code, diffs or session text),
|
|
1626
|
+
* then `done`. Offline, init still sets the repo up and says how to connect.
|
|
1627
|
+
*/
|
|
1628
|
+
async function runConnectInit(dir, opts) {
|
|
1629
|
+
if (opts.listAgents || opts.dryRun) {
|
|
1630
|
+
if (opts.dryRun)
|
|
1631
|
+
console.error("· --dry-run: not connecting");
|
|
1632
|
+
await runInitCommand(dir, opts);
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
const repo = resolve(dir);
|
|
1636
|
+
if (!useServerOption(opts.server))
|
|
1637
|
+
return;
|
|
1638
|
+
const base = trailUrl();
|
|
1639
|
+
const code = normalizeCode(opts.connect ?? "");
|
|
1640
|
+
if (!code) {
|
|
1641
|
+
console.error(`✗ "${opts.connect ?? ""}" isn't a Trail code · it looks like TRL-7F2A-K9QD, from ${base}/welcome/code`);
|
|
1642
|
+
process.exitCode = 1;
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
const ref = repoRef(repo);
|
|
1646
|
+
const machine = machineName();
|
|
1647
|
+
const got = await claimConnectCode(base, code, { machine, repo: ref });
|
|
1648
|
+
const cloud = "cloud" in got ? got.cloud : null;
|
|
1649
|
+
if ("cloud" in got) {
|
|
1650
|
+
const cloud = got.cloud;
|
|
1651
|
+
writeCloud(cloud);
|
|
1652
|
+
// A sign-in link out from before is moot now.
|
|
1653
|
+
clearPending();
|
|
1654
|
+
console.error(`✓ connected to workspace ${cloud.workspace.name}`);
|
|
1655
|
+
track("brain_signup_settled", { outcome: "linked", mode: "connect", duration_bucket: durationBucket(0) }, { repo });
|
|
1656
|
+
}
|
|
1657
|
+
else if (got.reason === "invalid") {
|
|
1658
|
+
console.error(`✗ ${got.error} · get a new code at ${base}/welcome/code`);
|
|
1659
|
+
process.exitCode = 1;
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
else {
|
|
1663
|
+
console.error(offlineLine(got.error, base, code, cmd("graft init")));
|
|
1664
|
+
}
|
|
1665
|
+
const reporter = cloud ? new OnboardingReporter(cloud, ref, { machine }) : null;
|
|
1666
|
+
void reporter?.stage("connected");
|
|
1667
|
+
// What init learned this run, kept for the summary.
|
|
1668
|
+
const heard = { notes: [], sessions: 0 };
|
|
1669
|
+
const progress = reporter
|
|
1670
|
+
? {
|
|
1671
|
+
mapped: (m) => void reporter.stage("mapped", m),
|
|
1672
|
+
learned: (l) => {
|
|
1673
|
+
Object.assign(heard, l);
|
|
1674
|
+
const team = learningsSummary(repo, l.notes);
|
|
1675
|
+
void reporter.stage("learned", { learnings: team.length, sessions: l.sessions || Math.max(0, ...team.map((t) => t.sessions)) });
|
|
1676
|
+
},
|
|
1677
|
+
}
|
|
1678
|
+
: undefined;
|
|
1679
|
+
// The page's command is pasted into a terminal, but an agent may run it for
|
|
1680
|
+
// them: with no terminal to pick in, wire the agents this machine has.
|
|
1681
|
+
const interactive = process.stdin.isTTY && process.stderr.isTTY;
|
|
1682
|
+
const res = await runInitCommand(dir, interactive ? opts : { ...opts, yes: true }, progress);
|
|
1683
|
+
if (!reporter || !cloud)
|
|
1684
|
+
return;
|
|
1685
|
+
if (!res) {
|
|
1686
|
+
await reporter.flush();
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
// What Trail found, for the page: route B, summaries only.
|
|
1690
|
+
const learnings = learningsSummary(repo, heard.notes);
|
|
1691
|
+
const sessions = heard.sessions || Math.max(0, ...learnings.map((l) => l.sessions));
|
|
1692
|
+
const tokens = tokensOnOneLookup(repo, { contextDir: program.opts().dir });
|
|
1693
|
+
const teamNotes = listNotes(repo, { branches: false }).filter((n) => n.scope === "repo" && !n.ref);
|
|
1694
|
+
const teammates = teammatesFromGit(repo, { learnings: teamNotes });
|
|
1695
|
+
const sent = await reporter.summary({ tokens, ...(sessions ? { sessions } : {}), learnings, teammates });
|
|
1696
|
+
await reporter.stage("done");
|
|
1697
|
+
await reporter.flush();
|
|
1698
|
+
// Claude Code gets the workspace's brains through one user-level `trail` entry.
|
|
1699
|
+
const brainsEntry = res.ids.includes("claude") && opts.global !== false ? installBrainsServer(homedir()) : null;
|
|
1700
|
+
if (sent) {
|
|
1701
|
+
const parts = [
|
|
1702
|
+
tokens ? `one lookup saved ${tokens.pct}% of the tokens` : null,
|
|
1703
|
+
`${learnings.length} learning${learnings.length === 1 ? "" : "s"}`,
|
|
1704
|
+
`${teammates.length} teammate branch${teammates.length === 1 ? "" : "es"} on your files`,
|
|
1705
|
+
].filter(Boolean);
|
|
1706
|
+
console.error(`✓ sent ${cloud.workspace.name} a summary: ${parts.join(", ")} · titles, counts and names only, no code`);
|
|
1707
|
+
console.error(`· see what Trail found: ${base}/welcome/code/found`);
|
|
1708
|
+
}
|
|
1709
|
+
else {
|
|
1710
|
+
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`);
|
|
1711
|
+
}
|
|
1712
|
+
if (brainsEntry?.action === "created" || brainsEntry?.action === "updated")
|
|
1713
|
+
console.error(`✓ Claude Code gets your workspace's brains in every project · restart it to pick them up`);
|
|
1714
|
+
}
|
|
1671
1715
|
/** One repo's worth of `init` writes — the parent, then each workspace child. */
|
|
1672
1716
|
function wireTarget(repo, ids, ctx) {
|
|
1673
1717
|
const { home, cliPath, plan, wantClaude } = ctx;
|
|
@@ -1690,7 +1734,7 @@ function wireTarget(repo, ids, ctx) {
|
|
|
1690
1734
|
// be rewritten, and the graph cache is kept (init is one step from using it).
|
|
1691
1735
|
const retracted = changed(runRetract(repo, { home, apply: true, global: opts.global, cache: false, exclude: ids })).filter((r) => r.action !== "skipped-unparseable");
|
|
1692
1736
|
for (const r of retracted)
|
|
1693
|
-
say(`- removed ${r.path} (${r.what})
|
|
1737
|
+
say(`- removed ${r.path} (${r.what}): agent not selected`);
|
|
1694
1738
|
report.retracted = [...new Set(retracted.map((r) => r.hostId))];
|
|
1695
1739
|
if (wantClaude) {
|
|
1696
1740
|
// `global`/`home` are threaded through alongside `statusline`: the claude layer
|
|
@@ -1706,11 +1750,11 @@ function wireTarget(repo, ids, ctx) {
|
|
|
1706
1750
|
// trail registers no server for Claude Code: the hooks and the `trail` command cover it.
|
|
1707
1751
|
}
|
|
1708
1752
|
else if (res.mcp.action === "skipped-unparseable")
|
|
1709
|
-
say(`⚠ .mcp.json: ${res.mcp.path} left unchanged (not valid JSON)
|
|
1753
|
+
say(`⚠ .mcp.json: ${res.mcp.path} left unchanged (not valid JSON); add the graft server manually`);
|
|
1710
1754
|
else if (res.mcp.action === "unchanged")
|
|
1711
1755
|
say(`· mcp claude: ${res.mcp.path} (already registered)`);
|
|
1712
1756
|
else
|
|
1713
|
-
say(`✓ mcp claude: ${res.mcp.path} (${res.mcp.action})
|
|
1757
|
+
say(`✓ mcp claude: ${res.mcp.path} (${res.mcp.action}); restart Claude Code to load the graft MCP server`);
|
|
1714
1758
|
say(res.built ? `✓ built the graph (${cmd("graft build")})` : "· skipped graph build");
|
|
1715
1759
|
if (!wantStatusline)
|
|
1716
1760
|
say("· skipped Claude Code statusLine (--no-statusline)");
|
|
@@ -1735,6 +1779,13 @@ function wireTarget(repo, ids, ctx) {
|
|
|
1735
1779
|
say(`✓ mcp ${m.id}: ${m.path} (${m.action})`);
|
|
1736
1780
|
for (const h of r.hooks)
|
|
1737
1781
|
say(`✓ hook ${h.id}: ${h.path} (${h.action})`);
|
|
1782
|
+
// Phase 2 (4.17, Q3): Codex runs Trail's hooks only once the person trusts them in /hooks.
|
|
1783
|
+
if (r.hooks.some((h) => h.id === "codex-hooks")) {
|
|
1784
|
+
const trust = codexTrustLine({ ...process.env, HOME: home, CODEX_HOME: process.env.CODEX_HOME || join(home, ".codex") });
|
|
1785
|
+
// A ⚠ line, so the short output keeps it too: until then Trail does nothing in Codex.
|
|
1786
|
+
if (trust)
|
|
1787
|
+
say(`⚠ ${trust}`);
|
|
1788
|
+
}
|
|
1738
1789
|
// Only worth saying when there was actually something out-of-repo to skip.
|
|
1739
1790
|
if (opts.global === false && selectedWrites(plan, ids).some((w) => w.scope === "global"))
|
|
1740
1791
|
say("· skipped out-of-repo writes (--no-global)");
|
|
@@ -1764,7 +1815,7 @@ function wireTarget(repo, ids, ctx) {
|
|
|
1764
1815
|
function formatRetractions(rs, apply) {
|
|
1765
1816
|
const hit = changed(rs);
|
|
1766
1817
|
if (hit.length === 0)
|
|
1767
|
-
return "· nothing to remove
|
|
1818
|
+
return "· nothing to remove: no graft wiring found here";
|
|
1768
1819
|
const verb = apply ? "removed" : "would remove";
|
|
1769
1820
|
const lines = [];
|
|
1770
1821
|
const byHost = new Map();
|
|
@@ -1782,9 +1833,9 @@ function formatRetractions(rs, apply) {
|
|
|
1782
1833
|
? "-"
|
|
1783
1834
|
: "~";
|
|
1784
1835
|
const note = r.action === "skipped-unparseable"
|
|
1785
|
-
? "
|
|
1836
|
+
? ": not valid JSON, left untouched (remove the graft entry by hand)"
|
|
1786
1837
|
: r.action === "deleted"
|
|
1787
|
-
? ` (${r.what}
|
|
1838
|
+
? ` (${r.what}, deleted)`
|
|
1788
1839
|
: ` (${r.what})`;
|
|
1789
1840
|
const scope = r.scope === "global" ? " [machine-wide]" : "";
|
|
1790
1841
|
lines.push(` ${mark} ${verb}: ${r.path}${scope}${note}`);
|
|
@@ -1796,7 +1847,7 @@ grouped(program.command("uninstall"), GROUP.setup)
|
|
|
1796
1847
|
.description(`Remove every file and config entry ${TRAIL ? "trail or graft" : "graft"} has written to this repo (the inverse of init)`)
|
|
1797
1848
|
.argument("[dir]", "target repo directory", ".")
|
|
1798
1849
|
.option("-y, --yes", "actually remove (without this, prints what it would remove and exits)")
|
|
1799
|
-
.option("--keep-cache", "keep graft/ and the .gitignore entries
|
|
1850
|
+
.option("--keep-cache", "keep graft/ and the .gitignore entries: wiring only")
|
|
1800
1851
|
.option("--no-global", "leave out-of-repo files alone (~/.codex, ~/.gemini)")
|
|
1801
1852
|
.action((dir, opts) => {
|
|
1802
1853
|
const repo = resolve(dir);
|
|
@@ -1804,7 +1855,7 @@ grouped(program.command("uninstall"), GROUP.setup)
|
|
|
1804
1855
|
const common = { home, global: opts.global, cache: opts.keepCache ? false : true };
|
|
1805
1856
|
if (!opts.yes) {
|
|
1806
1857
|
console.error(formatRetractions(planRetract(repo, common), false));
|
|
1807
|
-
console.error("\nDry run
|
|
1858
|
+
console.error("\nDry run: nothing was touched. Re-run with -y to remove.");
|
|
1808
1859
|
if (opts.global !== false)
|
|
1809
1860
|
console.error("Entries marked [machine-wide] affect every project; --no-global skips them.");
|
|
1810
1861
|
return;
|
|
@@ -1813,7 +1864,7 @@ grouped(program.command("uninstall"), GROUP.setup)
|
|
|
1813
1864
|
console.error(formatRetractions(done, true));
|
|
1814
1865
|
const bad = changed(done).filter((r) => r.action === "skipped-unparseable");
|
|
1815
1866
|
console.error(bad.length
|
|
1816
|
-
? `\n⚠ ${bad.length} file(s) could not be parsed and were left as-is
|
|
1867
|
+
? `\n⚠ ${bad.length} file(s) could not be parsed and were left as-is: see above.`
|
|
1817
1868
|
: `\n✓ ${brand()} fully removed. \`${cmd("graft init")}\` re-wires from scratch.`);
|
|
1818
1869
|
});
|
|
1819
1870
|
/* -------------------------------------------------------------------------- */
|
|
@@ -1823,23 +1874,22 @@ grouped(program.command("uninstall"), GROUP.setup)
|
|
|
1823
1874
|
* `trail login`: connect this machine to a Trail workspace by hand. Normally
|
|
1824
1875
|
* nobody runs it: Claude offers the same link once there's a learning to
|
|
1825
1876
|
* share. Prints the link, opens it, and waits for the person to sign in and
|
|
1826
|
-
*
|
|
1877
|
+
* add the repo they're in, then uploads its committed learnings. Other repos
|
|
1878
|
+
* are added one at a time afterwards (`trail workspace add`).
|
|
1827
1879
|
*/
|
|
1828
1880
|
function loginCommand(name = "login") {
|
|
1829
1881
|
return new Command(name)
|
|
1830
|
-
.description("Connect this machine to a Trail workspace: sign in,
|
|
1831
|
-
.argument("[dir]", "the repo
|
|
1832
|
-
.option("--server <url>", "a self-hosted Trail, e.g. https://trail.acme.internal (default: Trail cloud)")
|
|
1882
|
+
.description("Connect this machine to a Trail workspace: sign in, add this repo, invite your team")
|
|
1883
|
+
.argument("[dir]", "the repo to add", ".")
|
|
1884
|
+
.option("--server <url>", "a self-hosted Trail, e.g. https://trail.acme.internal (default: Trail cloud, https://team.trailhq.com)")
|
|
1833
1885
|
.action(async (dir, opts) => {
|
|
1834
1886
|
const repo = resolve(dir);
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
}
|
|
1842
|
-
process.env.TRAIL_URL = server;
|
|
1887
|
+
// Saved on the link, so its sign-in and every upload after go there; nothing in the environment.
|
|
1888
|
+
const server = opts.server === undefined ? undefined : serverUrl(opts.server);
|
|
1889
|
+
if (server === null) {
|
|
1890
|
+
console.error(`✗ --server takes a URL, like https://trail.acme.internal`);
|
|
1891
|
+
process.exitCode = 1;
|
|
1892
|
+
return;
|
|
1843
1893
|
}
|
|
1844
1894
|
const cloud = readCloud();
|
|
1845
1895
|
if (cloud) {
|
|
@@ -1847,8 +1897,8 @@ function loginCommand(name = "login") {
|
|
|
1847
1897
|
return;
|
|
1848
1898
|
}
|
|
1849
1899
|
rememberRepo(repo);
|
|
1850
|
-
const url = loginLink(repo);
|
|
1851
|
-
console.error(`· sign in and
|
|
1900
|
+
const url = loginLink(repo, process.env, Date.now(), server);
|
|
1901
|
+
console.error(`· sign in and add this repo to your workspace:\n ${url}`);
|
|
1852
1902
|
if (process.stdout.isTTY)
|
|
1853
1903
|
openBrowser(url);
|
|
1854
1904
|
console.error("· waiting for you to finish in the browser (ctrl-c stops; Claude picks it up later too)");
|
|
@@ -1869,6 +1919,7 @@ function logoutCommand(name = "logout") {
|
|
|
1869
1919
|
.action(() => {
|
|
1870
1920
|
const cloud = readCloud();
|
|
1871
1921
|
clearPending();
|
|
1922
|
+
removeBrainsServer(homedir());
|
|
1872
1923
|
if (!clearCloud()) {
|
|
1873
1924
|
console.error("· not connected to a workspace");
|
|
1874
1925
|
return;
|
|
@@ -1918,6 +1969,8 @@ function workspaceCommand(name = "workspace") {
|
|
|
1918
1969
|
if (here.git && !me.repos.some((r) => r.key === here.key)) {
|
|
1919
1970
|
console.log(` · ${here.name.padEnd(width)}not in the workspace · ${cmd("graft workspace add")} shares its learnings`);
|
|
1920
1971
|
}
|
|
1972
|
+
else if (here.git)
|
|
1973
|
+
printLines(leftOutLines(here.checkout));
|
|
1921
1974
|
});
|
|
1922
1975
|
ws.command("add")
|
|
1923
1976
|
.description("Add this repo to your workspace and share its learnings")
|
|
@@ -1951,18 +2004,33 @@ function workspaceCommand(name = "workspace") {
|
|
|
1951
2004
|
return;
|
|
1952
2005
|
}
|
|
1953
2006
|
console.error(`✓ added ${here.name} to ${cloud.workspace.name} · shared ${res.learnings} learning${res.learnings === 1 ? "" : "s"}`);
|
|
2007
|
+
for (const l of leftOutLines(here.checkout))
|
|
2008
|
+
console.error(l);
|
|
1954
2009
|
console.error(`· ${cloud.workspace.url}`);
|
|
1955
2010
|
});
|
|
1956
2011
|
return ws;
|
|
1957
2012
|
}
|
|
2013
|
+
/**
|
|
2014
|
+
* One line per learning of this repo's that the workspace doesn't get because
|
|
2015
|
+
* it looks like it holds a secret: named, with what was seen, never the
|
|
2016
|
+
* secret. Nothing when there are none or git can't read the repo.
|
|
2017
|
+
*/
|
|
2018
|
+
function leftOutLines(checkout) {
|
|
2019
|
+
const out = workspaceLearnings(checkout)?.leftOut ?? [];
|
|
2020
|
+
return out.map((l) => ` · left out: ${flatLine(l.name, 200)}${l.ref ? ` (on ${flatLine(l.ref, 120)})` : ""}: looks like it holds a secret (${l.why}). Take the secret out and commit, and it goes up with the next sync`);
|
|
2021
|
+
}
|
|
1958
2022
|
/* -------------------------------------------------------------------------- */
|
|
1959
2023
|
/* trail team · trail check */
|
|
1960
2024
|
/* -------------------------------------------------------------------------- */
|
|
1961
|
-
/**
|
|
2025
|
+
/**
|
|
2026
|
+
* Fold a long line at `width`, indenting what wraps. The text holds a
|
|
2027
|
+
* learning's or a teammate's words, and any word can start a line, so it's
|
|
2028
|
+
* one line first and no word starts with a `[trail]`-style marker (flatten.ts).
|
|
2029
|
+
*/
|
|
1962
2030
|
function wrap(text, width, indent) {
|
|
1963
2031
|
const out = [];
|
|
1964
2032
|
let line = "";
|
|
1965
|
-
for (const word of text
|
|
2033
|
+
for (const word of unmarkedWords(text)) {
|
|
1966
2034
|
if (line && line.length + 1 + word.length > width) {
|
|
1967
2035
|
out.push(line);
|
|
1968
2036
|
line = indent + word;
|
|
@@ -2085,7 +2153,7 @@ function localTeam(repo, days, json) {
|
|
|
2085
2153
|
}
|
|
2086
2154
|
function teamCheckCommand(name = "check") {
|
|
2087
2155
|
return new Command(name)
|
|
2088
|
-
.description("Check your branch's diff against
|
|
2156
|
+
.description("Check your branch's diff against the learnings that bear on it: this repo's, yours, and the skills")
|
|
2089
2157
|
.argument("[dir]", "repository root", ".")
|
|
2090
2158
|
.option("--base <ref>", "compare against where the branch left this ref (default: origin's default branch)")
|
|
2091
2159
|
.option("--json", "output as JSON")
|
|
@@ -2105,7 +2173,10 @@ function withoutTrailFiles(diff) {
|
|
|
2105
2173
|
* the repo's `.trail/` (teammates' pushed branches included), this person's
|
|
2106
2174
|
* own, and the repo's skills. No sign-in, no server. Exits 1 on a conflict.
|
|
2107
2175
|
*/
|
|
2108
|
-
async function runTeamCheck(
|
|
2176
|
+
async function runTeamCheck(dir, opts) {
|
|
2177
|
+
// From the top of the checkout: git names changed files from there, and a learning's touches have to
|
|
2178
|
+
// line up with them exactly to fail the check. From a folder inside, none ever would.
|
|
2179
|
+
const repo = checkoutRoot(dir) ?? dir;
|
|
2109
2180
|
const d = branchDiff(repo, opts.base);
|
|
2110
2181
|
if (!d) {
|
|
2111
2182
|
console.error("✗ not a git repository");
|
|
@@ -2125,17 +2196,20 @@ async function runTeamCheck(repo, opts) {
|
|
|
2125
2196
|
maybeRefreshBranches(repo);
|
|
2126
2197
|
if (d.truncated)
|
|
2127
2198
|
console.error("⚠ the diff is over 400 KB; only the first 400 KB is checked");
|
|
2128
|
-
|
|
2199
|
+
// Replaced and retired learnings are left out after the swap, so a branch can't retire a learning by editing it.
|
|
2200
|
+
const notes = withBaseLearnings(listNotes(repo), atBase);
|
|
2201
|
+
const r = checkLocally({ diff: withoutTrailFiles(d.diff), files, notes, skills: listSkills(repo), asker: me, committed: committedAtBase(repo, d.from) });
|
|
2129
2202
|
r.findings.unshift(...edits);
|
|
2130
|
-
|
|
2203
|
+
// Only learnings the workspace gets: a personal one's file name carries its title.
|
|
2204
|
+
const personal = new Set(notes.filter((n) => n.scope === "personal").map((n) => n.path));
|
|
2205
|
+
const sources = r.findings.map((f) => f.source).filter((s) => s.path && !personal.has(s.path));
|
|
2131
2206
|
recordActivity({
|
|
2132
2207
|
repo: repoPlace(repo).key,
|
|
2133
2208
|
kind: "check",
|
|
2134
2209
|
asker: me,
|
|
2135
2210
|
session: sessionKey(repo) ?? `${me}-${new Date().toISOString().slice(0, 10)}`,
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
shown_paths: [...new Set(sources.map((s) => s.path.slice(s.path.lastIndexOf("/") + 1)))],
|
|
2211
|
+
file_count: files.length,
|
|
2212
|
+
shown_paths: [...new Set(sources.map((s) => learningFileName(s.path)))],
|
|
2139
2213
|
shown_authors: [...new Set(sources.map((s) => s.author ?? ""))],
|
|
2140
2214
|
at: new Date().toISOString(),
|
|
2141
2215
|
});
|
|
@@ -2156,23 +2230,28 @@ async function runTeamCheck(repo, opts) {
|
|
|
2156
2230
|
function printCheck(r) {
|
|
2157
2231
|
console.log(`checking ${r.files} changed file${r.files === 1 ? "" : "s"} against ${r.notes} learning${r.notes === 1 ? "" : "s"} and ${r.skills} skill${r.skills === 1 ? "" : "s"}\n`);
|
|
2158
2232
|
const conflicts = r.findings.filter((f) => f.verdict === "conflict");
|
|
2159
|
-
|
|
2160
|
-
|
|
2233
|
+
const leads = r.findings.filter((f) => f.verdict === "lead");
|
|
2234
|
+
for (const f of [...conflicts, ...leads, ...r.findings.filter((x) => x.verdict === "follows")]) {
|
|
2235
|
+
const mark = f.verdict === "conflict" ? "✗" : f.verdict === "lead" ? "●" : "✓";
|
|
2161
2236
|
console.log(`${mark} ${f.file}${f.line ? `:${f.line}` : ""}`);
|
|
2162
2237
|
console.log(` ${f.summary}`);
|
|
2238
|
+
if (f.why)
|
|
2239
|
+
console.log(` worth a look, can't fail the check: ${f.why}`);
|
|
2240
|
+
if (f.replacedBy)
|
|
2241
|
+
console.log(` this branch replaces it with ${f.replacedBy.id} ("${f.replacedBy.title}"), which counts once it's on the base branch: until then, say why in the PR`);
|
|
2163
2242
|
const s = f.source;
|
|
2164
|
-
if (f.verdict
|
|
2243
|
+
if (f.verdict !== "follows" && s.quote) {
|
|
2165
2244
|
const who = [s.author, s.date ? shortDate(s.date) : ""].filter(Boolean).join(" · ");
|
|
2166
2245
|
for (const l of wrap(`${who ? `${who}: ` : ""}${s.quote}`, 70, " "))
|
|
2167
2246
|
console.log(` ${l.trimStart()}`);
|
|
2168
2247
|
}
|
|
2169
2248
|
}
|
|
2170
2249
|
if (r.findings.length === 0)
|
|
2171
|
-
console.log("✓
|
|
2250
|
+
console.log("✓ no learning bears on this change");
|
|
2172
2251
|
console.log("");
|
|
2173
2252
|
console.log(conflicts.length
|
|
2174
2253
|
? `· ${conflicts.length} conflict${conflicts.length === 1 ? "" : "s"} · fix ${conflicts.length === 1 ? "it" : "them"}, or say why in the commit message or PR description`
|
|
2175
|
-
:
|
|
2254
|
+
: `· no conflicts${leads.length ? ` · ${leads.length} worth a look` : ""}`);
|
|
2176
2255
|
}
|
|
2177
2256
|
/** Tokens saved by this repo's agent sessions touched in the last 7 days. */
|
|
2178
2257
|
function savedThisWeek(repo, now = Date.now()) {
|
|
@@ -2214,7 +2293,8 @@ function statusCommand(name = "status") {
|
|
|
2214
2293
|
const g = graph ? await engineFrom().checkGraph(repo) : null;
|
|
2215
2294
|
const fresh = g && !g.missing ? g.ok : null;
|
|
2216
2295
|
const kept = keepsNotes(repo);
|
|
2217
|
-
|
|
2296
|
+
// Only what still holds: a retired, held or replaced learning reaches no agent.
|
|
2297
|
+
const all = kept ? liveLearnings(listNotes(repo)) : [];
|
|
2218
2298
|
const inRepo = all.filter((n) => n.scope === "repo" && !n.ref).length;
|
|
2219
2299
|
const onBranches = all.filter((n) => n.ref).length;
|
|
2220
2300
|
const personal = all.filter((n) => n.scope === "personal").length;
|
|
@@ -2239,7 +2319,7 @@ function statusCommand(name = "status") {
|
|
|
2239
2319
|
else
|
|
2240
2320
|
row("code map", `✓ ${fmt(graph.meta.nodeCount)} nodes · in sync with the code`);
|
|
2241
2321
|
if (kept) {
|
|
2242
|
-
const parts = [`${inRepo}
|
|
2322
|
+
const parts = [`${inRepo} added to this repo, in ${LEARNINGS_DIR}/`];
|
|
2243
2323
|
if (onBranches)
|
|
2244
2324
|
parts.push(`${onBranches} on teammates' branches`);
|
|
2245
2325
|
if (personal)
|
|
@@ -2250,9 +2330,15 @@ function statusCommand(name = "status") {
|
|
|
2250
2330
|
if (cloudEnabled())
|
|
2251
2331
|
row("workspace", "not connected · Claude offers a link once there are learnings to share");
|
|
2252
2332
|
}
|
|
2333
|
+
else if (cloud.via === "connect") {
|
|
2334
|
+
row("workspace", `${cloud.workspace.name} · gets a summary of the repos you run ${cmd("graft init")} --connect in · ${cloud.workspace.url}`);
|
|
2335
|
+
}
|
|
2253
2336
|
else
|
|
2254
2337
|
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}`);
|
|
2255
2338
|
row("this week", saved > 0 ? `~${shortCount(saved)} tokens saved` : "nothing saved yet");
|
|
2339
|
+
// Phase 2 (4.17, D137): Codex's trust of Trail's hooks, and why it keeps nothing new there yet.
|
|
2340
|
+
for (const l of codexStatusLines())
|
|
2341
|
+
row("codex", l);
|
|
2256
2342
|
});
|
|
2257
2343
|
}
|
|
2258
2344
|
if (TRAIL) {
|
|
@@ -2263,7 +2349,7 @@ if (TRAIL) {
|
|
|
2263
2349
|
program.addCommand(c.helpGroup(GROUP.cloud), { hidden: !cloudEnabled() });
|
|
2264
2350
|
program.addCommand(statusCommand().helpGroup(GROUP.setup));
|
|
2265
2351
|
// Help lists the groups in this order, and the commands within each.
|
|
2266
|
-
const order = ["ask", "grep", "skeleton", "callers", "map", "note", "check", "team", "
|
|
2352
|
+
const order = ["ask", "grep", "skeleton", "callers", "map", "note", "check", "team", "skills", "init", "build", "status", "upgrade", "uninstall", "telemetry", "workspace", "login", "logout", "blast"];
|
|
2267
2353
|
const rank = (c) => {
|
|
2268
2354
|
const i = order.indexOf(c.name());
|
|
2269
2355
|
return i === -1 ? order.length : i;
|
|
@@ -2277,15 +2363,14 @@ if (TRAIL) {
|
|
|
2277
2363
|
callers: "who calls it, or what it calls with --direction out",
|
|
2278
2364
|
map: "directory clusters, hubs and hotspots",
|
|
2279
2365
|
note: "save what this session worked out, as a learning in .trail/",
|
|
2280
|
-
|
|
2281
|
-
skills: "list skills, show one, fold takeaways in, publish to the repo",
|
|
2366
|
+
skills: "the steps that worked, kept as skills when you say so · on, off, show, forget",
|
|
2282
2367
|
init: "wire trail into your agents",
|
|
2283
2368
|
build: "rebuild the code map · --check fails if it's stale",
|
|
2284
2369
|
status: "code map, learnings and tokens saved",
|
|
2285
2370
|
upgrade: "install the latest trail",
|
|
2286
2371
|
uninstall: "remove everything trail or graft wrote to this repo",
|
|
2287
2372
|
telemetry: "show or turn off the anonymous usage stats",
|
|
2288
|
-
check: "check your diff against
|
|
2373
|
+
check: "check your diff against this repo's learnings and yours",
|
|
2289
2374
|
team: "what the team learned this week, and where work overlaps",
|
|
2290
2375
|
workspace: "your workspace's repos, team page and invite link",
|
|
2291
2376
|
login: "connect a Trail workspace (Claude offers you the link)",
|