@cleocode/paths 2026.5.123 → 2026.5.125

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.
@@ -111,6 +111,102 @@ export declare function getCanonicalTemplatesTildePath(): string;
111
111
  * @public
112
112
  */
113
113
  export declare function resolveLegacyCleoDir(override?: string): string;
114
+ /**
115
+ * Result of {@link resolveProjectByCwd} — the project identity resolved
116
+ * from walking up from a working directory.
117
+ *
118
+ * @public
119
+ /** Result of {@link resolveProjectByCwd} — the project identity resolved from walking up from a working directory. */
120
+ export interface ResolvedProject {
121
+ /** Stable canonical project ID (12-hex-char SHA-256 of git-root|name|remote). */
122
+ projectId: string;
123
+ /** Absolute realpath to the project root directory. */
124
+ projectRoot: string;
125
+ /** The legacy UUID from project-info.json, if present. */
126
+ legacyUUID?: string;
127
+ }
128
+ /**
129
+ * Compute the canonical project ID for a given repository path (T9149/T11023).
130
+ *
131
+ * Algorithm:
132
+ * 1. Resolve `repoPath` to its `realpath` (resolves symlinks, normalises mounts).
133
+ * 2. Detect the git root via `git rev-parse --show-toplevel` (falls back to realpath).
134
+ * 3. Read `.cleo/project-info.json` name (optional).
135
+ * 4. Read `git remote get-url origin` (optional).
136
+ * 5. SHA-256 of `<gitRoot>|<projectName>|<remoteUrl>`, first 12 hex chars.
137
+ *
138
+ * This ensures `/mnt/projects/X` and `/workspace/X` (same git root,
139
+ * same remote) produce the same ID.
140
+ *
141
+ * @param repoPath - Absolute path to the project root.
142
+ * @returns The 12-hex-char canonical project ID.
143
+ *
144
+ * @task T11023
145
+ * @task T9149
146
+ */
147
+ export declare function computeCanonicalProjectId(repoPath: string): string;
148
+ /**
149
+ * Compute the legacy base64url(path) ID for a given path.
150
+ * This is the old algorithm used before T9149 W5.
151
+ */
152
+ export declare function legacyProjectId(repoPath: string): string;
153
+ /**
154
+ * Walk up from `cwd` (or `process.cwd()`) looking for `.cleo/project-info.json`
155
+ * and return the project identity if found.
156
+ *
157
+ * This replaces the ancestor-walk pattern in `getCleoDirAbsolute` with a
158
+ * projectId-aware lookup. Instead of resolving a `.cleo/` directory via
159
+ * git-root heuristics, it reads the stable `projectId` from the canonical
160
+ * project-info file — enabling move-safe project identification.
161
+ *
162
+ * **Cross-mount divergence (T11023):** Uses `realpathSync` to normalize
163
+ * bind-mounts and symlinks so the same repo at `/mnt/projects/X` and
164
+ * `/workspace/X` resolves to the same `projectRoot`. The `projectId` is
165
+ * the T9149 canonical 12-hex-char SHA-256 fingerprint of git-root + name
166
+ * + remote URL, which is also mount-invariant.
167
+ *
168
+ * @param cwd - Optional working directory to start the ancestor walk from.
169
+ * Defaults to `process.cwd()`.
170
+ * @returns The resolved project identity, or `null` if no CLEO project is
171
+ * found anywhere in the ancestor chain.
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * const project = resolveProjectByCwd('/repo/packages/core');
176
+ * // { projectId: 'a1b2c3d4e5f6', projectRoot: '/repo' }
177
+ *
178
+ * const notFound = resolveProjectByCwd('/tmp/empty');
179
+ * // null
180
+ * ```
181
+ *
182
+ * @public
183
+ * @task T11008
184
+ * @task T11023
185
+ */
186
+ export declare function resolveProjectByCwd(cwd?: string): ResolvedProject | null;
187
+ /**
188
+ * Resolve the canonical `.cleo` directory for a project given its `projectId`.
189
+ *
190
+ * Looks up the project in the global `nexus.db` registry (`project_registry`
191
+ * table) to find the project's root path, then returns the `.cleo/` directory
192
+ * under that root.
193
+ *
194
+ * **Legacy ID support (T11023 AC4):** If the `projectId` is not found in
195
+ * `project_registry`, also checks the `project_id_aliases` table for a
196
+ * legacy→canonical mapping before returning `null`.
197
+ *
198
+ * This enables cross-project lookups: given a stable project ID, resolve where
199
+ * that project lives on disk without walking from a working directory.
200
+ *
201
+ * @param projectId - The project ID to look up. Can be a canonical 12-hex-char
202
+ * ID, a legacy UUID, or a legacy base64url(path) ID.
203
+ * @returns Absolute path to the `.cleo/` directory, or `null` if the
204
+ * projectId is not found in the nexus registry (or its alias table).
205
+ *
206
+ * @task T11008
207
+ * @task T11023
208
+ */
209
+ export declare function resolveCanonicalCleoDir(projectId: string): string | null;
114
210
  /**
115
211
  * Invalidate the cached CLEO system info snapshot. Use in tests after
116
212
  * mutating `CLEO_HOME` or related env vars.
@@ -1 +1 @@
1
- {"version":3,"file":"cleo-paths.d.ts","sourceRoot":"","sources":["../src/cleo-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAM7B;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,IAAI,aAAa,CAEpD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,IAAI,UAAU,CAE9C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAelD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,8BAA8B,IAAI,MAAM,CAMvD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD"}
1
+ {"version":3,"file":"cleo-paths.d.ts","sourceRoot":"","sources":["../src/cleo-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAM7B;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,IAAI,aAAa,CAEpD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,IAAI,UAAU,CAE9C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAelD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,8BAA8B,IAAI,MAAM,CAMvD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED;;;;;uHAKuH;AACvH,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAuDD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWlE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CA8CxE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiExE;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD"}
@@ -8,8 +8,12 @@
8
8
  *
9
9
  * @task T1883
10
10
  */
