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,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runInit = runInit;
|
|
4
|
+
exports.createReadlinePrompt = createReadlinePrompt;
|
|
5
|
+
/**
|
|
6
|
+
* `wingfoil init` command handler (task-029-implement-wingfoil-init, P5.1.1, spec-008-cli-grammar §4,
|
|
7
|
+
* spec-005-cli-command-contract §1). `init` is a SPECIAL bootstrap command: it runs BEFORE any config
|
|
8
|
+
* exists, so it is not a standard noun-verb `CORE_MODULES` operation that loads config first — it is
|
|
9
|
+
* wired directly onto the program (see ./program.ts) and drives the `initWingfoilProject` core flow.
|
|
10
|
+
*
|
|
11
|
+
* `runInit` owns the spec-008 §4 prompt matrix (present/missing template x TTY/non-TTY x
|
|
12
|
+
* `--interactive`) and the spec-005 §1 exit-code contract, and takes its side-effecting pieces —
|
|
13
|
+
* the resolved `root`, TTY detection, the wizard `prompt`, and the `init` core function — as injected
|
|
14
|
+
* `deps` so the whole matrix is unit-testable without a real TTY, readline, or git repo. The only
|
|
15
|
+
* un-injected concern (a real readline prompt) lives in {@link createReadlinePrompt}, mirroring how
|
|
16
|
+
* ./program.ts's `commander` wiring is the one un-unit-tested mechanical seam.
|
|
17
|
+
*/
|
|
18
|
+
const node_readline_1 = require("node:readline");
|
|
19
|
+
const core_1 = require("../core");
|
|
20
|
+
const storage_1 = require("../storage");
|
|
21
|
+
const error_1 = require("./error");
|
|
22
|
+
const exit_1 = require("./exit");
|
|
23
|
+
const output_1 = require("./output");
|
|
24
|
+
/** The wizard question, listing the available templates and the default (spec-008 §4). */
|
|
25
|
+
function templateQuestion() {
|
|
26
|
+
return `Select a methodology template [${storage_1.TEMPLATE_NAMES.join(', ')}] (default: ${storage_1.DEFAULT_TEMPLATE}): `;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Decide the canonical template name to initialize with, applying the spec-008 §4 matrix. Returns the
|
|
30
|
+
* canonical name on success, or `null` after it has already emitted the usage error and exited `2`
|
|
31
|
+
* (the caller must stop) — so a `null` return is a terminal, already-handled state.
|
|
32
|
+
*/
|
|
33
|
+
async function selectTemplate(options, deps, format) {
|
|
34
|
+
// 1. --template present: resolve it (an unknown value is a usage error, exit 2).
|
|
35
|
+
if (options.template !== undefined && options.template.trim() !== '') {
|
|
36
|
+
const def = (0, storage_1.resolveTemplate)(options.template);
|
|
37
|
+
if (!def) {
|
|
38
|
+
(0, error_1.emitError)(`unknown template "${options.template}", expected one of: ${storage_1.TEMPLATE_NAMES.join(', ')}`, { format });
|
|
39
|
+
(0, exit_1.exitWith)(2);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return def.name;
|
|
43
|
+
}
|
|
44
|
+
// 2. --template absent + (non-interactive OR not a TTY): fail immediately (spec-008 §4).
|
|
45
|
+
if (!options.interactive || !deps.isTTY) {
|
|
46
|
+
(0, error_1.emitError)('missing required argument: --template', { format });
|
|
47
|
+
(0, exit_1.exitWith)(2);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
// 3. Interactive TTY: run the wizard. Empty answer -> the default template.
|
|
51
|
+
const answer = (await deps.prompt(templateQuestion())).trim();
|
|
52
|
+
if (answer === '')
|
|
53
|
+
return storage_1.DEFAULT_TEMPLATE;
|
|
54
|
+
const def = (0, storage_1.resolveTemplate)(answer);
|
|
55
|
+
if (!def) {
|
|
56
|
+
(0, error_1.emitError)(`unknown template "${answer}", expected one of: ${storage_1.TEMPLATE_NAMES.join(', ')}`, { format });
|
|
57
|
+
(0, exit_1.exitWith)(2);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return def.name;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Execute `wingfoil init`. Terminates through the single spec-005 §1 exit path (`exitWith`) exactly
|
|
64
|
+
* once: `2` on any usage error (invalid `--format`, unknown/omitted-required `--template`), otherwise
|
|
65
|
+
* the code the core `CoreResult` maps to (`0` success / `1` already-initialized or other logic error).
|
|
66
|
+
*/
|
|
67
|
+
async function runInit(options, deps) {
|
|
68
|
+
// Usage error first (pre-core), same as the registrar: an invalid --format never reaches core.
|
|
69
|
+
if (!(0, output_1.isValidFormat)(options.format)) {
|
|
70
|
+
(0, exit_1.exitWith)(2, `error: invalid --format value "${options.format}", expected one of: console, json, yaml`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const format = options.format;
|
|
74
|
+
const template = await selectTemplate(options, deps, format);
|
|
75
|
+
if (template === null)
|
|
76
|
+
return; // usage error already emitted + exited (2)
|
|
77
|
+
const init = deps.init ?? core_1.initWingfoilProject;
|
|
78
|
+
const result = init(deps.root, template);
|
|
79
|
+
if (result.ok) {
|
|
80
|
+
process.stdout.write((0, output_1.renderSuccess)(result.value, format));
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
(0, error_1.emitError)(result.error.message, { format });
|
|
84
|
+
}
|
|
85
|
+
(0, exit_1.exitWith)((0, core_1.exitCodeForResult)(result));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* A real readline-backed wizard prompt (used by ./program.ts's `init` command; not unit-tested — the
|
|
89
|
+
* same seam as `commander` wiring). Reads one line from stdin, resolves it, and closes the interface.
|
|
90
|
+
*/
|
|
91
|
+
function createReadlinePrompt() {
|
|
92
|
+
return (question) => new Promise((resolve) => {
|
|
93
|
+
const rl = (0, node_readline_1.createInterface)({ input: process.stdin, output: process.stdout });
|
|
94
|
+
rl.question(question, (answer) => {
|
|
95
|
+
rl.close();
|
|
96
|
+
resolve(answer);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=init-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-command.js","sourceRoot":"","sources":["../../src/cli/init-command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type OutputFormat } from './output';
|
|
2
|
+
/** The options `runMcp` acts on (injected so the resolve-root/error path needs no real stdio server). */
|
|
3
|
+
export interface McpCliDeps {
|
|
4
|
+
/** Resolve the project (git) root; throws when the cwd is not inside an initialized WingFoil project. */
|
|
5
|
+
readonly resolveRoot: () => string;
|
|
6
|
+
/** The CLI version to advertise as the MCP server's identity `version` (read from `package.json`). */
|
|
7
|
+
readonly version: string;
|
|
8
|
+
/** The output format for the pre-flight error path; defaults to `console`. */
|
|
9
|
+
readonly format?: OutputFormat;
|
|
10
|
+
/** Start the server; defaults to {@link startMcpServer} (overridden in tests to avoid real stdio). */
|
|
11
|
+
readonly start?: (options: {
|
|
12
|
+
resolveRoot: () => string;
|
|
13
|
+
name: string;
|
|
14
|
+
version: string;
|
|
15
|
+
}) => Promise<unknown>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Execute `wingfoil mcp`. Resolves the project root once, up front (fail-fast: an unresolvable root
|
|
19
|
+
* emits `error: <reason>` on stderr and exits `1`, never starting the server), then hands the server
|
|
20
|
+
* start a closure returning that already-resolved root. On success it does not exit — the server runs
|
|
21
|
+
* over stdio until the transport closes.
|
|
22
|
+
*/
|
|
23
|
+
export declare function runMcp(deps: McpCliDeps): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=mcp-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-command.d.ts","sourceRoot":"","sources":["../../src/cli/mcp-command.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAiB,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAE5D,yGAAyG;AACzG,MAAM,WAAW,UAAU;IACzB,yGAAyG;IACzG,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAC;IACnC,sGAAsG;IACtG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B,sGAAsG;IACtG,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,WAAW,EAAE,MAAM,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9G;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAc5D"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runMcp = runMcp;
|
|
4
|
+
/**
|
|
5
|
+
* `wingfoil mcp` command handler (task-030-implement-mcp-resources, P5.2.1,
|
|
6
|
+
* spec-014-mcp-server-entry-point §1). `mcp` is a SPECIAL command: it does not wrap a single `src/core`
|
|
7
|
+
* `CoreOperation` (so it is not a `CORE_MODULES` noun-verb op) — it owns a long-running process
|
|
8
|
+
* lifecycle, starting the production MCP server over stdio. It is wired directly onto the program
|
|
9
|
+
* (see ./program.ts), exactly like `wingfoil init` (task-029).
|
|
10
|
+
*
|
|
11
|
+
* `runMcp` owns the spec-014 §1 pre-flight: resolve the project root, and on failure emit the standard
|
|
12
|
+
* `error: <reason>` line and exit `1` (a user/logic error — not inside a WingFoil project) WITHOUT
|
|
13
|
+
* starting a partially-wired server. The side-effecting server start (`startMcpServer`, which opens a
|
|
14
|
+
* real `StdioServerTransport`) is injected as `deps.start`, so this whole pre-flight is unit-testable
|
|
15
|
+
* without ever opening a real stdio channel against a repo — the same seam `init-command.ts`'s
|
|
16
|
+
* injectable `runInit` uses.
|
|
17
|
+
*/
|
|
18
|
+
const server_1 = require("../mcp/server");
|
|
19
|
+
const error_1 = require("./error");
|
|
20
|
+
const exit_1 = require("./exit");
|
|
21
|
+
const output_1 = require("./output");
|
|
22
|
+
/**
|
|
23
|
+
* Execute `wingfoil mcp`. Resolves the project root once, up front (fail-fast: an unresolvable root
|
|
24
|
+
* emits `error: <reason>` on stderr and exits `1`, never starting the server), then hands the server
|
|
25
|
+
* start a closure returning that already-resolved root. On success it does not exit — the server runs
|
|
26
|
+
* over stdio until the transport closes.
|
|
27
|
+
*/
|
|
28
|
+
async function runMcp(deps) {
|
|
29
|
+
const format = deps.format && (0, output_1.isValidFormat)(deps.format) ? deps.format : 'console';
|
|
30
|
+
let root;
|
|
31
|
+
try {
|
|
32
|
+
root = deps.resolveRoot();
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
(0, error_1.emitError)(error instanceof Error ? error.message : String(error), { format });
|
|
36
|
+
(0, exit_1.exitWith)(1);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const start = deps.start ?? server_1.startMcpServer;
|
|
40
|
+
await start({ resolveRoot: () => root, name: 'wingfoil', version: deps.version });
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=mcp-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-command.js","sourceRoot":"","sources":["../../src/cli/mcp-command.ts"],"names":[],"mappings":";;AAsCA,wBAcC;AApDD;;;;;;;;;;;;;GAaG;AACH,0CAA+C;AAE/C,mCAAoC;AACpC,iCAAkC;AAClC,qCAA4D;AAc5D;;;;;GAKG;AACI,KAAK,UAAU,MAAM,CAAC,IAAgB;IAC3C,MAAM,MAAM,GAAiB,IAAI,CAAC,MAAM,IAAI,IAAA,sBAAa,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAEjG,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAA,iBAAS,EAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9E,IAAA,eAAQ,EAAC,CAAC,CAAC,CAAC;QACZ,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,uBAAc,CAAC;IAC3C,MAAM,KAAK,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AACpF,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** The accepted `--format` values (spec-005 §2); `console` is the default when the flag is omitted. */
|
|
2
|
+
export type OutputFormat = 'console' | 'json' | 'yaml';
|
|
3
|
+
/** Type guard: `true` (narrowing to {@link OutputFormat}) when `value` is one of the accepted `--format` values. */
|
|
4
|
+
export declare function isValidFormat(value: string): value is OutputFormat;
|
|
5
|
+
/**
|
|
6
|
+
* Render a successful `CoreResult.value` to stdout text, per the envelope rules in spec-005 §2:
|
|
7
|
+
* `json`/`yaml` carry only the structured payload (no banners/colour); `console` payload *shape*
|
|
8
|
+
* is otherwise owned by each command's own spec (none exists yet for today's read-only pillar
|
|
9
|
+
* queries — see task-006 Execution Notes — so `console` falls back to pretty-printed JSON, the
|
|
10
|
+
* same structure as `json`/`yaml`, until a command-specific spec defines a human-facing rendering).
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderSuccess(value: unknown, format: OutputFormat): string;
|
|
13
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"AAMA,uGAAuG;AACvG,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvD,oHAAoH;AACpH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,YAAY,CAElE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAI1E"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isValidFormat = isValidFormat;
|
|
4
|
+
exports.renderSuccess = renderSuccess;
|
|
5
|
+
/**
|
|
6
|
+
* Machine-readable output formats (spec-005-cli-command-contract §2, REQ-INT-05). `console` is the
|
|
7
|
+
* default when `--format` is omitted.
|
|
8
|
+
*/
|
|
9
|
+
const js_yaml_1 = require("js-yaml");
|
|
10
|
+
/** Type guard: `true` (narrowing to {@link OutputFormat}) when `value` is one of the accepted `--format` values. */
|
|
11
|
+
function isValidFormat(value) {
|
|
12
|
+
return value === 'console' || value === 'json' || value === 'yaml';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Render a successful `CoreResult.value` to stdout text, per the envelope rules in spec-005 §2:
|
|
16
|
+
* `json`/`yaml` carry only the structured payload (no banners/colour); `console` payload *shape*
|
|
17
|
+
* is otherwise owned by each command's own spec (none exists yet for today's read-only pillar
|
|
18
|
+
* queries — see task-006 Execution Notes — so `console` falls back to pretty-printed JSON, the
|
|
19
|
+
* same structure as `json`/`yaml`, until a command-specific spec defines a human-facing rendering).
|
|
20
|
+
*/
|
|
21
|
+
function renderSuccess(value, format) {
|
|
22
|
+
if (format === 'json')
|
|
23
|
+
return JSON.stringify(value) + '\n';
|
|
24
|
+
if (format === 'yaml')
|
|
25
|
+
return (0, js_yaml_1.dump)(value);
|
|
26
|
+
return JSON.stringify(value, null, 2) + '\n';
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":";;AAUA,sCAEC;AASD,sCAIC;AAzBD;;;GAGG;AACH,qCAA2C;AAK3C,oHAAoH;AACpH,SAAgB,aAAa,CAAC,KAAa;IACzC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,MAAM,CAAC;AACrE,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,KAAc,EAAE,MAAoB;IAChE,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAC3D,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,IAAA,cAAQ,EAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Command } from 'commander' with { 'resolution-mode': 'import' };
|
|
2
|
+
import type { CoreModule } from '../core/registry';
|
|
3
|
+
import { type BuildCommandsOptions } from './registrar';
|
|
4
|
+
/**
|
|
5
|
+
* Build the root `commander` program: register the global flags, the special `init`/`mcp` bootstrap
|
|
6
|
+
* commands, and one `wingfoil <noun> <verb>` (or flat `<noun>`) command per `CoreModule` operation
|
|
7
|
+
* derived through {@link BuildCommandsOptions}. Async because `commander` is imported dynamically
|
|
8
|
+
* (see the module doc). The command *behaviour* lives in `./registrar.ts`; this only wires it onto
|
|
9
|
+
* Commander.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildProgram(modules: readonly CoreModule[], options: BuildCommandsOptions): Promise<Command>;
|
|
12
|
+
//# sourceMappingURL=program.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC,OAAO,iBAAiB,EAAE,QAAQ,EAAE,CAAC;AAE9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAKnD,OAAO,EAAoB,KAAK,oBAAoB,EAAmB,MAAM,aAAa,CAAC;AAkB3F;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,CAwHlH"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildProgram = buildProgram;
|
|
4
|
+
/**
|
|
5
|
+
* Wires `./registrar.ts`'s Commander-independent command model onto a real `commander` `Command`
|
|
6
|
+
* tree — the only place this module (or any module reachable from a test file) imports `commander`.
|
|
7
|
+
*
|
|
8
|
+
* ENVIRONMENT NOTE (task-006, see the task's Execution Notes for the full write-up): `commander`
|
|
9
|
+
* v15 ships ESM-only (no CJS build — its `package.json` has `"type": "module"` and a single
|
|
10
|
+
* `"default": "./index.js"` export). This project's `tsconfig.json` (`module: Node16`, no
|
|
11
|
+
* top-level `"type": "module"` in `package.json`) compiles `.ts` files as CommonJS by default, and
|
|
12
|
+
* a *static* `import { Command } from 'commander'` from a CJS-resolved file is downleveled to a
|
|
13
|
+
* `require()` call — which `tsc --noEmit` itself refuses to emit for an ESM-only target (TS1479),
|
|
14
|
+
* and which, even if suppressed, would crash at Jest-test runtime (`ts-jest`'s CommonJS test
|
|
15
|
+
* environment cannot `require()` an ESM module; confirmed empirically — this predates task-006, a
|
|
16
|
+
* consequence of task-001's dependency pick, not something in this task's scope to fix
|
|
17
|
+
* project-wide, e.g. by migrating the whole Jest config to ESM or downgrading `commander`).
|
|
18
|
+
*
|
|
19
|
+
* The fix that keeps `commander` as the real, declared CLI dependency (per `dna.yaml` /
|
|
20
|
+
* spec-005/008) without touching the project's Jest/tsconfig setup: a *dynamic* `import('commander')`
|
|
21
|
+
* here, inside an async function. Dynamic `import()` is never downleveled by `tsc` regardless of
|
|
22
|
+
* module target, so it does not trigger TS1479, and it resolves correctly at real `node`/`wingfoil`
|
|
23
|
+
* runtime (Node 22's ESM-aware module loader).
|
|
24
|
+
*
|
|
25
|
+
* TESTING (task-065-fix-commander-esm-jest-harness, `bug-007-commander-esm-jest-untestable`): this
|
|
26
|
+
* file's wiring — construct the global flags, register a noun/verb `Command` per `CliCommand`,
|
|
27
|
+
* forward to `command.run` — used to be verified by hand, because a test importing it hit that same
|
|
28
|
+
* Jest-runtime wall (`module: Node16` *preserves* the dynamic `import()`, and jest's CommonJS runtime
|
|
29
|
+
* has no dynamic-import callback). It is now covered two ways, with no change to this file: in-process
|
|
30
|
+
* by `test/cli/program.test.ts` — jest compiles the test runtime as CommonJS against
|
|
31
|
+
* `tsconfig.test.json` and transforms `commander`'s ESM on the way in, see `jest.config.js` — and
|
|
32
|
+
* black-box by `test/cli/program.integration.test.ts`, which spawns the compiled `dist/` and so
|
|
33
|
+
* exercises the real ESM `import()` this file actually ships with.
|
|
34
|
+
* `buildCliCommands`/`listRegisteredCliCommands` (`./registrar.ts`) still carry 100% of the
|
|
35
|
+
* AC-relevant behavior; this file adds no logic of its own beyond Commander's own API calls.
|
|
36
|
+
*/
|
|
37
|
+
const node_fs_1 = require("node:fs");
|
|
38
|
+
const node_path_1 = require("node:path");
|
|
39
|
+
// Direct module import, not the `../core` barrel — the same path `./registrar.ts` already uses for the
|
|
40
|
+
// other two exit-code mappings (task-101; keeps this file out of the barrel's merge surface).
|
|
41
|
+
const exit_code_1 = require("../core/exit-code");
|
|
42
|
+
const registrar_1 = require("./registrar");
|
|
43
|
+
const init_command_1 = require("./init-command");
|
|
44
|
+
const mcp_command_1 = require("./mcp-command");
|
|
45
|
+
const error_1 = require("./error");
|
|
46
|
+
const exit_1 = require("./exit");
|
|
47
|
+
const output_1 = require("./output");
|
|
48
|
+
/**
|
|
49
|
+
* The CLI version, read from `package.json` deterministically (REQ-SYS-07 — no wall-clock, no
|
|
50
|
+
* inference): the manifest sits two levels up from this module in both the `src/cli/` source layout
|
|
51
|
+
* and the compiled `dist/cli/` layout, and npm always ships `package.json` at the package root, so
|
|
52
|
+
* the same relative path resolves for `wingfoil --version` after a global install.
|
|
53
|
+
*/
|
|
54
|
+
function readPackageVersion() {
|
|
55
|
+
const manifestPath = (0, node_path_1.join)(__dirname, '..', '..', 'package.json');
|
|
56
|
+
return JSON.parse((0, node_fs_1.readFileSync)(manifestPath, 'utf-8')).version;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build the root `commander` program: register the global flags, the special `init`/`mcp` bootstrap
|
|
60
|
+
* commands, and one `wingfoil <noun> <verb>` (or flat `<noun>`) command per `CoreModule` operation
|
|
61
|
+
* derived through {@link BuildCommandsOptions}. Async because `commander` is imported dynamically
|
|
62
|
+
* (see the module doc). The command *behaviour* lives in `./registrar.ts`; this only wires it onto
|
|
63
|
+
* Commander.
|
|
64
|
+
*/
|
|
65
|
+
async function buildProgram(modules, options) {
|
|
66
|
+
const { Command: CommandCtor } = await import('commander');
|
|
67
|
+
const program = new CommandCtor('wingfoil');
|
|
68
|
+
// Route Commander's OWN terminations through the spec-005 §1 exit-code contract
|
|
69
|
+
// (task-101-route-commander-parse-errors-through-the-exit-code-contract, `bug-098`). Commander
|
|
70
|
+
// detects an unknown command / unknown option / missing option argument before any WingFoil code
|
|
71
|
+
// runs and, left alone, ends the process itself at its suggested exit code — `1` for every error it
|
|
72
|
+
// raises, which is the code spec-005 §1 reserves for a well-formed invocation that failed. The
|
|
73
|
+
// callback is the whole interception: the *code* is chosen by `classifyParseOutcome`
|
|
74
|
+
// (`src/core/exit-code.ts`, beside the other two mappings — this file adds no second decision site),
|
|
75
|
+
// and the process ends through `exitWith`, the single exit seam of `./exit.ts`, like every other
|
|
76
|
+
// outcome.
|
|
77
|
+
//
|
|
78
|
+
// COMMANDER'S OWN MESSAGES ARE UNAFFECTED: for every error it raises, Commander has already written
|
|
79
|
+
// its `error: …` line before calling this, so nothing is added on top of it. The ONE outcome where
|
|
80
|
+
// it writes no error message at all is an invocation it found incomplete — a noun with no verb —
|
|
81
|
+
// where it prints help to stderr and nothing else; `spec-005` §1 requires a non-zero exit to carry
|
|
82
|
+
// an error message *always*, so that outcome (and only that one, flagged by `needsErrorLine`) gets
|
|
83
|
+
// the line composed here by `incompleteInvocationReason`
|
|
84
|
+
// (task-103-a-missing-verb-exits-2-with-an-error-line, `bug-103`).
|
|
85
|
+
//
|
|
86
|
+
// ORDER MATTERS: `.command()` copies the parent's `_exitCallback` into each subcommand at
|
|
87
|
+
// *registration* time (`Command#copyInheritedSettings`, commander@15.0.0), so this must be installed
|
|
88
|
+
// before the first `.command()` call below or a subcommand's parse errors would still bypass it.
|
|
89
|
+
// The same fact is why the callback reads `program.args` rather than the erroring command: the
|
|
90
|
+
// *root* program is the one object every copy of this closure shares, and Commander has already put
|
|
91
|
+
// the invocation's operands there (`Command#_parseCommand`'s `this.args = operands.concat(unknown)`,
|
|
92
|
+
// set before it dispatches) with the global options removed.
|
|
93
|
+
program.exitOverride((error) => {
|
|
94
|
+
const termination = (0, exit_code_1.classifyParseOutcome)(error);
|
|
95
|
+
if (termination.needsErrorLine)
|
|
96
|
+
(0, error_1.emitError)(incompleteInvocationReason(program.args), { format: 'console' });
|
|
97
|
+
(0, exit_1.exitWith)(termination.exitCode);
|
|
98
|
+
});
|
|
99
|
+
// Register `-V, --version` so `wingfoil --version` prints the version and exits 0
|
|
100
|
+
// (spec-008-cli-grammar §1, bug-001-cli-version-flag) — Commander handles it before any command.
|
|
101
|
+
program.version(readPackageVersion());
|
|
102
|
+
program
|
|
103
|
+
.option('--format <format>', 'output format (console|json|yaml)', 'console')
|
|
104
|
+
.option('--verbose', 'emit diagnostic logs to stderr')
|
|
105
|
+
.option('--no-color', 'disable ANSI colors')
|
|
106
|
+
.option('--no-interactive', 'fail on missing args instead of prompting');
|
|
107
|
+
// `wingfoil init` is a SPECIAL bootstrap command (task-029, P5.1.1): it runs BEFORE config exists,
|
|
108
|
+
// so it is NOT a `CORE_MODULES` noun-verb op — it is wired directly here and drives `runInit`
|
|
109
|
+
// (./init-command.ts). The wizard/`--template`/prompt-matrix logic is fully unit-tested in
|
|
110
|
+
// ./init-command.ts; this registration is the same thin, un-unit-tested `commander` seam as the
|
|
111
|
+
// rest of this file (see the module doc).
|
|
112
|
+
program
|
|
113
|
+
.command('init')
|
|
114
|
+
.description('initialize WingFoil in the current git repository')
|
|
115
|
+
.option('--template <name>', 'methodology template to initialize with (non-interactive)')
|
|
116
|
+
.action(async (localOpts) => {
|
|
117
|
+
const globalOpts = program.opts();
|
|
118
|
+
let root;
|
|
119
|
+
try {
|
|
120
|
+
root = options.resolveRoot();
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
(0, error_1.emitError)(error instanceof Error ? error.message : String(error), { format: 'console' });
|
|
124
|
+
(0, exit_1.exitWith)(1);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
await (0, init_command_1.runInit)({ template: localOpts.template, interactive: globalOpts.interactive, format: globalOpts.format }, { root, isTTY: Boolean(process.stdout.isTTY), prompt: (0, init_command_1.createReadlinePrompt)() });
|
|
128
|
+
});
|
|
129
|
+
// `wingfoil mcp` is a SPECIAL command (task-030, P5.2.1, spec-014-mcp-server-entry-point §1): it
|
|
130
|
+
// starts the long-running production MCP server over stdio rather than wrapping a `CORE_MODULES`
|
|
131
|
+
// noun-verb op, so — like `init` above — it is wired directly here and drives `runMcp`
|
|
132
|
+
// (./mcp-command.ts). The pre-flight (resolve-root / error / exit 1) is unit-tested in
|
|
133
|
+
// ./mcp-command.ts with an injected server-start; this registration is the same thin, un-unit-tested
|
|
134
|
+
// `commander` seam as the rest of this file (see the module doc).
|
|
135
|
+
program
|
|
136
|
+
.command('mcp')
|
|
137
|
+
.description('start the WingFoil MCP server (read-only Resources and role Prompts) over stdio')
|
|
138
|
+
.action(async () => {
|
|
139
|
+
const globalOpts = program.opts();
|
|
140
|
+
const format = (0, output_1.isValidFormat)(globalOpts.format) ? globalOpts.format : 'console';
|
|
141
|
+
await (0, mcp_command_1.runMcp)({ resolveRoot: options.resolveRoot, version: readPackageVersion(), format });
|
|
142
|
+
});
|
|
143
|
+
const nounCommands = new Map();
|
|
144
|
+
for (const command of (0, registrar_1.buildCliCommands)(modules, options)) {
|
|
145
|
+
const target = resolveCommandTarget(program, nounCommands, command);
|
|
146
|
+
// A variadic optional bare positional list (task-025-implement-dna-set's `positionals` seam,
|
|
147
|
+
// generalizing task-026's single `[positional]`) — registered on every command regardless of how
|
|
148
|
+
// many positionals its operation reads (harmless if ignored), so `dna show [section]`,
|
|
149
|
+
// `paths [category]`, `memory approve [id]` and `dna set|add|remove|update <path>` share ONE
|
|
150
|
+
// positional mechanism. Every one of them now reads at most ONE positional: `dl-082-cli-parameter-shape`
|
|
151
|
+
// states the rule the nine older commands already followed — a positional carries the identity of
|
|
152
|
+
// the command's target, an option a named attribute — and task-093 moved `dna set`'s second
|
|
153
|
+
// positional into `--value` to match. The list stays variadic so an operation can refuse an extra
|
|
154
|
+
// positional with its own message rather than have Commander refuse it with an arity error.
|
|
155
|
+
// Plus this command's own `--{flag}` options (task-028-implement-paths-category's
|
|
156
|
+
// `CoreOperation.flags`, e.g. `paths`'s `--list`): Commander rejects an unknown option, so each
|
|
157
|
+
// declared flag must be registered explicitly.
|
|
158
|
+
target.argument('[positionals...]', 'optional positional arguments (the command target — e.g. a section/category name, a document id, or a `dna` field path)');
|
|
159
|
+
for (const name of command.flags ?? []) {
|
|
160
|
+
target.option(`--${name}`, `${name} flag`);
|
|
161
|
+
}
|
|
162
|
+
// Value-bearing `--{name} <value>` options (task-020-implement-memory-add's `memory add
|
|
163
|
+
// --type/--title/--tags`): Commander rejects an unknown option, so each declared option must be
|
|
164
|
+
// registered explicitly with a `<value>` operand (distinguishing it from a boolean `--flag`).
|
|
165
|
+
for (const option of command.options ?? []) {
|
|
166
|
+
target.option(`--${option.name} <value>`, option.description ?? `${option.name} value`);
|
|
167
|
+
}
|
|
168
|
+
// Commander's action callback for a `[positionals...]` variadic + options command is
|
|
169
|
+
// `(positionalsArray, optionsObject, commandObject)`. Forward the whole array (task-025),
|
|
170
|
+
// collapse this command's declared flags into a `{ name: boolean }` record (task-028) and its
|
|
171
|
+
// declared value options into a `{ name: value }` record (task-020) for `run`.
|
|
172
|
+
target.action(async (positionals = [], options = {}) => {
|
|
173
|
+
const globalOpts = program.opts();
|
|
174
|
+
await command.run(globalOpts.format, positionals, buildFlagValues(command, options), buildOptionValues(command, options));
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return program;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The `error: <reason>` text (spec-005 §3.1) for an invocation Commander found incomplete —
|
|
181
|
+
* task-103-a-missing-verb-exits-2-with-an-error-line (`bug-103`). Commander supplies no message on
|
|
182
|
+
* this path, so the wording is a **ruling**, recorded here rather than left to the reader:
|
|
183
|
+
*
|
|
184
|
+
* - `wingfoil dna`, `wingfoil memory`, and `wingfoil` with no arguments at all →
|
|
185
|
+
* `missing required argument: wingfoil dna <command>` / `missing required argument: wingfoil
|
|
186
|
+
* <command>`. The key is the one `spec-005` §1 already lists among its malformed invocations, and
|
|
187
|
+
* the shape is the one WingFoil already emits for a missing positional (`src/core/index.ts`'s
|
|
188
|
+
* `missing required argument: wingfoil dna set <path> --value <value>`, task-093): the incomplete
|
|
189
|
+
* invocation echoed back with the token that would complete it. `<command>` rather than `<verb>`
|
|
190
|
+
* matches Commander's own placeholder in the usage line printed directly above it.
|
|
191
|
+
* - `wingfoil help nosuchnoun` → `unknown command 'nosuchnoun'`, byte-for-byte what `wingfoil
|
|
192
|
+
* nosuchnoun` already emits (Commander's `unknownCommand()`), because asking about an unknown noun
|
|
193
|
+
* through `help` is the same mistake and a script should have one shape to grep. The single quotes
|
|
194
|
+
* are Commander's; `spec-005` §4's example shows double ones, and reconciling the two spellings
|
|
195
|
+
* (along with the missing `hint:` suggestion) is `bug-104`'s, which owns both lines at once. Picking
|
|
196
|
+
* a third spelling here would make that reconciliation harder, not easier.
|
|
197
|
+
*
|
|
198
|
+
* @param operands - the root program's `args`: the invocation's operands, global options already
|
|
199
|
+
* removed by Commander itself. This function never re-parses argv.
|
|
200
|
+
*/
|
|
201
|
+
function incompleteInvocationReason(operands) {
|
|
202
|
+
const [first, second] = operands;
|
|
203
|
+
// `help <name>` reaches the incomplete-invocation path only when `<name>` matched no command —
|
|
204
|
+
// `Command#_dispatchHelpCommand` falls through to `_dispatchSubcommand`, whose `if (!subCommand)`
|
|
205
|
+
// raises it. `help` alone and `help <known-noun>` exit 0 and never arrive here.
|
|
206
|
+
if (first === 'help' && second !== undefined)
|
|
207
|
+
return `unknown command '${second}'`;
|
|
208
|
+
return `missing required argument: ${['wingfoil', ...operands].join(' ')} <command>`;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The Commander `Command` a `CliCommand` registers itself on: a flat, no-verb command
|
|
212
|
+
* (`command.verb === ''` — `deriveVerb`'s self-named-operation case, spec-008-cli-grammar §1's
|
|
213
|
+
* `wingfoil <noun> [args] [flags]` form, e.g. `wingfoil paths [category]` —
|
|
214
|
+
* task-028-implement-paths-category) registers directly on the noun `Command` itself; every other
|
|
215
|
+
* (`<noun> <verb>`) command keeps nesting under it exactly as before task-028.
|
|
216
|
+
*/
|
|
217
|
+
function resolveCommandTarget(program, nounCommands, command) {
|
|
218
|
+
if (!command.verb)
|
|
219
|
+
return program.command(command.noun);
|
|
220
|
+
let nounCommand = nounCommands.get(command.noun);
|
|
221
|
+
if (!nounCommand) {
|
|
222
|
+
nounCommand = program.command(command.noun);
|
|
223
|
+
nounCommands.set(command.noun, nounCommand);
|
|
224
|
+
}
|
|
225
|
+
return nounCommand.command(command.verb);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Collapse this command's declared `CoreOperation.flags` names (`./registrar.ts`'s `CliCommand.flags`)
|
|
229
|
+
* into a `{ name: boolean }` record read from Commander's parsed options object, so `command.run`
|
|
230
|
+
* (Commander-independent) never has to know Commander's option-object shape. Returns `undefined` when
|
|
231
|
+
* the command declares no flags (every command before task-028-implement-paths-category), matching
|
|
232
|
+
* `CliCommand.run`'s already-optional `flags` parameter.
|
|
233
|
+
*/
|
|
234
|
+
function buildFlagValues(command, options) {
|
|
235
|
+
const flagNames = command.flags ?? [];
|
|
236
|
+
if (flagNames.length === 0)
|
|
237
|
+
return undefined;
|
|
238
|
+
const flagValues = {};
|
|
239
|
+
for (const name of flagNames) {
|
|
240
|
+
flagValues[name] = Boolean(options[name]);
|
|
241
|
+
}
|
|
242
|
+
return flagValues;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Collapse this command's declared `CoreOperation.options` (`./registrar.ts`'s `CliCommand.options`)
|
|
246
|
+
* into a `{ name: value }` record read from Commander's parsed options object (task-020), so
|
|
247
|
+
* `command.run` never has to know Commander's option-object shape. An option the invocation omitted is
|
|
248
|
+
* simply absent from the record (not present-as-`undefined`), so a core op can distinguish "not given"
|
|
249
|
+
* from an empty string. Returns `undefined` when the command declares no value options (every command
|
|
250
|
+
* before task-020-implement-memory-add), matching `CliCommand.run`'s already-optional `options` param.
|
|
251
|
+
*/
|
|
252
|
+
function buildOptionValues(command, options) {
|
|
253
|
+
const declared = command.options ?? [];
|
|
254
|
+
if (declared.length === 0)
|
|
255
|
+
return undefined;
|
|
256
|
+
const optionValues = {};
|
|
257
|
+
for (const { name } of declared) {
|
|
258
|
+
const value = options[commanderKey(name)];
|
|
259
|
+
if (typeof value === 'string')
|
|
260
|
+
optionValues[name] = value;
|
|
261
|
+
}
|
|
262
|
+
return optionValues;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* The property Commander stores a `--{name} <value>` option under: it camel-cases across `-`
|
|
266
|
+
* (`--entry-executes_as` -> `entryExecutes_as`) and leaves every other character alone.
|
|
267
|
+
*
|
|
268
|
+
* `CoreOption.name` is the declared name, and `CliCommand.run` hands core its options keyed by THAT,
|
|
269
|
+
* so this is the one place the two spellings meet. Before task-093 the lookup used the declared name
|
|
270
|
+
* directly, which worked only because every option declared so far happened to be a single word: a
|
|
271
|
+
* dashed name would have been read as `undefined` and dropped in silence — the same failure mode as
|
|
272
|
+
* the shadowing this task fixes, one layer further in. `test/cli/derived-option-namespace.test.ts`
|
|
273
|
+
* drives every dashed option the registry declares through the real CLI, so neither can return
|
|
274
|
+
* unnoticed.
|
|
275
|
+
*/
|
|
276
|
+
function commanderKey(name) {
|
|
277
|
+
return name.replace(/-([a-zA-Z0-9])/g, (_match, char) => char.toUpperCase());
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=program.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"program.js","sourceRoot":"","sources":["../../src/cli/program.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CLI adapter's command registrar (spec-006-core-domain-api §2/§4, spec-005-cli-command-contract,
|
|
3
|
+
* spec-008-cli-grammar — task-006). `buildCliCommands` is the "command registrar" spec-006 §4.1
|
|
4
|
+
* refers to: it imports nothing but the `CoreModule[]` it is given, iterates `enumerateOperations`
|
|
5
|
+
* (never a hand-copied operation list), and derives **every** operation it finds into a
|
|
6
|
+
* `wingfoil <noun> <verb>` command descriptor — regardless of `mutates` (§2: "CLI exposes every
|
|
7
|
+
* operation regardless of `mutates`"). No business logic lives outside `run`: it only (a)
|
|
8
|
+
* validates the global `--format` flag, (b) turns the invocation into typed params via the
|
|
9
|
+
* caller-supplied `buildParams`, (c) calls the one core function, (d) renders the resulting
|
|
10
|
+
* `CoreResult` via `renderSuccess`/`emitError` and terminates via `exitWith` — matching spec-005
|
|
11
|
+
* §1's "exactly one process exit call per invocation".
|
|
12
|
+
*
|
|
13
|
+
* Deliberately Commander-independent (see `./program.ts`'s module doc for why): this module owns
|
|
14
|
+
* 100% of the AC-relevant behavior — naming, dispatch, exit codes, output rendering — and is fully
|
|
15
|
+
* unit-testable; wiring this command model onto a real `commander` `Command` tree is a separate,
|
|
16
|
+
* thin, mechanical concern.
|
|
17
|
+
*/
|
|
18
|
+
import type { CoreModule, CoreOption, ParamsBuilder } from '../core/registry';
|
|
19
|
+
/** Ambient dependencies {@link buildCliCommands} needs: how to resolve the project root and how to shape each operation's params. */
|
|
20
|
+
export interface BuildCommandsOptions {
|
|
21
|
+
/** Resolves the project root a core call needs — an ambient/environment concern, not a CLI flag. */
|
|
22
|
+
readonly resolveRoot: () => string;
|
|
23
|
+
/** Turns a `ParamsContext` into one operation's typed params (`../core/registry.ts`'s {@link ParamsBuilder}). */
|
|
24
|
+
readonly buildParams: ParamsBuilder;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* One derived `wingfoil <noun> <verb>` command (or, when `verb === ''`, a flat `wingfoil <noun>`
|
|
28
|
+
* command — spec-008-cli-grammar §1, task-028-implement-paths-category): its dispatch is a pure
|
|
29
|
+
* function of the ambient `--format` value plus this operation's own positional/flag values, if any.
|
|
30
|
+
*/
|
|
31
|
+
export interface CliCommand {
|
|
32
|
+
readonly noun: string;
|
|
33
|
+
readonly verb: string;
|
|
34
|
+
readonly mutates: boolean;
|
|
35
|
+
/** Copied from `CoreOperation.flags` (`../core/registry.ts`) — the boolean flag names `program.ts`
|
|
36
|
+
* registers as Commander `--{name}` options for this command (empty/absent for every command
|
|
37
|
+
* before task-028-implement-paths-category; `['list']` for `paths`). */
|
|
38
|
+
readonly flags?: readonly string[];
|
|
39
|
+
/** Copied from `CoreOperation.options` (`../core/registry.ts`) — the value-bearing `--{name} <value>`
|
|
40
|
+
* options `program.ts` registers for this command (task-020-implement-memory-add; empty/absent for
|
|
41
|
+
* every command before it). */
|
|
42
|
+
readonly options?: readonly CoreOption[];
|
|
43
|
+
/**
|
|
44
|
+
* Execute this command given the resolved `--format` flag value (still unvalidated at this point),
|
|
45
|
+
* the FULL list of bare positional arguments the invocation supplied (task-025-implement-dna-set's
|
|
46
|
+
* additive `positionals` seam — e.g. `wingfoil dna show tech_stack` -> `['tech_stack']`,
|
|
47
|
+
* `wingfoil dna set project.license --value MIT` -> `['project.license']`, one positional per
|
|
48
|
+
* command since `dl-082-cli-parameter-shape`), this command's own parsed `--{flag}`
|
|
49
|
+
* values (task-028, e.g. `{ list: true }`), and its parsed value-bearing `--{name} <value>` OPTIONS
|
|
50
|
+
* (task-020, e.g. `{ type: 'decision', title: 'Use PostgreSQL' }`). All are additive/optional — a
|
|
51
|
+
* command that reads no positional and declares no flags/options is still called exactly as before:
|
|
52
|
+
* `run(format)`. The single-positional read ops (`dna show`, `paths`) read `positionals[0]` via
|
|
53
|
+
* `ParamsContext.positional`.
|
|
54
|
+
*/
|
|
55
|
+
readonly run: (formatValue: string, positionals?: readonly string[], flags?: Readonly<Record<string, boolean>>, options?: Readonly<Record<string, string>>) => Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Derive one `CliCommand` per operation in `modules`, in `enumerateOperations`'s deterministic
|
|
59
|
+
* order (REQ-SYS-07). The verb is derived via `deriveVerb` (spec-006 §5).
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildCliCommands(modules: readonly CoreModule[], options: BuildCommandsOptions): CliCommand[];
|
|
62
|
+
/**
|
|
63
|
+
* Every `"{noun} {verb}"` command derived from `commands`, sorted (REQ-SYS-07) — the CLI-side
|
|
64
|
+
* enumeration the REQ-SYS-05 parity test (`test/core/parity.test.ts`) diffs against the MCP Tool
|
|
65
|
+
* list. A flat, no-verb command (`verb === ''`, task-028-implement-paths-category) renders as the
|
|
66
|
+
* bare noun (`"paths"`), not `"paths "` with a trailing space.
|
|
67
|
+
*/
|
|
68
|
+
export declare function listRegisteredCliCommands(commands: readonly CliCommand[]): string[];
|
|
69
|
+
//# sourceMappingURL=registrar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registrar.d.ts","sourceRoot":"","sources":["../../src/cli/registrar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAS9E,qIAAqI;AACrI,MAAM,WAAW,oBAAoB;IACnC,oGAAoG;IACpG,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAC;IACnC,iHAAiH;IACjH,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC;CACrC;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;4EAEwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;mCAE+B;IAC/B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACzC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,GAAG,EAAE,CACZ,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,EAC/B,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KACvC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,EAAE,OAAO,EAAE,oBAAoB,GAAG,UAAU,EAAE,CA+D5G;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,EAAE,CAEnF"}
|