projectkeeper 0.0.0-stage → 1.0.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/CHANGELOG.md +56 -0
- package/LICENSE +21 -0
- package/README.md +283 -2
- package/README.zh-CN.md +285 -0
- package/THIRD_PARTY_NOTICES.md +53 -0
- package/bin/pk.mjs +6 -0
- package/bin/postinstall.mjs +5 -0
- package/bin/strip-types.mjs +28 -0
- package/package.json +68 -4
- package/patches/README.md +111 -0
- package/patches/pi-ai-0.85.0.ts +122 -0
- package/patches/pi-ai-0.87.1.ts +122 -0
- package/scripts/apply-pi-streaming-patch.ts +86 -0
- package/skills/clerk/coverage/SKILL.md +35 -0
- package/skills/clerk/cross-check/SKILL.md +118 -0
- package/skills/clerk/deepen-dispatch/SKILL.md +69 -0
- package/skills/clerk/lane-follow-up/SKILL.md +94 -0
- package/skills/clerk/lane-plan/SKILL.md +107 -0
- package/skills/clerk/lane-slot/SKILL.md +113 -0
- package/skills/clerk/lane-topic/SKILL.md +116 -0
- package/skills/clerk/orientation/SKILL.md +160 -0
- package/skills/clerk/reconcile/SKILL.md +77 -0
- package/skills/clerk/skeleton-dispatch/SKILL.md +43 -0
- package/skills/clerk/spot-check/SKILL.md +52 -0
- package/skills/clerk/synthesis/SKILL.md +65 -0
- package/skills/table-follow-up/SKILL.md +42 -0
- package/src/cli.ts +319 -0
- package/src/codemap/README.md +13 -0
- package/src/codemap/engines.ts +102 -0
- package/src/codemap/facts.ts +504 -0
- package/src/context/agent-entry.ts +232 -0
- package/src/context/ask.ts +97 -0
- package/src/context/assemble.ts +1058 -0
- package/src/context/code-source.ts +103 -0
- package/src/context/k-briefs.ts +114 -0
- package/src/context/lineage.ts +100 -0
- package/src/context/object-brief.ts +300 -0
- package/src/context/owner-words.ts +191 -0
- package/src/context/pack-fixture.ts +342 -0
- package/src/context/project-rules.ts +160 -0
- package/src/intake/intake.ts +392 -0
- package/src/intake/material-rules.ts +166 -0
- package/src/intake/removal.ts +316 -0
- package/src/intake/skipped.ts +72 -0
- package/src/keeper/adjustment.ts +565 -0
- package/src/keeper/bounds/boundary.ts +509 -0
- package/src/keeper/bounds/command.ts +1665 -0
- package/src/keeper/bounds/paths.ts +164 -0
- package/src/keeper/bounds/reads.ts +718 -0
- package/src/keeper/bounds/shell-write-guard.ts +521 -0
- package/src/keeper/capabilities.ts +136 -0
- package/src/keeper/clerk-steps.ts +151 -0
- package/src/keeper/clerk-tools.ts +1424 -0
- package/src/keeper/conversation.ts +253 -0
- package/src/keeper/evidence.ts +662 -0
- package/src/keeper/fake-provider.ts +132 -0
- package/src/keeper/held.ts +15 -0
- package/src/keeper/history-tools.ts +163 -0
- package/src/keeper/jobs/answering.ts +53 -0
- package/src/keeper/key-check.ts +105 -0
- package/src/keeper/ledger-tools.ts +295 -0
- package/src/keeper/link-check.ts +185 -0
- package/src/keeper/merge.ts +260 -0
- package/src/keeper/note-origin.ts +56 -0
- package/src/keeper/numbers-check.ts +102 -0
- package/src/keeper/open-in-pi.ts +57 -0
- package/src/keeper/organize/absence.ts +266 -0
- package/src/keeper/organize/carried-on.ts +214 -0
- package/src/keeper/organize/clerk-coverage.ts +293 -0
- package/src/keeper/organize/clerk-prompts.ts +132 -0
- package/src/keeper/organize/clerk.ts +1395 -0
- package/src/keeper/organize/coverage-gate.ts +19 -0
- package/src/keeper/organize/coverage-tools.ts +765 -0
- package/src/keeper/organize/entries.ts +153 -0
- package/src/keeper/organize/entry-gate.ts +289 -0
- package/src/keeper/organize/fill-tools.ts +718 -0
- package/src/keeper/organize/follow-up.ts +280 -0
- package/src/keeper/organize/generation-check.ts +504 -0
- package/src/keeper/organize/graph.ts +230 -0
- package/src/keeper/organize/lane-tools.ts +289 -0
- package/src/keeper/organize/looked-tools.ts +54 -0
- package/src/keeper/organize/materials.ts +243 -0
- package/src/keeper/organize/owner-lines.ts +493 -0
- package/src/keeper/organize/place-tools.ts +216 -0
- package/src/keeper/organize/placement-inference.ts +1045 -0
- package/src/keeper/organize/placement-time.ts +320 -0
- package/src/keeper/organize/placing.ts +821 -0
- package/src/keeper/organize/positioning.ts +33 -0
- package/src/keeper/organize/readiness.ts +47 -0
- package/src/keeper/organize/reading.ts +401 -0
- package/src/keeper/organize/round-blocks.ts +65 -0
- package/src/keeper/organize/round-news.ts +164 -0
- package/src/keeper/organize/round-open.ts +586 -0
- package/src/keeper/organize/schedule-state.ts +39 -0
- package/src/keeper/organize/schedule.ts +137 -0
- package/src/keeper/organize/service.ts +409 -0
- package/src/keeper/organize/skills.ts +116 -0
- package/src/keeper/organize/slots.ts +85 -0
- package/src/keeper/organize/stage-gate.ts +175 -0
- package/src/keeper/organize/stage-tools.ts +471 -0
- package/src/keeper/organize/standing-notes.ts +141 -0
- package/src/keeper/organize/table-follow-up.ts +330 -0
- package/src/keeper/organize/takeover-clerk.ts +832 -0
- package/src/keeper/organize/takeover-state.ts +103 -0
- package/src/keeper/organize/takeover.ts +216 -0
- package/src/keeper/organize/update-pending.ts +535 -0
- package/src/keeper/organize/workbench-placement.ts +92 -0
- package/src/keeper/own-keys.ts +114 -0
- package/src/keeper/owner-text.ts +165 -0
- package/src/keeper/project-folder.ts +334 -0
- package/src/keeper/prompts.ts +101 -0
- package/src/keeper/references.ts +177 -0
- package/src/keeper/relook/compose.ts +120 -0
- package/src/keeper/relook/prompts.ts +36 -0
- package/src/keeper/roles.ts +259 -0
- package/src/keeper/round-tools.ts +227 -0
- package/src/keeper/route.ts +148 -0
- package/src/keeper/rules.ts +97 -0
- package/src/keeper/runtime.ts +1858 -0
- package/src/keeper/scope-tools.ts +194 -0
- package/src/keeper/sendback-text.ts +83 -0
- package/src/keeper/step-timing.ts +207 -0
- package/src/keeper/testing/clerk-script.ts +212 -0
- package/src/keeper/testing/streaming-arguments.ts +57 -0
- package/src/keeper/tools.ts +2340 -0
- package/src/keeper/utterance-tools.ts +64 -0
- package/src/keeper/verbatim.ts +397 -0
- package/src/ledger/adapters.ts +186 -0
- package/src/ledger/arrangements.ts +257 -0
- package/src/ledger/blame.ts +58 -0
- package/src/ledger/code-engine-child.ts +69 -0
- package/src/ledger/code-engine.ts +548 -0
- package/src/ledger/code.ts +607 -0
- package/src/ledger/docs.ts +294 -0
- package/src/ledger/git-read.ts +321 -0
- package/src/ledger/index.ts +1850 -0
- package/src/ledger/item-range.ts +16 -0
- package/src/ledger/lines.ts +328 -0
- package/src/ledger/numbering.ts +269 -0
- package/src/ledger/ranges.ts +74 -0
- package/src/ledger/rebuild-worker.ts +15 -0
- package/src/ledger/rebuild.ts +387 -0
- package/src/ledger/repo-scan.ts +181 -0
- package/src/ledger/schema.ts +450 -0
- package/src/ledger/sessions.ts +134 -0
- package/src/ledger/text-scan.ts +466 -0
- package/src/ledger/time.ts +185 -0
- package/src/ledger/ts-symbols.ts +180 -0
- package/src/ledger/views.ts +377 -0
- package/src/mcp/server.ts +127 -0
- package/src/mcp/tools.ts +187 -0
- package/src/model/ids.ts +23 -0
- package/src/model/k-types.ts +912 -0
- package/src/model/time.ts +85 -0
- package/src/model/types.ts +1382 -0
- package/src/model/views-k.ts +707 -0
- package/src/model/vocab.ts +269 -0
- package/src/process/analysis.ts +79 -0
- package/src/process/breakpoint-candidates.ts +383 -0
- package/src/process/breakpoints.ts +746 -0
- package/src/process/claim-check.ts +295 -0
- package/src/process/delivery-links.ts +52 -0
- package/src/process/facts.ts +487 -0
- package/src/process/generations.ts +138 -0
- package/src/process/index.ts +243 -0
- package/src/process/integration.ts +128 -0
- package/src/process/placement.ts +107 -0
- package/src/process/plan.ts +276 -0
- package/src/process/sendbacks.ts +244 -0
- package/src/process/text.ts +185 -0
- package/src/process/units.ts +543 -0
- package/src/process/unnumbered.ts +431 -0
- package/src/process/work.ts +884 -0
- package/src/scope/candidates.ts +86 -0
- package/src/scope/decisions.ts +229 -0
- package/src/scope/discover.ts +741 -0
- package/src/scope/ignore.ts +126 -0
- package/src/scope/ignored-place.ts +93 -0
- package/src/scope/skip.ts +161 -0
- package/src/scope/toolchain.ts +180 -0
- package/src/scope/worktree.ts +187 -0
- package/src/server/api.ts +549 -0
- package/src/server/app.ts +595 -0
- package/src/server/drafts-api.ts +170 -0
- package/src/server/folders-api.ts +243 -0
- package/src/server/graph-view.ts +639 -0
- package/src/server/http.ts +224 -0
- package/src/server/k-agent-api.ts +56 -0
- package/src/server/k-rounds-d99.ts +91 -0
- package/src/server/k-views.ts +405 -0
- package/src/server/keeper-page.ts +446 -0
- package/src/server/keys-api.ts +239 -0
- package/src/server/project-folder-api.ts +104 -0
- package/src/server/vendor.ts +14 -0
- package/src/server/workbench-content.ts +561 -0
- package/src/sources/anchor.ts +129 -0
- package/src/sources/files.ts +267 -0
- package/src/sources/gitobs.ts +41 -0
- package/src/sources/history.ts +231 -0
- package/src/sources/scan-worker.ts +10 -0
- package/src/sources/sessions/locate.ts +292 -0
- package/src/sources/sessions/read.ts +346 -0
- package/src/sources/sessions/scope.ts +42 -0
- package/src/sources/sessions/utterances.ts +223 -0
- package/src/sources/watch.ts +215 -0
- package/src/store/json-file.ts +50 -0
- package/src/store/paths.ts +39 -0
- package/src/store/project-store.ts +295 -0
- package/src/store/versions.ts +172 -0
- package/src/store/workspace.ts +216 -0
- package/src/table/check.ts +333 -0
- package/src/table/follow-up-tools.ts +30 -0
- package/src/table/follow-up.ts +344 -0
- package/src/table/import.ts +610 -0
- package/src/table/project-home.ts +61 -0
- package/src/table/sessions.ts +168 -0
- package/src/table/takeover.ts +483 -0
- package/src/util/git-check.ts +29 -0
- package/src/util/git.ts +411 -0
- package/src/util/own-ts.ts +15 -0
- package/src/util/paths.ts +225 -0
- package/template/FILLING-IN.md +148 -0
- package/template/examples/papertrail.json +747 -0
- package/template/projectkeeper.schema.json +1001 -0
- package/ui/app.js +1135 -0
- package/ui/failures.js +82 -0
- package/ui/flyout.js +120 -0
- package/ui/folder-chooser.js +264 -0
- package/ui/glossary.js +156 -0
- package/ui/graph-fit.js +157 -0
- package/ui/graph-material.js +61 -0
- package/ui/graph-palette.js +109 -0
- package/ui/graph-tools.js +62 -0
- package/ui/graph.js +1712 -0
- package/ui/index.html +31 -0
- package/ui/k/code-view.js +230 -0
- package/ui/k/draft-view.js +93 -0
- package/ui/k/k-views.css +224 -0
- package/ui/k/round-tree.js +450 -0
- package/ui/k/scope-k.js +159 -0
- package/ui/k-data.js +118 -0
- package/ui/k-fold.js +106 -0
- package/ui/k-process.css +302 -0
- package/ui/k-process.js +934 -0
- package/ui/keeper-activity.js +175 -0
- package/ui/keeper-view.js +801 -0
- package/ui/keeper.css +101 -0
- package/ui/key-order.js +62 -0
- package/ui/list-fold.js +67 -0
- package/ui/markdown.d.ts +21 -0
- package/ui/markdown.js +472 -0
- package/ui/placement.d.ts +92 -0
- package/ui/placement.js +445 -0
- package/ui/popover-place.d.ts +16 -0
- package/ui/popover-place.js +89 -0
- package/ui/popover.js +198 -0
- package/ui/project-folder.js +37 -0
- package/ui/shell-state.js +40 -0
- package/ui/strip-rows.js +171 -0
- package/ui/styles.css +492 -0
- package/ui/theme-pick.js +36 -0
- package/ui/theme.js +74 -0
- package/ui/themes/LICENSE.md +13 -0
- package/ui/themes/README.md +44 -0
- package/ui/themes/TOKENS.md +291 -0
- package/ui/themes/a1/board.css +1557 -0
- package/ui/themes/a1/pk.css +392 -0
- package/ui/themes/b1/board.css +1163 -0
- package/ui/themes/b1/pk.css +218 -0
- package/ui/themes/d2/board.css +518 -0
- package/ui/themes/d2/pk.css +243 -0
- package/ui/themes/guard.css +73 -0
- package/ui/themes/i1/board.css +776 -0
- package/ui/themes/i1/pk.css +223 -0
- package/ui/themes/manifest.json +13 -0
- package/ui/views.js +2796 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A worktree registered under a repository, measured against that repository's trunk from git's own records (Spec
|
|
3
|
+
* §1.1; CKC-04 AC-15, AC-16; owner 2026-09-21, subagent/DECISIONS.md E60), in the owner's order:
|
|
4
|
+
*
|
|
5
|
+
* 1. is its branch completely merged into the trunk (no commit the trunk does not have);
|
|
6
|
+
* 2. files the same as the trunk are skipped;
|
|
7
|
+
* 3. older versions — files that differ only because the trunk moved on — are left to the version history;
|
|
8
|
+
* 4. only uncommitted changes and what the trunk does not have are taken.
|
|
9
|
+
*
|
|
10
|
+
* Whether it is merged, and whether a file is the same, are counted from the records, never judged by reading files.
|
|
11
|
+
* The trunk is the branch checked out in the repository's main directory (its commit when detached): what the main
|
|
12
|
+
* project is read as. Read-only throughout; `hash-object` runs without `-w`.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, statSync } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { relativeDisplay } from '../util/paths.ts';
|
|
17
|
+
import type { ScopeItem, Source, WorktreeSummary, WorktreeTaken } from '../model/types.ts';
|
|
18
|
+
import { git } from '../util/git.ts';
|
|
19
|
+
import { skippedSegment } from './skip.ts';
|
|
20
|
+
|
|
21
|
+
const LONG = 60_000;
|
|
22
|
+
|
|
23
|
+
export function trunkOf(repo: string): { ref: string; commit: string } | null {
|
|
24
|
+
const commit = git(repo, ['rev-parse', '--verify', '-q', 'HEAD^{commit}']).out.trim();
|
|
25
|
+
if (!/^[0-9a-f]{40}$/.test(commit)) return null;
|
|
26
|
+
const branch = git(repo, ['symbolic-ref', '-q', '--short', 'HEAD']);
|
|
27
|
+
return { ref: branch.ok && branch.out.trim() ? branch.out.trim() : commit.slice(0, 12), commit };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const zsplit = (out: string) => out.split('\0').filter(Boolean);
|
|
31
|
+
|
|
32
|
+
/** `git diff --name-status -z` between two commits: path → status letter. */
|
|
33
|
+
function changedBetween(cwd: string, from: string, to: string): Map<string, string> | null {
|
|
34
|
+
const r = git(cwd, ['diff', '--name-status', '--no-renames', '-z', from, to], LONG);
|
|
35
|
+
if (!r.ok) return null;
|
|
36
|
+
const parts = zsplit(r.out);
|
|
37
|
+
const out = new Map<string, string>();
|
|
38
|
+
for (let i = 0; i + 1 < parts.length; i += 2) out.set(parts[i + 1]!, parts[i]!.charAt(0));
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Uncommitted changes (staged, unstaged, untracked; not ignored): path → untracked or not. */
|
|
43
|
+
function uncommitted(cwd: string): Map<string, { untracked: boolean }> | null {
|
|
44
|
+
const r = git(cwd, ['status', '--porcelain=v1', '-z', '--untracked-files=all', '--no-renames'], LONG);
|
|
45
|
+
if (!r.ok) return null;
|
|
46
|
+
const out = new Map<string, { untracked: boolean }>();
|
|
47
|
+
for (const rec of zsplit(r.out)) {
|
|
48
|
+
if (rec.length < 4) continue;
|
|
49
|
+
const xy = rec.slice(0, 2);
|
|
50
|
+
if (xy === '!!') continue;
|
|
51
|
+
out.set(rec.slice(3), { untracked: xy === '??' });
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function batches<T>(items: readonly T[], size = 150): T[][] {
|
|
57
|
+
const out: T[][] = [];
|
|
58
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The trunk's blob at each of these paths, where it has one. */
|
|
63
|
+
function trunkBlobs(cwd: string, trunk: string, paths: readonly string[]): Map<string, string> {
|
|
64
|
+
const out = new Map<string, string>();
|
|
65
|
+
for (const batch of batches(paths)) {
|
|
66
|
+
const tree = git(cwd, ['--literal-pathspecs', 'ls-tree', '-r', '-z', trunk, '--', ...batch], LONG);
|
|
67
|
+
for (const rec of zsplit(tree.out)) {
|
|
68
|
+
const m = /^\d+ blob ([0-9a-f]{40})\t(.+)$/s.exec(rec);
|
|
69
|
+
if (m) out.set(m[2]!, m[1]!);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Of these files (present in the worktree), the ones whose content is the trunk's at the same path. */
|
|
76
|
+
function sameContent(cwd: string, inTrunk: ReadonlyMap<string, string>, paths: readonly string[]): Set<string> {
|
|
77
|
+
const same = new Set<string>();
|
|
78
|
+
for (const batch of batches(paths.filter((p) => inTrunk.has(p)))) {
|
|
79
|
+
const hashed = git(cwd, ['hash-object', '--', ...batch], LONG);
|
|
80
|
+
if (!hashed.ok) continue;
|
|
81
|
+
const shas = hashed.out.split(/\r?\n/).filter(Boolean);
|
|
82
|
+
batch.forEach((p, i) => { if (shas[i] === inTrunk.get(p)) same.add(p); });
|
|
83
|
+
}
|
|
84
|
+
return same;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const isFile = (path: string) => { try { return statSync(path).isFile(); } catch { return false; } };
|
|
88
|
+
|
|
89
|
+
function failed(error: string, trunk: WorktreeSummary['trunk'], branch: string | null = null, head: string | null = null): WorktreeSummary {
|
|
90
|
+
return { trunk, branch, head, merged: null, uniqueCommits: 0, files: 0, sameAsTrunk: 0, olderVersions: 0, taken: [], error };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Measure `worktree` against the trunk of `repo`. Files under directories the skip list never reads (build output,
|
|
95
|
+
* dependencies) are left out of every count, as they are left out of the main project.
|
|
96
|
+
*/
|
|
97
|
+
export function measureWorktree(
|
|
98
|
+
worktree: string, repo: string, trunk: { ref: string; commit: string } | null = trunkOf(repo),
|
|
99
|
+
known: { readonly head: string | null; readonly branch: string | null } | null = null,
|
|
100
|
+
): WorktreeSummary {
|
|
101
|
+
if (!existsSync(worktree)) return failed('the worktree is not on disk', trunk);
|
|
102
|
+
if (!trunk) return failed(`the trunk of ${repo} could not be read`, trunk);
|
|
103
|
+
// `git worktree list` already says where each worktree's HEAD is and on which branch.
|
|
104
|
+
const head = known?.head ?? git(worktree, ['rev-parse', '--verify', '-q', 'HEAD^{commit}']).out.trim();
|
|
105
|
+
const branchR = known ? null : git(worktree, ['symbolic-ref', '-q', '--short', 'HEAD']);
|
|
106
|
+
const branch = known ? known.branch : branchR!.ok && branchR!.out.trim() ? branchR!.out.trim() : null;
|
|
107
|
+
if (!/^[0-9a-f]{40}$/.test(head)) return failed('its HEAD could not be read', trunk, branch);
|
|
108
|
+
const atTrunk = head === trunk.commit;
|
|
109
|
+
const count = atTrunk ? { ok: true, out: '0', err: '' } : git(worktree, ['rev-list', '--count', `${trunk.commit}..${head}`], LONG);
|
|
110
|
+
if (!count.ok) return failed(`git could not compare it with ${trunk.ref}: ${count.err.split('\n')[0]}`, trunk, branch, head);
|
|
111
|
+
const uniqueCommits = Number(count.out.trim()) || 0;
|
|
112
|
+
const merged = uniqueCommits === 0;
|
|
113
|
+
// What its branch changed since it left the trunk: nothing when merged.
|
|
114
|
+
const base = merged ? head : git(worktree, ['merge-base', head, trunk.commit], LONG).out.trim();
|
|
115
|
+
const onBranch = merged ? new Map<string, string>() : /^[0-9a-f]{40}$/.test(base) ? changedBetween(worktree, base, head) : null;
|
|
116
|
+
const vsTrunk = atTrunk ? new Map<string, string>() : changedBetween(worktree, trunk.commit, head);
|
|
117
|
+
const dirty = uncommitted(worktree);
|
|
118
|
+
const trackedR = git(worktree, ['ls-files', '-z'], LONG);
|
|
119
|
+
if (!onBranch || !vsTrunk || !dirty || !trackedR.ok) return failed(`git could not list its changes against ${trunk.ref}`, trunk, branch, head);
|
|
120
|
+
|
|
121
|
+
const counted = (p: string) => skippedSegment(p) === null;
|
|
122
|
+
const tracked = zsplit(trackedR.out).filter(counted);
|
|
123
|
+
const trackedSet = new Set(tracked);
|
|
124
|
+
const untracked = [...dirty].filter(([p, s]) => s.untracked && !trackedSet.has(p)).map(([p]) => p).filter(counted);
|
|
125
|
+
const candidates = [...new Set([...onBranch.keys(), ...dirty.keys()])].filter(counted).sort();
|
|
126
|
+
const present = new Set(candidates.filter((p) => isFile(join(worktree, p))));
|
|
127
|
+
const inTrunk = trunkBlobs(worktree, trunk.commit, candidates);
|
|
128
|
+
const same = sameContent(worktree, inTrunk, [...present]);
|
|
129
|
+
|
|
130
|
+
const taken: WorktreeTaken[] = [];
|
|
131
|
+
for (const p of candidates) {
|
|
132
|
+
// The same as the trunk: the same content, or gone from both.
|
|
133
|
+
if (present.has(p) ? same.has(p) : !inTrunk.has(p)) continue;
|
|
134
|
+
taken.push({ path: p, kind: dirty.has(p) ? 'Uncommitted change' : 'Changed on branch', ...(present.has(p) ? {} : { deleted: true }) });
|
|
135
|
+
}
|
|
136
|
+
const takenFiles = new Set(taken.filter((t) => !t.deleted).map((t) => t.path));
|
|
137
|
+
const onDisk = [...tracked.filter((p) => isFile(join(worktree, p))), ...untracked];
|
|
138
|
+
let sameCount = 0;
|
|
139
|
+
let older = 0;
|
|
140
|
+
for (const p of onDisk) {
|
|
141
|
+
if (takenFiles.has(p)) continue;
|
|
142
|
+
if (same.has(p) || !vsTrunk.has(p)) sameCount += 1;
|
|
143
|
+
else older += 1; // committed, not changed on its branch, yet not the trunk's: the trunk moved on (or dropped it)
|
|
144
|
+
}
|
|
145
|
+
return { trunk, branch, head, merged, uniqueCommits, files: onDisk.length, sameAsTrunk: sameCount, olderVersions: older, taken, error: null };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface WorktreeOrigin {
|
|
149
|
+
readonly worktree: string;
|
|
150
|
+
readonly branch: string | null;
|
|
151
|
+
readonly merged: boolean | null;
|
|
152
|
+
readonly kind: string;
|
|
153
|
+
readonly label: string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Whose a source is when it came from a worktree (CKC-04 AC-16): which worktree, which branch, and that it is work in
|
|
158
|
+
* progress, not the trunk's current state — so the round's main job can put it with the work it belongs to. Null for
|
|
159
|
+
* anything else.
|
|
160
|
+
*/
|
|
161
|
+
export function worktreeOrigin(scope: readonly ScopeItem[], source: Source): WorktreeOrigin | null {
|
|
162
|
+
const item = scope.find((i) => i.id === source.scopeItemId);
|
|
163
|
+
if (!item || item.category !== 'Worktree' || !item.worktree) return null;
|
|
164
|
+
const w = item.worktree;
|
|
165
|
+
const trunk = w.trunk?.ref ?? 'the trunk';
|
|
166
|
+
const branch = w.branch ?? 'detached';
|
|
167
|
+
const a = source.anchor;
|
|
168
|
+
const kind = a.kind === 'commit' ? 'Commit the trunk does not have'
|
|
169
|
+
: a.kind === 'file' ? w.taken.find((t) => t.path === relativeDisplay(item.path, a.path))?.kind ?? 'Uncommitted change'
|
|
170
|
+
: 'Worktree status';
|
|
171
|
+
const label = w.merged === false
|
|
172
|
+
? `In progress in worktree ${item.path} (branch ${branch}): ${kind.toLowerCase()}; not the trunk (${trunk}), not its current state`
|
|
173
|
+
: `${kind} in worktree ${item.path} (branch ${branch}, merged into ${trunk}): not the trunk's current state; whether it is current work is a judgement to make`;
|
|
174
|
+
return { worktree: item.path, branch: w.branch, merged: w.merged, kind, label };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** The listing's sentence for a measured worktree (Spec §6.7: merged or not, how many skipped, what was taken). */
|
|
178
|
+
export function worktreeSentence(w: WorktreeSummary): string {
|
|
179
|
+
if (w.error) return `could not be measured against the trunk: ${w.error}; nothing taken from it`;
|
|
180
|
+
const trunk = w.trunk!.ref;
|
|
181
|
+
const plural = (n: number, word: string) => `${n} ${word}${n === 1 ? '' : 's'}`;
|
|
182
|
+
const state = w.merged ? `merged into ${trunk}` : `not merged into ${trunk}: ${plural(w.uniqueCommits, 'commit')} ${trunk} does not have (work in progress, not ${trunk}'s current state)`;
|
|
183
|
+
const skipped = `${plural(w.files, 'file')}: ${w.sameAsTrunk} the same as ${trunk}, skipped${w.olderVersions ? `; ${w.olderVersions} older ${w.olderVersions === 1 ? 'version' : 'versions'} ${trunk} has moved past, left to the version history` : ''}`;
|
|
184
|
+
if (w.taken.length === 0) return `${state} · ${skipped} · nothing taken`;
|
|
185
|
+
const shown = w.taken.slice(0, 12).map((t) => `${t.path} (${t.deleted ? `${t.kind.toLowerCase()}, removed` : t.kind.toLowerCase()})`).join(', ');
|
|
186
|
+
return `${state} · ${skipped} · took ${w.taken.length}: ${shown}${w.taken.length > 12 ? `, and ${w.taken.length - 12} more` : ''}`;
|
|
187
|
+
}
|