11
+ import { execFileSync } from 'node:child_process';
12
+ import { createHash } from 'node:crypto';
13
+ import { existsSync, readFileSync, realpathSync } from 'node:fs';
11
14
  import { homedir } from 'node:os';
12
- import { join } from 'node:path';
15
+ import { dirname, join, resolve } from 'node:path';
16
+ import { DatabaseSync } from 'node:sqlite';
13
17
  import { createPlatformPathsResolver, } from './platform-paths.js';
14
18
  const TEMPLATES_SUBDIR = 'templates';
15
19
  const cleoResolver = createPlatformPathsResolver('cleo', 'CLEO_HOME', 'CLEO_CONFIG_HOME');
@@ -145,6 +149,243 @@ export function resolveLegacyCleoDir(override) {
145
149
  return override;
146
150
  return join(homedir(), '.cleo');
147
151
  }
152
+ // ---------------------------------------------------------------------------
153
+ // Canonical project ID computation (T11023 — cross-mount divergence)
154
+ // ---------------------------------------------------------------------------
155
+ /**
156
+ * Synchronously find the git root for a given directory.
157
+ * Returns `null` if the directory is not inside a git repo.
158
+ */
159
+ function _findGitRootSync(fromPath) {
160
+ try {
161
+ const stdout = execFileSync('git', ['rev-parse', '--show-toplevel'], {
162
+ cwd: resolve(fromPath),
163
+ encoding: 'utf-8',
164
+ stdio: ['ignore', 'pipe', 'ignore'],
165
+ });
166
+ return resolve(stdout.trim());
167
+ }
168
+ catch {
169
+ return null;
170
+ }
171
+ }
172
+ /**
173
+ * Synchronously find the primary git remote URL (origin fetch URL).
174
+ * Returns `null` when there are no remotes or git is unavailable.
175
+ */
176
+ function _findGitRemoteUrlSync(fromPath) {
177
+ try {
178
+ const stdout = execFileSync('git', ['remote', 'get-url', 'origin'], {
179
+ cwd: resolve(fromPath),
180
+ encoding: 'utf-8',
181
+ stdio: ['ignore', 'pipe', 'ignore'],
182
+ });
183
+ const url = stdout.trim();
184
+ return url || null;
185
+ }
186
+ catch {
187
+ return null;
188
+ }
189
+ }
190
+ /**
191
+ * Read the project name from `.cleo/project-info.json` (if present).
192
+ * Non-fatal on any I/O or parse error.
193
+ */
194
+ function _readProjectInfoName(repoRoot) {
195
+ try {
196
+ const raw = readFileSync(join(repoRoot, '.cleo', 'project-info.json'), 'utf-8');
197
+ const parsed = JSON.parse(raw);
198
+ return typeof parsed.name === 'string' ? parsed.name : undefined;
199
+ }
200
+ catch {
201
+ return undefined;
202
+ }
203
+ }
204
+ /**
205
+ * Compute the canonical project ID for a given repository path (T9149/T11023).
206
+ *
207
+ * Algorithm:
208
+ * 1. Resolve `repoPath` to its `realpath` (resolves symlinks, normalises mounts).
209
+ * 2. Detect the git root via `git rev-parse --show-toplevel` (falls back to realpath).
210
+ * 3. Read `.cleo/project-info.json` name (optional).
211
+ * 4. Read `git remote get-url origin` (optional).
212
+ * 5. SHA-256 of `<gitRoot>|<projectName>|<remoteUrl>`, first 12 hex chars.
213
+ *
214
+ * This ensures `/mnt/projects/X` and `/workspace/X` (same git root,
215
+ * same remote) produce the same ID.
216
+ *
217
+ * @param repoPath - Absolute path to the project root.
218
+ * @returns The 12-hex-char canonical project ID.
219
+ *
220
+ * @task T11023
221
+ * @task T9149
222
+ */
223
+ export function computeCanonicalProjectId(repoPath) {
224
+ const realRepoPath = realpathSync(resolve(repoPath));
225
+ const gitRoot = _findGitRootSync(realRepoPath);
226
+ const effectiveRoot = gitRoot ?? realRepoPath;
227
+ const remoteUrl = gitRoot ? _findGitRemoteUrlSync(gitRoot) : null;
228
+ const projectName = _readProjectInfoName(effectiveRoot);
229
+ const fingerprint = [effectiveRoot, projectName ?? '', remoteUrl ?? ''].join('|');
230
+ return createHash('sha256').update(fingerprint).digest('hex').substring(0, 12);
231
+ }
232
+ /**
233
+ * Compute the legacy base64url(path) ID for a given path.
234
+ * This is the old algorithm used before T9149 W5.
235
+ */
236
+ export function legacyProjectId(repoPath) {
237
+ return Buffer.from(repoPath).toString('base64url').slice(0, 32);
238
+ }
239
+ /**
240
+ * Walk up from `cwd` (or `process.cwd()`) looking for `.cleo/project-info.json`
241
+ * and return the project identity if found.
242
+ *
243
+ * This replaces the ancestor-walk pattern in `getCleoDirAbsolute` with a
244
+ * projectId-aware lookup. Instead of resolving a `.cleo/` directory via
245
+ * git-root heuristics, it reads the stable `projectId` from the canonical
246
+ * project-info file — enabling move-safe project identification.
247
+ *
248
+ * **Cross-mount divergence (T11023):** Uses `realpathSync` to normalize
249
+ * bind-mounts and symlinks so the same repo at `/mnt/projects/X` and
250
+ * `/workspace/X` resolves to the same `projectRoot`. The `projectId` is
251
+ * the T9149 canonical 12-hex-char SHA-256 fingerprint of git-root + name
252
+ * + remote URL, which is also mount-invariant.
253
+ *
254
+ * @param cwd - Optional working directory to start the ancestor walk from.
255
+ * Defaults to `process.cwd()`.
256
+ * @returns The resolved project identity, or `null` if no CLEO project is
257
+ * found anywhere in the ancestor chain.
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * const project = resolveProjectByCwd('/repo/packages/core');
262
+ * // { projectId: 'a1b2c3d4e5f6', projectRoot: '/repo' }
263
+ *
264
+ * const notFound = resolveProjectByCwd('/tmp/empty');
265
+ * // null
266
+ * ```
267
+ *
268
+ * @public
269
+ * @task T11008
270
+ * @task T11023
271
+ */
272
+ export function resolveProjectByCwd(cwd) {
273
+ const start = resolve(cwd ?? process.cwd());
274
+ let current = start;
275
+ while (true) {
276
+ const infoPath = join(current, '.cleo', 'project-info.json');
277
+ if (existsSync(infoPath)) {
278
+ try {
279
+ const raw = readFileSync(infoPath, 'utf-8');
280
+ const data = JSON.parse(raw);
281
+ if (typeof data.projectId === 'string' && data.projectId.length > 0) {
282
+ // T11023: Normalize projectRoot via realpathSync to handle cross-mount
283
+ // divergence — same repo at /mnt/projects/X and /workspace/X resolves
284
+ // to the same real path (AC2, AC3).
285
+ let realRoot;
286
+ try {
287
+ realRoot = realpathSync(current);
288
+ }
289
+ catch {
290
+ // realpathSync fails on nonexistent paths — fall back to resolved path
291
+ realRoot = current;
292
+ }
293
+ // T11023: Compute canonical projectId using T9149 algorithm
294
+ // (git-root + realpath fingerprint) for mount-invariant identity (AC1).
295
+ const canonicalId = computeCanonicalProjectId(realRoot);
296
+ return {
297
+ projectId: canonicalId,
298
+ projectRoot: realRoot,
299
+ legacyUUID: data.projectId,
300
+ };
301
+ }
302
+ }
303
+ catch {
304
+ // Corrupt or unparseable project-info.json — keep walking up.
305
+ // A higher ancestor may have a valid one.
306
+ }
307
+ }
308
+ const parent = dirname(current);
309
+ if (parent === current)
310
+ break; // Reached filesystem root
311
+ current = parent;
312
+ }
313
+ return null;
314
+ }
315
+ /**
316
+ * Resolve the canonical `.cleo` directory for a project given its `projectId`.
317
+ *
318
+ * Looks up the project in the global `nexus.db` registry (`project_registry`
319
+ * table) to find the project's root path, then returns the `.cleo/` directory
320
+ * under that root.
321
+ *
322
+ * **Legacy ID support (T11023 AC4):** If the `projectId` is not found in
323
+ * `project_registry`, also checks the `project_id_aliases` table for a
324
+ * legacy→canonical mapping before returning `null`.
325
+ *
326
+ * This enables cross-project lookups: given a stable project ID, resolve where
327
+ * that project lives on disk without walking from a working directory.
328
+ *
329
+ * @param projectId - The project ID to look up. Can be a canonical 12-hex-char
330
+ * ID, a legacy UUID, or a legacy base64url(path) ID.
331
+ * @returns Absolute path to the `.cleo/` directory, or `null` if the
332
+ * projectId is not found in the nexus registry (or its alias table).
333
+ *
334
+ * @task T11008
335
+ * @task T11023
336
+ */
337
+ export function resolveCanonicalCleoDir(projectId) {
338
+ const cleoHome = getCleoHome();
339
+ const nexusDbPath = join(cleoHome, 'nexus.db');
340
+ if (!existsSync(nexusDbPath))
341
+ return null;
342
+ let db;
343
+ try {
344
+ db = new DatabaseSync(nexusDbPath, { readOnly: true });
345
+ // Try direct project_registry lookup first.
346
+ const directStmt = db.prepare('SELECT project_path FROM project_registry WHERE project_id = ? LIMIT 1');
347
+ const directRow = directStmt.get(projectId);
348
+ if (directRow &&
349
+ typeof directRow.project_path === 'string' &&
350
+ directRow.project_path.length > 0) {
351
+ return join(directRow.project_path, '.cleo');
352
+ }
353
+ // T11023 AC4: Fall back to project_id_aliases for legacy ID resolution.
354
+ // Legacy base64url(path) IDs and old UUIDs are mapped to canonical IDs
355
+ // in the aliases table. Try resolving the input as a legacy ID first,
356
+ // then look up the canonical ID.
357
+ try {
358
+ const aliasStmt = db.prepare('SELECT canonical_id FROM project_id_aliases WHERE legacy_id = ? LIMIT 1');
359
+ const aliasRow = aliasStmt.get(projectId);
360
+ if (aliasRow &&
361
+ typeof aliasRow.canonical_id === 'string' &&
362
+ aliasRow.canonical_id.length > 0) {
363
+ // Resolved a legacy alias — look up the canonical ID.
364
+ const canonicalRow = directStmt.get(aliasRow.canonical_id);
365
+ if (canonicalRow &&
366
+ typeof canonicalRow.project_path === 'string' &&
367
+ canonicalRow.project_path.length > 0) {
368
+ return join(canonicalRow.project_path, '.cleo');
369
+ }
370
+ }
371
+ }
372
+ catch {
373
+ // project_id_aliases table may not exist yet (pre-migration) — non-fatal.
374
+ }
375
+ return null;
376
+ }
377
+ catch {
378
+ return null;
379
+ }
380
+ finally {
381
+ try {
382
+ db?.close();
383
+ }
384
+ catch {
385
+ // Best-effort close
386
+ }
387
+ }
388
+ }
148
389
  /**
149
390
  * Invalidate the cached CLEO system info snapshot. Use in tests after
150
391
  * mutating `CLEO_HOME` or related env vars.
@@ -1 +1 @@
1
- {"version":3,"file":"cleo-paths.js","sourceRoot":"","sources":["../src/cleo-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,2BAA2B,GAG5B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAErC,MAAM,YAAY,GAAG,2BAA2B,CAAC,MAAM,EAAE,WAAW,EAAE,kBAAkB,CAAC,CAAC;AAE1F;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,YAAY,CAAC,gBAAgB,EAAE,CAAC;AACzC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,YAAY,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,YAAY,CAAC,aAAa,EAAE,CAAC;AACtC,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,yBAAyB;IACvC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,qEAAqE;IACrE,qEAAqE;IACrE,wCAAwC;IACxC,MAAM,OAAO,GACX,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;QAChD,CAAC,CAAC,GAAG,QAAQ,IAAI,gBAAgB,EAAE;QACnC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAChE,OAAO,IAAI,QAAQ,EAAE,CAAC;IACxB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,8BAA8B;IAC5C,4EAA4E;IAC5E,+EAA+E;IAC/E,8EAA8E;IAC9E,6EAA6E;IAC7E,OAAO,mBAAmB,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAiB;IACpD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AAClC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B;IAC1C,YAAY,CAAC,UAAU,EAAE,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"cleo-paths.js","sourceRoot":"","sources":["../src/cleo-paths.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@
10
10
  * - {@link createPlatformPathsResolver} — generic factory bindable to any app
11
11
  * - CLEO-bound helpers: {@link getCleoHome}, {@link getCleoPlatformPaths},
12
12
  * {@link getCleoSystemInfo}, {@link getCleoTemplatesTildePath}
13
+ * - Project resolution: {@link resolveProjectByCwd}, {@link resolveCanonicalCleoDir}
13
14
  * - Worktree primitives: {@link computeProjectHash},
14
15
  * {@link resolveWorktreeRootForHash}, {@link resolveTaskWorktreePath},
15
16
  * {@link getCleoWorktreesRoot}, {@link resolveWorktreeIndexPath}
@@ -17,9 +18,10 @@
17
18
  *
18
19
  * @packageDocumentation
19
20
  * @task T1883
21
+ * @task T11008
20
22
  */
