orbitmap 0.4.2 → 0.4.3

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.
Files changed (156) hide show
  1. package/README.md +60 -13
  2. package/dist/adapters/cloud.d.ts +53 -21
  3. package/dist/adapters/cloud.js +95 -29
  4. package/dist/adapters/cloud.js.map +1 -1
  5. package/dist/adapters/factory.d.ts +3 -0
  6. package/dist/adapters/factory.js +8 -1
  7. package/dist/adapters/factory.js.map +1 -1
  8. package/dist/adapters/local/adapter.d.ts +0 -20
  9. package/dist/adapters/local/adapter.js +0 -18
  10. package/dist/adapters/local/adapter.js.map +1 -1
  11. package/dist/adapters/local/context.d.ts +1 -1
  12. package/dist/adapters/local/context.js +10 -2
  13. package/dist/adapters/local/context.js.map +1 -1
  14. package/dist/adapters/local/entities/tasks.d.ts +9 -1
  15. package/dist/adapters/local/entities/tasks.js +47 -5
  16. package/dist/adapters/local/entities/tasks.js.map +1 -1
  17. package/dist/adapters/local/entities/work-items.d.ts +14 -30
  18. package/dist/adapters/local/entities/work-items.js +63 -47
  19. package/dist/adapters/local/entities/work-items.js.map +1 -1
  20. package/dist/adapters/local/rules.d.ts +4 -4
  21. package/dist/adapters/local/rules.js +4 -19
  22. package/dist/adapters/local/rules.js.map +1 -1
  23. package/dist/adapters/local/shapes.d.ts +27 -7
  24. package/dist/adapters/local/shapes.js +41 -5
  25. package/dist/adapters/local/shapes.js.map +1 -1
  26. package/dist/adapters/local/store.d.ts +55 -6
  27. package/dist/adapters/local/store.js +122 -8
  28. package/dist/adapters/local/store.js.map +1 -1
  29. package/dist/adapters/local/types.d.ts +9 -2
  30. package/dist/adapters/local/types.js +9 -2
  31. package/dist/adapters/local/types.js.map +1 -1
  32. package/dist/adapters/types.d.ts +7 -20
  33. package/dist/agent-instructions.d.ts +21 -8
  34. package/dist/agent-instructions.js +91 -35
  35. package/dist/agent-instructions.js.map +1 -1
  36. package/dist/area-directories.d.ts +75 -0
  37. package/dist/area-directories.js +162 -0
  38. package/dist/area-directories.js.map +1 -0
  39. package/dist/area-grouping.d.ts +55 -0
  40. package/dist/area-grouping.js +72 -0
  41. package/dist/area-grouping.js.map +1 -0
  42. package/dist/commands/areas.js +30 -2
  43. package/dist/commands/areas.js.map +1 -1
  44. package/dist/commands/assign.d.ts +1 -0
  45. package/dist/commands/assign.js +2 -0
  46. package/dist/commands/assign.js.map +1 -1
  47. package/dist/commands/context.d.ts +95 -0
  48. package/dist/commands/context.js +104 -2
  49. package/dist/commands/context.js.map +1 -1
  50. package/dist/commands/create.d.ts +1 -0
  51. package/dist/commands/create.js +19 -14
  52. package/dist/commands/create.js.map +1 -1
  53. package/dist/commands/dep.js +5 -8
  54. package/dist/commands/dep.js.map +1 -1
  55. package/dist/commands/doc-import.d.ts +1 -0
  56. package/dist/commands/doc-import.js +8 -26
  57. package/dist/commands/doc-import.js.map +1 -1
  58. package/dist/commands/doc-patch.js +16 -52
  59. package/dist/commands/doc-patch.js.map +1 -1
  60. package/dist/commands/doc-share.js +4 -0
  61. package/dist/commands/doc-share.js.map +1 -1
  62. package/dist/commands/doc-update.js +4 -8
  63. package/dist/commands/doc-update.js.map +1 -1
  64. package/dist/commands/doc.js +7 -12
  65. package/dist/commands/doc.js.map +1 -1
  66. package/dist/commands/docs-cache.js +23 -10
  67. package/dist/commands/docs-cache.js.map +1 -1
  68. package/dist/commands/docs.js +29 -10
  69. package/dist/commands/docs.js.map +1 -1
  70. package/dist/commands/get.js +4 -10
  71. package/dist/commands/get.js.map +1 -1
  72. package/dist/commands/ideas.d.ts +3 -1
  73. package/dist/commands/ideas.js +26 -49
  74. package/dist/commands/ideas.js.map +1 -1
  75. package/dist/commands/init.d.ts +24 -1
  76. package/dist/commands/init.js +0 -0
  77. package/dist/commands/init.js.map +1 -1
  78. package/dist/commands/intent.js +10 -14
  79. package/dist/commands/intent.js.map +1 -1
  80. package/dist/commands/issues.d.ts +3 -1
  81. package/dist/commands/issues.js +47 -72
  82. package/dist/commands/issues.js.map +1 -1
  83. package/dist/commands/log.d.ts +1 -0
  84. package/dist/commands/log.js +18 -17
  85. package/dist/commands/log.js.map +1 -1
  86. package/dist/commands/mission.js +3 -12
  87. package/dist/commands/mission.js.map +1 -1
  88. package/dist/commands/orbit.d.ts +7 -1
  89. package/dist/commands/orbit.js +13 -24
  90. package/dist/commands/orbit.js.map +1 -1
  91. package/dist/commands/setup-agent.d.ts +97 -4
  92. package/dist/commands/setup-agent.js +265 -38
  93. package/dist/commands/setup-agent.js.map +1 -1
  94. package/dist/commands/setup-mcp.d.ts +2 -0
  95. package/dist/commands/setup-mcp.js +17 -40
  96. package/dist/commands/setup-mcp.js.map +1 -1
  97. package/dist/commands/start.d.ts +43 -0
  98. package/dist/commands/start.js +158 -3
  99. package/dist/commands/start.js.map +1 -1
  100. package/dist/commands/status.d.ts +1 -0
  101. package/dist/commands/status.js +5 -15
  102. package/dist/commands/status.js.map +1 -1
  103. package/dist/commands/subtask.d.ts +1 -0
  104. package/dist/commands/subtask.js +9 -2
  105. package/dist/commands/subtask.js.map +1 -1
  106. package/dist/commands/task-edit-content.js +4 -8
  107. package/dist/commands/task-edit-content.js.map +1 -1
  108. package/dist/commands/task.d.ts +7 -0
  109. package/dist/commands/task.js +5 -2
  110. package/dist/commands/task.js.map +1 -1
  111. package/dist/commands/tasks.d.ts +35 -0
  112. package/dist/commands/tasks.js +45 -9
  113. package/dist/commands/tasks.js.map +1 -1
  114. package/dist/commands/vibes.d.ts +3 -1
  115. package/dist/commands/vibes.js +41 -31
  116. package/dist/commands/vibes.js.map +1 -1
  117. package/dist/config.d.ts +11 -0
  118. package/dist/config.js +14 -1
  119. package/dist/config.js.map +1 -1
  120. package/dist/doc-cache.d.ts +74 -5
  121. package/dist/doc-cache.js +106 -22
  122. package/dist/doc-cache.js.map +1 -1
  123. package/dist/enums.d.ts +26 -0
  124. package/dist/enums.js +53 -0
  125. package/dist/enums.js.map +1 -0
  126. package/dist/errors.d.ts +67 -0
  127. package/dist/errors.js +82 -0
  128. package/dist/errors.js.map +1 -1
  129. package/dist/index.js +134 -56
  130. package/dist/index.js.map +1 -1
  131. package/dist/mcp-config.d.ts +42 -1
  132. package/dist/mcp-config.js +82 -2
  133. package/dist/mcp-config.js.map +1 -1
  134. package/dist/oauth.js +10 -10
  135. package/dist/output.d.ts +12 -0
  136. package/dist/output.js +39 -1
  137. package/dist/output.js.map +1 -1
  138. package/dist/paths.d.ts +40 -0
  139. package/dist/paths.js +174 -9
  140. package/dist/paths.js.map +1 -1
  141. package/dist/project-config.d.ts +42 -1
  142. package/dist/project-config.js +51 -0
  143. package/dist/project-config.js.map +1 -1
  144. package/dist/workspace-resolve.d.ts +179 -9
  145. package/dist/workspace-resolve.js +288 -20
  146. package/dist/workspace-resolve.js.map +1 -1
  147. package/dist/write-target.d.ts +56 -0
  148. package/dist/write-target.js +170 -0
  149. package/dist/write-target.js.map +1 -0
  150. package/package.json +1 -1
  151. package/dist/commands/create-project.d.ts +0 -6
  152. package/dist/commands/create-project.js +0 -28
  153. package/dist/commands/create-project.js.map +0 -1
  154. package/dist/commands/projects.d.ts +0 -3
  155. package/dist/commands/projects.js +0 -28
  156. package/dist/commands/projects.js.map +0 -1
