@metaobjectsdev/sdk 0.23.2 → 0.24.1

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 (75) hide show
  1. package/agent-context/skills/metaobjects-audit/SKILL.md +20 -7
  2. package/agent-context/skills/metaobjects-audit/references/capability-checklist.md +65 -19
  3. package/agent-context/skills/metaobjects-audit/references/csharp.md +2 -1
  4. package/agent-context/skills/metaobjects-audit/references/java.md +3 -2
  5. package/agent-context/skills/metaobjects-audit/references/kotlin.md +2 -1
  6. package/agent-context/skills/metaobjects-audit/references/requirements.md +3 -4
  7. package/agent-context/skills/metaobjects-audit/references/typescript.md +1 -1
  8. package/agent-context/skills/metaobjects-authoring/SKILL.md +53 -10
  9. package/agent-context/skills/metaobjects-authoring/references/requirements.md +49 -23
  10. package/agent-context/skills/metaobjects-codegen/SKILL.md +4 -3
  11. package/agent-context/skills/metaobjects-codegen/references/csharp.md +12 -2
  12. package/agent-context/skills/metaobjects-codegen/references/java.md +3 -3
  13. package/agent-context/skills/metaobjects-codegen/references/kotlin.md +4 -4
  14. package/agent-context/skills/metaobjects-codegen/references/python.md +12 -2
  15. package/agent-context/skills/metaobjects-codegen/references/typescript.md +14 -3
  16. package/agent-context/skills/metaobjects-fit-assessment/SKILL.md +4 -4
  17. package/agent-context/skills/metaobjects-prompts/SKILL.md +53 -32
  18. package/agent-context/skills/metaobjects-prompts/references/csharp.md +26 -21
  19. package/agent-context/skills/metaobjects-prompts/references/java.md +24 -18
  20. package/agent-context/skills/metaobjects-prompts/references/kotlin.md +25 -20
  21. package/agent-context/skills/metaobjects-prompts/references/python.md +68 -26
  22. package/agent-context/skills/metaobjects-prompts/references/typescript.md +29 -23
  23. package/agent-context/skills/metaobjects-verify/references/migration.md +1 -1
  24. package/agent-context/skills/metaobjects-verify/references/requirements.md +36 -22
  25. package/agent-context/templates/always-on.md.mustache +3 -1
  26. package/dist/agent-docs/body.d.ts +1 -1
  27. package/dist/agent-docs/body.d.ts.map +1 -1
  28. package/dist/agent-docs/body.js +24 -2
  29. package/dist/agent-docs/body.js.map +1 -1
  30. package/dist/collection.d.ts +71 -0
  31. package/dist/collection.d.ts.map +1 -0
  32. package/dist/collection.js +203 -0
  33. package/dist/collection.js.map +1 -0
  34. package/dist/config.d.ts +51 -21
  35. package/dist/config.d.ts.map +1 -1
  36. package/dist/config.js +76 -10
  37. package/dist/config.js.map +1 -1
  38. package/dist/discovery.d.ts +43 -0
  39. package/dist/discovery.d.ts.map +1 -0
  40. package/dist/discovery.js +100 -0
  41. package/dist/discovery.js.map +1 -0
  42. package/dist/index.d.ts +10 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +17 -4
  45. package/dist/index.js.map +1 -1
  46. package/dist/memory.d.ts +42 -19
  47. package/dist/memory.d.ts.map +1 -1
  48. package/dist/memory.js +38 -95
  49. package/dist/memory.js.map +1 -1
  50. package/dist/metadata-files.d.ts +67 -0
  51. package/dist/metadata-files.d.ts.map +1 -0
  52. package/dist/metadata-files.js +155 -0
  53. package/dist/metadata-files.js.map +1 -0
  54. package/dist/records/any.d.ts +8 -8
  55. package/dist/records/decision.d.ts +2 -2
  56. package/dist/records/glossary.d.ts +4 -4
  57. package/dist/records/principle.d.ts +2 -2
  58. package/dist/scope.d.ts +16 -0
  59. package/dist/scope.d.ts.map +1 -0
  60. package/dist/scope.js +75 -0
  61. package/dist/scope.js.map +1 -0
  62. package/dist/sources.d.ts +86 -0
  63. package/dist/sources.d.ts.map +1 -0
  64. package/dist/sources.js +130 -0
  65. package/dist/sources.js.map +1 -0
  66. package/package.json +2 -2
  67. package/src/agent-docs/body.ts +24 -2
  68. package/src/collection.ts +268 -0
  69. package/src/config.ts +80 -12
  70. package/src/discovery.ts +110 -0
  71. package/src/index.ts +26 -9
  72. package/src/memory.ts +65 -99
  73. package/src/metadata-files.ts +158 -0
  74. package/src/scope.ts +95 -0
  75. package/src/sources.ts +160 -0