21
23
  export { isAbsolutePath } from './abs-path.js';
22
- export { _resetCleoPlatformPathsCache, getCanonicalTemplatesTildePath, getCleoHome, getCleoPlatformPaths, getCleoSystemInfo, getCleoTemplatesTildePath, resolveLegacyCleoDir, } from './cleo-paths.js';
24
+ export { _resetCleoPlatformPathsCache, computeCanonicalProjectId, getCanonicalTemplatesTildePath, getCleoHome, getCleoPlatformPaths, getCleoSystemInfo, getCleoTemplatesTildePath, legacyProjectId, type ResolvedProject, resolveCanonicalCleoDir, resolveLegacyCleoDir, resolveProjectByCwd, } from './cleo-paths.js';
23
25
  export { createPlatformPathsResolver, type PlatformPaths, type PlatformPathsResolver, type SystemInfo, } from './platform-paths.js';
24
26
  export { computeProjectHash, getCleoWorktreesRoot, resolveTaskWorktreePath, resolveWorktreeIndexPath, resolveWorktreeRootForHash, } from './worktree-paths.js';
25
27
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,4BAA4B,EAC5B,8BAA8B,EAC9B,WAAW,EACX,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,2BAA2B,EAC3B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,EACzB,8BAA8B,EAC9B,WAAW,EACX,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,eAAe,EACf,KAAK,eAAe,EACpB,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,2BAA2B,EAC3B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@
10
10
  * - {@link createPlatformPathsResolver} — generic factory bindable to any app
