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,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path canonicalisation and the allowed-root matcher for the read boundary (Spec §3.1; CKC-03
|
|
3
|
+
* AC-23). A path is compared to the allowed roots only after junctions and symlinks are resolved,
|
|
4
|
+
* so a link that sits at an allowed path but points outside is caught. Windows case, both
|
|
5
|
+
* separators, `~`, and Git Bash / MSYS `/c/...` spellings are all recognised as the same path.
|
|
6
|
+
*/
|
|
7
|
+
import { realpathSync } from 'node:fs';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { dirname, isAbsolute, resolve, sep } from 'node:path';
|
|
11
|
+
|
|
12
|
+
const WIN = process.platform === 'win32';
|
|
13
|
+
|
|
14
|
+
/** Git Bash, MSYS, Cygwin and WSL drive paths → native Windows form: /c/x, /mnt/c/x, /cygdrive/c/x → C:\x. */
|
|
15
|
+
export function fromMsys(path: string): string {
|
|
16
|
+
if (!WIN) return path;
|
|
17
|
+
if (!path.startsWith('/') || path.startsWith('//') || path.includes('\\')) return path;
|
|
18
|
+
const m = /^\/(?:mnt\/|cygdrive\/)?([a-zA-Z])(?:\/(.*))?$/.exec(path);
|
|
19
|
+
if (!m) return path;
|
|
20
|
+
return `${m[1]!.toUpperCase()}:\\${(m[2] ?? '').replaceAll('/', '\\')}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function expandTilde(path: string): string {
|
|
24
|
+
if (path === '~') return homedir();
|
|
25
|
+
if (path.startsWith('~/') || (WIN && path.startsWith('~\\'))) return resolve(homedir(), path.slice(2));
|
|
26
|
+
return path;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* pi's file tools resolve a `path` argument through `resolvePath(path, cwd, { normalizeUnicodeSpaces: true,
|
|
31
|
+
* stripAtPrefix: true })` before opening it. The boundary must resolve the same way, or a spelling pi accepts but the
|
|
32
|
+
* boundary reads differently (a leading `@`, a unicode space) reads one file at the tool and checks another. This
|
|
33
|
+
* mirrors pi's `utils/paths.ts` exactly: normalise unicode spaces, strip a leading `@`, convert a Git Bash / MSYS
|
|
34
|
+
* drive path, expand `~` and `~/` (not `~user`, which pi leaves literal), then a `file://` URL. Not yet real-path'd.
|
|
35
|
+
*/
|
|
36
|
+
const UNICODE_SPACES = /[ - ]/g;
|
|
37
|
+
export function toAbsolute(path: string, cwd: string): string {
|
|
38
|
+
let s = path.replace(UNICODE_SPACES, ' ');
|
|
39
|
+
if (s.startsWith('@')) s = s.slice(1);
|
|
40
|
+
s = fromMsys(s);
|
|
41
|
+
s = expandTilde(s);
|
|
42
|
+
if (/^file:\/\//i.test(s)) {
|
|
43
|
+
try { s = fileURLToPath(s); } catch { /* keep as-is */ }
|
|
44
|
+
}
|
|
45
|
+
return isAbsolute(s) ? resolve(s) : resolve(cwd, s);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Resolve symlinks/junctions on the longest existing ancestor and re-join the missing tail, so a
|
|
50
|
+
* path that does not exist yet (a file about to be written, or a read that will miss) still maps to
|
|
51
|
+
* one canonical location instead of throwing.
|
|
52
|
+
*/
|
|
53
|
+
export function realExisting(absolute: string): string {
|
|
54
|
+
const real = (p: string) => {
|
|
55
|
+
try { return realpathSync.native(p); } catch { return realpathSync(p); }
|
|
56
|
+
};
|
|
57
|
+
let current = absolute;
|
|
58
|
+
const tail: string[] = [];
|
|
59
|
+
for (;;) {
|
|
60
|
+
try {
|
|
61
|
+
const resolved = real(current);
|
|
62
|
+
return tail.length ? resolve(resolved, tail.reverse().join(sep)) : resolved;
|
|
63
|
+
} catch {
|
|
64
|
+
const parent = dirname(current);
|
|
65
|
+
if (parent === current) return absolute; // reached the root; nothing to resolve
|
|
66
|
+
tail.push(current.slice(parent.length + 1));
|
|
67
|
+
current = parent;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Comparison key: absolute, real-path'd, case-folded on Windows. Never shown to the user. */
|
|
73
|
+
export function canonicalKey(path: string, cwd: string): string {
|
|
74
|
+
const real = realExisting(toAbsolute(path, cwd));
|
|
75
|
+
return WIN ? real.toLowerCase() : real;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** What a candidate root is compared against to decide whether it is too broad to open. */
|
|
79
|
+
export interface BreadthContext {
|
|
80
|
+
/** The user's home directory. */
|
|
81
|
+
readonly home: string;
|
|
82
|
+
/** The ProjectKeeper home in use (the directory above `projects/`). */
|
|
83
|
+
readonly projectKeeperHome: string;
|
|
84
|
+
/** The project's own locations. */
|
|
85
|
+
readonly projectLocations: readonly string[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** `ancestor` strictly contains `descendant` (both canonical keys). */
|
|
89
|
+
function containsKey(ancestor: string, descendant: string): boolean {
|
|
90
|
+
return descendant !== ancestor && descendant.startsWith(ancestor.endsWith(sep) ? ancestor : ancestor + sep);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Why a candidate root that does not come from the project itself (a toolchain location, the folder of a skill pi
|
|
95
|
+
* loaded) is too broad to become an allowed read root, or null when it is narrow enough. Compared on canonical keys, so
|
|
96
|
+
* a junction or symlink that points at a broad directory is judged by what it opens. Refused: a filesystem root; the
|
|
97
|
+
* home directory or anything containing it; the ProjectKeeper home, anything containing it, or anything inside it
|
|
98
|
+
* (other projects' assets); anything containing one of the project's own locations.
|
|
99
|
+
*/
|
|
100
|
+
export function broadRootReason(path: string, ctx: BreadthContext): string | null {
|
|
101
|
+
const key = canonicalKey(path, path);
|
|
102
|
+
if (dirname(key) === key) return 'not used: too broad (a filesystem root)';
|
|
103
|
+
const home = canonicalKey(ctx.home, ctx.home);
|
|
104
|
+
if (key === home) return 'not used: too broad (the whole home directory)';
|
|
105
|
+
if (containsKey(key, home)) return 'not used: too broad (it contains the home directory)';
|
|
106
|
+
const pk = canonicalKey(ctx.projectKeeperHome, ctx.projectKeeperHome);
|
|
107
|
+
if (key === pk) return 'not used: too broad (the whole ProjectKeeper home)';
|
|
108
|
+
if (containsKey(key, pk)) return 'not used: too broad (it contains the ProjectKeeper home)';
|
|
109
|
+
if (containsKey(pk, key)) return "not used: inside the ProjectKeeper home (other projects' assets)";
|
|
110
|
+
for (const location of ctx.projectLocations) {
|
|
111
|
+
if (containsKey(key, canonicalKey(location, location))) return 'not used: too broad (it contains the project directory)';
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface AllowedRoot {
|
|
117
|
+
readonly path: string;
|
|
118
|
+
/** Short label for the refusal message ("the project directory", "the toolchain the build config points at"). */
|
|
119
|
+
readonly label: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface BoundaryDecision {
|
|
123
|
+
readonly ok: boolean;
|
|
124
|
+
readonly reason: string | null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface Boundary {
|
|
128
|
+
/** Whether reading `path` (resolved against `cwd`) is inside the boundary. */
|
|
129
|
+
decide(path: string, cwd: string): BoundaryDecision;
|
|
130
|
+
/** One-line description of what is allowed, for refusal messages and reports. */
|
|
131
|
+
describe(): string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface BoundaryInput {
|
|
135
|
+
readonly roots: readonly AllowedRoot[];
|
|
136
|
+
/** Individual files that are allowed even though their directory is not a root (session logs, single resources). */
|
|
137
|
+
readonly files: readonly string[];
|
|
138
|
+
/** Given a canonical key, a non-null string means "refuse and say this" (credential stores). */
|
|
139
|
+
readonly denyFile?: (canonical: string) => string | null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const HOW_TO_STAY_IN_BOUNDS =
|
|
143
|
+
'Read only this project: its own scope, its ProjectKeeper assets, the toolchain its config points at, and the session files that belong to it. '
|
|
144
|
+
+ 'To find which of your own records cite a source or an id, use pk_find_references / pk_read_assets — do not look in the file system. '
|
|
145
|
+
+ 'Do not read other projects, other ProjectKeeper assets (including the one the owner is using), or credential stores.';
|
|
146
|
+
|
|
147
|
+
export function makeBoundary(input: BoundaryInput): Boundary {
|
|
148
|
+
const rootKeys = input.roots.map((r) => ({ key: canonicalKey(r.path, r.path), label: r.label }));
|
|
149
|
+
const fileKeys = new Set(input.files.map((f) => canonicalKey(f, f)));
|
|
150
|
+
const within = (key: string, root: string) => key === root || key.startsWith(root.endsWith(sep) ? root : root + sep);
|
|
151
|
+
return {
|
|
152
|
+
decide(path, cwd) {
|
|
153
|
+
const key = canonicalKey(path, cwd);
|
|
154
|
+
const denied = input.denyFile?.(key);
|
|
155
|
+
if (denied) return { ok: false, reason: denied };
|
|
156
|
+
if (fileKeys.has(key)) return { ok: true, reason: null };
|
|
157
|
+
if (rootKeys.some((r) => within(key, r.key))) return { ok: true, reason: null };
|
|
158
|
+
return { ok: false, reason: `Out of the project's read boundary: ${path}. ${HOW_TO_STAY_IN_BOUNDS}` };
|
|
159
|
+
},
|
|
160
|
+
describe() {
|
|
161
|
+
return input.roots.map((r) => `${r.label} (${r.path})`).join('; ');
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|