pi-monofold 0.12.4 → 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 +7 -3
- package/index.ts +11 -6
- package/monofold-read-ops.ts +6 -2
- package/package.json +3 -2
- 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:
|
|
@@ -171,6 +171,7 @@ pi-monofold/
|
|
|
171
171
|
│ ├── focus-skills-auto-load.test.ts
|
|
172
172
|
│ ├── focus-skills.test.ts
|
|
173
173
|
│ ├── changelog-hygiene.test.ts
|
|
174
|
+
│ ├── contributing-docs.test.ts
|
|
174
175
|
│ ├── github-actions-pin.test.ts
|
|
175
176
|
│ ├── monofold-read-caps.test.ts
|
|
176
177
|
│ ├── monofold-read-ops.test.ts
|
|
@@ -179,7 +180,9 @@ pi-monofold/
|
|
|
179
180
|
│ ├── release-docs.test.ts
|
|
180
181
|
│ ├── readme-version-pin.test.ts
|
|
181
182
|
│ ├── runtime-path-overlays.test.ts
|
|
182
|
-
│
|
|
183
|
+
│ ├── tsconfig-publishable-files.test.ts
|
|
184
|
+
│ ├── unknown-path-allows.test.ts
|
|
185
|
+
│ └── workspace-path-lookup.test.ts
|
|
183
186
|
├── CHANGELOG.md
|
|
184
187
|
├── SECURITY.md
|
|
185
188
|
├── file-read-preview.ts
|
|
@@ -197,6 +200,7 @@ pi-monofold/
|
|
|
197
200
|
├── README.md
|
|
198
201
|
├── unknown-path-allows.ts
|
|
199
202
|
├── validation.ts
|
|
203
|
+
├── workspace-path-lookup.ts
|
|
200
204
|
└── tsconfig.json
|
|
201
205
|
```
|
|
202
206
|
|
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": {
|
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
|
+
}
|