11
11
  * - CLEO-bound helpers: {@link getCleoHome}, {@link getCleoPlatformPaths},
12
12
  * {@link getCleoSystemInfo}, {@link getCleoTemplatesTildePath}
13
+ * - Project resolution: {@link resolveProjectByCwd}, {@link resolveCanonicalCleoDir}
13
14
  * - Worktree primitives: {@link computeProjectHash},
14
15
  * {@link resolveWorktreeRootForHash}, {@link resolveTaskWorktreePath},
15
16
  * {@link getCleoWorktreesRoot}, {@link resolveWorktreeIndexPath}
@@ -17,9 +18,10 @@
17
18
  *
18
19
  * @packageDocumentation
19
20
  * @task T1883
21
+ * @task T11008
20
22
  */
21
23
  export { isAbsolutePath } from './abs-path.js';
22
- export { _resetCleoPlatformPathsCache, getCanonicalTemplatesTildePath, getCleoHome, getCleoPlatformPaths, getCleoSystemInfo, getCleoTemplatesTildePath, resolveLegacyCleoDir, } from './cleo-paths.js';
24
+ export { _resetCleoPlatformPathsCache, computeCanonicalProjectId, getCanonicalTemplatesTildePath, getCleoHome, getCleoPlatformPaths, getCleoSystemInfo, getCleoTemplatesTildePath, legacyProjectId, resolveCanonicalCleoDir, resolveLegacyCleoDir, resolveProjectByCwd, } from './cleo-paths.js';
23
25
  export { createPlatformPathsResolver, } from './platform-paths.js';
