@agentxm/workspace-state 0.28.4-bootstrap.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +11 -0
  3. package/dist/src/index.d.ts +72 -0
  4. package/dist/src/index.js +88 -0
  5. package/dist/src/knowledge/discovery-config.d.ts +12 -0
  6. package/dist/src/knowledge/discovery-config.js +9 -0
  7. package/dist/src/lockfile/accepted-registry-version.d.ts +12 -0
  8. package/dist/src/lockfile/accepted-registry-version.js +13 -0
  9. package/dist/src/lockfile/entry-fields.d.ts +189 -0
  10. package/dist/src/lockfile/entry-fields.js +89 -0
  11. package/dist/src/lockfile/errors.d.ts +48 -0
  12. package/dist/src/lockfile/errors.js +27 -0
  13. package/dist/src/lockfile/index.d.ts +18 -0
  14. package/dist/src/lockfile/index.js +16 -0
  15. package/dist/src/lockfile/lockfile.d.ts +2255 -0
  16. package/dist/src/lockfile/lockfile.js +235 -0
  17. package/dist/src/lockfile/resolved-version.d.ts +4 -0
  18. package/dist/src/lockfile/resolved-version.js +7 -0
  19. package/dist/src/lockfile/schema.d.ts +3062 -0
  20. package/dist/src/lockfile/schema.js +406 -0
  21. package/dist/src/schema/index.d.ts +3 -0
  22. package/dist/src/schema/index.js +2 -0
  23. package/dist/src/schema/read-and-validate-json-file.d.ts +20 -0
  24. package/dist/src/schema/read-and-validate-json-file.js +85 -0
  25. package/dist/src/schema/types.d.ts +26 -0
  26. package/dist/src/schema/types.js +2 -0
  27. package/dist/src/settings/errors.d.ts +22 -0
  28. package/dist/src/settings/errors.js +15 -0
  29. package/dist/src/settings/format-preserving-json.d.ts +40 -0
  30. package/dist/src/settings/format-preserving-json.js +80 -0
  31. package/dist/src/settings/index.d.ts +15 -0
  32. package/dist/src/settings/index.js +14 -0
  33. package/dist/src/settings/schema.d.ts +687 -0
  34. package/dist/src/settings/schema.js +977 -0
  35. package/dist/src/settings/settings.d.ts +38 -0
  36. package/dist/src/settings/settings.js +213 -0
  37. package/dist/src/testing.d.ts +17 -0
  38. package/dist/src/testing.js +17 -0
  39. package/dist/src/utils/atomic-write.d.ts +67 -0
  40. package/dist/src/utils/atomic-write.js +71 -0
  41. package/dist/src/utils/environment.d.ts +16 -0
  42. package/dist/src/utils/environment.js +19 -0
  43. package/dist/src/utils/path-safety.d.ts +25 -0
  44. package/dist/src/utils/path-safety.js +34 -0
  45. package/dist/src/utils/resolve-parent-symlinks.d.ts +16 -0
  46. package/dist/src/utils/resolve-parent-symlinks.js +36 -0
  47. package/dist/src/workspace/accepted-canonical-ref.d.ts +58 -0
  48. package/dist/src/workspace/accepted-canonical-ref.js +229 -0
  49. package/dist/src/workspace/artifact-change.d.ts +14 -0
  50. package/dist/src/workspace/artifact-change.js +18 -0
  51. package/dist/src/workspace/canonical-observation.d.ts +39 -0
  52. package/dist/src/workspace/canonical-observation.js +307 -0
  53. package/dist/src/workspace/configured-agent-outcome.d.ts +22 -0
  54. package/dist/src/workspace/configured-agent-outcome.js +33 -0
  55. package/dist/src/workspace/configured-agent-outcomes-provider.d.ts +50 -0
  56. package/dist/src/workspace/configured-agent-outcomes-provider.js +26 -0
  57. package/dist/src/workspace/configured-agent-outcomes.d.ts +48 -0
  58. package/dist/src/workspace/configured-agent-outcomes.js +113 -0
  59. package/dist/src/workspace/configured-entry-resolution/types.d.ts +32 -0
  60. package/dist/src/workspace/configured-entry-resolution/types.js +2 -0
  61. package/dist/src/workspace/configured-entry-resolution/workspace-ref.d.ts +32 -0
  62. package/dist/src/workspace/configured-entry-resolution/workspace-ref.js +176 -0
  63. package/dist/src/workspace/constants.d.ts +6 -0
  64. package/dist/src/workspace/constants.js +6 -0
  65. package/dist/src/workspace/create-symlink.d.ts +26 -0
  66. package/dist/src/workspace/create-symlink.js +87 -0
  67. package/dist/src/workspace/desired-pack-lock.d.ts +18 -0
  68. package/dist/src/workspace/desired-pack-lock.js +132 -0
  69. package/dist/src/workspace/desired-state-enabled.d.ts +18 -0
  70. package/dist/src/workspace/desired-state-enabled.js +16 -0
  71. package/dist/src/workspace/desired-state-graph.d.ts +114 -0
  72. package/dist/src/workspace/desired-state-graph.js +384 -0
  73. package/dist/src/workspace/desired-state-problem-text.d.ts +6 -0
  74. package/dist/src/workspace/desired-state-problem-text.js +27 -0
  75. package/dist/src/workspace/errors.d.ts +184 -0
  76. package/dist/src/workspace/errors.js +79 -0
  77. package/dist/src/workspace/extension-name.d.ts +28 -0
  78. package/dist/src/workspace/extension-name.js +66 -0
  79. package/dist/src/workspace/extension-paths.d.ts +82 -0
  80. package/dist/src/workspace/extension-paths.js +172 -0
  81. package/dist/src/workspace/footprint-recorder.d.ts +30 -0
  82. package/dist/src/workspace/footprint-recorder.js +32 -0
  83. package/dist/src/workspace/layout.d.ts +43 -0
  84. package/dist/src/workspace/layout.js +164 -0
  85. package/dist/src/workspace/lock-entry-to-ref.d.ts +40 -0
  86. package/dist/src/workspace/lock-entry-to-ref.js +433 -0
  87. package/dist/src/workspace/lock-entry-to-source-params.d.ts +26 -0
  88. package/dist/src/workspace/lock-entry-to-source-params.js +81 -0
  89. package/dist/src/workspace/locked-entries.d.ts +30 -0
  90. package/dist/src/workspace/locked-entries.js +34 -0
  91. package/dist/src/workspace/materialized-file-target.d.ts +14 -0
  92. package/dist/src/workspace/materialized-file-target.js +14 -0
  93. package/dist/src/workspace/materialized-tree.d.ts +23 -0
  94. package/dist/src/workspace/materialized-tree.js +90 -0
  95. package/dist/src/workspace/mcp-entry-semantics.d.ts +31 -0
  96. package/dist/src/workspace/mcp-entry-semantics.js +60 -0
  97. package/dist/src/workspace/observed-installed.d.ts +9 -0
  98. package/dist/src/workspace/observed-installed.js +9 -0
  99. package/dist/src/workspace/pack-manifest-content-identity.d.ts +12 -0
  100. package/dist/src/workspace/pack-manifest-content-identity.js +20 -0
  101. package/dist/src/workspace/pack-paths.d.ts +24 -0
  102. package/dist/src/workspace/pack-paths.js +16 -0
  103. package/dist/src/workspace/package-hash.d.ts +25 -0
  104. package/dist/src/workspace/package-hash.js +45 -0
  105. package/dist/src/workspace/paths.d.ts +24 -0
  106. package/dist/src/workspace/paths.js +52 -0
  107. package/dist/src/workspace/read-model/__fixtures__/builder.d.ts +199 -0
  108. package/dist/src/workspace/read-model/__fixtures__/builder.js +588 -0
  109. package/dist/src/workspace/read-model/__fixtures__/decoders.d.ts +15 -0
  110. package/dist/src/workspace/read-model/__fixtures__/decoders.js +15 -0
  111. package/dist/src/workspace/read-model/__fixtures__/occurrences.d.ts +72 -0
  112. package/dist/src/workspace/read-model/__fixtures__/occurrences.js +134 -0
  113. package/dist/src/workspace/read-model/__fixtures__/scenario-harness.d.ts +99 -0
  114. package/dist/src/workspace/read-model/__fixtures__/scenario-harness.js +142 -0
  115. package/dist/src/workspace/read-model/__fixtures__/test-layer.d.ts +60 -0
  116. package/dist/src/workspace/read-model/__fixtures__/test-layer.js +57 -0
  117. package/dist/src/workspace/read-model/agent-presence.d.ts +32 -0
  118. package/dist/src/workspace/read-model/agent-presence.js +21 -0
  119. package/dist/src/workspace/read-model/agent-root-resolver.d.ts +38 -0
  120. package/dist/src/workspace/read-model/agent-root-resolver.js +35 -0
  121. package/dist/src/workspace/read-model/agents/index.d.ts +38 -0
  122. package/dist/src/workspace/read-model/agents/index.js +45 -0
  123. package/dist/src/workspace/read-model/agents/shared.d.ts +27 -0
  124. package/dist/src/workspace/read-model/agents/shared.js +63 -0
  125. package/dist/src/workspace/read-model/agents/types.d.ts +133 -0
  126. package/dist/src/workspace/read-model/agents/types.js +51 -0
  127. package/dist/src/workspace/read-model/diagnostics.d.ts +18 -0
  128. package/dist/src/workspace/read-model/diagnostics.js +14 -0
  129. package/dist/src/workspace/read-model/discovery/index.d.ts +5 -0
  130. package/dist/src/workspace/read-model/discovery/index.js +5 -0
  131. package/dist/src/workspace/read-model/discovery/plugin-manifests.d.ts +16 -0
  132. package/dist/src/workspace/read-model/discovery/plugin-manifests.js +168 -0
  133. package/dist/src/workspace/read-model/discovery/skills.d.ts +58 -0
  134. package/dist/src/workspace/read-model/discovery/skills.js +263 -0
  135. package/dist/src/workspace/read-model/discovery/subagents.d.ts +37 -0
  136. package/dist/src/workspace/read-model/discovery/subagents.js +48 -0
  137. package/dist/src/workspace/read-model/errors.d.ts +93 -0
  138. package/dist/src/workspace/read-model/errors.js +30 -0
  139. package/dist/src/workspace/read-model/extensions/actual-helpers.d.ts +4 -0
  140. package/dist/src/workspace/read-model/extensions/actual-helpers.js +4 -0
  141. package/dist/src/workspace/read-model/extensions/hook.d.ts +104 -0
  142. package/dist/src/workspace/read-model/extensions/hook.js +119 -0
  143. package/dist/src/workspace/read-model/extensions/index.d.ts +19 -0
  144. package/dist/src/workspace/read-model/extensions/index.js +43 -0
  145. package/dist/src/workspace/read-model/extensions/indexByName.d.ts +31 -0
  146. package/dist/src/workspace/read-model/extensions/indexByName.js +30 -0
  147. package/dist/src/workspace/read-model/extensions/inventory.d.ts +99 -0
  148. package/dist/src/workspace/read-model/extensions/inventory.js +111 -0
  149. package/dist/src/workspace/read-model/extensions/knowledge.d.ts +101 -0
  150. package/dist/src/workspace/read-model/extensions/knowledge.js +121 -0
  151. package/dist/src/workspace/read-model/extensions/mcp-server.d.ts +106 -0
  152. package/dist/src/workspace/read-model/extensions/mcp-server.js +122 -0
  153. package/dist/src/workspace/read-model/extensions/pack.d.ts +104 -0
  154. package/dist/src/workspace/read-model/extensions/pack.js +123 -0
  155. package/dist/src/workspace/read-model/extensions/package-root.d.ts +39 -0
  156. package/dist/src/workspace/read-model/extensions/package-root.js +48 -0
  157. package/dist/src/workspace/read-model/extensions/projection.d.ts +137 -0
  158. package/dist/src/workspace/read-model/extensions/projection.js +180 -0
  159. package/dist/src/workspace/read-model/extensions/rule.d.ts +104 -0
  160. package/dist/src/workspace/read-model/extensions/rule.js +113 -0
  161. package/dist/src/workspace/read-model/extensions/skill.d.ts +170 -0
  162. package/dist/src/workspace/read-model/extensions/skill.js +148 -0
  163. package/dist/src/workspace/read-model/extensions/subagent.d.ts +97 -0
  164. package/dist/src/workspace/read-model/extensions/subagent.js +120 -0
  165. package/dist/src/workspace/read-model/scanners/agent-dir.d.ts +49 -0
  166. package/dist/src/workspace/read-model/scanners/agent-dir.js +180 -0
  167. package/dist/src/workspace/read-model/scanners/agent-root.d.ts +73 -0
  168. package/dist/src/workspace/read-model/scanners/agent-root.js +148 -0
  169. package/dist/src/workspace/read-model/scanners/agent-settings.d.ts +43 -0
  170. package/dist/src/workspace/read-model/scanners/agent-settings.js +54 -0
  171. package/dist/src/workspace/read-model/scanners/canonical-extensions.d.ts +48 -0
  172. package/dist/src/workspace/read-model/scanners/canonical-extensions.js +237 -0
  173. package/dist/src/workspace/read-model/scanners/fs-helpers.d.ts +63 -0
  174. package/dist/src/workspace/read-model/scanners/fs-helpers.js +106 -0
  175. package/dist/src/workspace/read-model/scanners/index.d.ts +15 -0
  176. package/dist/src/workspace/read-model/scanners/index.js +34 -0
  177. package/dist/src/workspace/read-model/scanners/mcp-config.d.ts +47 -0
  178. package/dist/src/workspace/read-model/scanners/mcp-config.js +212 -0
  179. package/dist/src/workspace/read-model/scanners/types.d.ts +170 -0
  180. package/dist/src/workspace/read-model/scanners/types.js +86 -0
  181. package/dist/src/workspace/read-model/service.d.ts +103 -0
  182. package/dist/src/workspace/read-model/service.js +391 -0
  183. package/dist/src/workspace/read-model/state.d.ts +35 -0
  184. package/dist/src/workspace/read-model/state.js +97 -0
  185. package/dist/src/workspace/read-model/types.d.ts +75 -0
  186. package/dist/src/workspace/read-model/types.js +26 -0
  187. package/dist/src/workspace/read-model-record-readers.d.ts +34 -0
  188. package/dist/src/workspace/read-model-record-readers.js +467 -0
  189. package/dist/src/workspace/read-model-record-rows.d.ts +45 -0
  190. package/dist/src/workspace/read-model-record-rows.js +33 -0
  191. package/dist/src/workspace/read-model-record-types.d.ts +45 -0
  192. package/dist/src/workspace/read-model-record-types.js +13 -0
  193. package/dist/src/workspace/remove-if-exists.d.ts +13 -0
  194. package/dist/src/workspace/remove-if-exists.js +26 -0
  195. package/dist/src/workspace/rendered-files.d.ts +53 -0
  196. package/dist/src/workspace/rendered-files.js +50 -0
  197. package/dist/src/workspace/scope-refusal.d.ts +26 -0
  198. package/dist/src/workspace/scope-refusal.js +29 -0
  199. package/dist/src/workspace/service-interface.d.ts +479 -0
  200. package/dist/src/workspace/service-interface.js +27 -0
  201. package/dist/src/workspace/service.d.ts +1665 -0
  202. package/dist/src/workspace/service.js +1263 -0
  203. package/dist/src/workspace/setup-scope-support.d.ts +27 -0
  204. package/dist/src/workspace/setup-scope-support.js +299 -0
  205. package/dist/src/workspace/skill-paths.d.ts +33 -0
  206. package/dist/src/workspace/skill-paths.js +14 -0
  207. package/dist/src/workspace/source-metadata.d.ts +7 -0
  208. package/dist/src/workspace/source-metadata.js +10 -0
  209. package/dist/src/workspace/source-to-lock-entry.d.ts +32 -0
  210. package/dist/src/workspace/source-to-lock-entry.js +68 -0
  211. package/dist/src/workspace/test-stubs.d.ts +144 -0
  212. package/dist/src/workspace/test-stubs.js +412 -0
  213. package/dist/src/workspace/transaction.d.ts +218 -0
  214. package/dist/src/workspace/transaction.js +177 -0
  215. package/package.json +57 -0
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Agent-directory scanner: enumerates per-agent skill and subagent
3
+ * directories declared by the existing `AgentRegistry`. Each occurrence
4
+ * carries an `agent-dir` discriminator parameterized by `agentId` and the
5
+ * subject `type`.
6
+ *
7
+ * Subject coverage in v1:
8
+ *
9
+ * - skill — every agent with a non-empty `agent.skills.dir`.
10
+ * - subagent — every agent with `agent.subagents?.dir`. Single-file
11
+ * subagent surfaces (`isFile === true`, e.g., `roo`'s `.roomodes`) emit one
12
+ * occurrence per file path; the file itself is the materialization.
13
+ *
14
+ * Rules: no agent in the v1 `AgentRegistry` exposes a rules directory, so the
15
+ * scanner emits no rule occurrences.
16
+ *
17
+ * Each occurrence carries structural fields the subject modules need for
18
+ * cross-platform path handling — `pathSegments` (the absolute path split via
19
+ * the `Path` service) and `subjectFile` (the canonical primary file inside
20
+ * the subject directory) — plus a probed `subjectFileExists` flag so subject
21
+ * modules do not hardcode presence.
22
+ *
23
+ * Per Decision 5, scanner output is occurrence-shaped. The public effect
24
+ * carries no `FileSystem | Path` requirement; per-file partial failures
25
+ * publish diagnostic warnings rather than failing the cell.
26
+ *
27
+ * The scanner avoids `fs.stat` and uses only `fs.exists` / `fs.readDirectory`
28
+ * so it remains compatible with the fixture builder's in-memory `FileSystem`.
29
+ */
30
+ import * as Effect from "effect/Effect";
31
+ import * as Option from "effect/Option";
32
+ import { AGENTS } from "@agentxm/extension-model/unstable/agents/registry";
33
+ import { makeAbsolutePath } from "@agentxm/extension-model/unstable/path-types";
34
+ import { childEntries, fileExists, filterDirectories, splitAbsolutePathSegments, } from "./fs-helpers.js";
35
+ const SCANNER_NAME = "agent-dir";
36
+ const normalizeFileBackedName = (name) => {
37
+ const normalized = name
38
+ .toLowerCase()
39
+ .replace(/[^a-z0-9]+/g, "-")
40
+ .replace(/^-+|-+$/g, "")
41
+ .slice(0, 64)
42
+ .replace(/-+$/g, "");
43
+ return normalized === "" ? "unnamed" : normalized;
44
+ };
45
+ /**
46
+ * Closure helper: returns the dependency-closed scanner effect.
47
+ */
48
+ export const makeAgentDirScanner = (deps) => scanAgentDirs(deps);
49
+ const subjectsForAgent = (descriptor) => {
50
+ const out = [];
51
+ if (descriptor.skills !== undefined) {
52
+ if (descriptor.skills.dir.length > 0) {
53
+ out.push({
54
+ type: "skill",
55
+ relativeDir: descriptor.skills.dir,
56
+ isFile: false,
57
+ readPathStatus: "primary",
58
+ });
59
+ }
60
+ for (const { path, status } of descriptor.skills.additionalReadPaths) {
61
+ out.push({ type: "skill", relativeDir: path, isFile: false, readPathStatus: status });
62
+ }
63
+ }
64
+ if (descriptor.subagents !== undefined) {
65
+ out.push({
66
+ type: "subagent",
67
+ relativeDir: descriptor.subagents.dir,
68
+ isFile: descriptor.subagents.isFile === true,
69
+ });
70
+ }
71
+ return out;
72
+ };
73
+ /**
74
+ * Map a directory-style subject + name to the canonical primary file inside
75
+ * its `<dir>/<name>/` content root. Mirrors the convention used by the
76
+ * canonical-extensions scanner.
77
+ *
78
+ * - `skill` → `SKILL.md` (fixed)
79
+ */
80
+ const subjectFileNameFor = () => "SKILL.md";
81
+ /**
82
+ * Skills use one subdirectory per subject. Subagents use the flat files AXM's
83
+ * agent renderers produce.
84
+ *
85
+ * For each occurrence, probe the canonical subject file (e.g., `SKILL.md`)
86
+ * via `fs.exists` so subject modules can consume `subjectFileExists` rather
87
+ * than re-checking presence themselves.
88
+ */
89
+ const scanSubjectDirectory = (deps, agentId, subject) => Effect.gen(function* () {
90
+ const { fs, path, scope, workspaceRoot, diagnostics } = deps;
91
+ const subjectAbsolute = path.join(workspaceRoot, subject.relativeDir);
92
+ if (subject.isFile) {
93
+ const present = yield* fileExists(SCANNER_NAME, fs, diagnostics, subjectAbsolute);
94
+ if (!present)
95
+ return [];
96
+ const contentLocation = makeAbsolutePath(path, subjectAbsolute);
97
+ const occurrence = {
98
+ _tag: "agent-dir",
99
+ scope,
100
+ type: subject.type,
101
+ agentId,
102
+ ...(subject.readPathStatus === undefined ? {} : { readPathStatus: subject.readPathStatus }),
103
+ name: normalizeFileBackedName(path.basename(subjectAbsolute)),
104
+ contentLocation,
105
+ pathSegments: splitAbsolutePathSegments(path, subjectAbsolute),
106
+ // Single-file surfaces ARE the materialization; the file itself is
107
+ // the subject file and presence has already been confirmed.
108
+ subjectFile: Option.some(contentLocation),
109
+ subjectFileExists: true,
110
+ };
111
+ return [occurrence];
112
+ }
113
+ const candidates = yield* childEntries(SCANNER_NAME, fs, diagnostics, path, subjectAbsolute);
114
+ const nameDirs = yield* filterDirectories(fs, candidates);
115
+ if (subject.type === "skill") {
116
+ return yield* Effect.forEach(nameDirs, (nameDir) => Effect.gen(function* () {
117
+ const name = path.basename(nameDir);
118
+ const subjectFilePath = path.join(nameDir, subjectFileNameFor());
119
+ const subjectFileExists = yield* fileExists(SCANNER_NAME, fs, diagnostics, subjectFilePath);
120
+ const contentLocation = makeAbsolutePath(path, nameDir);
121
+ return {
122
+ _tag: "agent-dir",
123
+ scope,
124
+ type: subject.type,
125
+ agentId,
126
+ ...(subject.readPathStatus === undefined
127
+ ? {}
128
+ : { readPathStatus: subject.readPathStatus }),
129
+ name,
130
+ contentLocation,
131
+ pathSegments: splitAbsolutePathSegments(path, nameDir),
132
+ subjectFile: Option.some(makeAbsolutePath(path, subjectFilePath)),
133
+ subjectFileExists,
134
+ };
135
+ }), { concurrency: "unbounded" });
136
+ }
137
+ const supportedExtensions = [".md", ".json", ".toml"];
138
+ const directorySet = new Set(nameDirs);
139
+ const fileOccurrences = yield* Effect.forEach(candidates, (candidate) => Effect.gen(function* () {
140
+ if (directorySet.has(candidate))
141
+ return Option.none();
142
+ const extension = path.extname(candidate).toLowerCase();
143
+ if (!supportedExtensions.includes(extension)) {
144
+ return Option.none();
145
+ }
146
+ const readable = yield* Effect.result(fs.readFile(candidate));
147
+ if (readable._tag === "Failure")
148
+ return Option.none();
149
+ const fileName = path.basename(candidate);
150
+ const name = normalizeFileBackedName(fileName.slice(0, -extension.length));
151
+ const contentLocation = makeAbsolutePath(path, candidate);
152
+ return Option.some({
153
+ _tag: "agent-dir",
154
+ scope,
155
+ type: subject.type,
156
+ agentId,
157
+ ...(subject.readPathStatus === undefined
158
+ ? {}
159
+ : { readPathStatus: subject.readPathStatus }),
160
+ name,
161
+ contentLocation,
162
+ pathSegments: splitAbsolutePathSegments(path, candidate),
163
+ subjectFile: Option.some(contentLocation),
164
+ subjectFileExists: true,
165
+ });
166
+ }), { concurrency: "unbounded" });
167
+ return fileOccurrences.flatMap(Option.toArray);
168
+ });
169
+ // ---------------------------------------------------------------------------
170
+ // Scanner body
171
+ // ---------------------------------------------------------------------------
172
+ const scanAgentDirs = Effect.fn("workspace.read-model.scanner.agent-dir")(function* (deps) {
173
+ const registry = deps.agentRegistry ?? AGENTS;
174
+ const occurrences = yield* Effect.forEach(Object.values(registry), (descriptor) => {
175
+ const subjects = subjectsForAgent(descriptor);
176
+ return Effect.forEach(subjects, (subject) => scanSubjectDirectory(deps, descriptor.id, subject), { concurrency: "unbounded" }).pipe(Effect.map((results) => results.flat()));
177
+ }, { concurrency: "unbounded" });
178
+ return occurrences.flat();
179
+ });
180
+ //# sourceMappingURL=agent-dir.js.map
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Shared agent-root resolution.
3
+ *
4
+ * `agent-settings.ts` and `mcp-config.ts` both need to resolve a per-agent
5
+ * root directory (e.g., `.claude` for `claude-code`, `.cursor` for `cursor`)
6
+ * to compose agent-specific config paths (`<rootDir>/settings.json`,
7
+ * `<rootDir>/mcp.json`).
8
+ *
9
+ * Resolution rules (with `exactOptionalPropertyTypes: true`, the descriptor
10
+ * shape `rootDir?: string | undefined` distinguishes three states):
11
+ *
12
+ * 1. `descriptor.rootDir` is a `string` — use it directly.
13
+ * 2. `descriptor.rootDir` is explicitly `undefined` (key present but
14
+ * nullish) — the agent has opted out of native-config scanning. Return
15
+ * `Option.none()` and the scanners SHALL skip this agent.
16
+ * 3. `descriptor.rootDir` is omitted entirely — use the first segment of
17
+ * `descriptor.skills.dir` when the Skill surface exists. This handles
18
+ * platform-native separators and collapses `.` segments through the Path
19
+ * service's normalization.
20
+ * 4. If there is no Skill surface, or the heuristic produces nothing, fall
21
+ * back to `.${descriptor.id}`.
22
+ *
23
+ * The heuristic-fallback path emits a one-time `scanner-config` diagnostic
24
+ * warning per scanner instance per agent so agent maintainers see a nudge
25
+ * to set `rootDir` explicitly. Collisions detected at construction time
26
+ * also warn through the same `scanner-config` code.
27
+ */
28
+ import * as Effect from "effect/Effect";
29
+ import * as Option from "effect/Option";
30
+ import type * as Path from "effect/Path";
31
+ import type { AgentDescriptor } from "@agentxm/extension-model/unstable/agents/types";
32
+ import type { Diagnostics } from "../diagnostics.js";
33
+ /**
34
+ * Per-instance state shared across the scanners that resolve agent roots.
35
+ * Both `mcp-config` and `agent-settings` share one tracker so a single
36
+ * heuristic-fallback warning per agent fires across both scanners (rather
37
+ * than once each).
38
+ *
39
+ * Construction is cheap: a `Set<string>` allocated by the live layer and
40
+ * passed to both scanners.
41
+ */
42
+ export interface AgentRootResolverState {
43
+ readonly heuristicWarned: Set<string>;
44
+ }
45
+ export declare const makeAgentRootResolverState: () => AgentRootResolverState;
46
+ /**
47
+ * Resolve the per-agent root directory for one descriptor. Returns
48
+ * `Option.none()` when the agent has explicitly opted out of native-config
49
+ * scanning (descriptor's `rootDir` is `undefined`); returns
50
+ * `Option.some(segment)` otherwise.
51
+ *
52
+ * The result is intentionally a relative segment, not an absolute path:
53
+ * each caller resolves the per-file path differently (`mcp.json` vs
54
+ * `settings.json`), and the join concern stays at the call site.
55
+ */
56
+ export declare const agentRootSegment: (path: Path.Path, descriptor: AgentDescriptor, diagnostics: Diagnostics, state: AgentRootResolverState) => Effect.Effect<Option.Option<string>>;
57
+ /**
58
+ * Detect cases where two or more registry descriptors resolve to the same
59
+ * `agentRootSegment`. The detection runs once at construction time (per
60
+ * `WorkspaceReadModelLive` instance); each collision emits a
61
+ * `scanner-config` diagnostic warning. The function does NOT throw and
62
+ * does NOT skip scanning — callers continue to scan; the warning is the
63
+ * signal.
64
+ *
65
+ * The check uses the same resolution rules as `agentRootSegment` but
66
+ * inlined to avoid producing fallback warnings during collision checking
67
+ * (the heuristic-fallback warning fires once per agent on first scanner
68
+ * use; running it during collision detection would race with normal
69
+ * scanner emission). Agents that opt out (`rootDir: undefined`) are
70
+ * excluded from collision checking entirely.
71
+ */
72
+ export declare const detectAgentRootCollisions: (path: Path.Path, descriptors: ReadonlyArray<AgentDescriptor>, diagnostics: Diagnostics) => Effect.Effect<void>;
73
+ //# sourceMappingURL=agent-root.d.ts.map
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Shared agent-root resolution.
3
+ *
4
+ * `agent-settings.ts` and `mcp-config.ts` both need to resolve a per-agent
5
+ * root directory (e.g., `.claude` for `claude-code`, `.cursor` for `cursor`)
6
+ * to compose agent-specific config paths (`<rootDir>/settings.json`,
7
+ * `<rootDir>/mcp.json`).
8
+ *
9
+ * Resolution rules (with `exactOptionalPropertyTypes: true`, the descriptor
10
+ * shape `rootDir?: string | undefined` distinguishes three states):
11
+ *
12
+ * 1. `descriptor.rootDir` is a `string` — use it directly.
13
+ * 2. `descriptor.rootDir` is explicitly `undefined` (key present but
14
+ * nullish) — the agent has opted out of native-config scanning. Return
15
+ * `Option.none()` and the scanners SHALL skip this agent.
16
+ * 3. `descriptor.rootDir` is omitted entirely — use the first segment of
17
+ * `descriptor.skills.dir` when the Skill surface exists. This handles
18
+ * platform-native separators and collapses `.` segments through the Path
19
+ * service's normalization.
20
+ * 4. If there is no Skill surface, or the heuristic produces nothing, fall
21
+ * back to `.${descriptor.id}`.
22
+ *
23
+ * The heuristic-fallback path emits a one-time `scanner-config` diagnostic
24
+ * warning per scanner instance per agent so agent maintainers see a nudge
25
+ * to set `rootDir` explicitly. Collisions detected at construction time
26
+ * also warn through the same `scanner-config` code.
27
+ */
28
+ import * as Effect from "effect/Effect";
29
+ import * as Option from "effect/Option";
30
+ export const makeAgentRootResolverState = () => ({
31
+ heuristicWarned: new Set(),
32
+ });
33
+ // ---------------------------------------------------------------------------
34
+ // Public resolver
35
+ // ---------------------------------------------------------------------------
36
+ /**
37
+ * Resolve the per-agent root directory for one descriptor. Returns
38
+ * `Option.none()` when the agent has explicitly opted out of native-config
39
+ * scanning (descriptor's `rootDir` is `undefined`); returns
40
+ * `Option.some(segment)` otherwise.
41
+ *
42
+ * The result is intentionally a relative segment, not an absolute path:
43
+ * each caller resolves the per-file path differently (`mcp.json` vs
44
+ * `settings.json`), and the join concern stays at the call site.
45
+ */
46
+ export const agentRootSegment = (path, descriptor, diagnostics, state) => {
47
+ // 1. Explicit `string` rootDir wins.
48
+ if (typeof descriptor.rootDir === "string") {
49
+ return Effect.succeed(Option.some(descriptor.rootDir));
50
+ }
51
+ // 2. The descriptor key is present and set to `undefined` — explicit
52
+ // opt-out. With `exactOptionalPropertyTypes: true`, this is
53
+ // distinguishable from key omission via the runtime `in` check.
54
+ if ("rootDir" in descriptor && descriptor.rootDir === undefined) {
55
+ return Effect.succeed(Option.none());
56
+ }
57
+ // 3. Heuristic fallback. Emit a one-time scanner-config warning per
58
+ // agent so maintainers see the nudge to confirm `rootDir`.
59
+ return Effect.gen(function* () {
60
+ if (!state.heuristicWarned.has(descriptor.id)) {
61
+ state.heuristicWarned.add(descriptor.id);
62
+ yield* diagnostics.append({
63
+ source: "scanner",
64
+ message: `agent-root: descriptor for "${descriptor.id}" has no rootDir; falling back to its Skill directory or agent id`,
65
+ code: "scanner-config",
66
+ });
67
+ }
68
+ return Option.some(heuristicSegment(path, descriptor));
69
+ });
70
+ };
71
+ // ---------------------------------------------------------------------------
72
+ // Internal helpers
73
+ // ---------------------------------------------------------------------------
74
+ const heuristicSegment = (path, descriptor) => {
75
+ const dir = descriptor.skills?.dir;
76
+ if (dir === undefined)
77
+ return `.${descriptor.id}`;
78
+ if (dir.length === 0)
79
+ return `.${descriptor.id}`;
80
+ const segments = splitPathSegments(path, dir);
81
+ const first = segments[0];
82
+ if (first === undefined || first.length === 0)
83
+ return `.${descriptor.id}`;
84
+ return first;
85
+ };
86
+ /**
87
+ * Split a path into its constituent segments using the Path service. Honors
88
+ * the platform separator and skips empty segments produced by leading or
89
+ * duplicate separators.
90
+ */
91
+ const splitPathSegments = (path, value) => {
92
+ // Normalize first so `.` and `..` segments collapse the way the platform
93
+ // expects, then split on the platform separator. Path.normalize preserves
94
+ // a trailing separator only if one was present, which we drop by filtering
95
+ // out empty segments.
96
+ const normalized = path.normalize(value);
97
+ return normalized.split(path.sep).filter((segment) => segment.length > 0);
98
+ };
99
+ // ---------------------------------------------------------------------------
100
+ // Collision detection (called at scanner-set construction time)
101
+ // ---------------------------------------------------------------------------
102
+ /**
103
+ * Detect cases where two or more registry descriptors resolve to the same
104
+ * `agentRootSegment`. The detection runs once at construction time (per
105
+ * `WorkspaceReadModelLive` instance); each collision emits a
106
+ * `scanner-config` diagnostic warning. The function does NOT throw and
107
+ * does NOT skip scanning — callers continue to scan; the warning is the
108
+ * signal.
109
+ *
110
+ * The check uses the same resolution rules as `agentRootSegment` but
111
+ * inlined to avoid producing fallback warnings during collision checking
112
+ * (the heuristic-fallback warning fires once per agent on first scanner
113
+ * use; running it during collision detection would race with normal
114
+ * scanner emission). Agents that opt out (`rootDir: undefined`) are
115
+ * excluded from collision checking entirely.
116
+ */
117
+ export const detectAgentRootCollisions = (path, descriptors, diagnostics) => Effect.gen(function* () {
118
+ const buckets = new Map();
119
+ for (const descriptor of descriptors) {
120
+ const segment = resolveCollisionSegment(path, descriptor);
121
+ if (Option.isNone(segment))
122
+ continue;
123
+ const existing = buckets.get(segment.value);
124
+ if (existing === undefined) {
125
+ buckets.set(segment.value, [descriptor.id]);
126
+ }
127
+ else {
128
+ existing.push(descriptor.id);
129
+ }
130
+ }
131
+ for (const [segment, ids] of buckets) {
132
+ if (ids.length < 2)
133
+ continue;
134
+ yield* diagnostics.append({
135
+ source: "scanner",
136
+ message: `agent-root: collision on "${segment}" across agents: ${ids.join(", ")}`,
137
+ code: "scanner-config",
138
+ });
139
+ }
140
+ });
141
+ const resolveCollisionSegment = (path, descriptor) => {
142
+ if (typeof descriptor.rootDir === "string")
143
+ return Option.some(descriptor.rootDir);
144
+ if ("rootDir" in descriptor && descriptor.rootDir === undefined)
145
+ return Option.none();
146
+ return Option.some(heuristicSegment(path, descriptor));
147
+ };
148
+ //# sourceMappingURL=agent-root.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Agent-settings scanner: enumerates per-agent native settings files (e.g.,
3
+ * `.claude/settings.json`, `.cursor/settings.json`).
4
+ *
5
+ * Per Decision 5, scanner output is occurrence-shaped. Each occurrence
6
+ * represents one observable settings file — present and readable on disk —
7
+ * keyed by `agentId`. The per-file payload is intentionally not parsed here:
8
+ * Phase 8's per-agent modules decode the typed `nativeConfig` shape through
9
+ * their own schemas. The scanner's contract is "this agent has a settings
10
+ * file at this absolute path", not "this settings file is well-formed".
11
+ *
12
+ * Per-file partial failures (e.g., unreadable directory) become diagnostic
13
+ * warnings rather than errors. The error channel stays empty.
14
+ */
15
+ import * as Effect from "effect/Effect";
16
+ import type * as FileSystem from "effect/FileSystem";
17
+ import type * as Path from "effect/Path";
18
+ import type { AgentDescriptor, AgentId } from "@agentxm/extension-model/unstable/agents/types";
19
+ import type { Diagnostics } from "../diagnostics.js";
20
+ import type { Scope } from "../types.js";
21
+ import { type AgentRootResolverState } from "./agent-root.js";
22
+ import type { AgentSettingsOccurrence } from "./types.js";
23
+ /**
24
+ * Inputs the live layer captures before invoking the scanner. The optional
25
+ * `agentRegistry` mirrors the agent-dir scanner. `rootResolverState` lets
26
+ * the live layer share heuristic-warning state across `mcp-config` and
27
+ * `agent-settings`; when omitted, a fresh state is used (one warning per
28
+ * scanner invocation per agent).
29
+ */
30
+ export interface AgentSettingsScannerDeps {
31
+ readonly fs: FileSystem.FileSystem;
32
+ readonly path: Path.Path;
33
+ readonly workspaceRoot: string;
34
+ readonly scope: Scope;
35
+ readonly diagnostics: Diagnostics;
36
+ readonly agentRegistry?: Readonly<Record<AgentId, AgentDescriptor>>;
37
+ readonly rootResolverState?: AgentRootResolverState;
38
+ }
39
+ /**
40
+ * Closure helper: returns the dependency-closed scanner effect.
41
+ */
42
+ export declare const makeAgentSettingsScanner: (deps: AgentSettingsScannerDeps) => Effect.Effect<ReadonlyArray<AgentSettingsOccurrence>>;
43
+ //# sourceMappingURL=agent-settings.d.ts.map
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Agent-settings scanner: enumerates per-agent native settings files (e.g.,
3
+ * `.claude/settings.json`, `.cursor/settings.json`).
4
+ *
5
+ * Per Decision 5, scanner output is occurrence-shaped. Each occurrence
6
+ * represents one observable settings file — present and readable on disk —
7
+ * keyed by `agentId`. The per-file payload is intentionally not parsed here:
8
+ * Phase 8's per-agent modules decode the typed `nativeConfig` shape through
9
+ * their own schemas. The scanner's contract is "this agent has a settings
10
+ * file at this absolute path", not "this settings file is well-formed".
11
+ *
12
+ * Per-file partial failures (e.g., unreadable directory) become diagnostic
13
+ * warnings rather than errors. The error channel stays empty.
14
+ */
15
+ import * as Array from "effect/Array";
16
+ import * as Effect from "effect/Effect";
17
+ import * as Option from "effect/Option";
18
+ import { AGENTS } from "@agentxm/extension-model/unstable/agents/registry";
19
+ import { makeAbsolutePath } from "@agentxm/extension-model/unstable/path-types";
20
+ import { agentRootSegment, makeAgentRootResolverState, } from "./agent-root.js";
21
+ import { fileExists } from "./fs-helpers.js";
22
+ const SCANNER_NAME = "agent-settings";
23
+ /**
24
+ * Closure helper: returns the dependency-closed scanner effect.
25
+ */
26
+ export const makeAgentSettingsScanner = (deps) => scanAgentSettings(deps);
27
+ // ---------------------------------------------------------------------------
28
+ // Scanner body
29
+ // ---------------------------------------------------------------------------
30
+ const scanAgentSettings = Effect.fn("workspace.read-model.scanner.agent-settings")(function* (deps) {
31
+ const { fs, path, workspaceRoot, scope, diagnostics } = deps;
32
+ const registry = deps.agentRegistry ?? AGENTS;
33
+ const rootResolverState = deps.rootResolverState ?? makeAgentRootResolverState();
34
+ const occurrences = yield* Effect.forEach(Object.values(registry), (descriptor) => Effect.gen(function* () {
35
+ // Resolve the per-agent root segment. `Option.none()` means the
36
+ // descriptor opts out of native-config scanning — skip it.
37
+ const rootSegmentOpt = yield* agentRootSegment(path, descriptor, diagnostics, rootResolverState);
38
+ if (Option.isNone(rootSegmentOpt)) {
39
+ return Option.none();
40
+ }
41
+ const filePath = path.join(workspaceRoot, rootSegmentOpt.value, "settings.json");
42
+ const present = yield* fileExists(SCANNER_NAME, fs, diagnostics, filePath);
43
+ if (!present)
44
+ return Option.none();
45
+ return Option.some({
46
+ _tag: "agent-settings",
47
+ scope,
48
+ agentId: descriptor.id,
49
+ contentLocation: makeAbsolutePath(path, filePath),
50
+ });
51
+ }), { concurrency: "unbounded" });
52
+ return Array.getSomes(occurrences);
53
+ });
54
+ //# sourceMappingURL=agent-settings.js.map
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Canonical-extensions scanner: enumerates authored project type roots and
3
+ * source-qualified acquired packages beneath `agent_extensions/` or the
4
+ * corresponding user-scope `agent_extensions/` root.
5
+ *
6
+ * Per Decision 5 of the workspace read-model design, scanner output is
7
+ * occurrence-shaped. Each emitted occurrence carries the scanner-tier origin
8
+ * discriminator (`canonical-axm` | `external-axm`) plus the extension type;
9
+ * Phase 7 maps these into per-subject origin unions.
10
+ *
11
+ * Each occurrence carries structural fields the subject modules need for
12
+ * cross-platform path handling — `pathSegments` (the absolute path split via
13
+ * the `Path` service) and `subjectFile` (the canonical primary content file
14
+ * for the subject type, e.g., `<dir>/SKILL.md` for skills) — plus a probed
15
+ * `subjectFileExists` flag so subject modules do not hardcode presence.
16
+ *
17
+ * Scanner contract:
18
+ *
19
+ * - Public effect carries no `FileSystem | Path` requirement. Construction
20
+ * takes a deps record and returns an `Effect<ReadonlyArray<…>>`.
21
+ * - Per-file partial failures (a directory we cannot enumerate) become
22
+ * diagnostic warnings, not errors. The error channel stays empty.
23
+ * - WorkspaceMutations-root path-escape is checked at provider construction (Phase 9),
24
+ * not inside the scanner.
25
+ *
26
+ * The scanner avoids `fs.stat` and uses only `fs.exists` / `fs.readDirectory`
27
+ * so it remains compatible with the fixture builder's in-memory `FileSystem`.
28
+ * "Is this a directory" is decided by whether `readDirectory` returns
29
+ * successfully; non-directory entries are silently ignored.
30
+ */
31
+ import * as Effect from "effect/Effect";
32
+ import type * as FileSystem from "effect/FileSystem";
33
+ import type * as Path from "effect/Path";
34
+ import type { Diagnostics } from "../diagnostics.js";
35
+ import type { WorkspaceLayout } from "../../layout.js";
36
+ import type { CanonicalExtensionOccurrence } from "./types.js";
37
+ export interface CanonicalExtensionsScannerDeps {
38
+ readonly fs: FileSystem.FileSystem;
39
+ readonly path: Path.Path;
40
+ readonly workspaceRoot: string;
41
+ readonly diagnostics: Diagnostics;
42
+ readonly layout: WorkspaceLayout;
43
+ }
44
+ /**
45
+ * Closure helper: returns the dependency-closed scanner effect.
46
+ */
47
+ export declare const makeCanonicalExtensionsScanner: (deps: CanonicalExtensionsScannerDeps) => Effect.Effect<ReadonlyArray<CanonicalExtensionOccurrence>>;
48
+ //# sourceMappingURL=canonical-extensions.d.ts.map