package/src/sources.ts ADDED
@@ -0,0 +1,160 @@
1
+ // server/typescript/packages/sdk/src/sources.ts
2
+ //
3
+ // Phase-1 metadata-source-resolution — source spec resolution.
4
+ //
5
+ // Turns a declared source SET (`.metaobjects/config.json`'s `sources`) into a
6
+ // canonically-ordered, de-duplicated list of metadata file paths. The FULL
7
+ // result — including which spec each entry attributes to — is a pure
8
+ // function of the source SET, never of declaration order: permuting `specs`
9
+ // cannot change the output, even when two specs overlap on the same file.
10
+ // `test/order-independence.test.ts` pins that (the design's linchpin), so the
11
+ // canonical spec ordering below is load-bearing.
12
+ //
13
+ // Canonical is NOT the same as "flat-sorted". Within one directory spec the
14
+ // order is `listMetadataFiles`'s (metadata-files.ts) — files at a level, then
15
+ // that level's subdirectories, depth-first — because that is the order production
16
+ // has always handed the loader, and declaration order survives into generated
17
+ // output (the barrel's export list, the shared `enums.ts`, `meta docs` page
18
+ // order, `meta export`'s sibling order). A flat sort of absolute paths
19
+ // silently reorders any project with a subdirectory whose name sorts before a
20
+ // sibling file. Across specs, order is decided by spec CONTENT, which is what
21
+ // keeps the whole result permutation-invariant. `test/source-order.test.ts`
22
+ // pins both halves.
23
+ import { stat } from "node:fs/promises";
24
+ import { isAbsolute, resolve } from "node:path";
25
+ import { ParseError, codeSource } from "@metaobjectsdev/metadata";
26
+ import { DEFAULT_METADATA_DIR, listMetadataFiles } from "./metadata-files.js";
27
+
28
+ /** Tagged union of source kinds. `resource` and `package` are declared now so
29
+ * the config shape is stable across phases; only `path` resolves in phase 1 —
30
+ * `resource`/`package` throw `ERR_SOURCE_KIND_UNSUPPORTED`. */
31
+ export type SourceSpec =
32
+ | { readonly path: string }
33
+ | { readonly resource: string }
34
+ | { readonly package: string };
35
+
36
+ export interface ResolvedSource {
37
+ /** Absolute path of one metadata file. */
38
+ readonly file: string;
39
+ /** The spec that contributed it — provenance for diagnostics. */
40
+ readonly spec: SourceSpec;
41
+ }
42
+
43
+ /** Used when `sources` is absent or empty in `.metaobjects/config.json`. A
44
+ * DEFAULT, never a requirement — a project that declares `sources` explicitly
45
+ * need not include the default directory at all. Built from
46
+ * `DEFAULT_METADATA_DIR` (`metadata-files.ts`'s single definition) rather than
47
+ * restating that name here: a second independent encoding of the same
48
+ * default would let `resolveCollection`'s "does the default dir exist"
49
+ * check (`collection.ts`) desync from what `resolveSources` actually
50
+ * resolves the moment the default ever changed — silently reproducing the
51
+ * "two code paths disagree about where metadata lives" class of bug this
52
+ * whole mechanism exists to eliminate. */
53
+ export const DEFAULT_SOURCES: readonly SourceSpec[] = [{ path: DEFAULT_METADATA_DIR }];
54
+
55
+ /** Narrows `spec` to its `path` arm, throwing `ERR_SOURCE_KIND_UNSUPPORTED`
56
+ * for `resource`/`package` — phase 1 resolves `path` only. Returns rather than
57
+ * asserting so one call both validates and narrows: an `asserts` signature has
58
+ * to be re-invoked wherever TypeScript's control-flow analysis cannot carry the
59
+ * narrowing, which is a language workaround masquerading as a second check. */
60
+ function toPathSpec(spec: SourceSpec): { readonly path: string } {
61
+ if ("path" in spec) return spec;
62
+ const kind = "resource" in spec ? "resource" : "package";
63
+ throw new ParseError(
64
+ `source kind "${kind}" is not supported by this toolchain yet; use a "path" source`,
65
+ { code: "ERR_SOURCE_KIND_UNSUPPORTED", source: codeSource("resolveSources") },
66
+ );
67
+ }
68
+
69
+ /**
70
+ * The declared source SET in CANONICAL order — kind-validated, then sorted by
71
+ * spec CONTENT rather than by declaration order.
72
+ *
73
+ * This is the ONE place declaration order is discarded, and the module's "pure
74
+ * function of the SET" invariant rests on it: the emitted file order, the spec
75
+ * attributed to a file two specs both reach, and which of several unresolvable
76
+ * paths reports its `ERR_SOURCE_UNRESOLVED` first are all decided here.
77
+ * Validation runs across the WHOLE list before any sorting or filesystem I/O —
78
+ * interleaved with resolution, which error code came back would depend on
79
+ * declaration order, contradicting that same invariant.
80
+ *
81
+ * Exported because `resolveCollection` derives `sourceRoots` from the declared
82
+ * specs and must use this identical ordering; a second sort would be a second
83
+ * definition of "canonical".
84
+ */
85
+ export function orderedPathSpecs(specs: readonly SourceSpec[]): { readonly path: string }[] {
86
+ return specs.map(toPathSpec).sort((a, b) => {
87
+ const [ja, jb] = [JSON.stringify(a), JSON.stringify(b)];
88
+ return ja < jb ? -1 : ja > jb ? 1 : 0;
89
+ });
90
+ }
91
+
92
+ /**
93
+ * Where a declared `path` source lives on disk: absolute as written, otherwise
94
+ * relative to the DECLARING config's directory — never to ambient
95
+ * `process.cwd()`.
96
+ *
97
+ * One definition, because this expression *is* the rule for where a declared
98
+ * source lives, which is the single piece of knowledge this module exists to
99
+ * own. A caller that needs a source's root directory (rather than its files)
100
+ * calls this rather than restating it.
101
+ */
102
+ export function resolveSpecPath(configDir: string, spec: { readonly path: string }): string {
103
+ return isAbsolute(spec.path) ? spec.path : resolve(configDir, spec.path);
104
+ }
105
+
106
+ /**
107
+ * Resolve a declared source SET to a canonically-ordered list of metadata files.
108
+ *
109
+ * The full result — each entry's `.file` AND its `.spec` — is a pure function
110
+ * of the SET of `specs`: permuting `specs` cannot change the output. One thing
111
+ * makes that hold: the specs are processed in CONTENT order
112
+ * (`JSON.stringify(spec)`, ascending) rather than declared order, so both the
113
+ * emitted file order and the spec attributed to a file overlapping two specs
114
+ * are decided by content alone. Declared order carries no information anywhere
115
+ * in this function.
116
+ *
117
+ * Within one directory spec the file order is `listMetadataFiles`'s — files at
118
+ * a level, then that level's subdirectories, depth-first. That is deliberately
119
+ * NOT a flat sort of absolute paths: see the file header, and
120
+ * `test/source-order.test.ts`.
121
+ *
122
+ * Only `path` specs resolve in phase 1: a directory is walked recursively, a
123
+ * file is taken as-is. An unresolvable `path` throws `ERR_SOURCE_UNRESOLVED`
124
+ * rather than silently contributing nothing; `resource`/`package` specs throw
125
+ * `ERR_SOURCE_KIND_UNSUPPORTED`.
126
+ *
127
+ * @param configDir absolute directory of the declaring config (the parent of
128
+ * `.metaobjects/`) — relative `path` specs resolve against it, never against
129
+ * ambient `process.cwd()`.
130
+ */
131
+ export async function resolveSources(
132
+ configDir: string,
133
+ specs: readonly SourceSpec[],
134
+ ): Promise<ResolvedSource[]> {
135
+ // Kind-validated and content-ordered in one pass — see `orderedPathSpecs`.
136
+ const ordered = orderedPathSpecs(specs);
137
+
138
+ // Insertion order IS output order — a Map preserves it, so the per-spec walk
139
+ // order above survives to the caller. First contributor wins a shared file,
140
+ // which is content-determined because `ordered` is.
141
+ const byFile = new Map<string, SourceSpec>();
142
+
143
+ for (const spec of ordered) {
144
+ const target = resolveSpecPath(configDir, spec);
145
+ const stats = await stat(target).catch(() => undefined);
146
+ if (stats === undefined) {
147
+ throw new ParseError(
148
+ `source path "${spec.path}" does not exist (resolved to ${target}, relative to ${configDir})`,
149
+ { code: "ERR_SOURCE_UNRESOLVED", source: codeSource("resolveSources") },
150
+ );
151
+ }
152
+
153
+ const found = stats.isDirectory() ? await listMetadataFiles(target) : [target];
154
+ for (const file of found) {
155
+ if (!byFile.has(file)) byFile.set(file, spec);
156
+ }
157
+ }
158
+
159
+ return [...byFile].map(([file, spec]) => ({ file, spec }));
160
+ }