24
26
  export { computeProjectHash, getCleoWorktreesRoot, resolveTaskWorktreePath, resolveWorktreeIndexPath, resolveWorktreeRootForHash, } from './worktree-paths.js';
25
27
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,4BAA4B,EAC5B,8BAA8B,EAC9B,WAAW,EACX,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,2BAA2B,GAI5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,EACzB,8BAA8B,EAC9B,WAAW,EACX,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,eAAe,EAEf,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,2BAA2B,GAI5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC"}
@@ -21,6 +21,10 @@
21
21
  * the historical implementation in `branch-lock.ts#resolveAgentWorktreeRoot`
22
22
  * (the root cause of the duplicated logic this package consolidates).
23
23
  *
24
+ * **Cross-mount normalization (T11023 AC3):** The input path is resolved
25
+ * through `realpathSync` before hashing so that different mount points
26
+ * (/mnt/projects/X vs /workspace/X) produce the same hash.
27
+ *
24
28
  * @param projectRoot - Absolute path to the project root.
25
29
  * @returns 16-character lowercase hex string.
26
30
  *
@@ -1 +1 @@
1
- {"version":3,"file":"worktree-paths.d.ts","sourceRoot":"","sources":["../src/worktree-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA0BH;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAG7F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAGR;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEpE"}
1
+ {"version":3,"file":"worktree-paths.d.ts","sourceRoot":"","sources":["../src/worktree-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA2BH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAU9D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAG7F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAGR;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEpE"}
@@ -15,6 +15,7 @@
15
15
  * @task T9802
16
16
  */
17
17
  import { createHash } from 'node:crypto';
18
+ import { realpathSync } from 'node:fs';
18
19
  import { join } from 'node:path';
19
20
  import { getCleoHome } from './cleo-paths.js';
20
21
  const WORKTREES_SUBDIR = 'worktrees';
@@ -42,13 +43,26 @@ function joinSegments(base, ...parts) {
42
43
  * the historical implementation in `branch-lock.ts#resolveAgentWorktreeRoot`
43
44
  * (the root cause of the duplicated logic this package consolidates).
44
45
  *
46
+ * **Cross-mount normalization (T11023 AC3):** The input path is resolved
47
+ * through `realpathSync` before hashing so that different mount points
48
+ * (/mnt/projects/X vs /workspace/X) produce the same hash.
49
+ *
45
50
  * @param projectRoot - Absolute path to the project root.
46
51
  * @returns 16-character lowercase hex string.
47
52
  *
48
53
  * @public
49
54
  */
50
55
  export function computeProjectHash(projectRoot) {
51
- return createHash('sha256').update(projectRoot).digest('hex').slice(0, PROJECT_HASH_LENGTH);
56
+ // T11023: Normalize via realpath to handle cross-mount divergence.
57
+ // realpathSync throws on nonexistent paths — fall back to raw path.
58
+ let normalized;
59
+ try {
60
+ normalized = realpathSync(projectRoot);
61
+ }
62
+ catch {
63
+ normalized = projectRoot;
64
+ }
65
+ return createHash('sha256').update(normalized).digest('hex').slice(0, PROJECT_HASH_LENGTH);
52
66
  }
53
67
  /**
54
68
  * Resolve the worktrees root directory for a given project hash.
@@ -1 +1 @@
1
- {"version":3,"file":"worktree-paths.js","sourceRoot":"","sources":["../src/worktree-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,gBAAgB,GAAG,WAAW,CAAC;AACrC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B;;;;;;;;;GASG;AACH,SAAS,YAAY,CAAC,IAAY,EAAE,GAAG,KAAe;IACpD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,OAAO,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAmB;IACpD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC;AAC9F,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,0BAA0B,CAAC,WAAmB,EAAE,YAAqB;IACnF,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,OAAO,YAAY,CAAC,WAAW,EAAE,EAAE,gBAAgB,EAAE,WAAW,CAAC,CAAC;AACpE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CACrC,WAAmB,EACnB,MAAc,EACd,YAAqB;IAErB,MAAM,IAAI,GAAG,0BAA0B,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IACnE,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,YAAY,CAAC,WAAW,EAAE,EAAE,gBAAgB,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,UAAU,wBAAwB,CAAC,WAAmB;IAC1D,OAAO,YAAY,CAAC,WAAW,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC;AAC9D,CAAC"}
1
+ {"version":3,"file":"worktree-paths.js","sourceRoot":"","sources":["../src/worktree-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,gBAAgB,GAAG,WAAW,CAAC;AACrC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B;;;;;;;;;GASG;AACH,SAAS,YAAY,CAAC,IAAY,EAAE,GAAG,KAAe;IACpD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,OAAO,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAmB;IACpD,mEAAmE;IACnE,oEAAoE;IACpE,IAAI,UAAkB,CAAC;IACvB,IAAI,CAAC;QACH,UAAU,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,UAAU,GAAG,WAAW,CAAC;IAC3B,CAAC;IACD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC;AAC7F,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,0BAA0B,CAAC,WAAmB,EAAE,YAAqB;IACnF,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,OAAO,YAAY,CAAC,WAAW,EAAE,EAAE,gBAAgB,EAAE,WAAW,CAAC,CAAC;AACpE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CACrC,WAAmB,EACnB,MAAc,EACd,YAAqB;IAErB,MAAM,IAAI,GAAG,0BAA0B,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IACnE,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,YAAY,CAAC,WAAW,EAAE,EAAE,gBAAgB,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,UAAU,wBAAwB,CAAC,WAAmB;IAC1D,OAAO,YAAY,CAAC,WAAW,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC;AAC9D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/paths",
3
- "version": "2026.5.123",
3
+ "version": "2026.5.125",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "CLEO XDG/env-paths SSoT — createPlatformPathsResolver factory, cleo-bound platform paths, project hash + worktree root primitives, isAbsolutePath. Zero-dep leaf package consumed by core, worktree, brain, adapters, and caamp.",