wingfoil 0.2.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.
- package/LICENSE +21 -0
- package/README.md +334 -0
- package/dist/cli/error.d.ts +11 -0
- package/dist/cli/error.d.ts.map +1 -0
- package/dist/cli/error.js +27 -0
- package/dist/cli/error.js.map +1 -0
- package/dist/cli/exit.d.ts +19 -0
- package/dist/cli/exit.d.ts.map +1 -0
- package/dist/cli/exit.js +14 -0
- package/dist/cli/exit.js.map +1 -0
- package/dist/cli/index.d.ts +28 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +36 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init-command.d.ts +34 -0
- package/dist/cli/init-command.d.ts.map +1 -0
- package/dist/cli/init-command.js +100 -0
- package/dist/cli/init-command.js.map +1 -0
- package/dist/cli/mcp-command.d.ts +24 -0
- package/dist/cli/mcp-command.d.ts.map +1 -0
- package/dist/cli/mcp-command.js +42 -0
- package/dist/cli/mcp-command.js.map +1 -0
- package/dist/cli/output.d.ts +13 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +28 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/program.d.ts +12 -0
- package/dist/cli/program.d.ts.map +1 -0
- package/dist/cli/program.js +279 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/registrar.d.ts +69 -0
- package/dist/cli/registrar.d.ts.map +1 -0
- package/dist/cli/registrar.js +82 -0
- package/dist/cli/registrar.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +45 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/approval-authority.d.ts +83 -0
- package/dist/core/approval-authority.d.ts.map +1 -0
- package/dist/core/approval-authority.js +112 -0
- package/dist/core/approval-authority.js.map +1 -0
- package/dist/core/builtin-asset.d.ts +71 -0
- package/dist/core/builtin-asset.d.ts.map +1 -0
- package/dist/core/builtin-asset.js +137 -0
- package/dist/core/builtin-asset.js.map +1 -0
- package/dist/core/builtin-integrity.d.ts +32 -0
- package/dist/core/builtin-integrity.d.ts.map +1 -0
- package/dist/core/builtin-integrity.js +148 -0
- package/dist/core/builtin-integrity.js.map +1 -0
- package/dist/core/confinement.d.ts +57 -0
- package/dist/core/confinement.d.ts.map +1 -0
- package/dist/core/confinement.js +108 -0
- package/dist/core/confinement.js.map +1 -0
- package/dist/core/context.d.ts +187 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +192 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/directive-assign.d.ts +131 -0
- package/dist/core/directive-assign.d.ts.map +1 -0
- package/dist/core/directive-assign.js +352 -0
- package/dist/core/directive-assign.js.map +1 -0
- package/dist/core/directives-list.d.ts +80 -0
- package/dist/core/directives-list.d.ts.map +1 -0
- package/dist/core/directives-list.js +148 -0
- package/dist/core/directives-list.js.map +1 -0
- package/dist/core/exit-code.d.ts +85 -0
- package/dist/core/exit-code.d.ts.map +1 -0
- package/dist/core/exit-code.js +163 -0
- package/dist/core/exit-code.js.map +1 -0
- package/dist/core/git-identity.d.ts +32 -0
- package/dist/core/git-identity.d.ts.map +1 -0
- package/dist/core/git-identity.js +72 -0
- package/dist/core/git-identity.js.map +1 -0
- package/dist/core/index.d.ts +388 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +1331 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/init.d.ts +78 -0
- package/dist/core/init.d.ts.map +1 -0
- package/dist/core/init.js +167 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/loaders.d.ts +174 -0
- package/dist/core/loaders.d.ts.map +1 -0
- package/dist/core/loaders.js +364 -0
- package/dist/core/loaders.js.map +1 -0
- package/dist/core/memory-add-type.d.ts +43 -0
- package/dist/core/memory-add-type.d.ts.map +1 -0
- package/dist/core/memory-add-type.js +151 -0
- package/dist/core/memory-add-type.js.map +1 -0
- package/dist/core/memory-transition.d.ts +111 -0
- package/dist/core/memory-transition.d.ts.map +1 -0
- package/dist/core/memory-transition.js +269 -0
- package/dist/core/memory-transition.js.map +1 -0
- package/dist/core/registry.d.ts +189 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +66 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/relevance.d.ts +86 -0
- package/dist/core/relevance.d.ts.map +1 -0
- package/dist/core/relevance.js +258 -0
- package/dist/core/relevance.js.map +1 -0
- package/dist/core/require-reason.d.ts +27 -0
- package/dist/core/require-reason.d.ts.map +1 -0
- package/dist/core/require-reason.js +95 -0
- package/dist/core/require-reason.js.map +1 -0
- package/dist/core/types.d.ts +45 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +22 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/usage-error.d.ts +20 -0
- package/dist/core/usage-error.d.ts.map +1 -0
- package/dist/core/usage-error.js +29 -0
- package/dist/core/usage-error.js.map +1 -0
- package/dist/core/write-guard.d.ts +99 -0
- package/dist/core/write-guard.d.ts.map +1 -0
- package/dist/core/write-guard.js +247 -0
- package/dist/core/write-guard.js.map +1 -0
- package/dist/directives/create.d.ts +47 -0
- package/dist/directives/create.d.ts.map +1 -0
- package/dist/directives/create.js +82 -0
- package/dist/directives/create.js.map +1 -0
- package/dist/directives/index.d.ts +20 -0
- package/dist/directives/index.d.ts.map +1 -0
- package/dist/directives/index.js +32 -0
- package/dist/directives/index.js.map +1 -0
- package/dist/directives/roles-edit.d.ts +53 -0
- package/dist/directives/roles-edit.d.ts.map +1 -0
- package/dist/directives/roles-edit.js +286 -0
- package/dist/directives/roles-edit.js.map +1 -0
- package/dist/directives/schema.d.ts +65 -0
- package/dist/directives/schema.d.ts.map +1 -0
- package/dist/directives/schema.js +68 -0
- package/dist/directives/schema.js.map +1 -0
- package/dist/dna/edit.d.ts +61 -0
- package/dist/dna/edit.d.ts.map +1 -0
- package/dist/dna/edit.js +429 -0
- package/dist/dna/edit.js.map +1 -0
- package/dist/dna/index.d.ts +24 -0
- package/dist/dna/index.d.ts.map +1 -0
- package/dist/dna/index.js +59 -0
- package/dist/dna/index.js.map +1 -0
- package/dist/dna/mutate.d.ts +74 -0
- package/dist/dna/mutate.d.ts.map +1 -0
- package/dist/dna/mutate.js +318 -0
- package/dist/dna/mutate.js.map +1 -0
- package/dist/dna/path.d.ts +175 -0
- package/dist/dna/path.d.ts.map +1 -0
- package/dist/dna/path.js +289 -0
- package/dist/dna/path.js.map +1 -0
- package/dist/dna/roles.d.ts +57 -0
- package/dist/dna/roles.d.ts.map +1 -0
- package/dist/dna/roles.js +50 -0
- package/dist/dna/roles.js.map +1 -0
- package/dist/dna/schema.d.ts +172 -0
- package/dist/dna/schema.d.ts.map +1 -0
- package/dist/dna/schema.js +182 -0
- package/dist/dna/schema.js.map +1 -0
- package/dist/dna/set.d.ts +145 -0
- package/dist/dna/set.d.ts.map +1 -0
- package/dist/dna/set.js +428 -0
- package/dist/dna/set.js.map +1 -0
- package/dist/mcp/dna-resource.d.ts +16 -0
- package/dist/mcp/dna-resource.d.ts.map +1 -0
- package/dist/mcp/dna-resource.js +56 -0
- package/dist/mcp/dna-resource.js.map +1 -0
- package/dist/mcp/index.d.ts +49 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +60 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/memory-resource.d.ts +17 -0
- package/dist/mcp/memory-resource.d.ts.map +1 -0
- package/dist/mcp/memory-resource.js +133 -0
- package/dist/mcp/memory-resource.js.map +1 -0
- package/dist/mcp/prompt.d.ts +48 -0
- package/dist/mcp/prompt.d.ts.map +1 -0
- package/dist/mcp/prompt.js +176 -0
- package/dist/mcp/prompt.js.map +1 -0
- package/dist/mcp/read-only.d.ts +42 -0
- package/dist/mcp/read-only.d.ts.map +1 -0
- package/dist/mcp/read-only.js +93 -0
- package/dist/mcp/read-only.js.map +1 -0
- package/dist/mcp/registrar.d.ts +54 -0
- package/dist/mcp/registrar.d.ts.map +1 -0
- package/dist/mcp/registrar.js +72 -0
- package/dist/mcp/registrar.js.map +1 -0
- package/dist/mcp/server.d.ts +58 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +64 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/workflow-resource.d.ts +17 -0
- package/dist/mcp/workflow-resource.d.ts.map +1 -0
- package/dist/mcp/workflow-resource.js +63 -0
- package/dist/mcp/workflow-resource.js.map +1 -0
- package/dist/memory/add.d.ts +50 -0
- package/dist/memory/add.d.ts.map +1 -0
- package/dist/memory/add.js +131 -0
- package/dist/memory/add.js.map +1 -0
- package/dist/memory/audit.d.ts +177 -0
- package/dist/memory/audit.d.ts.map +1 -0
- package/dist/memory/audit.js +287 -0
- package/dist/memory/audit.js.map +1 -0
- package/dist/memory/commit-message.d.ts +146 -0
- package/dist/memory/commit-message.d.ts.map +1 -0
- package/dist/memory/commit-message.js +203 -0
- package/dist/memory/commit-message.js.map +1 -0
- package/dist/memory/entry.d.ts +56 -0
- package/dist/memory/entry.d.ts.map +1 -0
- package/dist/memory/entry.js +28 -0
- package/dist/memory/entry.js.map +1 -0
- package/dist/memory/frontmatter-edit.d.ts +68 -0
- package/dist/memory/frontmatter-edit.d.ts.map +1 -0
- package/dist/memory/frontmatter-edit.js +333 -0
- package/dist/memory/frontmatter-edit.js.map +1 -0
- package/dist/memory/git-log.d.ts +32 -0
- package/dist/memory/git-log.d.ts.map +1 -0
- package/dist/memory/git-log.js +100 -0
- package/dist/memory/git-log.js.map +1 -0
- package/dist/memory/history.d.ts +142 -0
- package/dist/memory/history.d.ts.map +1 -0
- package/dist/memory/history.js +250 -0
- package/dist/memory/history.js.map +1 -0
- package/dist/memory/index.d.ts +58 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +107 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/query.d.ts +150 -0
- package/dist/memory/query.d.ts.map +1 -0
- package/dist/memory/query.js +307 -0
- package/dist/memory/query.js.map +1 -0
- package/dist/memory/reject.d.ts +11 -0
- package/dist/memory/reject.d.ts.map +1 -0
- package/dist/memory/reject.js +32 -0
- package/dist/memory/reject.js.map +1 -0
- package/dist/memory/schema.d.ts +104 -0
- package/dist/memory/schema.d.ts.map +1 -0
- package/dist/memory/schema.js +161 -0
- package/dist/memory/schema.js.map +1 -0
- package/dist/memory/state-machine.d.ts +166 -0
- package/dist/memory/state-machine.d.ts.map +1 -0
- package/dist/memory/state-machine.js +431 -0
- package/dist/memory/state-machine.js.map +1 -0
- package/dist/memory/submit.d.ts +11 -0
- package/dist/memory/submit.d.ts.map +1 -0
- package/dist/memory/submit.js +44 -0
- package/dist/memory/submit.js.map +1 -0
- package/dist/storage/builtin-directives.d.ts +54 -0
- package/dist/storage/builtin-directives.d.ts.map +1 -0
- package/dist/storage/builtin-directives.js +152 -0
- package/dist/storage/builtin-directives.js.map +1 -0
- package/dist/storage/commit.d.ts +92 -0
- package/dist/storage/commit.d.ts.map +1 -0
- package/dist/storage/commit.js +201 -0
- package/dist/storage/commit.js.map +1 -0
- package/dist/storage/confinement.d.ts +93 -0
- package/dist/storage/confinement.d.ts.map +1 -0
- package/dist/storage/confinement.js +164 -0
- package/dist/storage/confinement.js.map +1 -0
- package/dist/storage/document.d.ts +27 -0
- package/dist/storage/document.d.ts.map +1 -0
- package/dist/storage/document.js +52 -0
- package/dist/storage/document.js.map +1 -0
- package/dist/storage/errors.d.ts +36 -0
- package/dist/storage/errors.d.ts.map +1 -0
- package/dist/storage/errors.js +46 -0
- package/dist/storage/errors.js.map +1 -0
- package/dist/storage/frontmatter.d.ts +27 -0
- package/dist/storage/frontmatter.d.ts.map +1 -0
- package/dist/storage/frontmatter.js +37 -0
- package/dist/storage/frontmatter.js.map +1 -0
- package/dist/storage/git-root.d.ts +17 -0
- package/dist/storage/git-root.d.ts.map +1 -0
- package/dist/storage/git-root.js +49 -0
- package/dist/storage/git-root.js.map +1 -0
- package/dist/storage/index.d.ts +29 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +66 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/init-state.d.ts +9 -0
- package/dist/storage/init-state.d.ts.map +1 -0
- package/dist/storage/init-state.js +24 -0
- package/dist/storage/init-state.js.map +1 -0
- package/dist/storage/layout.d.ts +51 -0
- package/dist/storage/layout.d.ts.map +1 -0
- package/dist/storage/layout.js +88 -0
- package/dist/storage/layout.js.map +1 -0
- package/dist/storage/memory-path.d.ts +72 -0
- package/dist/storage/memory-path.d.ts.map +1 -0
- package/dist/storage/memory-path.js +127 -0
- package/dist/storage/memory-path.js.map +1 -0
- package/dist/storage/snapshot.d.ts +16 -0
- package/dist/storage/snapshot.d.ts.map +1 -0
- package/dist/storage/snapshot.js +72 -0
- package/dist/storage/snapshot.js.map +1 -0
- package/dist/storage/templates.d.ts +88 -0
- package/dist/storage/templates.d.ts.map +1 -0
- package/dist/storage/templates.js +422 -0
- package/dist/storage/templates.js.map +1 -0
- package/dist/validation/error-mapper.d.ts +16 -0
- package/dist/validation/error-mapper.d.ts.map +1 -0
- package/dist/validation/error-mapper.js +18 -0
- package/dist/validation/error-mapper.js.map +1 -0
- package/dist/validation/errors.d.ts +69 -0
- package/dist/validation/errors.d.ts.map +1 -0
- package/dist/validation/errors.js +69 -0
- package/dist/validation/errors.js.map +1 -0
- package/dist/validation/id.d.ts +16 -0
- package/dist/validation/id.d.ts.map +1 -0
- package/dist/validation/id.js +177 -0
- package/dist/validation/id.js.map +1 -0
- package/dist/validation/index.d.ts +18 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +37 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/secret-scan.d.ts +129 -0
- package/dist/validation/secret-scan.d.ts.map +1 -0
- package/dist/validation/secret-scan.js +367 -0
- package/dist/validation/secret-scan.js.map +1 -0
- package/dist/validation/two-pass.d.ts +24 -0
- package/dist/validation/two-pass.d.ts.map +1 -0
- package/dist/validation/two-pass.js +24 -0
- package/dist/validation/two-pass.js.map +1 -0
- package/dist/validation/warning.d.ts +31 -0
- package/dist/validation/warning.d.ts.map +1 -0
- package/dist/validation/warning.js +81 -0
- package/dist/validation/warning.js.map +1 -0
- package/dist/validation/yaml.d.ts +9 -0
- package/dist/validation/yaml.d.ts.map +1 -0
- package/dist/validation/yaml.js +30 -0
- package/dist/validation/yaml.js.map +1 -0
- package/dist/workflow/index.d.ts +10 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +16 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/schema.d.ts +89 -0
- package/dist/workflow/schema.d.ts.map +1 -0
- package/dist/workflow/schema.js +73 -0
- package/dist/workflow/schema.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.E_EMPTY_SEARCH_QUERY = void 0;
|
|
4
|
+
exports.computeMemoryContentRoots = computeMemoryContentRoots;
|
|
5
|
+
exports.listMemoryDocumentPaths = listMemoryDocumentPaths;
|
|
6
|
+
exports.loadMemoryDocumentSummary = loadMemoryDocumentSummary;
|
|
7
|
+
exports.findMemoryDocumentById = findMemoryDocumentById;
|
|
8
|
+
exports.listMemoryDocumentsByType = listMemoryDocumentsByType;
|
|
9
|
+
exports.findMemoryDocumentByTypeAndId = findMemoryDocumentByTypeAndId;
|
|
10
|
+
exports.validateSearchQuery = validateSearchQuery;
|
|
11
|
+
exports.searchMemoryDocuments = searchMemoryDocuments;
|
|
12
|
+
/**
|
|
13
|
+
* Memory query primitives (task-008-dna-memory-query-latency, REQ-PERF-02; match/rank algorithm and
|
|
14
|
+
* empty-query validation hardened by task-023-implement-keyword-search, P1.12) — the performance-
|
|
15
|
+
* bearing foundation `wingfoil memory search` (task-021-implement-memory-search, P1.5) builds its
|
|
16
|
+
* CLI/MCP surface on top of. Deliberately NOT wired into `src/core`'s `CORE_MODULES` registry yet
|
|
17
|
+
* (see the task's Execution Notes for the scoping decision): this module ships the scan +
|
|
18
|
+
* keyword/frontmatter relevance primitives plus the query-validation guard, not the `--tag` CLI
|
|
19
|
+
* grammar, output rendering, or the "no documents matched" exit-code contract, which are task-021's
|
|
20
|
+
* own scope.
|
|
21
|
+
*
|
|
22
|
+
* Three things keep this fast and correct at the 1,000-Memory-document reference scale (REQ-PERF-02)
|
|
23
|
+
* and satisfy P1.12's fit criteria:
|
|
24
|
+
*
|
|
25
|
+
* - **Path-pattern-derived scan roots** (spec-011-storage-layout): rather than walking the whole
|
|
26
|
+
* project tree, {@link computeMemoryContentRoots} derives the minimal set of directories to scan
|
|
27
|
+
* from `memory.yaml`'s per-type `path` patterns (spec-001-memory-yaml-schema) — the same
|
|
28
|
+
* predictability spec-011 calls out as what keeps `memory search`/`history` from needing a
|
|
29
|
+
* full-repo walk for every query.
|
|
30
|
+
* - **Deterministic keyword/frontmatter relevance, not full-text/semantic search**
|
|
31
|
+
* (spec-012-context-loader-relevance-filtering's discipline, applied here to `memory search`
|
|
32
|
+
* rather than the Agent Context Loader spec-012 itself defines): a metadata match (title/id/tag)
|
|
33
|
+
* ranks above a body-only match (P1.12-keyword-search.feature Scenario 1), case-insensitive
|
|
34
|
+
* substring matching (Scenario 2), sorted with a total, deterministic order (REQ-SYS-07: no
|
|
35
|
+
* unordered iteration in a query-building path). Both were already satisfied by
|
|
36
|
+
* {@link searchMemoryDocuments} as shipped by task-008 — task-023 verified this against P1.12's
|
|
37
|
+
* fit criteria and added characterization tests, no algorithm change was needed.
|
|
38
|
+
* - **Empty-query rejection** ({@link validateSearchQuery}, P1.12-keyword-search.feature Scenario 3):
|
|
39
|
+
* a genuine gap task-008 left open (an empty query previously matched every document instead of
|
|
40
|
+
* being rejected) — task-023 closed it with a `ValidationError.semantic` guard a caller runs before
|
|
41
|
+
* invoking {@link searchMemoryDocuments}, exiting 2 with message "empty search query".
|
|
42
|
+
*
|
|
43
|
+
* **Archived-exclusion (REQ-STATE-06, task-038-deprecated-excluded-from-context; set widened by
|
|
44
|
+
* `dl-028-archived-states-excluded-from-context`):** {@link searchMemoryDocuments} excludes documents
|
|
45
|
+
* in an archived state from its default result — an archived document "never appears in … default
|
|
46
|
+
* `memory search` results" (the SARD Fit Criterion) while staying present on disk and in git history.
|
|
47
|
+
* The archived set is `{deprecated, superseded}` and the single shared check is
|
|
48
|
+
* `state-machine.ts`'s {@link isArchivedStatus} (it superseded task-038's `isDeprecatedStatus`), so
|
|
49
|
+
* the Agent Context Loader relevance-filter (spec-012 §6, `src/core/relevance.ts`) applies the same
|
|
50
|
+
* predicate rather than re-deciding what "archived" means. {@link MemorySearchOptions.includeArchived}
|
|
51
|
+
* is the explicit opt-out.
|
|
52
|
+
*
|
|
53
|
+
* `draft` is deliberately NOT excluded here: spec-012 §6 excludes drafts from an assembled agent
|
|
54
|
+
* *context*, but REQ-STATE-06 scopes default-search exclusion to archived content only, so a draft
|
|
55
|
+
* document a user is actively working on stays findable. The two filters are different sets on purpose.
|
|
56
|
+
*/
|
|
57
|
+
const fs_1 = require("fs");
|
|
58
|
+
const path_1 = require("path");
|
|
59
|
+
const validation_1 = require("../validation");
|
|
60
|
+
const state_machine_1 = require("./state-machine");
|
|
61
|
+
const storage_1 = require("../storage");
|
|
62
|
+
/**
|
|
63
|
+
* The static (placeholder-free) directory prefix of a `memory.yaml` type `path` pattern — e.g.
|
|
64
|
+
* `"docs/04_memory/design/adrs/{id}.md"` -> `"docs/04_memory/design/adrs"`. A pattern whose first
|
|
65
|
+
* placeholder sits in the directory portion (e.g. task's `"docs/04_memory/{release}/{id}.md"`)
|
|
66
|
+
* collapses to the parent of that placeholder — `"docs/04_memory"` — because every possible
|
|
67
|
+
* `{release}` value lives under it.
|
|
68
|
+
*/
|
|
69
|
+
function staticDirPrefix(pattern) {
|
|
70
|
+
const braceIndex = pattern.indexOf('{');
|
|
71
|
+
const staticPart = braceIndex === -1 ? pattern : pattern.slice(0, braceIndex);
|
|
72
|
+
const lastSlash = staticPart.lastIndexOf('/');
|
|
73
|
+
return lastSlash === -1 ? '' : staticPart.slice(0, lastSlash);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Derive the minimal set of root-relative directories a scan needs to walk to see every Memory
|
|
77
|
+
* document declared by `memoryYaml`'s types (spec-011: path-pattern predictability, not a full-repo
|
|
78
|
+
* walk). A directory already covered by another (shorter) directory in the set is dropped, so the
|
|
79
|
+
* result never contains redundant, nested roots.
|
|
80
|
+
*/
|
|
81
|
+
function computeMemoryContentRoots(memoryYaml) {
|
|
82
|
+
const dirs = new Set();
|
|
83
|
+
for (const typeEntry of Object.values(memoryYaml.types)) {
|
|
84
|
+
const dir = staticDirPrefix(typeEntry.path);
|
|
85
|
+
if (dir)
|
|
86
|
+
dirs.add(dir);
|
|
87
|
+
}
|
|
88
|
+
const all = [...dirs].sort();
|
|
89
|
+
return all.filter((dir) => !all.some((other) => other !== dir && dir.startsWith(`${other}/`)));
|
|
90
|
+
}
|
|
91
|
+
/** Recursively list every `.md` file under `root/dir`, as root-relative POSIX paths, sorted. */
|
|
92
|
+
function listMarkdownFilesUnder(root, dir) {
|
|
93
|
+
const absoluteDir = (0, path_1.join)(root, dir);
|
|
94
|
+
if (!(0, fs_1.existsSync)(absoluteDir) || !(0, fs_1.statSync)(absoluteDir).isDirectory())
|
|
95
|
+
return [];
|
|
96
|
+
const out = [];
|
|
97
|
+
const walk = (current, relativePrefix) => {
|
|
98
|
+
for (const entry of (0, fs_1.readdirSync)(current).sort()) {
|
|
99
|
+
const full = (0, path_1.join)(current, entry);
|
|
100
|
+
const relative = `${relativePrefix}/${entry}`;
|
|
101
|
+
if ((0, fs_1.statSync)(full).isDirectory()) {
|
|
102
|
+
walk(full, relative);
|
|
103
|
+
}
|
|
104
|
+
else if (entry.endsWith('.md')) {
|
|
105
|
+
out.push(relative);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
walk(absoluteDir, dir);
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* List every Memory document under `root`, as root-relative POSIX paths, sorted lexicographically
|
|
114
|
+
* (REQ-SYS-07 — deterministic, no unordered iteration). Scans only the directories
|
|
115
|
+
* {@link computeMemoryContentRoots} derives from `memoryYaml`, never the whole project tree.
|
|
116
|
+
*/
|
|
117
|
+
function listMemoryDocumentPaths(root, memoryYaml) {
|
|
118
|
+
const out = new Set();
|
|
119
|
+
for (const dir of computeMemoryContentRoots(memoryYaml)) {
|
|
120
|
+
for (const path of listMarkdownFilesUnder(root, dir))
|
|
121
|
+
out.add(path);
|
|
122
|
+
}
|
|
123
|
+
return [...out].sort();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Read one Memory document and split it into its parsed frontmatter and body text, without running
|
|
127
|
+
* it through any type's Zod schema — `memory search`/`history` must handle documents of every type
|
|
128
|
+
* and any (even structurally imperfect) state, so this is a read, never a validation.
|
|
129
|
+
*/
|
|
130
|
+
function loadMemoryDocumentSummary(root, relativePath) {
|
|
131
|
+
const absolute = (0, path_1.join)(root, relativePath);
|
|
132
|
+
const raw = (0, storage_1.readDocument)(absolute);
|
|
133
|
+
const { frontmatter: frontmatterText, body } = (0, storage_1.splitFrontmatter)(raw);
|
|
134
|
+
let frontmatter = {};
|
|
135
|
+
if (frontmatterText) {
|
|
136
|
+
const parsed = (0, validation_1.parseYaml)(frontmatterText, absolute);
|
|
137
|
+
if (parsed !== null && typeof parsed === 'object')
|
|
138
|
+
frontmatter = parsed;
|
|
139
|
+
}
|
|
140
|
+
return { path: relativePath, frontmatter, body };
|
|
141
|
+
}
|
|
142
|
+
function asStringArray(value) {
|
|
143
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : [];
|
|
144
|
+
}
|
|
145
|
+
function asString(value) {
|
|
146
|
+
return typeof value === 'string' ? value : undefined;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Resolve a single Memory document by its `id` frontmatter value alone — REQ-PERF-04 /
|
|
150
|
+
* task-009-mcp-resource-fetch-latency's Acceptance Criteria, which name a `wingfoil://memory/{id}`
|
|
151
|
+
* benchmark fetch. This is NOT spec-004-mcp-surface-contract §2.1's Resource addressing: spec-004's
|
|
152
|
+
* actual scheme is `wingfoil://memory/{type}` (collection listing) and `wingfoil://memory/{type}/{id}`
|
|
153
|
+
* (single document) — there is no bare, single-segment `wingfoil://memory/{id}` form there, and this
|
|
154
|
+
* `{id}` segment would collide with spec-004's `{type}` segment. task-011-mcp-resources-read-only has
|
|
155
|
+
* since replaced the `wingfoil://memory/{id}` *Resource* this primitive originally backed with the
|
|
156
|
+
* conformant `wingfoil://memory/{type}/{id}` form (see {@link findMemoryDocumentByTypeAndId}) — this
|
|
157
|
+
* bare-id primitive itself is left in place (still covered by its own `test/memory/query.test.ts`
|
|
158
|
+
* unit tests and REQ-PERF-04's ported benchmark) as a generic, type-agnostic lookup; nothing in
|
|
159
|
+
* `src/mcp` calls it anymore.
|
|
160
|
+
*
|
|
161
|
+
* A linear scan over every document {@link listMemoryDocumentPaths} returns, in its
|
|
162
|
+
* already-deterministic sorted order, stopping at the first document whose frontmatter `id` matches
|
|
163
|
+
* *exactly* (never a substring — that remains `searchMemoryDocuments`'/task-021's keyword-search
|
|
164
|
+
* surface, not this primitive's). Returns `undefined` — never throws — when no document matches;
|
|
165
|
+
* turning that into a protocol-level "resource not found" failure is the MCP Resource adapter's job
|
|
166
|
+
* (spec-004 §2.2's general unresolvable-URI contract), not this primitive's.
|
|
167
|
+
*/
|
|
168
|
+
function findMemoryDocumentById(root, memoryYaml, id) {
|
|
169
|
+
for (const path of listMemoryDocumentPaths(root, memoryYaml)) {
|
|
170
|
+
const summary = loadMemoryDocumentSummary(root, path);
|
|
171
|
+
if (asString(summary.frontmatter.id) === id)
|
|
172
|
+
return summary;
|
|
173
|
+
}
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* List every Memory document whose frontmatter `type:` field equals `type` (a `memory.yaml` `types:`
|
|
178
|
+
* key), as frontmatter-only summaries — spec-004 §2.1's `wingfoil://memory/{type}` collection
|
|
179
|
+
* addressing (task-011-mcp-resources-read-only, REQ-INT-01). Sorted by `id` (falling back to `path`
|
|
180
|
+
* when a document has no `id`) ascending (REQ-SYS-07 — deterministic, no unordered iteration).
|
|
181
|
+
*
|
|
182
|
+
* Membership is decided by each document's own frontmatter `type:` field, not by which directory it
|
|
183
|
+
* lives in: several types' `path` patterns collapse to the *same* static directory prefix (e.g.
|
|
184
|
+
* `release`/`release-line` both resolve to `docs/04_memory/planning`; `task`'s own pattern collapses
|
|
185
|
+
* all the way to `docs/04_memory` itself — see {@link computeMemoryContentRoots}'s doc comment), so a
|
|
186
|
+
* directory-only filter would wrongly fold sibling types' documents into this type's collection.
|
|
187
|
+
*/
|
|
188
|
+
function listMemoryDocumentsByType(root, memoryYaml, type) {
|
|
189
|
+
const out = [];
|
|
190
|
+
for (const path of listMemoryDocumentPaths(root, memoryYaml)) {
|
|
191
|
+
const { frontmatter } = loadMemoryDocumentSummary(root, path);
|
|
192
|
+
if (asString(frontmatter.type) !== type)
|
|
193
|
+
continue;
|
|
194
|
+
out.push({
|
|
195
|
+
path,
|
|
196
|
+
id: asString(frontmatter.id),
|
|
197
|
+
title: asString(frontmatter.title),
|
|
198
|
+
status: asString(frontmatter.status),
|
|
199
|
+
tags: asStringArray(frontmatter.tags),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return out.sort((a, b) => {
|
|
203
|
+
const keyA = a.id ?? a.path;
|
|
204
|
+
const keyB = b.id ?? b.path;
|
|
205
|
+
return keyA < keyB ? -1 : keyA > keyB ? 1 : 0;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Resolve a single Memory document by (`type`, `id`) — spec-004 §2.1's `wingfoil://memory/{type}/{id}`
|
|
210
|
+
* single-document addressing (task-011-mcp-resources-read-only, REQ-INT-01), the conformant
|
|
211
|
+
* replacement for task-009's bare-`{id}` `findMemoryDocumentById` Resource usage. Both `type` and `id`
|
|
212
|
+
* must match a document's own frontmatter — same directory-collision reasoning as
|
|
213
|
+
* {@link listMemoryDocumentsByType} — so this never returns a document of a *different* type merely
|
|
214
|
+
* because that type's path pattern happens to also resolve under the same directory. Returns
|
|
215
|
+
* `undefined` — never throws — when nothing matches; surfacing that as a protocol-level "resource not
|
|
216
|
+
* found" failure is the MCP Resource adapter's job (spec-004 §2.2), not this primitive's.
|
|
217
|
+
*/
|
|
218
|
+
function findMemoryDocumentByTypeAndId(root, memoryYaml, type, id) {
|
|
219
|
+
for (const path of listMemoryDocumentPaths(root, memoryYaml)) {
|
|
220
|
+
const summary = loadMemoryDocumentSummary(root, path);
|
|
221
|
+
if (asString(summary.frontmatter.type) === type && asString(summary.frontmatter.id) === id)
|
|
222
|
+
return summary;
|
|
223
|
+
}
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
/** `E_EMPTY_SEARCH_QUERY` field-level code (spec-009-validation-strategy §3) for a rejected empty
|
|
227
|
+
* or whitespace-only `wingfoil memory search` query. */
|
|
228
|
+
exports.E_EMPTY_SEARCH_QUERY = 'E_EMPTY_SEARCH_QUERY';
|
|
229
|
+
/**
|
|
230
|
+
* Reject an empty or whitespace-only search query (P1.12 BDD Scenario "Error - empty query string":
|
|
231
|
+
* "no search is performed" and exit code 2, message "empty search query"). Mirrors
|
|
232
|
+
* `resolveTransitionTarget`'s `ValidationError.semantic` pattern (`./state-machine.ts`) so the shared
|
|
233
|
+
* exit-code mapping (spec-009 §3) surfaces this as exit 2 without a bespoke error path; task-021's
|
|
234
|
+
* `wingfoil memory search` CLI/MCP surface calls this on the user-supplied query string before it
|
|
235
|
+
* ever reaches {@link searchMemoryDocuments}.
|
|
236
|
+
*
|
|
237
|
+
* Deliberately a separate guard, not a change to `searchMemoryDocuments`'s own signature/behavior:
|
|
238
|
+
* `searchMemoryDocuments(root, memoryYaml, '', { tag })` remains a legitimate "browse by tag alone,
|
|
239
|
+
* no keyword" call (task-008's own characterization test) — the empty-query rejection is this task's
|
|
240
|
+
* (P1.12's) algorithm-level validation concern, applied at the point a *user-facing* query string is
|
|
241
|
+
* about to be searched, not baked into the lower-level scan primitive that also serves tag-only
|
|
242
|
+
* listing.
|
|
243
|
+
*/
|
|
244
|
+
function validateSearchQuery(query) {
|
|
245
|
+
if (query.trim().length === 0) {
|
|
246
|
+
throw validation_1.ValidationError.semantic([
|
|
247
|
+
{ code: exports.E_EMPTY_SEARCH_QUERY, path: '', file: '', message: 'empty search query' },
|
|
248
|
+
]);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Deterministic keyword/frontmatter search over every Memory document `memoryYaml` declares
|
|
253
|
+
* (P1.5/P1.12): case-insensitive substring matching against title/id/tags (metadata) and body text,
|
|
254
|
+
* optionally narrowed to one exact `tag`. A query that matches nothing returns `[]` — a successful,
|
|
255
|
+
* empty result, never a thrown error (that "no documents matched" outcome is a CLI/MCP-surface
|
|
256
|
+
* concern, task-021's, not this primitive's).
|
|
257
|
+
*
|
|
258
|
+
* Ordering is a total, deterministic order (REQ-SYS-07): metadata matches before body-only matches,
|
|
259
|
+
* then by `id` (falling back to `path` when a document has no `id`) ascending — so calling this
|
|
260
|
+
* twice against unchanged state always returns the exact same array.
|
|
261
|
+
*
|
|
262
|
+
* **REQ-STATE-06:** a document whose frontmatter `status` is archived — `deprecated` or `superseded`
|
|
263
|
+
* (`isArchivedStatus`, `dl-028`) — is excluded by default, so it never appears in these "default
|
|
264
|
+
* `memory search` results", matching the Fit Criterion verbatim, while the file itself is untouched on
|
|
265
|
+
* disk and in git history (`memory deprecate`, P1.9, never deletes it; `superseded` is reached by an
|
|
266
|
+
* ordinary `approve`). Pass `options.includeArchived: true` for the one legitimate exception: an
|
|
267
|
+
* explicit, intentional request to see archived documents too. `draft` documents are NOT excluded
|
|
268
|
+
* here — see this module's header for why the search set and the context set differ.
|
|
269
|
+
*/
|
|
270
|
+
function searchMemoryDocuments(root, memoryYaml, query, options = {}) {
|
|
271
|
+
const needle = query.trim().toLowerCase();
|
|
272
|
+
const matches = [];
|
|
273
|
+
for (const path of listMemoryDocumentPaths(root, memoryYaml)) {
|
|
274
|
+
const { frontmatter, body } = loadMemoryDocumentSummary(root, path);
|
|
275
|
+
const status = asString(frontmatter.status);
|
|
276
|
+
if (!options.includeArchived && (0, state_machine_1.isArchivedStatus)(status))
|
|
277
|
+
continue;
|
|
278
|
+
const tags = asStringArray(frontmatter.tags);
|
|
279
|
+
if (options.tag && !tags.includes(options.tag))
|
|
280
|
+
continue;
|
|
281
|
+
const title = asString(frontmatter.title);
|
|
282
|
+
const id = asString(frontmatter.id);
|
|
283
|
+
const type = asString(frontmatter.type);
|
|
284
|
+
let metadataMatch = false;
|
|
285
|
+
let bodyMatch = false;
|
|
286
|
+
if (needle.length > 0) {
|
|
287
|
+
metadataMatch =
|
|
288
|
+
(title !== undefined && title.toLowerCase().includes(needle)) ||
|
|
289
|
+
(id !== undefined && id.toLowerCase().includes(needle)) ||
|
|
290
|
+
tags.some((tag) => tag.toLowerCase().includes(needle));
|
|
291
|
+
bodyMatch = body.toLowerCase().includes(needle);
|
|
292
|
+
if (!metadataMatch && !bodyMatch)
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
matches.push({ path, id, title, tags, status, type, metadataMatch, bodyMatch });
|
|
296
|
+
}
|
|
297
|
+
return matches.sort((a, b) => {
|
|
298
|
+
const scoreA = a.metadataMatch ? 1 : 0;
|
|
299
|
+
const scoreB = b.metadataMatch ? 1 : 0;
|
|
300
|
+
if (scoreA !== scoreB)
|
|
301
|
+
return scoreB - scoreA;
|
|
302
|
+
const keyA = a.id ?? a.path;
|
|
303
|
+
const keyB = b.id ?? b.path;
|
|
304
|
+
return keyA < keyB ? -1 : keyA > keyB ? 1 : 0;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/memory/query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rejected document: `status` set to `target` (the type's `gates.<state>.reject` value, taken
|
|
3
|
+
* verbatim per `spec-001-memory-yaml-schema`) and `rejection_reason` set to `reason`. Every other byte
|
|
4
|
+
* — the remaining frontmatter entries, their inline comments, and the whole body — is untouched.
|
|
5
|
+
*
|
|
6
|
+
* `reason` is arbitrary user input (`--reason <text>`, `spec-008-cli-grammar` §2), so it is written
|
|
7
|
+
* through `setFrontmatterField`'s YAML-safe serialization: quotes, colons, `#`, newlines, a leading
|
|
8
|
+
* `-`, YAML-typed words such as `no`/`null` and unicode all parse back to exactly the text given.
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderRejectDocument(content: string, target: string, reason: string): string;
|
|
11
|
+
//# sourceMappingURL=reject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reject.d.ts","sourceRoot":"","sources":["../../src/memory/reject.ts"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE5F"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderRejectDocument = renderRejectDocument;
|
|
4
|
+
/**
|
|
5
|
+
* The pure document edit behind `wingfoil memory reject` (P1.8, task-047-memory-reject), kept out of
|
|
6
|
+
* the `CoreResult`-wrapped operation in `src/core` exactly as `./submit.ts` serves `memory submit`.
|
|
7
|
+
*
|
|
8
|
+
* `spec-010-memory-frontmatter-schema`'s field-write ownership table makes `memory.reject` the one
|
|
9
|
+
* transition verb that writes TWO frontmatter fields — "`memory.reject` changes `status` plus
|
|
10
|
+
* `rejection_reason` — the one exception to 'status only' among the transition verbs". The reason is
|
|
11
|
+
* stored verbatim, as the mirror of the `Reason:` trailer the commit body carries (the commit stays
|
|
12
|
+
* the authoritative audit record, P1.7/REQ-SEC-04); the next `memory.submit` removes the key again
|
|
13
|
+
* ({@link renderSubmitDocument}), so it always reflects only the most recent reject.
|
|
14
|
+
*
|
|
15
|
+
* Deterministic (REQ-SYS-07): a pure function of its arguments, with no clock, randomness or
|
|
16
|
+
* object-key iteration.
|
|
17
|
+
*/
|
|
18
|
+
const frontmatter_edit_1 = require("./frontmatter-edit");
|
|
19
|
+
const submit_1 = require("./submit");
|
|
20
|
+
/**
|
|
21
|
+
* The rejected document: `status` set to `target` (the type's `gates.<state>.reject` value, taken
|
|
22
|
+
* verbatim per `spec-001-memory-yaml-schema`) and `rejection_reason` set to `reason`. Every other byte
|
|
23
|
+
* — the remaining frontmatter entries, their inline comments, and the whole body — is untouched.
|
|
24
|
+
*
|
|
25
|
+
* `reason` is arbitrary user input (`--reason <text>`, `spec-008-cli-grammar` §2), so it is written
|
|
26
|
+
* through `setFrontmatterField`'s YAML-safe serialization: quotes, colons, `#`, newlines, a leading
|
|
27
|
+
* `-`, YAML-typed words such as `no`/`null` and unicode all parse back to exactly the text given.
|
|
28
|
+
*/
|
|
29
|
+
function renderRejectDocument(content, target, reason) {
|
|
30
|
+
return (0, frontmatter_edit_1.setFrontmatterField)((0, frontmatter_edit_1.setFrontmatterField)(content, 'status', target), submit_1.REJECTION_REASON_FIELD, reason);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=reject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reject.js","sourceRoot":"","sources":["../../src/memory/reject.ts"],"names":[],"mappings":";;AA0BA,oDAEC;AA5BD;;;;;;;;;;;;;GAaG;AACH,yDAAyD;AACzD,qCAAkD;AAElD;;;;;;;;GAQG;AACH,SAAgB,oBAAoB,CAAC,OAAe,EAAE,MAAc,EAAE,MAAc;IAClF,OAAO,IAAA,sCAAmB,EAAC,IAAA,sCAAmB,EAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,+BAAsB,EAAE,MAAM,CAAC,CAAC;AAC7G,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryYaml schema (spec-001-memory-yaml-schema) — the Project Memory type registry (P1.13). This
|
|
3
|
+
* is one of the three independent per-pillar schemas task-004-decoupled-pillars keeps decoupled
|
|
4
|
+
* (REQ-SYS-02): it has no dependency on `dna.yaml`'s or `workflows.yaml`'s schema, and its own
|
|
5
|
+
* validation never inspects any other file.
|
|
6
|
+
*
|
|
7
|
+
* Every object node is `.passthrough()` per spec-009-validation-strategy §2 (unknown fields are
|
|
8
|
+
* preserved and warned about, not fatal) — see `src/core/loaders.ts` for the loader that wires this
|
|
9
|
+
* schema through the shared two-pass pipeline.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
/**
|
|
13
|
+
* The sequence/gates/waiting state-machine encoding (spec-001 "Sub-schema: StateMachine"),
|
|
14
|
+
* replacing the earlier ambiguous `transitions: {state: [target, ...]}` dict-of-arrays format.
|
|
15
|
+
*
|
|
16
|
+
* The semantic rules from spec-001's "Semantic validation (post-parse)" section are embedded here
|
|
17
|
+
* as a `.superRefine()` — spec-009 §1 frames exactly this kind of same-document, cross-field rule
|
|
18
|
+
* ("a phase's `gates` key must reference a step name that is itself a member of that phase's
|
|
19
|
+
* `sequence` array") as expressible via `.superRefine()`, "still inside the Zod schema, logically
|
|
20
|
+
* Pass 2":
|
|
21
|
+
*
|
|
22
|
+
* - Every key in `gates` and every entry in `waiting` MUST be a member of `sequence`.
|
|
23
|
+
* - A `gates.<state>.reject` target need NOT be a member of `sequence` (it may revert into the
|
|
24
|
+
* chain or name an off-chain decline state reached by no forward edge).
|
|
25
|
+
* - The literal string `"deprecated"` is reserved: it may not appear in `sequence`, as a `gates`
|
|
26
|
+
* key, as a `gates.<state>.reject` target, or in `waiting` — it is an implicit wildcard edge to a
|
|
27
|
+
* reserved state, never declared explicitly.
|
|
28
|
+
*/
|
|
29
|
+
export declare const StateMachine: z.ZodObject<{
|
|
30
|
+
sequence: z.ZodArray<z.ZodString>;
|
|
31
|
+
gates: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
32
|
+
reject: z.ZodString;
|
|
33
|
+
}, z.core.$loose>>>;
|
|
34
|
+
waiting: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
35
|
+
}, z.core.$loose>;
|
|
36
|
+
export type StateMachine = z.infer<typeof StateMachine>;
|
|
37
|
+
/** `template.frontmatter.required` + the scaffold file path a `memory.add --type <t>` copies. */
|
|
38
|
+
export declare const TemplateConfig: z.ZodObject<{
|
|
39
|
+
frontmatter: z.ZodObject<{
|
|
40
|
+
required: z.ZodArray<z.ZodString>;
|
|
41
|
+
}, z.core.$loose>;
|
|
42
|
+
file: z.ZodString;
|
|
43
|
+
}, z.core.$loose>;
|
|
44
|
+
export type TemplateConfig = z.infer<typeof TemplateConfig>;
|
|
45
|
+
/** One `types.<name>` entry — path pattern, id pattern, human metadata, template, state machine. */
|
|
46
|
+
export declare const MemoryTypeEntry: z.ZodObject<{
|
|
47
|
+
path: z.ZodString;
|
|
48
|
+
id_pattern: z.ZodOptional<z.ZodString>;
|
|
49
|
+
name: z.ZodOptional<z.ZodString>;
|
|
50
|
+
description: z.ZodOptional<z.ZodString>;
|
|
51
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
52
|
+
template: z.ZodOptional<z.ZodObject<{
|
|
53
|
+
frontmatter: z.ZodObject<{
|
|
54
|
+
required: z.ZodArray<z.ZodString>;
|
|
55
|
+
}, z.core.$loose>;
|
|
56
|
+
file: z.ZodString;
|
|
57
|
+
}, z.core.$loose>>;
|
|
58
|
+
states: z.ZodOptional<z.ZodObject<{
|
|
59
|
+
sequence: z.ZodArray<z.ZodString>;
|
|
60
|
+
gates: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
61
|
+
reject: z.ZodString;
|
|
62
|
+
}, z.core.$loose>>>;
|
|
63
|
+
waiting: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
64
|
+
}, z.core.$loose>>;
|
|
65
|
+
}, z.core.$loose>;
|
|
66
|
+
export type MemoryTypeEntry = z.infer<typeof MemoryTypeEntry>;
|
|
67
|
+
/**
|
|
68
|
+
* `memory.yaml`'s top-level shape. `version` is `z.number().positive()`, NOT `.int()` — the file's
|
|
69
|
+
* `version: 1.0` (and `1.1`) is a YAML float and `.int()` would spuriously reject it (spec-001).
|
|
70
|
+
*/
|
|
71
|
+
export declare const MemoryYaml: z.ZodObject<{
|
|
72
|
+
version: z.ZodNumber;
|
|
73
|
+
defaults: z.ZodOptional<z.ZodObject<{
|
|
74
|
+
states: z.ZodObject<{
|
|
75
|
+
sequence: z.ZodArray<z.ZodString>;
|
|
76
|
+
gates: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
77
|
+
reject: z.ZodString;
|
|
78
|
+
}, z.core.$loose>>>;
|
|
79
|
+
waiting: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
80
|
+
}, z.core.$loose>;
|
|
81
|
+
}, z.core.$loose>>;
|
|
82
|
+
types: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
83
|
+
path: z.ZodString;
|
|
84
|
+
id_pattern: z.ZodOptional<z.ZodString>;
|
|
85
|
+
name: z.ZodOptional<z.ZodString>;
|
|
86
|
+
description: z.ZodOptional<z.ZodString>;
|
|
87
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
88
|
+
template: z.ZodOptional<z.ZodObject<{
|
|
89
|
+
frontmatter: z.ZodObject<{
|
|
90
|
+
required: z.ZodArray<z.ZodString>;
|
|
91
|
+
}, z.core.$loose>;
|
|
92
|
+
file: z.ZodString;
|
|
93
|
+
}, z.core.$loose>>;
|
|
94
|
+
states: z.ZodOptional<z.ZodObject<{
|
|
95
|
+
sequence: z.ZodArray<z.ZodString>;
|
|
96
|
+
gates: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
97
|
+
reject: z.ZodString;
|
|
98
|
+
}, z.core.$loose>>>;
|
|
99
|
+
waiting: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
100
|
+
}, z.core.$loose>>;
|
|
101
|
+
}, z.core.$loose>>;
|
|
102
|
+
}, z.core.$loose>;
|
|
103
|
+
export type MemoryYaml = z.infer<typeof MemoryYaml>;
|
|
104
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/memory/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY;;;;;;iBA2DrB,CAAC;AACL,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD,iGAAiG;AACjG,eAAO,MAAM,cAAc;;;;;iBAKX,CAAC;AACjB,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,oGAAoG;AACpG,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;iBAUZ,CAAC;AACjB,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8CnB,CAAC;AACL,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemoryYaml = exports.MemoryTypeEntry = exports.TemplateConfig = exports.StateMachine = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* MemoryYaml schema (spec-001-memory-yaml-schema) — the Project Memory type registry (P1.13). This
|
|
6
|
+
* is one of the three independent per-pillar schemas task-004-decoupled-pillars keeps decoupled
|
|
7
|
+
* (REQ-SYS-02): it has no dependency on `dna.yaml`'s or `workflows.yaml`'s schema, and its own
|
|
8
|
+
* validation never inspects any other file.
|
|
9
|
+
*
|
|
10
|
+
* Every object node is `.passthrough()` per spec-009-validation-strategy §2 (unknown fields are
|
|
11
|
+
* preserved and warned about, not fatal) — see `src/core/loaders.ts` for the loader that wires this
|
|
12
|
+
* schema through the shared two-pass pipeline.
|
|
13
|
+
*/
|
|
14
|
+
const zod_1 = require("zod");
|
|
15
|
+
const RESERVED_STATE = 'deprecated';
|
|
16
|
+
/**
|
|
17
|
+
* The sequence/gates/waiting state-machine encoding (spec-001 "Sub-schema: StateMachine"),
|
|
18
|
+
* replacing the earlier ambiguous `transitions: {state: [target, ...]}` dict-of-arrays format.
|
|
19
|
+
*
|
|
20
|
+
* The semantic rules from spec-001's "Semantic validation (post-parse)" section are embedded here
|
|
21
|
+
* as a `.superRefine()` — spec-009 §1 frames exactly this kind of same-document, cross-field rule
|
|
22
|
+
* ("a phase's `gates` key must reference a step name that is itself a member of that phase's
|
|
23
|
+
* `sequence` array") as expressible via `.superRefine()`, "still inside the Zod schema, logically
|
|
24
|
+
* Pass 2":
|
|
25
|
+
*
|
|
26
|
+
* - Every key in `gates` and every entry in `waiting` MUST be a member of `sequence`.
|
|
27
|
+
* - A `gates.<state>.reject` target need NOT be a member of `sequence` (it may revert into the
|
|
28
|
+
* chain or name an off-chain decline state reached by no forward edge).
|
|
29
|
+
* - The literal string `"deprecated"` is reserved: it may not appear in `sequence`, as a `gates`
|
|
30
|
+
* key, as a `gates.<state>.reject` target, or in `waiting` — it is an implicit wildcard edge to a
|
|
31
|
+
* reserved state, never declared explicitly.
|
|
32
|
+
*/
|
|
33
|
+
exports.StateMachine = zod_1.z
|
|
34
|
+
.object({
|
|
35
|
+
sequence: zod_1.z.array(zod_1.z.string()).min(1),
|
|
36
|
+
gates: zod_1.z.record(zod_1.z.string(), zod_1.z.object({ reject: zod_1.z.string() }).passthrough()).optional(),
|
|
37
|
+
waiting: zod_1.z.array(zod_1.z.string()).optional(),
|
|
38
|
+
})
|
|
39
|
+
.passthrough()
|
|
40
|
+
.superRefine((value, ctx) => {
|
|
41
|
+
const sequenceSet = new Set(value.sequence);
|
|
42
|
+
value.sequence.forEach((state, index) => {
|
|
43
|
+
if (state === RESERVED_STATE) {
|
|
44
|
+
ctx.addIssue({
|
|
45
|
+
code: 'custom',
|
|
46
|
+
message: `"${RESERVED_STATE}" is a reserved implicit state and may not appear in \`sequence\``,
|
|
47
|
+
path: ['sequence', index],
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
for (const [state, gate] of Object.entries(value.gates ?? {})) {
|
|
52
|
+
if (state === RESERVED_STATE) {
|
|
53
|
+
ctx.addIssue({
|
|
54
|
+
code: 'custom',
|
|
55
|
+
message: `"${RESERVED_STATE}" is a reserved implicit state and may not be declared as a \`gates\` key`,
|
|
56
|
+
path: ['gates', state],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else if (!sequenceSet.has(state)) {
|
|
60
|
+
ctx.addIssue({
|
|
61
|
+
code: 'custom',
|
|
62
|
+
message: `\`gates\` key "${state}" must be a member of \`sequence\``,
|
|
63
|
+
path: ['gates', state],
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (gate.reject === RESERVED_STATE) {
|
|
67
|
+
ctx.addIssue({
|
|
68
|
+
code: 'custom',
|
|
69
|
+
message: `"${RESERVED_STATE}" is a reserved implicit state and may not be a \`gates.<state>.reject\` target`,
|
|
70
|
+
path: ['gates', state, 'reject'],
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// Note: gate.reject need NOT be a member of `sequence` (spec-001) — no membership check here.
|
|
74
|
+
}
|
|
75
|
+
(value.waiting ?? []).forEach((state, index) => {
|
|
76
|
+
if (state === RESERVED_STATE) {
|
|
77
|
+
ctx.addIssue({
|
|
78
|
+
code: 'custom',
|
|
79
|
+
message: `"${RESERVED_STATE}" is a reserved implicit state and may not be declared in \`waiting\``,
|
|
80
|
+
path: ['waiting', index],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else if (!sequenceSet.has(state)) {
|
|
84
|
+
ctx.addIssue({
|
|
85
|
+
code: 'custom',
|
|
86
|
+
message: `\`waiting\` entry "${state}" must be a member of \`sequence\``,
|
|
87
|
+
path: ['waiting', index],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
/** `template.frontmatter.required` + the scaffold file path a `memory.add --type <t>` copies. */
|
|
93
|
+
exports.TemplateConfig = zod_1.z
|
|
94
|
+
.object({
|
|
95
|
+
frontmatter: zod_1.z.object({ required: zod_1.z.array(zod_1.z.string()) }).passthrough(),
|
|
96
|
+
file: zod_1.z.string(),
|
|
97
|
+
})
|
|
98
|
+
.passthrough();
|
|
99
|
+
/** One `types.<name>` entry — path pattern, id pattern, human metadata, template, state machine. */
|
|
100
|
+
exports.MemoryTypeEntry = zod_1.z
|
|
101
|
+
.object({
|
|
102
|
+
path: zod_1.z.string(),
|
|
103
|
+
id_pattern: zod_1.z.string().optional(),
|
|
104
|
+
name: zod_1.z.string().optional(),
|
|
105
|
+
description: zod_1.z.string().optional(),
|
|
106
|
+
tags: zod_1.z.array(zod_1.z.string()).optional(),
|
|
107
|
+
template: exports.TemplateConfig.optional(),
|
|
108
|
+
states: exports.StateMachine.optional(), // absent ⇒ `defaults.states` applies (REQ-STATE-08)
|
|
109
|
+
})
|
|
110
|
+
.passthrough();
|
|
111
|
+
/**
|
|
112
|
+
* `memory.yaml`'s top-level shape. `version` is `z.number().positive()`, NOT `.int()` — the file's
|
|
113
|
+
* `version: 1.0` (and `1.1`) is a YAML float and `.int()` would spuriously reject it (spec-001).
|
|
114
|
+
*/
|
|
115
|
+
exports.MemoryYaml = zod_1.z
|
|
116
|
+
.object({
|
|
117
|
+
version: zod_1.z.number().positive(),
|
|
118
|
+
defaults: zod_1.z.object({ states: exports.StateMachine }).passthrough().optional(),
|
|
119
|
+
types: zod_1.z.record(zod_1.z.string(), exports.MemoryTypeEntry),
|
|
120
|
+
})
|
|
121
|
+
.passthrough()
|
|
122
|
+
/**
|
|
123
|
+
* Type-contextualized restatement of spec-001's "Semantic validation (post-parse)" membership rule
|
|
124
|
+
* — the P1.13 acceptance contract (task-024-implement-memory-element-schema): a state referenced by
|
|
125
|
+
* a type's machine (a `gates` key or a `waiting` entry) that is NOT one of that type's declared
|
|
126
|
+
* states (`sequence`) is a malformed schema and must be rejected at load time, **before any document
|
|
127
|
+
* of that type can be created or transitioned** (REQ-STATE-01). The `StateMachine` sub-schema's own
|
|
128
|
+
* `.superRefine` already flags the same structural violation, but only this top-level refinement
|
|
129
|
+
* knows the *owning type name*, so P1.13's required wording
|
|
130
|
+
* (`transition target '<state>' not in declared states for type '<type>'`) can only be produced
|
|
131
|
+
* here. `defaults.states` (which has no owning type) is left to the `StateMachine`-level check.
|
|
132
|
+
*
|
|
133
|
+
* `gates.<state>.reject` targets are intentionally NOT checked: spec-001 explicitly allows them to
|
|
134
|
+
* be off-chain ("need **not** be a member of `sequence` ... e.g. `bug`'s `open: { reject: closed }`").
|
|
135
|
+
* Iteration is over insertion-ordered `Object.entries`/`Object.keys` only — no wall-clock, no
|
|
136
|
+
* randomness, no set ordering (REQ-SYS-07 determinism).
|
|
137
|
+
*/
|
|
138
|
+
.superRefine((value, ctx) => {
|
|
139
|
+
for (const [typeName, entry] of Object.entries(value.types)) {
|
|
140
|
+
const machine = entry.states;
|
|
141
|
+
if (!machine)
|
|
142
|
+
continue; // no own machine ⇒ `defaults.states` applies (REQ-STATE-08)
|
|
143
|
+
const sequenceSet = new Set(machine.sequence);
|
|
144
|
+
const flag = (state, path) => {
|
|
145
|
+
if (state !== RESERVED_STATE && !sequenceSet.has(state)) {
|
|
146
|
+
ctx.addIssue({
|
|
147
|
+
code: 'custom',
|
|
148
|
+
message: `transition target '${state}' not in declared states for type '${typeName}'`,
|
|
149
|
+
path: ['types', typeName, 'states', ...path],
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
for (const gateState of Object.keys(machine.gates ?? {})) {
|
|
154
|
+
flag(gateState, ['gates', gateState]);
|
|
155
|
+
}
|
|
156
|
+
(machine.waiting ?? []).forEach((waitState, index) => {
|
|
157
|
+
flag(waitState, ['waiting', index]);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
//# sourceMappingURL=schema.js.map
|