package/dist/paths.js CHANGED
@@ -1,6 +1,6 @@
1
- import { existsSync, realpathSync } from 'node:fs';
1
+ import { existsSync, readFileSync, realpathSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
- import { dirname, join, resolve } from 'node:path';
3
+ import { basename, dirname, join, resolve } from 'node:path';
4
4
  /** Name of the per-project OrbitMap directory (`<project>/.orbitmap`). */
5
5
  export const ORBITMAP_DIR = '.orbitmap';
6
6
  /**
@@ -12,6 +12,28 @@ export const ORBITMAP_DIR = '.orbitmap';
12
12
  export const CONFIG_FILENAME = 'config.json';
13
13
  /** Filename of the machine-local secret store (`~/.orbitmap/credentials.json`). */
14
14
  export const CREDENTIALS_FILENAME = 'credentials.json';
15
+ /**
16
+ * Filename of the gitignored, machine-specific link file (`<project>/.orbitmap/link.json`).
17
+ *
18
+ * Duplicated from `workspace-resolve.ts`'s `LINK_FILE` rather than imported: that module
19
+ * sits above this one, and this is a leaf.
20
+ */
21
+ const LINK_FILENAME = 'link.json';
22
+ /**
23
+ * The one directory holding documents, in **both** modes (IN-rwb5qu).
24
+ *
25
+ * Cloud mode: `<project>/.orbitmap/docs/` is the download cache. Local mode: the same
26
+ * `docs/` inside the workspace directory is the domain data itself — and for the usual
27
+ * in-project workspace (`<project>/.orbitmap`) that is literally the same path. Hence one
28
+ * name here rather than one per layer: there is exactly ONE copy of a document on disk,
29
+ * globally, with **no per-area subdirectories** (the data model does not record a
30
+ * document's home area, so the directory tree must not pretend it does).
31
+ */
32
+ export const DOCS_DIR = 'docs';
33
+ /** Pre-v2 local-workspace name of {@link DOCS_DIR}; migrated away by `LocalStore.migrate()`. */
34
+ export const LEGACY_DOCUMENTS_DIR = 'documents';
35
+ /** Cache bookkeeping inside {@link DOCS_DIR}; written in cloud mode only. */
36
+ export const DOC_CACHE_MANIFEST = '.manifest.json';
15
37
  let homeCache;
16
38
  /**
17
39
  * The resolved OrbitMap home, memoised.
@@ -141,22 +163,165 @@ export function findProjectConfigFiles(startDir = process.cwd()) {
141
163
  const found = [];
142
164
  const globals = new Set(globalConfigPaths().map(canonical));
143
165
  const homeStop = canonical(homedir());
166
+ const seenFiles = new Set();
167
+ const seenStarts = new Set();
168
+ // Directories whose walk-up still has to run: `startDir` first, then any workspace root a
169
+ // `link.json` back-pointer found along the way names (see the doc comment above).
170
+ const queue = [resolve(startDir)];
171
+ while (queue.length > 0) {
172
+ const start = queue.shift();
173
+ if (seenStarts.has(canonical(start)))
174
+ continue;
175
+ seenStarts.add(canonical(start));
176
+ let dir = start;
177
+ for (;;) {
178
+ const orbitmapDir = join(dir, ORBITMAP_DIR);
179
+ const candidate = join(orbitmapDir, CONFIG_FILENAME);
180
+ if (existsSync(candidate) &&
181
+ !globals.has(canonical(candidate)) &&
182
+ !isOrbitMapHomeDir(orbitmapDir)) {
183
+ const key = canonical(candidate);
184
+ if (!seenFiles.has(key)) {
185
+ seenFiles.add(key);
186
+ found.push(candidate);
187
+ }
188
+ }
189
+ // An out-of-tree area cannot reach its workspace root by walking up; its machine-local
190
+ // `link.json` says where the root is, so the walk hops there and continues rather than
191
+ // ending with `agent`/`mode`/`workspace` unresolved (IS-ktfg5z).
192
+ const pointer = workspacePointer(orbitmapDir);
193
+ if (pointer && !seenStarts.has(canonical(pointer)))
194
+ queue.push(pointer);
195
+ if (canonical(dir) === homeStop)
196
+ break;
197
+ const parent = dirname(dir);
198
+ if (parent === dir)
199
+ break;
200
+ dir = parent;
201
+ }
202
+ }
203
+ return found;
204
+ }
205
+ /**
206
+ * The nearest directory at or above `startDir` that owns an `.orbitmap/` — the project (or
207
+ * workspace) root the doc cache and the `.gitignore` entries belong to. `undefined` when the
208
+ * walk finds none.
209
+ *
210
+ * The same walk and the same guards as {@link findProjectConfigFiles}, for the same reasons:
211
+ * an OrbitMap *home* is never a project (a relocated or mis-`$HOME`-ed `~/.orbitmap` sits on
212
+ * the walk path as an ordinary ancestor), and nothing above the user's home is that user's
213
+ * project. Only the *directory* is required to exist here — a project that has never had a
214
+ * `config.json` still owns its `.orbitmap/link.json` or `.orbitmap/docs/`.
215
+ *
216
+ * This is what replaces the doc cache's old `join('.orbitmap', 'docs')`, which resolved
217
+ * against `process.cwd()` with no walk-up at all and therefore scattered a fresh copy of
218
+ * every document into every subdirectory a command happened to run from.
219
+ *
220
+ * **One workspace = one directory = one `.orbitmap`.** The walk is not a search for any
221
+ * `.orbitmap` that happens to look like a workspace: it stops at the first ancestor that
222
+ * declares one ({@link declaresWorkspace}) and returns it, or follows an explicit
223
+ * back-pointer ({@link workspacePointer}) when the area lives outside the tree. Climbing
224
+ * past the first declaration used to hand the deepest directory to the *furthest* ancestor
225
+ * declaring a workspace — by construction not its owner — so two workspaces side by side
226
+ * under a third pooled their documents into the outermost one's flat `<slug>.md` namespace
227
+ * and overwrote each other (IS-8hv2wn).
228
+ */
229
+ export function findProjectOrbitMapDir(startDir = process.cwd()) {
230
+ const globals = new Set(globalConfigPaths().map(canonical));
231
+ const homeStop = canonical(homedir());
232
+ let nearest;
144
233
  let dir = resolve(startDir);
145
234
  for (;;) {
146
235
  const orbitmapDir = join(dir, ORBITMAP_DIR);
147
- const candidate = join(orbitmapDir, CONFIG_FILENAME);
148
- if (existsSync(candidate) &&
149
- !globals.has(canonical(candidate)) &&
150
- !isOrbitMapHomeDir(orbitmapDir)) {
151
- found.push(candidate);
236
+ if (existsSync(orbitmapDir) &&
237
+ !isOrbitMapHomeDir(orbitmapDir) &&
238
+ !globals.has(canonical(join(orbitmapDir, CONFIG_FILENAME)))) {
239
+ nearest ??= dir;
240
+ // A back-pointer wins outright: this directory has been told, in writing, where its
241
+ // workspace lives, so there is nothing left to search for.
242
+ const pointer = workspacePointer(orbitmapDir);
243
+ if (pointer)
244
+ return pointer;
245
+ // The FIRST declaring config is the owner. Climbing further would hand `startDir`
246
+ // to whichever unrelated ancestor happens to declare a workspace too — by
247
+ // definition not the one that owns it (IS-8hv2wn).
248
+ if (declaresWorkspace(orbitmapDir))
249
+ return dir;
152
250
  }
153
251
  if (canonical(dir) === homeStop)
154
- return found;
252
+ break;
155
253
  const parent = dirname(dir);
156
254
  if (parent === dir)
157
- return found;
255
+ break;
158
256
  dir = parent;
159
257
  }
258
+ return nearest;
259
+ }
260
+ /** Parse a JSON file into a plain object, or `undefined` when it is missing/unreadable. */
261
+ function readJsonObject(file) {
262
+ try {
263
+ const raw = JSON.parse(readFileSync(file, 'utf-8'));
264
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
265
+ return undefined;
266
+ return raw;
267
+ }
268
+ catch {
269
+ return undefined;
270
+ }
271
+ }
272
+ /**
273
+ * The workspace **directory** this `.orbitmap/` points at, when it points at one elsewhere.
274
+ *
275
+ * An area directory living outside the workspace tree cannot reach the workspace root by
276
+ * walking up — there is nothing above it but unrelated directories. `init` therefore writes
277
+ * `{"workspace": "<path>"}` into that area's own machine-local `link.json` (the same field,
278
+ * the same meaning, as in local mode: *where* the workspace is). Following it is what keeps
279
+ * such an area sharing the one global `docs/` instead of quietly starting a second copy.
280
+ *
281
+ * Accepts either the workspace root or its `.orbitmap/` directory — `resolveWorkspacePath`
282
+ * hands back the latter for a `workspace.yml`-marked local workspace, so both spellings of
283
+ * "the workspace is over there" exist in the wild, and both must mean the same directory
284
+ * here. Relative paths resolve against the link file, exactly as in `resolveWorkspacePath`.
285
+ */
286
+ function workspacePointer(orbitmapDir) {
287
+ const link = readJsonObject(join(orbitmapDir, LINK_FILENAME));
288
+ const target = link?.['workspace'];
289
+ if (typeof target !== 'string' || !target)
290
+ return undefined;
291
+ const resolved = resolve(orbitmapDir, target);
292
+ return basename(resolved) === ORBITMAP_DIR ? dirname(resolved) : resolved;
293
+ }
294
+ /**
295
+ * Does this `.orbitmap/` belong to a *workspace* root rather than an area directory?
296
+ *
297
+ * The distinction is **location**, not identity. An out-of-tree area marker also carries a
298
+ * `workspace` field, because it needs the slug for the `X-Orbitmap-Workspace` header and has
299
+ * no root config to inherit it from — but naming your workspace is not the same as being its
300
+ * root, and reading it as one gave that directory its own second copy of every document.
301
+ * A file that pins an `area` is a marker, full stop; a root is what maps `areas` (or names a
302
+ * workspace without claiming to be an area). Read directly rather than through
303
+ * `project-config.ts`, which sits above this module.
304
+ *
305
+ * `link.json` is deliberately *not* enough on its own: its `workspace` field is a pointer at
306
+ * a workspace somewhere else (see {@link workspacePointer}) — the opposite of being one. Only
307
+ * its `areas` map, which lists the out-of-tree areas *of this workspace*, marks a root.
308
+ */
309
+ function declaresWorkspace(orbitmapDir) {
310
+ const link = readJsonObject(join(orbitmapDir, LINK_FILENAME));
311
+ if (isPlainObject(link?.['areas']))
312
+ return true;
313
+ const raw = readJsonObject(join(orbitmapDir, CONFIG_FILENAME));
314
+ if (!raw)
315
+ return false;
316
+ if (isPlainObject(raw['areas']))
317
+ return true;
318
+ // `typeof null === 'object'` used to make `"areas": null` a workspace root; hence the
319
+ // explicit shape check above and the `area`-marker exclusion here.
320
+ return typeof raw['workspace'] === 'string' && typeof raw['area'] !== 'string';
321
+ }
322
+ /** A non-null, non-array object — what an `areas` map has to be to mean anything. */
323
+ function isPlainObject(value) {
324
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
160
325
  }
161
326
  /**
162
327
  * Per-field walk-up: hand each project config file, nearest first, to `read` and return the
package/dist/paths.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC;AAExC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;AAE7C,mFAAmF;AACnF,MAAM,CAAC,MAAM,oBAAoB,GAAG,kBAAkB,CAAC;AAkBvD,IAAI,SAA0D,CAAC;AAE/D;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB;IACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,EAAE,CAAC;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC;IACjD,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,GAAG,GAAG,GAAG,QAAQ,SAAS,GAAG,SAAS,IAAI,EAAE,CAAC;IAEnD,IAAI,SAAS,EAAE,GAAG,KAAK,GAAG;QAAE,OAAO,SAAS,CAAC,IAAI,CAAC;IAElD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,QAAQ,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAElE,MAAM,QAAQ,GAAiB,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;IAClD,SAAS,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IACpC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,IAAkB,EAAE,QAAgB;IAClE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACzC,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAEvE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC9C,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;AAC/C,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,IAAkB,EAAE,QAAgB;IACnE,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED;2EAC2E;AAC3E,MAAM,UAAU,oBAAoB;IAClC,OAAO,eAAe,CAAC,mBAAmB,EAAE,EAAE,eAAe,CAAC,CAAC;AACjE,CAAC;AAED;mEACmE;AACnE,MAAM,UAAU,qBAAqB;IACnC,OAAO,gBAAgB,CAAC,mBAAmB,EAAE,EAAE,eAAe,CAAC,CAAC;AAClE,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,eAAe;IAC7B,OAAO,gBAAgB,CAAC,mBAAmB,EAAE,EAAE,oBAAoB,CAAC,CAAC;AACvE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC;IAChD,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,GAAG;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC;IACnF,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAS,SAAS,CAAC,IAAY;IAC7B,IAAI,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,gFAAgF;QAChF,gFAAgF;IAClF,CAAC;IACD,OAAO,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;AAChE,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAS,iBAAiB,CAAC,WAAmB;IAC5C,OAAO,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,oBAAoB,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,sBAAsB,CAAC,WAAmB,OAAO,CAAC,GAAG,EAAE;IACrE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;IAEtC,IAAI,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5B,SAAS,CAAC;QACR,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QACrD,IACE,UAAU,CAAC,SAAS,CAAC;YACrB,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YAClC,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAC/B,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;QAED,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAE9C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,KAAK,CAAC;QACjC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAgB,EAChB,IAAqC;IAErC,KAAK,MAAM,IAAI,IAAI,sBAAsB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QACzB,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAClD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
@@ -41,11 +41,52 @@ export interface OrbitMapConfig {
41
41
  workspace_path?: string;
42
42
  /** Name of the agent profile to use — looked up in `~/.orbitmap/credentials.json`. */
43
43
  agent?: string;
44
- /** Default area slug/id. */
44
+ /**
45
+ * Workspace this config belongs to — a workspace **slug or UUID**, never a path (the
46
+ * *directory* is `workspace_path` / the `link.json` pointer / the marker walk-up).
47
+ *
48
+ * Written into the workspace directory's own `.orbitmap/config.json` (schema v2), which is
49
+ * committable and holds no secrets. Purely declarative here: this field says *which*
50
+ * workspace the tree belongs to, it does not participate in the workspace-directory ladder.
51
+ */
52
+ workspace?: string;
53
+ /**
54
+ * Directory → area slug map (schema v2): "the code for area `x` lives in this directory".
55
+ *
56
+ * **Keys are relative directory paths, resolved against the parent of the `.orbitmap`
57
+ * directory that holds this config file** — i.e. against the workspace/project root, not
58
+ * against the current directory and not against `.orbitmap/` itself. `"."` is that root.
59
+ * Keys are normalised on read ({@link normalizeAreaPathKey}): backslashes become `/`,
60
+ * `./` prefixes and trailing slashes are dropped, and an empty result becomes `"."`.
61
+ *
62
+ * Values are area slugs.
63
+ *
64
+ * **Absolute keys and keys that escape upward (`..`) are rejected**, deliberately: a
65
+ * directory outside the tree is machine-specific and therefore belongs in the gitignored
66
+ * `link.json` (see `WorkspaceLink.areas` in `workspace-resolve.ts`), not in a file meant to
67
+ * be committed and to mean the same thing in every checkout.
68
+ */
69
+ areas?: Record<string, string>;
70
+ /**
71
+ * Default area slug/id.
72
+ *
73
+ * Still read on every rung for back-compat — an existing project config that pins `area`
74
+ * keeps working unchanged. Schema v2 replaces it with the directory-scoped {@link areas}
75
+ * map plus a per-area marker config, so `orbitmap init` no longer *writes* this field.
76
+ */
45
77
  area?: string;
46
78
  /** @deprecated Legacy secret, read once for migration then dropped. Never written. */
47
79
  api_key?: string;
48
80
  }
81
+ /**
82
+ * Canonical form of one {@link OrbitMapConfig.areas} key, or `undefined` when the key is not
83
+ * a usable relative directory path.
84
+ *
85
+ * Normalises what a hand-written (or Windows-written) config may contain — `.\packages\api`,
86
+ * `packages/api/`, `./` — into one comparable form (`packages/api`, `.`). Rejects absolute
87
+ * paths and any `..` segment; see {@link OrbitMapConfig.areas} for why.
88
+ */
89
+ export declare function normalizeAreaPathKey(key: string): string | undefined;
49
90
  /**
50
91
  * Parse and validate a raw config object (either file) — mistyped fields drop out instead
51
92
  * of propagating a garbage value into the resolution ladders. Unknown keys are not
@@ -1,5 +1,51 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { resolveProjectField } from './paths.js';
3
+ /** Matches a Windows drive prefix (`C:`), which `node:path`'s POSIX `isAbsolute` does not —
4
+ * an `areas` key is a portable, committable path and must be rejected on every platform. */
5
+ const WINDOWS_DRIVE = /^[a-zA-Z]:/;
6
+ /**
7
+ * Canonical form of one {@link OrbitMapConfig.areas} key, or `undefined` when the key is not
8
+ * a usable relative directory path.
9
+ *
10
+ * Normalises what a hand-written (or Windows-written) config may contain — `.\packages\api`,
11
+ * `packages/api/`, `./` — into one comparable form (`packages/api`, `.`). Rejects absolute
12
+ * paths and any `..` segment; see {@link OrbitMapConfig.areas} for why.
13
+ */
14
+ export function normalizeAreaPathKey(key) {
15
+ const slashed = key.replace(/\\/g, '/').trim();
16
+ if (!slashed)
17
+ return undefined;
18
+ if (slashed.startsWith('/') || WINDOWS_DRIVE.test(slashed))
19
+ return undefined;
20
+ const segments = slashed.split('/').filter((part) => part.length > 0 && part !== '.');
21
+ if (segments.some((part) => part === '..'))
22
+ return undefined;
23
+ return segments.length === 0 ? '.' : segments.join('/');
24
+ }
25
+ /**
26
+ * Validate the `areas` map: a plain object of relative-directory → non-empty area slug.
27
+ *
28
+ * **Rejected per entry, not per file.** One unusable key (absolute, `..`, non-string value)
29
+ * must not take the rest of a repository's mapping down with it — the same reasoning as the
30
+ * per-field validation in {@link parseConfig}. When nothing valid is left the field itself is
31
+ * dropped, so a ladder sees "not set" rather than an empty map. On a collision (two keys
32
+ * normalising to the same directory) the first entry wins.
33
+ */
34
+ function parseAreasMap(raw) {
35
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
36
+ return undefined;
37
+ const areas = {};
38
+ for (const [key, value] of Object.entries(raw)) {
39
+ if (typeof value !== 'string' || !value)
40
+ continue;
41
+ const dir = normalizeAreaPathKey(key);
42
+ if (dir === undefined)
43
+ continue;
44
+ if (!(dir in areas))
45
+ areas[dir] = value;
46
+ }
47
+ return Object.keys(areas).length > 0 ? areas : undefined;
48
+ }
3
49
  /**
4
50
  * Parse and validate a raw config object (either file) — mistyped fields drop out instead
5
51
  * of propagating a garbage value into the resolution ladders. Unknown keys are not
@@ -19,6 +65,11 @@ export function parseConfig(raw) {
19
65
  }
20
66
  if (typeof r['agent'] === 'string' && r['agent'])
21
67
  config.agent = r['agent'];
68
+ if (typeof r['workspace'] === 'string' && r['workspace'])
69
+ config.workspace = r['workspace'];
70
+ const areas = parseAreasMap(r['areas']);
71
+ if (areas)
72
+ config.areas = areas;
22
73
  if (typeof r['area'] === 'string' && r['area'])
23
74
  config.area = r['area'];
24
75
  if (typeof r['api_key'] === 'string' && r['api_key'])
@@ -1 +1 @@
1
- {"version":3,"file":"project-config.js","sourceRoot":"","sources":["../src/project-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAqDjD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACvD,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,IAAI,OAAO,CAAC,CAAC,gBAAgB,CAAC,KAAK,QAAQ;QAAE,MAAM,CAAC,cAAc,GAAG,CAAC,CAAC,gBAAgB,CAAC,CAAC;IACzF,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,OAAO;QAAE,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAC5E,IAAI,OAAO,CAAC,CAAC,gBAAgB,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACnE,MAAM,CAAC,cAAc,GAAG,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC;QAAE,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;IAC5E,IAAI,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC;QAAE,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IACxE,IAAI,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,CAAC;QAAE,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;IAEpF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;sFACsF;AACtF,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AASD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAAQ,EACR,WAAmB,OAAO,CAAC,GAAG,EAAE;IAEhC,MAAM,GAAG,GAAG,mBAAmB,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1F,OAAO,GAAG;QACR,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAuC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE;QACxE,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"project-config.js","sourceRoot":"","sources":["../src/project-config.ts"],"names":[],"mappings":"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"}
@@ -9,12 +9,37 @@ export { ORBITMAP_DIR };
9
9
  export declare const LINK_FILE = "link.json";
10
10
  /** Marker file that identifies a directory as a local workspace. */
11
11
  export declare const WORKSPACE_FILE = "workspace.yml";
12
- /** Contents of `<project>/.orbitmap/link.json`. */
12
+ /**
13
+ * Contents of a `.orbitmap/link.json` — the **machine-specific, gitignored** half of the
14
+ * OrbitMap files, holding everything that is a path on *this* disk.
15
+ *
16
+ * Two shapes, both valid, distinguished only by which fields are present:
17
+ *
18
+ * - **project-level** (the original): `workspace` points at the workspace directory living
19
+ * elsewhere on this machine, `area` names the area this project is.
20
+ * - **workspace-level** (schema v2): the file sits in the workspace's own `.orbitmap/` and
21
+ * carries {@link WorkspaceLink.areas} — the areas whose code is *not* under the workspace
22
+ * tree, which therefore cannot be expressed in the committable
23
+ * `config.json` `areas` map (see `OrbitMapConfig.areas`).
24
+ *
25
+ * Nothing forbids a single file from having all three fields; they are independent.
26
+ */
13
27
  export interface WorkspaceLink {
14
28
  /** Absolute path, or a path relative to the link file itself. */
15
29
  workspace?: string;
16
30
  /** Area slug within that workspace. */
17
31
  area?: string;
32
+ /**
33
+ * Area slug → the directory holding that area's code, for areas that live **outside the
34
+ * workspace tree** (the inverse direction of `OrbitMapConfig.areas`, which maps
35
+ * in-tree directory → slug).
36
+ *
37
+ * Values are absolute paths; a relative one is resolved against the link file's own
38
+ * directory, exactly like {@link WorkspaceLink.workspace}. This is why the mapping is
39
+ * split across two files: an absolute path is true only on one machine, so it must not
40
+ * land in the committable `config.json`.
41
+ */
42
+ areas?: Record<string, string>;
18
43
  }
19
44
  /** A `link.json` that was found on disk, with the file it came from. */
20
45
  export interface FoundWorkspaceLink {
@@ -24,6 +49,8 @@ export interface FoundWorkspaceLink {
24
49
  }
25
50
  export declare function setWorkspaceFlag(value?: string): void;
26
51
  export declare function getWorkspaceFlag(): string | undefined;
52
+ export declare function setWorkspaceIdFlag(value?: string): void;
53
+ export declare function getWorkspaceIdFlag(): string | undefined;
27
54
  /**
28
55
  * Walk up from `startDir` to the filesystem root looking for `.orbitmap/link.json`
29
56
  * (same discovery behaviour as git). Returns the nearest one, or `undefined`.
@@ -38,9 +65,22 @@ export declare function findLinkFile(startDir?: string): FoundWorkspaceLink | un
38
65
  * subdirectory is both what makes the mode local and what the path ladder returns.
39
66
  */
40
67
  export declare function findWorkspaceDir(startDir?: string): string | undefined;
68
+ /**
69
+ * Parse and validate a raw `link.json` object — **per field**, like `parseConfig`: a
70
+ * mistyped `area` must not stop the `workspace` pointer next to it from working, and a
71
+ * single bad entry in {@link WorkspaceLink.areas} must not discard the rest of the map.
72
+ * Unknown keys are not represented here; nothing in the CLI rewrites this file wholesale.
73
+ */
74
+ export declare function parseWorkspaceLink(raw: unknown): WorkspaceLink | undefined;
41
75
  export interface ResolveOptions {
42
76
  /** Explicit `--workspace <path>`; defaults to the captured global flag. */
43
77
  workspace?: string;
78
+ /**
79
+ * Explicit `--workspace-id <slug-or-uuid>`; defaults to the captured global flag.
80
+ * Identifies a workspace in the backend — unrelated to {@link ResolveOptions.workspace},
81
+ * which is a directory on this disk.
82
+ */
83
+ workspaceId?: string;
44
84
  /** Explicit `--area <slug>`. */
45
85
  area?: string;
46
86
  /** Directory the resolution starts from (defaults to `process.cwd()`). */
@@ -105,13 +145,54 @@ export declare function resolveWorkspacePath(options?: ResolveOptions): string;
105
145
  /**
106
146
  * Which rung of the area ladder produced a slug.
107
147
  *
108
- * `flag`/`env`/`link`/`project-config` are **explicit**: the user, the project's own
109
- * `link.json`, or the project's own `.orbitmap/config.json` named that area, so a slug that
110
- * does not exist must fail loudly. `config` is the machine-wide default (the deprecated
111
- * global `~/.orbitmap/config.json` `area` ADR 0002) the weakest rung, and the only one
112
- * a local workspace is allowed to override or ignore (see `createLocalContext`).
148
+ * `flag`/`env`/`link`/`project-config`/`cwd-map` are **explicit**: the user, the project's
149
+ * own `link.json`, or the project's own `.orbitmap/config.json` named that area (directly or
150
+ * by mapping the current directory to it), so a slug that does not exist must fail loudly.
151
+ * `config` is the machine-wide default (the deprecated global `~/.orbitmap/config.json`
152
+ * `area` — ADR 0002) — the weakest rung, and the only one a local workspace is allowed to
153
+ * override or ignore (see `createLocalContext`).
154
+ */
155
+ export type AreaSource = 'flag' | 'env' | 'link' | 'project-config' | 'cwd-map' | 'config';
156
+ /** One entry of the directory ↔ area mapping: "the code for `area` lives in `dir`". */
157
+ export interface AreaDirectoryMapping {
158
+ /** Area slug. */
159
+ area: string;
160
+ /** Absolute directory holding that area's code. */
161
+ dir: string;
162
+ /** Which file declared it: the committable `config.json` or the machine-local `link.json`. */
163
+ from: 'config' | 'link';
164
+ }
165
+ /**
166
+ * True when `child` is `dir` itself or lives underneath it. Segment-wise (via `relative`)
167
+ * rather than a string prefix, so `/repo/app` does not swallow `/repo/app-legacy`, and
168
+ * case-folded on win32 via {@link comparablePath}, where `C:\Work\Repo` and `c:\work\repo`
169
+ * are one directory.
170
+ *
171
+ * Exported because `init` asks the same question about the same paths on the write side. It
172
+ * had grown its own weaker copy — no case-fold, and a `/^[a-zA-Z]:/` test on the *relative*
173
+ * result standing in for "different drive" — under which a root recorded as `C:\Work\Repo`
174
+ * did not contain `c:\work\repo\pkg`, so `init` silently laid down a second, competing
175
+ * `areas` map for the same workspace (TS-5c4yg9). One question, one answer.
113
176
  */
114
- export type AreaSource = 'flag' | 'env' | 'link' | 'project-config' | 'config';
177
+ export declare function isWithin(dir: string, child: string): boolean;
178
+ /**
179
+ * Every directory → area mapping visible from `cwd`, **deepest first** (so the first match is
180
+ * the most specific one, exactly like the nearest-file rule everywhere else).
181
+ *
182
+ * Two sources, both schema v2, pointing in opposite directions on purpose:
183
+ *
184
+ * - `OrbitMapConfig.areas` (directory → slug) from the nearest `.orbitmap/config.json` that
185
+ * **sets `areas`** (per-field walk-up, ADR 0002 §2). Its keys are relative to the parent of
186
+ * the `.orbitmap` directory holding that file — the workspace/project root — so they mean
187
+ * the same thing in every checkout, which is why that file is the committable one.
188
+ * - `WorkspaceLink.areas` (slug → directory) from the nearest `.orbitmap/link.json`, for
189
+ * areas whose code sits *outside* the tree. Absolute, or relative to the link file itself.
190
+ *
191
+ * Purely lexical: no directory is required to exist. Used both by the `cwd-map` rung of
192
+ * {@link resolveArea} and by the `AREA_CONTEXT_REQUIRED` details, so the error always lists
193
+ * exactly the directories that would have resolved an area.
194
+ */
195
+ export declare function resolveAreaDirectoryMap(options?: ResolveOptions): AreaDirectoryMapping[];
115
196
  /** An area slug together with the rung it came from. */
116
197
  export interface ResolvedAreaSlug {
117
198
  area?: string;
@@ -120,11 +201,25 @@ export interface ResolvedAreaSlug {
120
201
  /**
121
202
  * Area resolution (ADR 0002, extending spec §3.3), explicit sources only:
122
203
  * `--area` → `ORBITMAP_AREA` → `link.json.area` → nearest `.orbitmap/config.json`
123
- * **that sets `area`** **[ADR 0002]** → global config `area` (deprecated).
204
+ * **that sets `area`** **[ADR 0002]** → **cwd matched against the `areas` directory map**
205
+ * (schema v2) → global config `area` (deprecated).
124
206
  *
125
207
  * The project-config rung is per-field (`resolveProjectConfigField`): a nested config that
126
208
  * sets only `agent` does not hide an `area` pinned by the repository root.
127
209
  *
210
+ * Every file-reading rung is bounded by {@link workspaceBoundary}: the config that names the
211
+ * `workspace` is the furthest ancestor allowed to name the area, because an area named above
212
+ * it belongs to another workspace (IS-e6q7je). The machine-wide `config` rung is left alone —
213
+ * it is already deprecated and warns on use.
214
+ *
215
+ * The `cwd-map` rung is what lets one workspace cover a whole monorepo without a config file
216
+ * in every package: the workspace's `config.json` says which directory is which area, and the
217
+ * current directory picks one (deepest mapping wins — see {@link resolveAreaDirectoryMap}).
218
+ * It sits *below* the `area` field on purpose, so a per-directory marker — a
219
+ * `.orbitmap/config.json` holding nothing but `area`, which is all schema v2 needs to pin one
220
+ * directory — still overrides the map, and every pre-v2 config that pins `area` keeps
221
+ * behaving exactly as it did.
222
+ *
128
223
  * The two remaining rungs — the workspace's own `workspace.yml` `default_area` (which
129
224
  * sits *between* the project config and the global config) and "workspace has exactly one
130
225
  * area → auto-select it" — require reading the workspace tree and therefore live in the
@@ -138,15 +233,90 @@ export declare function resolveAreaSlug(options?: ResolveOptions): string | unde
138
233
  /**
139
234
  * Assert that an area was resolved. Adapters call this after their own last-resort
140
235
  * auto-selection; `availableAreas` (when known) is surfaced in the error details.
236
+ *
237
+ * Missing area context is **not** a global error — most of the CLI (missions, intents, the
238
+ * workspace-level document tree) works without one, so `resolveWorkspaceContext` returns
239
+ * `scope: 'workspace'` rather than throwing. Only the operations that genuinely need an area
240
+ * come here, which is why the details carry `mapped_directories`: when an area exists but the
241
+ * current directory is not inside it, the fix is to `cd` into one of the listed directories
242
+ * (or pass `--area`), and the error is the only place that can say which ones those are.
243
+ *
244
+ * `options` scopes that lookup; omitted, it is computed from `process.cwd()` — the right
245
+ * answer for a real invocation, and what lets callers deep in the adapters (which have no
246
+ * `ResolveOptions` of their own) still produce a useful error.
247
+ */
248
+ export declare function requireArea(area: string | undefined, availableAreas?: string[], options?: ResolveOptions): string;
249
+ /**
250
+ * The same "which areas, and how to get into one" advice, for an `AREA_CONTEXT_REQUIRED`
251
+ * that arrived **from the server** (TS-jt6q36 follow-up).
252
+ *
253
+ * The API answers with its own text ("Pass area_id parameter or set the X-Orbitmap-Area
254
+ * header. Call GET /api/agent/areas…"), which describes the HTTP contract, not the CLI a
255
+ * person is actually holding — and it used to reach the terminal verbatim, so the improved
256
+ * local wording only ever showed up in local mode. This is what the cloud adapter wraps it
257
+ * with. Unlike {@link requireArea} it never knows the workspace's full area list (that would
258
+ * cost a request), so it falls back to the ways out alone, which are always actionable.
141
259
  */
142
- export declare function requireArea(area: string | undefined, availableAreas?: string[]): string;
260
+ export declare function areaContextHint(options?: ResolveOptions): {
261
+ message: string;
262
+ mappedDirectories: Array<{
263
+ area: string;
264
+ dir: string;
265
+ }>;
266
+ };
267
+ /**
268
+ * Which rung of the workspace-identity ladder produced the slug/uuid — mirrors
269
+ * {@link AreaSource}, minus the rungs that only ever name an area.
270
+ */
271
+ export type WorkspaceSource = 'flag' | 'env' | 'project-config' | 'config';
272
+ /** A workspace slug/uuid together with the rung it came from. */
273
+ export interface ResolvedWorkspaceId {
274
+ workspace?: string;
275
+ source?: WorkspaceSource;
276
+ }
277
+ /**
278
+ * Workspace **identity** resolution (schema v2), first match wins:
279
+ * 1. `--workspace-id <slug-or-uuid>` flag
280
+ * 2. `ORBITMAP_WORKSPACE_ID` env var
281
+ * 3. nearest `.orbitmap/config.json` **that sets `workspace`** (per-field, ADR 0002 §2)
282
+ * 4. global config `workspace`
283
+ *
284
+ * Strictly separate from {@link resolveWorkspacePath}, which answers a different question —
285
+ * *which directory on this disk* — and keeps its own unchanged ladder (`--workspace` →
286
+ * `ORBITMAP_WORKSPACE` → `link.json` → `workspace.yml` → `workspace_path`). A slug is never a
287
+ * path and a path is never a slug: `--workspace <path>` still means local mode and this rung
288
+ * never touches it, `--workspace-id` never implies a mode.
289
+ *
290
+ * Undefined is a normal outcome (an un-migrated cloud project, or local mode, where the
291
+ * workspace is a directory), not an error.
292
+ */
293
+ export declare function resolveWorkspaceId(options?: ResolveOptions): ResolvedWorkspaceId;
294
+ /**
295
+ * What the current invocation is scoped to.
296
+ *
297
+ * `'area'` when an area resolved — the historical behaviour, and what area-scoped commands
298
+ * need. `'workspace'` when none did: not an error, just a wider scope (missions, intents and
299
+ * workspace-level documents live there). Commands that cannot work without an area call
300
+ * {@link requireArea} and get `AREA_CONTEXT_REQUIRED`.
301
+ */
302
+ export type ContextScope = 'area' | 'workspace';
143
303
  export interface ResolvedWorkspaceContext {
144
304
  mode: OrbitMapMode;
145
305
  /** Absolute workspace directory; only resolved in local mode. */
146
306
  workspacePath?: string;
307
+ /**
308
+ * Workspace **identity** — slug or uuid, never a path (see {@link resolveWorkspaceId}).
309
+ * Independent of {@link ResolvedWorkspaceContext.workspacePath}; either, both or neither
310
+ * may be set.
311
+ */
312
+ workspace?: string;
313
+ /** Which rung produced {@link ResolvedWorkspaceContext.workspace}. */
314
+ workspaceSource?: WorkspaceSource;
147
315
  area?: string;
148
316
  /** Which rung of the area ladder produced {@link ResolvedWorkspaceContext.area}. */
149
317
  areaSource?: AreaSource;
318
+ /** `'area'` when an area resolved, `'workspace'` otherwise. Never absent. */
319
+ scope: ContextScope;
150
320
  link?: FoundWorkspaceLink;
151
321
  config: OrbitMapConfig;
152
322
  }