pi-monofold 0.12.3 → 0.12.5
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 +10 -4
- package/index.ts +11 -6
- package/monofold-read-ops.ts +6 -2
- package/package.json +5 -4
- package/path-normalize.ts +2 -2
- package/workspace-path-lookup.ts +45 -0
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ pi install -l git:github.com/eiei114/pi-monofold
|
|
|
52
52
|
Pin a version:
|
|
53
53
|
|
|
54
54
|
```powershell
|
|
55
|
-
pi install git:github.com/eiei114/pi-monofold@v0.12.
|
|
55
|
+
pi install git:github.com/eiei114/pi-monofold@v0.12.5
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
Try without installing:
|
|
@@ -76,7 +76,7 @@ pi install -l npm:pi-monofold
|
|
|
76
76
|
Pin a version:
|
|
77
77
|
|
|
78
78
|
```powershell
|
|
79
|
-
pi install npm:pi-monofold@0.12.
|
|
79
|
+
pi install npm:pi-monofold@0.12.5
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
Try without installing:
|
|
@@ -170,14 +170,19 @@ pi-monofold/
|
|
|
170
170
|
│ ├── focus-session-state.test.ts
|
|
171
171
|
│ ├── focus-skills-auto-load.test.ts
|
|
172
172
|
│ ├── focus-skills.test.ts
|
|
173
|
+
│ ├── changelog-hygiene.test.ts
|
|
174
|
+
│ ├── contributing-docs.test.ts
|
|
173
175
|
│ ├── github-actions-pin.test.ts
|
|
174
176
|
│ ├── monofold-read-caps.test.ts
|
|
175
177
|
│ ├── monofold-read-ops.test.ts
|
|
176
178
|
│ ├── path-normalize.test.ts
|
|
177
179
|
│ ├── readme-package-contents.test.ts
|
|
180
|
+
│ ├── release-docs.test.ts
|
|
178
181
|
│ ├── readme-version-pin.test.ts
|
|
179
182
|
│ ├── runtime-path-overlays.test.ts
|
|
180
|
-
│
|
|
183
|
+
│ ├── tsconfig-publishable-files.test.ts
|
|
184
|
+
│ ├── unknown-path-allows.test.ts
|
|
185
|
+
│ └── workspace-path-lookup.test.ts
|
|
181
186
|
├── CHANGELOG.md
|
|
182
187
|
├── SECURITY.md
|
|
183
188
|
├── file-read-preview.ts
|
|
@@ -195,6 +200,7 @@ pi-monofold/
|
|
|
195
200
|
├── README.md
|
|
196
201
|
├── unknown-path-allows.ts
|
|
197
202
|
├── validation.ts
|
|
203
|
+
├── workspace-path-lookup.ts
|
|
198
204
|
└── tsconfig.json
|
|
199
205
|
```
|
|
200
206
|
|
|
@@ -238,4 +244,4 @@ Report vulnerabilities per [SECURITY.md](./SECURITY.md).
|
|
|
238
244
|
|
|
239
245
|
## License
|
|
240
246
|
|
|
241
|
-
[MIT](LICENSE)
|
|
247
|
+
[MIT](LICENSE)
|
package/index.ts
CHANGED
|
@@ -25,6 +25,12 @@ import {
|
|
|
25
25
|
setActiveFocusPresetId,
|
|
26
26
|
warnZeroTargetMatchesForPreset,
|
|
27
27
|
} from "./focus-preset.js";
|
|
28
|
+
import {
|
|
29
|
+
buildPathLookupWorkspaces,
|
|
30
|
+
findWorkspaceForAbsolutePath,
|
|
31
|
+
isPathInsideWorkspace,
|
|
32
|
+
resolveAbsoluteTargetPath,
|
|
33
|
+
} from "./workspace-path-lookup.js";
|
|
28
34
|
import {
|
|
29
35
|
resolveWriteRouteType,
|
|
30
36
|
type MonofoldRouteType,
|
|
@@ -130,6 +136,7 @@ type LoadedConfig = {
|
|
|
130
136
|
root: string;
|
|
131
137
|
raw: MultiWorkspaceConfig;
|
|
132
138
|
workspaces: ResolvedWorkspace[];
|
|
139
|
+
pathLookupWorkspaces: ResolvedWorkspace[];
|
|
133
140
|
activeRuntime: RuntimeInfo;
|
|
134
141
|
};
|
|
135
142
|
|
|
@@ -278,10 +285,7 @@ function asPathOverlayMap(label: string, value: unknown): RuntimePathOverlayMap
|
|
|
278
285
|
}
|
|
279
286
|
|
|
280
287
|
function isInside(parent: string, child: string): boolean {
|
|
281
|
-
|
|
282
|
-
const normalizedChild = normalizeGuardPath(child);
|
|
283
|
-
const relative = path.relative(normalizedParent, normalizedChild);
|
|
284
|
-
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
288
|
+
return isPathInsideWorkspace(parent, child);
|
|
285
289
|
}
|
|
286
290
|
|
|
287
291
|
function assertWorkspaceInternalRelative(label: string, value: string): void {
|
|
@@ -585,6 +589,7 @@ async function validateConfigObject(cwd: string, configPath: string, parsed: unk
|
|
|
585
589
|
workspaces: workspaces.filter((workspace) => workspace.kind === "workspace"),
|
|
586
590
|
},
|
|
587
591
|
workspaces,
|
|
592
|
+
pathLookupWorkspaces: buildPathLookupWorkspaces(workspaces),
|
|
588
593
|
activeRuntime,
|
|
589
594
|
};
|
|
590
595
|
}
|
|
@@ -1498,8 +1503,8 @@ function classifyPath(targetPath: string): "docs" | "code" | "unknown" {
|
|
|
1498
1503
|
}
|
|
1499
1504
|
|
|
1500
1505
|
function findWorkspaceForPath(loaded: LoadedConfig, targetPath: string): ResolvedWorkspace | undefined {
|
|
1501
|
-
const absolute =
|
|
1502
|
-
return
|
|
1506
|
+
const absolute = resolveAbsoluteTargetPath(loaded.root, targetPath);
|
|
1507
|
+
return findWorkspaceForAbsolutePath(loaded.pathLookupWorkspaces, absolute);
|
|
1503
1508
|
}
|
|
1504
1509
|
|
|
1505
1510
|
async function confirm(ctx: ExtensionContext, title: string, body: string): Promise<boolean> {
|
package/monofold-read-ops.ts
CHANGED
|
@@ -51,7 +51,8 @@ export async function shallowTree(
|
|
|
51
51
|
}
|
|
52
52
|
const entries = await readdir(path.join(root, prefix), { withFileTypes: true });
|
|
53
53
|
const lines: string[] = [];
|
|
54
|
-
for (const entry of entries
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
55
56
|
if (budget && budget.remaining <= 0) {
|
|
56
57
|
budget.truncated = true;
|
|
57
58
|
break;
|
|
@@ -60,7 +61,10 @@ export async function shallowTree(
|
|
|
60
61
|
lines.push(entry.isDirectory() ? `${rel}/` : rel);
|
|
61
62
|
if (budget) budget.remaining -= 1;
|
|
62
63
|
if (entry.isDirectory() && depth > 0) {
|
|
63
|
-
|
|
64
|
+
const childLines = await shallowTree(root, depth - 1, rel, budget);
|
|
65
|
+
for (const childLine of childLines) {
|
|
66
|
+
lines.push(childLine);
|
|
67
|
+
}
|
|
64
68
|
}
|
|
65
69
|
}
|
|
66
70
|
return lines;
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"description": "Pi extension that folds multiple repositories and folders into a guarded virtual monorepo for AI agents.",
|
|
14
14
|
"type": "module",
|
|
15
|
-
"version": "0.12.
|
|
15
|
+
"version": "0.12.5",
|
|
16
16
|
"pi": {
|
|
17
17
|
"extensions": [
|
|
18
18
|
"./index.ts"
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"focus-session-state.ts",
|
|
33
33
|
"monofold-read-ops.ts",
|
|
34
34
|
"read-caps.ts",
|
|
35
|
-
"validation.ts"
|
|
35
|
+
"validation.ts",
|
|
36
|
+
"workspace-path-lookup.ts"
|
|
36
37
|
],
|
|
37
38
|
"name": "pi-monofold",
|
|
38
39
|
"devDependencies": {
|
|
@@ -40,8 +41,8 @@
|
|
|
40
41
|
"typebox": "^1.1.38",
|
|
41
42
|
"@types/node": "^26.0.0",
|
|
42
43
|
"tsx": "^4.20.5",
|
|
43
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
44
|
-
"@earendil-works/pi-ai": "^0.
|
|
44
|
+
"@earendil-works/pi-coding-agent": "^0.84.1",
|
|
45
|
+
"@earendil-works/pi-ai": "^0.84.2"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
48
|
"yaml": "^2.8.1"
|
package/path-normalize.ts
CHANGED
|
@@ -18,8 +18,8 @@ function repairMsysPath(value: string): string {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/** Normalizes local paths before workspace guard comparisons. */
|
|
21
|
-
export function normalizeGuardPath(input: string): string {
|
|
21
|
+
export function normalizeGuardPath(input: string, impl: Pick<typeof path, "resolve"> = path): string {
|
|
22
22
|
const trimmed = input.trim();
|
|
23
23
|
if (!trimmed) return trimmed;
|
|
24
|
-
return
|
|
24
|
+
return impl.resolve(repairMsysPath(trimmed));
|
|
25
25
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { normalizeGuardPath } from "./path-normalize.js";
|
|
3
|
+
|
|
4
|
+
type PathImpl = Pick<typeof path, "isAbsolute" | "relative" | "resolve" | "sep">;
|
|
5
|
+
|
|
6
|
+
/** Detects Windows drive-letter (`C:/...`) or UNC (`\\...`) syntax on any host. */
|
|
7
|
+
function isWindowsStylePath(value: string): boolean {
|
|
8
|
+
return /^[a-zA-Z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Selects the path implementation matching the given path syntax. */
|
|
12
|
+
function selectPathImpl(...values: string[]): PathImpl {
|
|
13
|
+
return values.some(isWindowsStylePath) ? path.win32 : path;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Returns true when `child` is inside or equal to `parent`. */
|
|
17
|
+
export function isPathInsideWorkspace(parent: string, child: string): boolean {
|
|
18
|
+
const impl = selectPathImpl(parent, child);
|
|
19
|
+
const normalizedParent = normalizeGuardPath(parent, impl);
|
|
20
|
+
const normalizedChild = normalizeGuardPath(child, impl);
|
|
21
|
+
const relative = impl.relative(normalizedParent, normalizedChild);
|
|
22
|
+
return (
|
|
23
|
+
relative === "" ||
|
|
24
|
+
(relative !== ".." && !relative.startsWith(`..${impl.sep}`) && !impl.isAbsolute(relative))
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Pre-sorts workspaces once so path guards prefer the longest matching root. */
|
|
29
|
+
export function buildPathLookupWorkspaces<T extends { resolvedPath: string }>(workspaces: T[]): T[] {
|
|
30
|
+
return [...workspaces].sort((a, b) => b.resolvedPath.length - a.resolvedPath.length);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function resolveAbsoluteTargetPath(root: string, targetPath: string): string {
|
|
34
|
+
const impl = selectPathImpl(root, targetPath);
|
|
35
|
+
return normalizeGuardPath(impl.isAbsolute(targetPath) ? targetPath : impl.resolve(root, targetPath), impl);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function findWorkspaceForAbsolutePath<T extends { resolvedPath: string }>(
|
|
39
|
+
pathLookupWorkspaces: readonly T[],
|
|
40
|
+
absolutePath: string,
|
|
41
|
+
): T | undefined {
|
|
42
|
+
const impl = selectPathImpl(absolutePath, ...pathLookupWorkspaces.map((workspace) => workspace.resolvedPath));
|
|
43
|
+
const absolute = normalizeGuardPath(absolutePath, impl);
|
|
44
|
+
return pathLookupWorkspaces.find((workspace) => isPathInsideWorkspace(workspace.resolvedPath, absolute));
|
|
45
|
+
}
|