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,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Core result & error shapes (spec-006-core-domain-api §2 — "Function shape").
|
|
4
|
+
*
|
|
5
|
+
* Every core function returns `Promise<CoreResult<T>>`, a discriminated union — never a thrown
|
|
6
|
+
* error for *expected* domain failures (illegal state transition, missing element, schema
|
|
7
|
+
* violation). That is what lets `src/cli`'s exit-code/stderr rendering and `src/mcp`'s Tool
|
|
8
|
+
* `isError`/Resource-read-failure response be driven off the very same value, rather than each
|
|
9
|
+
* surface independently deciding what counts as a failure and how to report it.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.coreOk = coreOk;
|
|
13
|
+
exports.coreErr = coreErr;
|
|
14
|
+
/** Build a success {@link CoreResult}, optionally carrying the git commit the operation produced. */
|
|
15
|
+
function coreOk(value, commit) {
|
|
16
|
+
return commit ? { ok: true, value, commit } : { ok: true, value };
|
|
17
|
+
}
|
|
18
|
+
/** Build a failure {@link CoreResult} from a {@link CoreError}. */
|
|
19
|
+
function coreErr(error) {
|
|
20
|
+
return { ok: false, error };
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AA8BH,wBAEC;AAGD,0BAEC;AARD,qGAAqG;AACrG,SAAgB,MAAM,CAAI,KAAQ,EAAE,MAAyC;IAC3E,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACpE,CAAC;AAED,mEAAmE;AACnE,SAAgB,OAAO,CAAY,KAAgB;IACjD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `UsageError` — a core operation's signal that its *arguments* were malformed (a usage/argument
|
|
3
|
+
* error, spec-005-cli-command-contract §1: "unknown flag, missing required argument, invalid flag
|
|
4
|
+
* value") as opposed to a business-logic failure. task-025-implement-dna-set introduces it for
|
|
5
|
+
* `dna set`'s invalid dotted key path (AC(c)), the first operation whose argument SHAPE must be
|
|
6
|
+
* interpreted inside the core function (a dotted path into `dna.yaml`) yet whose failure is classified
|
|
7
|
+
* as a usage error → exit **2**, not the exit **1** every `CoreError` maps to (`./exit-code.ts`).
|
|
8
|
+
*
|
|
9
|
+
* Why a thrown error rather than a `CoreResult.error`: the fixed `CoreError.code` enumeration
|
|
10
|
+
* (spec-006-core-domain-api §2) is all logic errors (exit 1) by design — `./exit-code.ts` documents
|
|
11
|
+
* that usage errors are deliberately not modeled there — so a usage error a core op detects is carried
|
|
12
|
+
* out to the single surface exit path as a throw, mapped by `exitCodeForThrow` (`./exit-code.ts`).
|
|
13
|
+
* `.message` is the clean, ready-to-render reason (spec-005 §3: `error: <reason>`), never a composite.
|
|
14
|
+
*/
|
|
15
|
+
export declare class UsageError extends Error {
|
|
16
|
+
/** Usage/argument error → exit 2 (spec-005-cli-command-contract §1, spec-008-cli-grammar §5). */
|
|
17
|
+
readonly exitCode = 2;
|
|
18
|
+
constructor(message: string);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=usage-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-error.d.ts","sourceRoot":"","sources":["../../src/core/usage-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC,iGAAiG;IACjG,QAAQ,CAAC,QAAQ,KAAK;gBAEV,OAAO,EAAE,MAAM;CAM5B"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UsageError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `UsageError` — a core operation's signal that its *arguments* were malformed (a usage/argument
|
|
6
|
+
* error, spec-005-cli-command-contract §1: "unknown flag, missing required argument, invalid flag
|
|
7
|
+
* value") as opposed to a business-logic failure. task-025-implement-dna-set introduces it for
|
|
8
|
+
* `dna set`'s invalid dotted key path (AC(c)), the first operation whose argument SHAPE must be
|
|
9
|
+
* interpreted inside the core function (a dotted path into `dna.yaml`) yet whose failure is classified
|
|
10
|
+
* as a usage error → exit **2**, not the exit **1** every `CoreError` maps to (`./exit-code.ts`).
|
|
11
|
+
*
|
|
12
|
+
* Why a thrown error rather than a `CoreResult.error`: the fixed `CoreError.code` enumeration
|
|
13
|
+
* (spec-006-core-domain-api §2) is all logic errors (exit 1) by design — `./exit-code.ts` documents
|
|
14
|
+
* that usage errors are deliberately not modeled there — so a usage error a core op detects is carried
|
|
15
|
+
* out to the single surface exit path as a throw, mapped by `exitCodeForThrow` (`./exit-code.ts`).
|
|
16
|
+
* `.message` is the clean, ready-to-render reason (spec-005 §3: `error: <reason>`), never a composite.
|
|
17
|
+
*/
|
|
18
|
+
class UsageError extends Error {
|
|
19
|
+
/** Usage/argument error → exit 2 (spec-005-cli-command-contract §1, spec-008-cli-grammar §5). */
|
|
20
|
+
exitCode = 2;
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = 'UsageError';
|
|
24
|
+
// Restore the prototype chain so `instanceof UsageError` holds after transpilation.
|
|
25
|
+
Object.setPrototypeOf(this, UsageError.prototype);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.UsageError = UsageError;
|
|
29
|
+
//# sourceMappingURL=usage-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-error.js","sourceRoot":"","sources":["../../src/core/usage-error.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;GAaG;AACH,MAAa,UAAW,SAAQ,KAAK;IACnC,iGAAiG;IACxF,QAAQ,GAAG,CAAC,CAAC;IAEtB,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,oFAAoF;QACpF,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAVD,gCAUC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type CoreResult } from './types';
|
|
2
|
+
/** How a refusal names the operation whose commit a dirty target would otherwise enlarge. */
|
|
3
|
+
export interface WriteTargetContract {
|
|
4
|
+
/** What the file is called in the message — `'document'` for Memory, `'file'` for configuration. */
|
|
5
|
+
readonly noun: string;
|
|
6
|
+
/** Fills "modifications this `<owner>` does not own" — e.g. `'transition'`, `'operation'`. */
|
|
7
|
+
readonly owner: string;
|
|
8
|
+
/** One clause saying what this kind of commit records, without a trailing separator. */
|
|
9
|
+
readonly records: string;
|
|
10
|
+
}
|
|
11
|
+
/** The contract every non-transition write verb refuses under (`dna set`, the `directive` verbs, `init`). */
|
|
12
|
+
export declare const CONFIG_WRITE_CONTRACT: WriteTargetContract;
|
|
13
|
+
/**
|
|
14
|
+
* Refuse, before anything is written, a target that already carries modifications this operation
|
|
15
|
+
* does not own — `dl-080`'s ratified write rule for a command that **edits a file in place**.
|
|
16
|
+
*
|
|
17
|
+
* Both of the user's declarations are inspected, the index (`:0`) and the working tree, because they
|
|
18
|
+
* can disagree and `commitPaths`'s `git add -- <path>` would silently replace the former with the
|
|
19
|
+
* latter. The refusal names the `git status` code and what is different, so the user can act on it
|
|
20
|
+
* without re-deriving it.
|
|
21
|
+
*
|
|
22
|
+
* **Why refusing rather than committing only the operation's own hunk.** Settled by `task-088` and
|
|
23
|
+
* ratified by `dl-080`, not re-argued here: partial staging is undefined before it is difficult
|
|
24
|
+
* (there are two candidate baselines on disk and "only my hunk" does not say onto which), and the
|
|
25
|
+
* edits it would leave behind are invisible, deferred, and swept into whatever commits next. A
|
|
26
|
+
* refusal costs two commands, now, on a file this function has provably not touched.
|
|
27
|
+
*
|
|
28
|
+
* @param worktree - The working-tree text when the caller has already read it (the transition verbs
|
|
29
|
+
* have); omitted, it is read here.
|
|
30
|
+
*/
|
|
31
|
+
export declare function requireUnmodifiedTarget(root: string, path: string, contract?: WriteTargetContract, worktree?: string): CoreResult<undefined>;
|
|
32
|
+
/** {@link requireUnmodifiedTarget} over several targets, refusing on the first one that is dirty. */
|
|
33
|
+
export declare function requireUnmodifiedTargets(root: string, paths: readonly string[], contract?: WriteTargetContract): CoreResult<undefined>;
|
|
34
|
+
/**
|
|
35
|
+
* Refuse, before anything is written, a target that is supposed to be **new** but is already
|
|
36
|
+
* occupied — `dl-080`'s write rule in the shape a *creating* command needs (`memory add`).
|
|
37
|
+
*
|
|
38
|
+
* "Unmodified" is the wrong question for a command whose contract is to register a new element: the
|
|
39
|
+
* path is normally absent, so the modification it does not own is any pre-existing content at all.
|
|
40
|
+
* `pathPorcelainStatus` cannot answer this on its own — it reports the empty string both for an
|
|
41
|
+
* absent path and for a clean, tracked one — so each of the three places a file can live is checked
|
|
42
|
+
* explicitly.
|
|
43
|
+
*
|
|
44
|
+
* Reachable because `memory add` derives its id's sequence number from `readdirSync` of the working
|
|
45
|
+
* tree (`nextSequenceNumber`, `../memory/add.ts`), so a working tree that disagrees with `HEAD` about
|
|
46
|
+
* how many elements exist can generate an id that lands on an occupied path; the write that follows
|
|
47
|
+
* is unconditional, and the commit's diff then becomes `HEAD` → scaffold rather than absent →
|
|
48
|
+
* scaffold. A commit whose subject says "add" and whose diff removes lines is `bug-078` at its
|
|
49
|
+
* sharpest.
|
|
50
|
+
*
|
|
51
|
+
* @param path - Root-relative POSIX path of the file about to be created.
|
|
52
|
+
*/
|
|
53
|
+
export declare function requireAbsentTarget(root: string, path: string): CoreResult<undefined>;
|
|
54
|
+
/**
|
|
55
|
+
* The paths a commit changed against its own parent that are **not** among `declared`, with the
|
|
56
|
+
* parent it was compared against. The parent is `<sha>^`, or git's empty tree for a root commit, so
|
|
57
|
+
* the comparison is total rather than conditional.
|
|
58
|
+
*
|
|
59
|
+
* Extracted so `verifyCommittedScope` (the Memory transition post-condition, `./memory-transition.ts`)
|
|
60
|
+
* and {@link verifyCommittedPaths} (its content-agnostic sibling, below) share one answer to "did
|
|
61
|
+
* anything else ride in".
|
|
62
|
+
*/
|
|
63
|
+
export declare function undeclaredCommittedPaths(root: string, sha: string, declared: readonly string[]): {
|
|
64
|
+
parent: string;
|
|
65
|
+
problems: string[];
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Verify what a commit **contains**, by diffing it against its own parent — the content-agnostic
|
|
69
|
+
* sibling of `verifyCommittedScope` (`./memory-transition.ts`), for targets that carry no declared
|
|
70
|
+
* frontmatter fields.
|
|
71
|
+
*
|
|
72
|
+
* `verifyCommittedScope` delegates its content half to `verifyDocumentEdit`, which compares *owned
|
|
73
|
+
* frontmatter fields* and a body. `dna.yaml` and `roles.yaml` have no frontmatter block at all, so
|
|
74
|
+
* for them the right — and strictly stronger — question is byte equality: the commit must carry, for
|
|
75
|
+
* each declared path, exactly the text this operation wrote, and nothing else.
|
|
76
|
+
*
|
|
77
|
+
* Same shape as `task-080`'s lockfile guard and `task-088`'s post-condition: assert the diff, not the
|
|
78
|
+
* end state. Reading the file back from disk answers "does `dna.yaml` now say `Renamed`?" — true no
|
|
79
|
+
* matter what else rode along.
|
|
80
|
+
*
|
|
81
|
+
* It is an **alarm, not a rollback**: it can only run once the commit exists, and rewriting history
|
|
82
|
+
* behind the user's back is a worse failure than reporting one (`dl-035` points the same way). With
|
|
83
|
+
* {@link requireUnmodifiedTarget} in place no argument to a verb can reach it; a repository-local
|
|
84
|
+
* `pre-commit` hook that rewrites and re-stages the file still can, which is precisely what it is for.
|
|
85
|
+
*
|
|
86
|
+
* @param expected - Each root-relative path the commit must carry, mapped to the exact content it
|
|
87
|
+
* must have there, or `null` when the commit must have **removed** it. Iterated in insertion order
|
|
88
|
+
* (REQ-SYS-07: a caller's fixed order produces a fixed problem order).
|
|
89
|
+
* @returns The problems found, empty when the commit carries exactly the declared change.
|
|
90
|
+
*/
|
|
91
|
+
export declare function verifyCommittedPaths(root: string, sha: string, expected: ReadonlyMap<string, string | null>): string[];
|
|
92
|
+
/**
|
|
93
|
+
* {@link verifyCommittedPaths} for the common single-path case, rendered as the `CoreResult.error`
|
|
94
|
+
* every caller returns — `undefined` when the commit is exactly what was declared.
|
|
95
|
+
*
|
|
96
|
+
* @param content - The text the operation wrote, or `null` when it removed the path.
|
|
97
|
+
*/
|
|
98
|
+
export declare function committedScopeError(root: string, sha: string, path: string, content: string | null): CoreResult<never> | undefined;
|
|
99
|
+
//# sourceMappingURL=write-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-guard.d.ts","sourceRoot":"","sources":["../../src/core/write-guard.ts"],"names":[],"mappings":"AAsDA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAE3D,6FAA6F;AAC7F,MAAM,WAAW,mBAAmB;IAClC,oGAAoG;IACpG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,6GAA6G;AAC7G,eAAO,MAAM,qBAAqB,EAAE,mBAInC,CAAC;AA2BF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,mBAA2C,EACrD,QAAQ,CAAC,EAAE,MAAM,GAChB,UAAU,CAAC,SAAS,CAAC,CAwBvB;AAED,qGAAqG;AACrG,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,QAAQ,GAAE,mBAA2C,GACpD,UAAU,CAAC,SAAS,CAAC,CAMvB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,CAcrF;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAMxC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAC3C,MAAM,EAAE,CAaV;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,UAAU,CAAC,KAAK,CAAC,GAAG,SAAS,CAO/B"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONFIG_WRITE_CONTRACT = void 0;
|
|
4
|
+
exports.requireUnmodifiedTarget = requireUnmodifiedTarget;
|
|
5
|
+
exports.requireUnmodifiedTargets = requireUnmodifiedTargets;
|
|
6
|
+
exports.requireAbsentTarget = requireAbsentTarget;
|
|
7
|
+
exports.undeclaredCommittedPaths = undeclaredCommittedPaths;
|
|
8
|
+
exports.verifyCommittedPaths = verifyCommittedPaths;
|
|
9
|
+
exports.committedScopeError = committedScopeError;
|
|
10
|
+
/**
|
|
11
|
+
* The write-side half of `dl-080-which-baseline-each-command-reads`, ratified as option **(B)**:
|
|
12
|
+
* *a write refuses while its target carries modifications the command does not own*
|
|
13
|
+
* (task-092-writes-refuse-a-dirty-target, `bug-078`).
|
|
14
|
+
*
|
|
15
|
+
* `task-088` landed that rule for the four gated Memory transition verbs, where the target is a
|
|
16
|
+
* Memory document and "what the verb owns" is a named set of frontmatter fields. `commitPaths` has
|
|
17
|
+
* six other callers — `dna set`, `directive create`, `directive assign`, `directive remove`,
|
|
18
|
+
* `wingfoil init`'s scaffold and `memory add` — whose targets are ordinary config files and brand-new
|
|
19
|
+
* documents, so the same rule needs two shapes rather than one:
|
|
20
|
+
*
|
|
21
|
+
* - {@link requireUnmodifiedTarget} — for a target the command **edits in place**. It must be
|
|
22
|
+
* identical in `HEAD`, in the index and in the working tree; anything else is a modification the
|
|
23
|
+
* command did not make and must not commit.
|
|
24
|
+
* - {@link requireAbsentTarget} — for a target the command **creates**. "Unmodified" is the wrong
|
|
25
|
+
* question there: the path is supposed to be new, so the rule is that nothing may already occupy
|
|
26
|
+
* it, in `HEAD`, in the index or in the working tree.
|
|
27
|
+
*
|
|
28
|
+
* Both are composed from the same primitives `task-088` introduced for the transition verbs —
|
|
29
|
+
* `pathPorcelainStatus`, `readPathAtRev`, `commitParent`, `changedPathsBetween` (`../storage`) and
|
|
30
|
+
* `describeDocumentChanges` (`../memory`) — and `requireUnmodifiedDocument` in `./memory-transition.ts`
|
|
31
|
+
* is itself re-expressed as a call into {@link requireUnmodifiedTarget}, so there is **one** gate in
|
|
32
|
+
* the codebase rather than two that can drift. What varies per call site is only the wording of the
|
|
33
|
+
* refusal, carried in a {@link WriteTargetContract}.
|
|
34
|
+
*
|
|
35
|
+
* **Why git's own answer decides "is it modified".** `pathPorcelainStatus` runs `git status
|
|
36
|
+
* --porcelain -- <path>`: git owns index refresh, `core.autocrlf` and `.gitattributes` filters, and
|
|
37
|
+
* an answer re-derived from bytes would disagree with `git status` on exactly the machines where
|
|
38
|
+
* that matters.
|
|
39
|
+
*
|
|
40
|
+
* **Why the guard is per-path and never per-tree.** `commitPaths` commits with
|
|
41
|
+
* `git commit --only -- <paths>` (`bug-027`), so an unrelated dirty file cannot ride into the commit
|
|
42
|
+
* in the first place. Refusing on one would be `dl-080`'s option (D), which its ratification
|
|
43
|
+
* rejected: "an unrelated uncommitted edit blocks a verb, so designing a new type in `memory.yaml`
|
|
44
|
+
* would prevent approving an element that has nothing to do with it".
|
|
45
|
+
*
|
|
46
|
+
* **Exit code.** Every refusal here is a `CoreResult.error` with `code: 'VALIDATION'`, which
|
|
47
|
+
* `exitCodeForError` maps to **1**. `spec-005-cli-command-contract` § "1. Exit-code contract
|
|
48
|
+
* (REQ-INT-04)" reserves `2` for a malformed *invocation*; a dirty target is a repository-state
|
|
49
|
+
* precondition, and re-typing the command cannot help. That is the ruling recorded on `bug-076`.
|
|
50
|
+
*/
|
|
51
|
+
const node_fs_1 = require("node:fs");
|
|
52
|
+
const node_path_1 = require("node:path");
|
|
53
|
+
const memory_1 = require("../memory");
|
|
54
|
+
const storage_1 = require("../storage");
|
|
55
|
+
const types_1 = require("./types");
|
|
56
|
+
/** The contract every non-transition write verb refuses under (`dna set`, the `directive` verbs, `init`). */
|
|
57
|
+
exports.CONFIG_WRITE_CONTRACT = {
|
|
58
|
+
noun: 'file',
|
|
59
|
+
owner: 'operation',
|
|
60
|
+
records: 'A `wf(...)` commit records the change its subject declares and nothing else',
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Name, in a form a person can act on, how `after` differs from `before` at `HEAD`.
|
|
64
|
+
*
|
|
65
|
+
* Delegates to {@link describeDocumentChanges} — which reports the frontmatter fields that moved and
|
|
66
|
+
* whether the body moved — but only for a file that actually HAS a frontmatter block. `dna.yaml` and
|
|
67
|
+
* `roles.yaml` have none (`splitFrontmatter` returns `{frontmatter: null}` and puts the whole file in
|
|
68
|
+
* `body`), and reporting "the body" for a YAML configuration file reads wrong; those get the honest
|
|
69
|
+
* `'the file content'` instead. Directive documents DO carry frontmatter, so they keep the
|
|
70
|
+
* field-level detail.
|
|
71
|
+
*/
|
|
72
|
+
function describeTargetChanges(before, after, noun) {
|
|
73
|
+
if (before === null)
|
|
74
|
+
return [`the ${noun} is not tracked at HEAD`];
|
|
75
|
+
if (before === after)
|
|
76
|
+
return [];
|
|
77
|
+
if ((0, storage_1.splitFrontmatter)(before).frontmatter === null && (0, storage_1.splitFrontmatter)(after).frontmatter === null) {
|
|
78
|
+
return [`the ${noun} content`];
|
|
79
|
+
}
|
|
80
|
+
return (0, memory_1.describeDocumentChanges)(before, after);
|
|
81
|
+
}
|
|
82
|
+
/** The working-tree text of `path`, or `null` when it is absent from the working tree. */
|
|
83
|
+
function readWorktree(root, path) {
|
|
84
|
+
const absolute = (0, node_path_1.join)(root, path);
|
|
85
|
+
return (0, node_fs_1.existsSync)(absolute) ? (0, storage_1.readDocument)(absolute) : null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Refuse, before anything is written, a target that already carries modifications this operation
|
|
89
|
+
* does not own — `dl-080`'s ratified write rule for a command that **edits a file in place**.
|
|
90
|
+
*
|
|
91
|
+
* Both of the user's declarations are inspected, the index (`:0`) and the working tree, because they
|
|
92
|
+
* can disagree and `commitPaths`'s `git add -- <path>` would silently replace the former with the
|
|
93
|
+
* latter. The refusal names the `git status` code and what is different, so the user can act on it
|
|
94
|
+
* without re-deriving it.
|
|
95
|
+
*
|
|
96
|
+
* **Why refusing rather than committing only the operation's own hunk.** Settled by `task-088` and
|
|
97
|
+
* ratified by `dl-080`, not re-argued here: partial staging is undefined before it is difficult
|
|
98
|
+
* (there are two candidate baselines on disk and "only my hunk" does not say onto which), and the
|
|
99
|
+
* edits it would leave behind are invisible, deferred, and swept into whatever commits next. A
|
|
100
|
+
* refusal costs two commands, now, on a file this function has provably not touched.
|
|
101
|
+
*
|
|
102
|
+
* @param worktree - The working-tree text when the caller has already read it (the transition verbs
|
|
103
|
+
* have); omitted, it is read here.
|
|
104
|
+
*/
|
|
105
|
+
function requireUnmodifiedTarget(root, path, contract = exports.CONFIG_WRITE_CONTRACT, worktree) {
|
|
106
|
+
const porcelain = (0, storage_1.pathPorcelainStatus)(root, path);
|
|
107
|
+
if (porcelain === '')
|
|
108
|
+
return (0, types_1.coreOk)(undefined);
|
|
109
|
+
const atHead = (0, storage_1.readPathAtRev)(root, 'HEAD', path);
|
|
110
|
+
const named = new Set();
|
|
111
|
+
const candidates = [
|
|
112
|
+
[(0, storage_1.readPathAtRev)(root, ':0', path), `the ${contract.noun} is not in the index`],
|
|
113
|
+
[worktree ?? readWorktree(root, path), `the ${contract.noun} is not in the working tree`],
|
|
114
|
+
];
|
|
115
|
+
for (const [candidate, absent] of candidates) {
|
|
116
|
+
if (candidate === null) {
|
|
117
|
+
named.add(absent);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
for (const change of describeTargetChanges(atHead, candidate, contract.noun))
|
|
121
|
+
named.add(change);
|
|
122
|
+
}
|
|
123
|
+
return (0, types_1.coreErr)({
|
|
124
|
+
code: 'VALIDATION',
|
|
125
|
+
message: `refusing to commit ${path}: it carries uncommitted modifications this ${contract.owner} does not own ` +
|
|
126
|
+
`[git status '${porcelain}'] — ${[...named].sort().join(', ')}. ${contract.records}; commit or stash ` +
|
|
127
|
+
'these changes first, then retry.',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/** {@link requireUnmodifiedTarget} over several targets, refusing on the first one that is dirty. */
|
|
131
|
+
function requireUnmodifiedTargets(root, paths, contract = exports.CONFIG_WRITE_CONTRACT) {
|
|
132
|
+
for (const path of paths) {
|
|
133
|
+
const clean = requireUnmodifiedTarget(root, path, contract);
|
|
134
|
+
if (!clean.ok)
|
|
135
|
+
return clean;
|
|
136
|
+
}
|
|
137
|
+
return (0, types_1.coreOk)(undefined);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Refuse, before anything is written, a target that is supposed to be **new** but is already
|
|
141
|
+
* occupied — `dl-080`'s write rule in the shape a *creating* command needs (`memory add`).
|
|
142
|
+
*
|
|
143
|
+
* "Unmodified" is the wrong question for a command whose contract is to register a new element: the
|
|
144
|
+
* path is normally absent, so the modification it does not own is any pre-existing content at all.
|
|
145
|
+
* `pathPorcelainStatus` cannot answer this on its own — it reports the empty string both for an
|
|
146
|
+
* absent path and for a clean, tracked one — so each of the three places a file can live is checked
|
|
147
|
+
* explicitly.
|
|
148
|
+
*
|
|
149
|
+
* Reachable because `memory add` derives its id's sequence number from `readdirSync` of the working
|
|
150
|
+
* tree (`nextSequenceNumber`, `../memory/add.ts`), so a working tree that disagrees with `HEAD` about
|
|
151
|
+
* how many elements exist can generate an id that lands on an occupied path; the write that follows
|
|
152
|
+
* is unconditional, and the commit's diff then becomes `HEAD` → scaffold rather than absent →
|
|
153
|
+
* scaffold. A commit whose subject says "add" and whose diff removes lines is `bug-078` at its
|
|
154
|
+
* sharpest.
|
|
155
|
+
*
|
|
156
|
+
* @param path - Root-relative POSIX path of the file about to be created.
|
|
157
|
+
*/
|
|
158
|
+
function requireAbsentTarget(root, path) {
|
|
159
|
+
const occupied = [];
|
|
160
|
+
if ((0, storage_1.readPathAtRev)(root, 'HEAD', path) !== null)
|
|
161
|
+
occupied.push('at HEAD');
|
|
162
|
+
if ((0, storage_1.readPathAtRev)(root, ':0', path) !== null)
|
|
163
|
+
occupied.push('in the index');
|
|
164
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(root, path)))
|
|
165
|
+
occupied.push('in the working tree');
|
|
166
|
+
if (occupied.length === 0)
|
|
167
|
+
return (0, types_1.coreOk)(undefined);
|
|
168
|
+
return (0, types_1.coreErr)({
|
|
169
|
+
code: 'VALIDATION',
|
|
170
|
+
message: `refusing to create ${path}: something already exists there (${occupied.join(', ')}). This operation ` +
|
|
171
|
+
'registers a NEW element, so writing over an existing file would produce a commit carrying changes its ' +
|
|
172
|
+
'subject does not declare; move or commit what is there first, or choose a different title.',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The paths a commit changed against its own parent that are **not** among `declared`, with the
|
|
177
|
+
* parent it was compared against. The parent is `<sha>^`, or git's empty tree for a root commit, so
|
|
178
|
+
* the comparison is total rather than conditional.
|
|
179
|
+
*
|
|
180
|
+
* Extracted so `verifyCommittedScope` (the Memory transition post-condition, `./memory-transition.ts`)
|
|
181
|
+
* and {@link verifyCommittedPaths} (its content-agnostic sibling, below) share one answer to "did
|
|
182
|
+
* anything else ride in".
|
|
183
|
+
*/
|
|
184
|
+
function undeclaredCommittedPaths(root, sha, declared) {
|
|
185
|
+
const parent = (0, storage_1.commitParent)(root, sha);
|
|
186
|
+
const problems = (0, storage_1.changedPathsBetween)(root, parent, sha)
|
|
187
|
+
.filter((changed) => !declared.includes(changed))
|
|
188
|
+
.map((changed) => `it also contains '${changed}'`);
|
|
189
|
+
return { parent, problems };
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Verify what a commit **contains**, by diffing it against its own parent — the content-agnostic
|
|
193
|
+
* sibling of `verifyCommittedScope` (`./memory-transition.ts`), for targets that carry no declared
|
|
194
|
+
* frontmatter fields.
|
|
195
|
+
*
|
|
196
|
+
* `verifyCommittedScope` delegates its content half to `verifyDocumentEdit`, which compares *owned
|
|
197
|
+
* frontmatter fields* and a body. `dna.yaml` and `roles.yaml` have no frontmatter block at all, so
|
|
198
|
+
* for them the right — and strictly stronger — question is byte equality: the commit must carry, for
|
|
199
|
+
* each declared path, exactly the text this operation wrote, and nothing else.
|
|
200
|
+
*
|
|
201
|
+
* Same shape as `task-080`'s lockfile guard and `task-088`'s post-condition: assert the diff, not the
|
|
202
|
+
* end state. Reading the file back from disk answers "does `dna.yaml` now say `Renamed`?" — true no
|
|
203
|
+
* matter what else rode along.
|
|
204
|
+
*
|
|
205
|
+
* It is an **alarm, not a rollback**: it can only run once the commit exists, and rewriting history
|
|
206
|
+
* behind the user's back is a worse failure than reporting one (`dl-035` points the same way). With
|
|
207
|
+
* {@link requireUnmodifiedTarget} in place no argument to a verb can reach it; a repository-local
|
|
208
|
+
* `pre-commit` hook that rewrites and re-stages the file still can, which is precisely what it is for.
|
|
209
|
+
*
|
|
210
|
+
* @param expected - Each root-relative path the commit must carry, mapped to the exact content it
|
|
211
|
+
* must have there, or `null` when the commit must have **removed** it. Iterated in insertion order
|
|
212
|
+
* (REQ-SYS-07: a caller's fixed order produces a fixed problem order).
|
|
213
|
+
* @returns The problems found, empty when the commit carries exactly the declared change.
|
|
214
|
+
*/
|
|
215
|
+
function verifyCommittedPaths(root, sha, expected) {
|
|
216
|
+
const { problems } = undeclaredCommittedPaths(root, sha, [...expected.keys()]);
|
|
217
|
+
for (const [path, content] of expected) {
|
|
218
|
+
const after = (0, storage_1.readPathAtRev)(root, sha, path);
|
|
219
|
+
if (content === null) {
|
|
220
|
+
if (after !== null)
|
|
221
|
+
problems.push(`it still contains '${path}', which this operation removed`);
|
|
222
|
+
}
|
|
223
|
+
else if (after === null) {
|
|
224
|
+
problems.push(`it does not contain '${path}'`);
|
|
225
|
+
}
|
|
226
|
+
else if (after !== content) {
|
|
227
|
+
problems.push(`'${path}' at the commit differs from what this operation wrote`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return problems;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* {@link verifyCommittedPaths} for the common single-path case, rendered as the `CoreResult.error`
|
|
234
|
+
* every caller returns — `undefined` when the commit is exactly what was declared.
|
|
235
|
+
*
|
|
236
|
+
* @param content - The text the operation wrote, or `null` when it removed the path.
|
|
237
|
+
*/
|
|
238
|
+
function committedScopeError(root, sha, path, content) {
|
|
239
|
+
const leaked = verifyCommittedPaths(root, sha, new Map([[path, content]]));
|
|
240
|
+
if (leaked.length === 0)
|
|
241
|
+
return undefined;
|
|
242
|
+
return (0, types_1.coreErr)({
|
|
243
|
+
code: 'VALIDATION',
|
|
244
|
+
message: `commit ${sha} carries more than the change it declares: ${leaked.join('; ')}`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=write-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-guard.js","sourceRoot":"","sources":["../../src/core/write-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure Directives-pillar helpers for `wingfoil directive create` (P3.1,
|
|
3
|
+
* task-050-directive-create). The `directives` pillar owns what a directive is *called* and what a
|
|
4
|
+
* newly-created directive file *contains*; `src/core`'s `directiveCreate` operation composes these
|
|
5
|
+
* with the git-identity pre-flight, the already-exists check, the write and the commit (cross-pillar
|
|
6
|
+
* / storage concerns that therefore live in `src/core`, not here). Nothing in this file imports
|
|
7
|
+
* `src/core` or touches the filesystem — the pillar stays a leaf under core
|
|
8
|
+
* (spec-006-core-domain-api §1) and every function here is pure (REQ-SYS-07: no wall-clock, no
|
|
9
|
+
* randomness, no environment read), so the same `--name` always yields byte-identical output.
|
|
10
|
+
*
|
|
11
|
+
* The generated frontmatter is a contract, not a formality: it must satisfy
|
|
12
|
+
* {@link DirectiveFrontmatter} (spec-013-directive-frontmatter-schema) so a created directive loads
|
|
13
|
+
* cleanly through `loadDirectives` alongside the ten `wingfoil init` scaffolds — `bug-006` was
|
|
14
|
+
* precisely that invariant broken from the init-generator side.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The exact refusal message P3.1's "Error - invalid directive name" scenario pins
|
|
18
|
+
* (`docs/02_requirements/02_bdd/features/p3-directives/P3.1-directive-create.feature`) — surfaced by
|
|
19
|
+
* `src/core` as a `UsageError`, hence exit `2` (spec-008-cli-grammar §5). Do not reword.
|
|
20
|
+
*/
|
|
21
|
+
export declare const INVALID_DIRECTIVE_NAME_MESSAGE = "invalid directive name (use kebab-case)";
|
|
22
|
+
/** Whether `name` is a legal custom-directive name (kebab-case — see {@link DIRECTIVE_NAME_RE}). */
|
|
23
|
+
export declare function isValidDirectiveName(name: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The human-readable title generated from a kebab-case directive name: hyphens become spaces and the
|
|
26
|
+
* first character is upper-cased (`no-direct-db-access` -> `No direct db access`). Deliberately the
|
|
27
|
+
* same shape `wingfoil init`'s own directive generator uses for its scaffolds (`code-quality` ->
|
|
28
|
+
* `Code quality`, `src/storage/templates.ts`), so a created directive is indistinguishable in style
|
|
29
|
+
* from a scaffolded one. A pure function of `name`; assumes {@link isValidDirectiveName} already
|
|
30
|
+
* passed (the caller validates first).
|
|
31
|
+
*/
|
|
32
|
+
export declare function directiveTitleFromName(name: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* The full Markdown document written for a new custom directive: a YAML frontmatter block that
|
|
35
|
+
* satisfies `DirectiveFrontmatter` (spec-013 — `id`, `name`, `type`, `kind`, `title` are the required
|
|
36
|
+
* fields) followed by a placeholder body for the rule text.
|
|
37
|
+
*
|
|
38
|
+
* - `id` is the name verbatim, matching the filename stem the caller writes (spec-013's `id` note).
|
|
39
|
+
* - `kind: custom` because the file lands under `directives/custom/` (spec-011's built-in/custom split;
|
|
40
|
+
* `built-in/` is reserved for the P3.8 templates the package will ship).
|
|
41
|
+
* - `tags`/`ref` are omitted: both are `.optional()` in the schema, and a user-authored rule has no
|
|
42
|
+
* upstream P3.8 reference to claim.
|
|
43
|
+
*
|
|
44
|
+
* The body is deliberately not schema-governed (spec-013: "Body content is not governed here").
|
|
45
|
+
*/
|
|
46
|
+
export declare function renderCustomDirective(name: string): string;
|
|
47
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/directives/create.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAaH;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,4CAA4C,CAAC;AAExF,oGAAoG;AACpG,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAc1D"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pure Directives-pillar helpers for `wingfoil directive create` (P3.1,
|
|
4
|
+
* task-050-directive-create). The `directives` pillar owns what a directive is *called* and what a
|
|
5
|
+
* newly-created directive file *contains*; `src/core`'s `directiveCreate` operation composes these
|
|
6
|
+
* with the git-identity pre-flight, the already-exists check, the write and the commit (cross-pillar
|
|
7
|
+
* / storage concerns that therefore live in `src/core`, not here). Nothing in this file imports
|
|
8
|
+
* `src/core` or touches the filesystem — the pillar stays a leaf under core
|
|
9
|
+
* (spec-006-core-domain-api §1) and every function here is pure (REQ-SYS-07: no wall-clock, no
|
|
10
|
+
* randomness, no environment read), so the same `--name` always yields byte-identical output.
|
|
11
|
+
*
|
|
12
|
+
* The generated frontmatter is a contract, not a formality: it must satisfy
|
|
13
|
+
* {@link DirectiveFrontmatter} (spec-013-directive-frontmatter-schema) so a created directive loads
|
|
14
|
+
* cleanly through `loadDirectives` alongside the ten `wingfoil init` scaffolds — `bug-006` was
|
|
15
|
+
* precisely that invariant broken from the init-generator side.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.INVALID_DIRECTIVE_NAME_MESSAGE = void 0;
|
|
19
|
+
exports.isValidDirectiveName = isValidDirectiveName;
|
|
20
|
+
exports.directiveTitleFromName = directiveTitleFromName;
|
|
21
|
+
exports.renderCustomDirective = renderCustomDirective;
|
|
22
|
+
/**
|
|
23
|
+
* A directive name is kebab-case: one or more lower-case alphanumeric segments joined by single
|
|
24
|
+
* hyphens. No upper case, no leading/trailing/doubled hyphen, no whitespace, dot, underscore or path
|
|
25
|
+
* separator.
|
|
26
|
+
*
|
|
27
|
+
* Rejecting everything else also makes the write target unescapable by construction: `..`, `../x`,
|
|
28
|
+
* `a/b` and `/etc/passwd` all fail this rule *before* `src/core` builds any path from the name, so
|
|
29
|
+
* the created file can only ever land directly inside `.wingfoil/directives/custom/`.
|
|
30
|
+
*/
|
|
31
|
+
const DIRECTIVE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
32
|
+
/**
|
|
33
|
+
* The exact refusal message P3.1's "Error - invalid directive name" scenario pins
|
|
34
|
+
* (`docs/02_requirements/02_bdd/features/p3-directives/P3.1-directive-create.feature`) — surfaced by
|
|
35
|
+
* `src/core` as a `UsageError`, hence exit `2` (spec-008-cli-grammar §5). Do not reword.
|
|
36
|
+
*/
|
|
37
|
+
exports.INVALID_DIRECTIVE_NAME_MESSAGE = 'invalid directive name (use kebab-case)';
|
|
38
|
+
/** Whether `name` is a legal custom-directive name (kebab-case — see {@link DIRECTIVE_NAME_RE}). */
|
|
39
|
+
function isValidDirectiveName(name) {
|
|
40
|
+
return DIRECTIVE_NAME_RE.test(name);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The human-readable title generated from a kebab-case directive name: hyphens become spaces and the
|
|
44
|
+
* first character is upper-cased (`no-direct-db-access` -> `No direct db access`). Deliberately the
|
|
45
|
+
* same shape `wingfoil init`'s own directive generator uses for its scaffolds (`code-quality` ->
|
|
46
|
+
* `Code quality`, `src/storage/templates.ts`), so a created directive is indistinguishable in style
|
|
47
|
+
* from a scaffolded one. A pure function of `name`; assumes {@link isValidDirectiveName} already
|
|
48
|
+
* passed (the caller validates first).
|
|
49
|
+
*/
|
|
50
|
+
function directiveTitleFromName(name) {
|
|
51
|
+
const words = name.replace(/-/g, ' ');
|
|
52
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The full Markdown document written for a new custom directive: a YAML frontmatter block that
|
|
56
|
+
* satisfies `DirectiveFrontmatter` (spec-013 — `id`, `name`, `type`, `kind`, `title` are the required
|
|
57
|
+
* fields) followed by a placeholder body for the rule text.
|
|
58
|
+
*
|
|
59
|
+
* - `id` is the name verbatim, matching the filename stem the caller writes (spec-013's `id` note).
|
|
60
|
+
* - `kind: custom` because the file lands under `directives/custom/` (spec-011's built-in/custom split;
|
|
61
|
+
* `built-in/` is reserved for the P3.8 templates the package will ship).
|
|
62
|
+
* - `tags`/`ref` are omitted: both are `.optional()` in the schema, and a user-authored rule has no
|
|
63
|
+
* upstream P3.8 reference to claim.
|
|
64
|
+
*
|
|
65
|
+
* The body is deliberately not schema-governed (spec-013: "Body content is not governed here").
|
|
66
|
+
*/
|
|
67
|
+
function renderCustomDirective(name) {
|
|
68
|
+
const title = directiveTitleFromName(name);
|
|
69
|
+
return `---
|
|
70
|
+
id: ${name}
|
|
71
|
+
name: ${name}
|
|
72
|
+
type: directive
|
|
73
|
+
kind: custom
|
|
74
|
+
title: "${title}"
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
# ${title}
|
|
78
|
+
|
|
79
|
+
<!-- Write the rule this directive enforces here. Directives are auto-loaded per role (roles.yaml). -->
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/directives/create.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAqBH,oDAEC;AAUD,wDAGC;AAeD,sDAcC;AA/DD;;;;;;;;GAQG;AACH,MAAM,iBAAiB,GAAG,4BAA4B,CAAC;AAEvD;;;;GAIG;AACU,QAAA,8BAA8B,GAAG,yCAAyC,CAAC;AAExF,oGAAoG;AACpG,SAAgB,oBAAoB,CAAC,IAAY;IAC/C,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,sBAAsB,CAAC,IAAY;IACjD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACtC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,qBAAqB,CAAC,IAAY;IAChD,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC3C,OAAO;MACH,IAAI;QACF,IAAI;;;UAGF,KAAK;;;IAGX,KAAK;;;CAGR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `directives` module — Project Directives pillar; role-based rules (built-in + custom) (P3.5/P3.8).
|
|
3
|
+
* `DirectiveFrontmatter` (an [AUTHORING]-level minimal schema — see `./schema` for why there is no
|
|
4
|
+
* dedicated tech-spec yet) lives here — the pillar owns its own independent schema (REQ-SYS-02);
|
|
5
|
+
* `src/core`'s loader wires it through the shared validation pipeline. `./create`
|
|
6
|
+
* (task-050-directive-create, P3.1) adds the pillar's first *authoring* helpers — the kebab-case
|
|
7
|
+
* name rule and the generated file's content — which `src/core`'s `directiveCreate` operation
|
|
8
|
+
* composes with the git-identity pre-flight, write and commit. `./roles-edit`
|
|
9
|
+
* (task-051-directive-assign, P3.2) adds the pure, comment-preserving `roles.yaml` assignment edit that
|
|
10
|
+
* `src/core`'s `directiveAssign` writes through, and `parseDirectiveIds`
|
|
11
|
+
* (task-056-role-based-directive-assignment, P3.7) the comma-separated `--directive` value parser that
|
|
12
|
+
* makes the same verb bind several directives at once. `directive remove` (P3.3, task-052) reuses none
|
|
13
|
+
* of it — it refuses a still-assigned directive rather than unbinding it. Further Directives behavior
|
|
14
|
+
* lands in later tasks.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MODULE_NAME: "directives";
|
|
17
|
+
export { DirectiveFrontmatter, RolesYaml } from './schema';
|
|
18
|
+
export { INVALID_DIRECTIVE_NAME_MESSAGE, directiveTitleFromName, isValidDirectiveName, renderCustomDirective, } from './create';
|
|
19
|
+
export { parseDirectiveIds, setRoleAssignmentsInText, withAssignedDirectives } from './roles-edit';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/directives/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW,EAAG,YAAqB,CAAC;AAEjD,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE3D,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC"}
|