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,85 @@
|
|
|
1
|
+
import type { CoreError, CoreResult } from './types';
|
|
2
|
+
/** The three-code exit contract (spec-005 §1). Canonical home for the type; `src/cli` re-exports it. */
|
|
3
|
+
export type ExitCode = 0 | 1 | 2;
|
|
4
|
+
/** The exit code a {@link CoreError} maps to (spec-005 §1). */
|
|
5
|
+
export declare function exitCodeForError(error: CoreError): ExitCode;
|
|
6
|
+
/** The exit code for a whole {@link CoreResult}: `0` on success, else the error's mapped code. */
|
|
7
|
+
export declare function exitCodeForResult(result: CoreResult<unknown>): ExitCode;
|
|
8
|
+
/**
|
|
9
|
+
* How a CLI argument parser terminated, reduced to the two fields the exit-code decision needs — the
|
|
10
|
+
* parser's own outcome identifier and the exit code it *suggests*. Commander's `CommanderError` is
|
|
11
|
+
* structurally this (`.code` / `.exitCode`), so `src/cli` hands one straight in; the interface is
|
|
12
|
+
* declared structurally rather than importing `commander` so `core` keeps no dependency on the CLI's
|
|
13
|
+
* parser (REQ-SYS-05 — the same mapping has to be applicable from the MCP surface).
|
|
14
|
+
*/
|
|
15
|
+
export interface ParseOutcome {
|
|
16
|
+
/** The parser's outcome identifier, e.g. Commander's `'commander.unknownCommand'`. */
|
|
17
|
+
readonly code: string;
|
|
18
|
+
/** The exit code the parser suggests for this outcome — advisory, not the contract's answer. */
|
|
19
|
+
readonly exitCode: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* How a parse outcome resolves against spec-005 §1: the exit code, and whether the **surface** still
|
|
23
|
+
* owes the `error:` line the section requires of every non-zero exit.
|
|
24
|
+
*/
|
|
25
|
+
export interface ParseTermination {
|
|
26
|
+
/** The contract's answer, overriding the parser's suggestion. */
|
|
27
|
+
readonly exitCode: ExitCode;
|
|
28
|
+
/**
|
|
29
|
+
* `true` when the parser terminated non-zero having written no error message of its own, so the
|
|
30
|
+
* surface must emit one to satisfy spec-005 §1's "a non-zero exit code is **always** accompanied by
|
|
31
|
+
* an error message on stderr". `false` when the parser already wrote its own `error: …` line (every
|
|
32
|
+
* code in {@link USAGE_ERROR_PARSE_CODES}) or when the exit is `0`. The message *text* is the
|
|
33
|
+
* surface's to compose — only the obligation is decided here.
|
|
34
|
+
*/
|
|
35
|
+
readonly needsErrorLine: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Resolve a CLI argument parser's own termination against spec-005 §1 (REQ-INT-04 — task-101 /
|
|
39
|
+
* `bug-098`, extended by task-103 / `bug-103`). The parser reaches this both when it *refuses* an
|
|
40
|
+
* invocation and when it *completes* one without running a command (`--help`, `--version`), so the
|
|
41
|
+
* rule has three branches:
|
|
42
|
+
*
|
|
43
|
+
* - an outcome in {@link USAGE_ERROR_PARSE_CODES} is a malformed invocation → exit **2**, overriding
|
|
44
|
+
* the parser's suggestion (Commander suggests `1` for every one of them). It wrote its own message,
|
|
45
|
+
* so the surface owes nothing;
|
|
46
|
+
* - {@link INCOMPLETE_INVOCATION_PARSE_CODE} with a **non-zero** suggestion is an invocation the
|
|
47
|
+
* parser found incomplete — a noun with no verb, `wingfoil` with no arguments, `help <unknown>`.
|
|
48
|
+
* §1 lists a "missing required argument" among the malformed invocations, so it is exit **2** as
|
|
49
|
+
* well; unlike the codes above it printed only help text, so `needsErrorLine` is `true`;
|
|
50
|
+
* - anything else is a *successful* termination or an outcome this contract does not classify, and
|
|
51
|
+
* keeps the parser's own suggested code, narrowed to the three-code contract: `0` stays `0` (so
|
|
52
|
+
* `--help`, `--version` and an explicit `help` still exit `0`, as spec-005 §1 requires), any
|
|
53
|
+
* non-zero suggestion becomes `1`. That default is deliberately the status quo rather than `2`: an
|
|
54
|
+
* unrecognised outcome must not silently acquire a usage-error meaning it was never shown to have —
|
|
55
|
+
* and, for the same reason, must not acquire a manufactured error message either. The only code
|
|
56
|
+
* that reaches it non-zero today is `commander.executeSubCommandAsync`, and WingFoil registers no
|
|
57
|
+
* executable subcommand.
|
|
58
|
+
*/
|
|
59
|
+
export declare function classifyParseOutcome(outcome: ParseOutcome): ParseTermination;
|
|
60
|
+
/**
|
|
61
|
+
* The exit code alone for a parse outcome — {@link classifyParseOutcome}'s `exitCode`, kept as a named
|
|
62
|
+
* function for callers (and tests) that care only about the code. It *delegates* rather than repeating
|
|
63
|
+
* the rule, so the code and the error-line obligation can never be decided from two different tables.
|
|
64
|
+
*/
|
|
65
|
+
export declare function exitCodeForParseOutcome(outcome: ParseOutcome): ExitCode;
|
|
66
|
+
/** A thrown error's surface rendering: the human `reason` (spec-005 §3) and the process exit code. */
|
|
67
|
+
export interface ThrownOutcome {
|
|
68
|
+
readonly reason: string;
|
|
69
|
+
readonly exitCode: ExitCode;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Select the exit code and reason for a core operation that *threw* (the throw-path companion to
|
|
73
|
+
* {@link exitCodeForResult}, which handles the return path). Keeping this in `src/core` is what lets a
|
|
74
|
+
* thrown usage/integrity error stay "core owns exit-code selection" (spec-008-cli-grammar
|
|
75
|
+
* Consequences), so both surfaces apply it identically rather than each re-deciding:
|
|
76
|
+
*
|
|
77
|
+
* - a {@link UsageError} (a malformed argument, e.g. `dna set`'s invalid key path) → exit **2**, with
|
|
78
|
+
* its already-clean `.message` as the reason (task-025-implement-dna-set);
|
|
79
|
+
* - a {@link ValidationError} carries its own `exitCode` (integrity/cross-field → 2, field-level → 1,
|
|
80
|
+
* per spec-009-validation-strategy §3), with the reason joined from its issue messages (never the
|
|
81
|
+
* composite `${code} ${path} (${file}): …` form its `.message` builds);
|
|
82
|
+
* - anything else is a logic error → exit **1**, with the error's message (or its string form).
|
|
83
|
+
*/
|
|
84
|
+
export declare function exitCodeForThrow(error: unknown): ThrownOutcome;
|
|
85
|
+
//# sourceMappingURL=exit-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exit-code.d.ts","sourceRoot":"","sources":["../../src/core/exit-code.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,SAAS,EAAiB,UAAU,EAAE,MAAM,SAAS,CAAC;AAGpE,wGAAwG;AACxG,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAgBjC,+DAA+D;AAC/D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,CAE3D;AAED,kGAAkG;AAClG,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,QAAQ,CAEvE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gGAAgG;IAChG,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAoDD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,gBAAgB,CAM5E;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,YAAY,GAAG,QAAQ,CAEvE;AAED,sGAAsG;AACtG,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAS9D"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.exitCodeForError = exitCodeForError;
|
|
4
|
+
exports.exitCodeForResult = exitCodeForResult;
|
|
5
|
+
exports.classifyParseOutcome = classifyParseOutcome;
|
|
6
|
+
exports.exitCodeForParseOutcome = exitCodeForParseOutcome;
|
|
7
|
+
exports.exitCodeForThrow = exitCodeForThrow;
|
|
8
|
+
/**
|
|
9
|
+
* Exit-code selection (spec-005-cli-command-contract §1, REQ-INT-04 — task-012-cli-exit-code-contract).
|
|
10
|
+
*
|
|
11
|
+
* The `0`/`1`/`2` contract has a single source of truth here in `core`, not duplicated across the CLI
|
|
12
|
+
* and MCP surfaces: both derive severity from the same `CoreError` model (REQ-SYS-05), so this module
|
|
13
|
+
* owns the mapping from a domain outcome to its exit code and each surface only *applies* it
|
|
14
|
+
* (`src/cli/exit.ts`'s `exitWith`).
|
|
15
|
+
*
|
|
16
|
+
* Parse-level usage errors (`2`) are *detected* by a surface before any core call — an unknown
|
|
17
|
+
* command, an unknown option, a missing required argument — but since
|
|
18
|
+
* task-101-route-commander-parse-errors-through-the-exit-code-contract (`bug-098`) their **code** is
|
|
19
|
+
* decided here too, by {@link classifyParseOutcome}. Before that task the CLI's argument parser
|
|
20
|
+
* terminated through its own `process.exit(1)` and those classes never reached this module at all, so
|
|
21
|
+
* an unknown command reported `1` — the code spec-005 §1 reserves for a *well-formed* invocation that
|
|
22
|
+
* failed. Keeping the parse mapping here is what that task's AC6 protects: one place decides an exit
|
|
23
|
+
* code, not one place per error origin.
|
|
24
|
+
*
|
|
25
|
+
* task-103-a-missing-verb-exits-2-with-an-error-line (`bug-103`) added the second half of the same
|
|
26
|
+
* §1 rule to that one place: the section also states that a non-zero exit is **always** accompanied by
|
|
27
|
+
* an error message on stderr, and one parse outcome — an invocation the parser found incomplete —
|
|
28
|
+
* carries no message of its own. So {@link classifyParseOutcome} answers two questions rather than
|
|
29
|
+
* one, from a single rule: which exit code, and whether the surface still owes the `error:` line.
|
|
30
|
+
*/
|
|
31
|
+
const validation_1 = require("../validation");
|
|
32
|
+
const usage_error_1 = require("./usage-error");
|
|
33
|
+
/**
|
|
34
|
+
* Every `CoreError.code` is a **logic** error — a well-formed invocation that failed on business logic
|
|
35
|
+
* — so all map to exit `1`. Declared as an exhaustive `Record<CoreErrorCode, ExitCode>` on purpose: a
|
|
36
|
+
* future `CoreErrorCode` will not compile until its exit code is chosen here, rather than silently
|
|
37
|
+
* defaulting.
|
|
38
|
+
*/
|
|
39
|
+
const EXIT_CODE_BY_ERROR = {
|
|
40
|
+
NOT_FOUND: 1,
|
|
41
|
+
INVALID_TRANSITION: 1,
|
|
42
|
+
VALIDATION: 1,
|
|
43
|
+
CONFLICT: 1,
|
|
44
|
+
IO: 1,
|
|
45
|
+
};
|
|
46
|
+
/** The exit code a {@link CoreError} maps to (spec-005 §1). */
|
|
47
|
+
function exitCodeForError(error) {
|
|
48
|
+
return EXIT_CODE_BY_ERROR[error.code];
|
|
49
|
+
}
|
|
50
|
+
/** The exit code for a whole {@link CoreResult}: `0` on success, else the error's mapped code. */
|
|
51
|
+
function exitCodeForResult(result) {
|
|
52
|
+
return result.ok ? 0 : exitCodeForError(result.error);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The argument-parser outcomes that are **usage errors** under spec-005 §1 ("the invocation itself is
|
|
56
|
+
* malformed: unknown command/pillar/verb, unknown flag, missing required argument, invalid flag
|
|
57
|
+
* value") and therefore exit `2`, whatever the parser itself suggests.
|
|
58
|
+
*
|
|
59
|
+
* The identifiers are Commander's, confirmed against the **installed** version (`commander@15.0.0`,
|
|
60
|
+
* `node_modules/commander/lib/command.js` — `unknownOption()`, `excessArguments()`,
|
|
61
|
+
* `unknownCommand()`, `missingArgument()`, `optionMissingArgument()`, `missingMandatoryOptionValue()`,
|
|
62
|
+
* `conflictingOption()`, each calling `this.error(message, { code })`, plus `commander.error` as
|
|
63
|
+
* `error()`'s own default and `commander.invalidArgument` from `InvalidArgumentError`). They are
|
|
64
|
+
* version surface: a Commander upgrade that renames or adds one must revisit this set, which is why
|
|
65
|
+
* the codes are listed explicitly here rather than inferred from the suggested exit code.
|
|
66
|
+
*
|
|
67
|
+
* One of them, `commander.excessArguments`, is mapped but **unreachable in production today**: every
|
|
68
|
+
* derived command registers a variadic `[positionals...]` (`src/cli/program.ts`), so Commander never
|
|
69
|
+
* has an excess argument to refuse — `wingfoil dna show project extra` is accepted and an extra
|
|
70
|
+
* positional on a write verb is refused by WingFoil's own check instead, already at exit `2`. It is
|
|
71
|
+
* listed because the mapping must stay correct for the code rather than for today's registration
|
|
72
|
+
* shape; a reader finding it never fires is not looking at a mistake.
|
|
73
|
+
*/
|
|
74
|
+
const USAGE_ERROR_PARSE_CODES = new Set([
|
|
75
|
+
'commander.unknownCommand',
|
|
76
|
+
'commander.unknownOption',
|
|
77
|
+
'commander.excessArguments',
|
|
78
|
+
'commander.missingArgument',
|
|
79
|
+
'commander.optionMissingArgument',
|
|
80
|
+
'commander.missingMandatoryOptionValue',
|
|
81
|
+
'commander.conflictingOption',
|
|
82
|
+
'commander.invalidArgument',
|
|
83
|
+
'commander.error',
|
|
84
|
+
]);
|
|
85
|
+
/**
|
|
86
|
+
* The parser outcome that reports an **incomplete invocation**: the parser had nothing to run and
|
|
87
|
+
* printed the command's help to stderr instead of an error (task-103, `bug-103`).
|
|
88
|
+
*
|
|
89
|
+
* This is *not* an error code — it is the same identifier Commander uses when the user asks for help
|
|
90
|
+
* with the built-in `help` command, which must keep exiting `0`. The two are separated by the
|
|
91
|
+
* **suggested exit code** carried alongside it: `Command#help(contextOptions)`
|
|
92
|
+
* (`node_modules/commander/lib/command.js`, commander@15.0.0) suggests `1` exactly when it was called
|
|
93
|
+
* as `help({ error: true })`, which is what its three "there is nothing here to run" call sites do —
|
|
94
|
+
* the missing-subcommand branch, the nothing-hooked-up branch, and `_dispatchSubcommand`'s
|
|
95
|
+
* `if (!subCommand)`, where `wingfoil help <unknown>` lands. A user-requested help suggests `0`.
|
|
96
|
+
*
|
|
97
|
+
* That one bit is the whole discriminator, and it is read from the parser's own two fields rather
|
|
98
|
+
* than by re-reading argv — so this module still decides alone, and the surface never re-parses an
|
|
99
|
+
* invocation the parser has already parsed.
|
|
100
|
+
*/
|
|
101
|
+
const INCOMPLETE_INVOCATION_PARSE_CODE = 'commander.help';
|
|
102
|
+
/**
|
|
103
|
+
* Resolve a CLI argument parser's own termination against spec-005 §1 (REQ-INT-04 — task-101 /
|
|
104
|
+
* `bug-098`, extended by task-103 / `bug-103`). The parser reaches this both when it *refuses* an
|
|
105
|
+
* invocation and when it *completes* one without running a command (`--help`, `--version`), so the
|
|
106
|
+
* rule has three branches:
|
|
107
|
+
*
|
|
108
|
+
* - an outcome in {@link USAGE_ERROR_PARSE_CODES} is a malformed invocation → exit **2**, overriding
|
|
109
|
+
* the parser's suggestion (Commander suggests `1` for every one of them). It wrote its own message,
|
|
110
|
+
* so the surface owes nothing;
|
|
111
|
+
* - {@link INCOMPLETE_INVOCATION_PARSE_CODE} with a **non-zero** suggestion is an invocation the
|
|
112
|
+
* parser found incomplete — a noun with no verb, `wingfoil` with no arguments, `help <unknown>`.
|
|
113
|
+
* §1 lists a "missing required argument" among the malformed invocations, so it is exit **2** as
|
|
114
|
+
* well; unlike the codes above it printed only help text, so `needsErrorLine` is `true`;
|
|
115
|
+
* - anything else is a *successful* termination or an outcome this contract does not classify, and
|
|
116
|
+
* keeps the parser's own suggested code, narrowed to the three-code contract: `0` stays `0` (so
|
|
117
|
+
* `--help`, `--version` and an explicit `help` still exit `0`, as spec-005 §1 requires), any
|
|
118
|
+
* non-zero suggestion becomes `1`. That default is deliberately the status quo rather than `2`: an
|
|
119
|
+
* unrecognised outcome must not silently acquire a usage-error meaning it was never shown to have —
|
|
120
|
+
* and, for the same reason, must not acquire a manufactured error message either. The only code
|
|
121
|
+
* that reaches it non-zero today is `commander.executeSubCommandAsync`, and WingFoil registers no
|
|
122
|
+
* executable subcommand.
|
|
123
|
+
*/
|
|
124
|
+
function classifyParseOutcome(outcome) {
|
|
125
|
+
if (USAGE_ERROR_PARSE_CODES.has(outcome.code))
|
|
126
|
+
return { exitCode: 2, needsErrorLine: false };
|
|
127
|
+
if (outcome.code === INCOMPLETE_INVOCATION_PARSE_CODE && outcome.exitCode !== 0) {
|
|
128
|
+
return { exitCode: 2, needsErrorLine: true };
|
|
129
|
+
}
|
|
130
|
+
return { exitCode: outcome.exitCode === 0 ? 0 : 1, needsErrorLine: false };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The exit code alone for a parse outcome — {@link classifyParseOutcome}'s `exitCode`, kept as a named
|
|
134
|
+
* function for callers (and tests) that care only about the code. It *delegates* rather than repeating
|
|
135
|
+
* the rule, so the code and the error-line obligation can never be decided from two different tables.
|
|
136
|
+
*/
|
|
137
|
+
function exitCodeForParseOutcome(outcome) {
|
|
138
|
+
return classifyParseOutcome(outcome).exitCode;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Select the exit code and reason for a core operation that *threw* (the throw-path companion to
|
|
142
|
+
* {@link exitCodeForResult}, which handles the return path). Keeping this in `src/core` is what lets a
|
|
143
|
+
* thrown usage/integrity error stay "core owns exit-code selection" (spec-008-cli-grammar
|
|
144
|
+
* Consequences), so both surfaces apply it identically rather than each re-deciding:
|
|
145
|
+
*
|
|
146
|
+
* - a {@link UsageError} (a malformed argument, e.g. `dna set`'s invalid key path) → exit **2**, with
|
|
147
|
+
* its already-clean `.message` as the reason (task-025-implement-dna-set);
|
|
148
|
+
* - a {@link ValidationError} carries its own `exitCode` (integrity/cross-field → 2, field-level → 1,
|
|
149
|
+
* per spec-009-validation-strategy §3), with the reason joined from its issue messages (never the
|
|
150
|
+
* composite `${code} ${path} (${file}): …` form its `.message` builds);
|
|
151
|
+
* - anything else is a logic error → exit **1**, with the error's message (or its string form).
|
|
152
|
+
*/
|
|
153
|
+
function exitCodeForThrow(error) {
|
|
154
|
+
if (error instanceof usage_error_1.UsageError) {
|
|
155
|
+
return { reason: error.message, exitCode: 2 };
|
|
156
|
+
}
|
|
157
|
+
if (error instanceof validation_1.ValidationError) {
|
|
158
|
+
const reason = error.issues.length > 0 ? error.issues.map((issue) => issue.message).join('; ') : error.message;
|
|
159
|
+
return { reason, exitCode: error.exitCode === 2 ? 2 : 1 };
|
|
160
|
+
}
|
|
161
|
+
return { reason: error instanceof Error ? error.message : String(error), exitCode: 1 };
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=exit-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exit-code.js","sourceRoot":"","sources":["../../src/core/exit-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type CoreResult } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* The single source of truth for "a configured/attributable identity": non-empty `name` AND
|
|
4
|
+
* non-empty `email`. Both the write-time precondition below (`requireGitIdentity`, REQ-SEC-01,
|
|
5
|
+
* task-014-git-identity-required) and the read-time historical audit (`isValidAttribution` in
|
|
6
|
+
* `src/memory/audit.ts`, REQ-SEC-02, task-015-complete-audit-trail) build on this exact predicate for
|
|
7
|
+
* their shared base check — see `isValidAttribution`'s doc-comment for the audit-only augmentations
|
|
8
|
+
* it layers on top for historical commits (a concern this write-time check has no need for, since it
|
|
9
|
+
* only ever looks at the *current* live git config). Pure predicate — no git/filesystem access.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isConfiguredIdentity(name: string, email: string): boolean;
|
|
12
|
+
/** The `user.name`/`user.email` pair `readGitIdentity` resolves at a given root — either may be `''` (unset). */
|
|
13
|
+
export interface GitIdentity {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly email: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Read the live `user.name`/`user.email` git identity configured at `root`, with no "is it valid"
|
|
19
|
+
* judgement of its own (that is {@link isConfiguredIdentity}'s job) — the single read primitive both
|
|
20
|
+
* {@link requireGitIdentity} (REQ-SEC-01) and `src/core/approval-authority.ts`'s
|
|
21
|
+
* `requireApprovalAuthority` (REQ-SEC-03, task-040-role-based-approval-authority) build on, so the two
|
|
22
|
+
* checks never each re-implement their own `git config` read.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readGitIdentity(root: string): GitIdentity;
|
|
25
|
+
/**
|
|
26
|
+
* Verify a git identity is configured at `root` before a state mutation. Returns a
|
|
27
|
+
* `CoreResult.error` (code `VALIDATION` — a failed precondition, mapped to exit `1` by
|
|
28
|
+
* `exitCodeForError`) carrying the exact REQ-SEC-01 message when either `user.name` or `user.email`
|
|
29
|
+
* is unset; otherwise `ok`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function requireGitIdentity(root: string): CoreResult<void>;
|
|
32
|
+
//# sourceMappingURL=git-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-identity.d.ts","sourceRoot":"","sources":["../../src/core/git-identity.ts"],"names":[],"mappings":"AAaA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAqB3D;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEzE;AAED,iHAAiH;AACjH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAMjE"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isConfiguredIdentity = isConfiguredIdentity;
|
|
4
|
+
exports.readGitIdentity = readGitIdentity;
|
|
5
|
+
exports.requireGitIdentity = requireGitIdentity;
|
|
6
|
+
/**
|
|
7
|
+
* Git-identity pre-flight check (REQ-SEC-01, adr-006-git-identity-role-based-authz —
|
|
8
|
+
* task-014-git-identity-required).
|
|
9
|
+
*
|
|
10
|
+
* Git identity is WingFoil's sole attribution mechanism: the commit author *is* the "who" of the
|
|
11
|
+
* audit trail (adr-001 / adr-006 — no separate auth/session/IAM layer). So every state-mutating
|
|
12
|
+
* operation must refuse to run when `user.name`/`user.email` are unset, rather than produce a commit
|
|
13
|
+
* with an unattributable author. This check lives in `src/core` so the CLI and every MCP Tool enforce
|
|
14
|
+
* it identically by construction (REQ-SYS-05) — a mutation calls it first and returns its
|
|
15
|
+
* `CoreResult.error` unchanged, so the surface renders the exact message and writes nothing.
|
|
16
|
+
*/
|
|
17
|
+
const node_child_process_1 = require("node:child_process");
|
|
18
|
+
const types_1 = require("./types");
|
|
19
|
+
/** Exact refusal message required by REQ-SEC-01's fit criterion — do not reword. */
|
|
20
|
+
const IDENTITY_ERROR = 'git identity not configured (user.name/user.email)';
|
|
21
|
+
/**
|
|
22
|
+
* The effective value of a git config `key` at `root`, or `''` when it is unset — git exits non-zero
|
|
23
|
+
* for an unset key, which we treat as absent (the same "empty means unconfigured" contract the check
|
|
24
|
+
* relies on). Resolves local → global → system exactly as any git command would.
|
|
25
|
+
*/
|
|
26
|
+
function readGitConfig(root, key) {
|
|
27
|
+
try {
|
|
28
|
+
// `env: process.env` is passed explicitly (identical to the default in production): it makes git's
|
|
29
|
+
// config resolution follow the current environment by design, so a caller/test can scope it via
|
|
30
|
+
// `GIT_CONFIG_GLOBAL`/`GIT_CONFIG_SYSTEM`/`GIT_CONFIG_NOSYSTEM` deterministically.
|
|
31
|
+
return (0, node_child_process_1.execFileSync)('git', ['-C', root, 'config', key], { encoding: 'utf-8', env: process.env }).trim();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The single source of truth for "a configured/attributable identity": non-empty `name` AND
|
|
39
|
+
* non-empty `email`. Both the write-time precondition below (`requireGitIdentity`, REQ-SEC-01,
|
|
40
|
+
* task-014-git-identity-required) and the read-time historical audit (`isValidAttribution` in
|
|
41
|
+
* `src/memory/audit.ts`, REQ-SEC-02, task-015-complete-audit-trail) build on this exact predicate for
|
|
42
|
+
* their shared base check — see `isValidAttribution`'s doc-comment for the audit-only augmentations
|
|
43
|
+
* it layers on top for historical commits (a concern this write-time check has no need for, since it
|
|
44
|
+
* only ever looks at the *current* live git config). Pure predicate — no git/filesystem access.
|
|
45
|
+
*/
|
|
46
|
+
function isConfiguredIdentity(name, email) {
|
|
47
|
+
return name.length > 0 && email.length > 0;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Read the live `user.name`/`user.email` git identity configured at `root`, with no "is it valid"
|
|
51
|
+
* judgement of its own (that is {@link isConfiguredIdentity}'s job) — the single read primitive both
|
|
52
|
+
* {@link requireGitIdentity} (REQ-SEC-01) and `src/core/approval-authority.ts`'s
|
|
53
|
+
* `requireApprovalAuthority` (REQ-SEC-03, task-040-role-based-approval-authority) build on, so the two
|
|
54
|
+
* checks never each re-implement their own `git config` read.
|
|
55
|
+
*/
|
|
56
|
+
function readGitIdentity(root) {
|
|
57
|
+
return { name: readGitConfig(root, 'user.name'), email: readGitConfig(root, 'user.email') };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Verify a git identity is configured at `root` before a state mutation. Returns a
|
|
61
|
+
* `CoreResult.error` (code `VALIDATION` — a failed precondition, mapped to exit `1` by
|
|
62
|
+
* `exitCodeForError`) carrying the exact REQ-SEC-01 message when either `user.name` or `user.email`
|
|
63
|
+
* is unset; otherwise `ok`.
|
|
64
|
+
*/
|
|
65
|
+
function requireGitIdentity(root) {
|
|
66
|
+
const { name, email } = readGitIdentity(root);
|
|
67
|
+
if (!isConfiguredIdentity(name, email)) {
|
|
68
|
+
return (0, types_1.coreErr)({ code: 'VALIDATION', message: IDENTITY_ERROR });
|
|
69
|
+
}
|
|
70
|
+
return (0, types_1.coreOk)(undefined);
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=git-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-identity.js","sourceRoot":"","sources":["../../src/core/git-identity.ts"],"names":[],"mappings":";;AA2CA,oDAEC;AAeD,0CAEC;AAQD,gDAMC;AA5ED;;;;;;;;;;GAUG;AACH,2DAAkD;AAElD,mCAA2D;AAE3D,oFAAoF;AACpF,MAAM,cAAc,GAAG,oDAAoD,CAAC;AAE5E;;;;GAIG;AACH,SAAS,aAAa,CAAC,IAAY,EAAE,GAAW;IAC9C,IAAI,CAAC;QACH,mGAAmG;QACnG,gGAAgG;QAChG,mFAAmF;QACnF,OAAO,IAAA,iCAAY,EAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1G,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,oBAAoB,CAAC,IAAY,EAAE,KAAa;IAC9D,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AAC7C,CAAC;AAQD;;;;;;GAMG;AACH,SAAgB,eAAe,CAAC,IAAY;IAC1C,OAAO,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,CAAC;AAC9F,CAAC;AAED;;;;;GAKG;AACH,SAAgB,kBAAkB,CAAC,IAAY;IAC7C,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,IAAA,eAAO,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,IAAA,cAAM,EAAO,SAAS,CAAC,CAAC;AACjC,CAAC"}
|