@vodmal/vdx-cli 0.4.0 → 0.5.0
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/README.md +35 -3
- package/package.json +1 -1
- package/src/index.ts +32 -0
- package/src/run.ts +56 -0
package/README.md
CHANGED
|
@@ -19,9 +19,34 @@ npx -y -p @vodmal/vdx-cli vdx audit /path/to/project
|
|
|
19
19
|
## Use
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
+
# Lifecycle verbs (pass-through to `mise run <verb>`; requires mise + mise.toml)
|
|
23
|
+
vdx up | down | build | test | check | fix
|
|
24
|
+
|
|
25
|
+
# Maturity audit against the owner baseline rubric
|
|
22
26
|
vdx audit <project-path> [--json] [--rubric <path>] [--stack <id>]
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
|
|
28
|
+
# Generate mise.toml + AGENTS.md for a project
|
|
29
|
+
vdx init <project-path> [--baseline github.com/org/repo@vX.Y] [--stack <id>] [--dry-run] [--force]
|
|
30
|
+
|
|
31
|
+
# Publish a library (Node MVP; PHP/Python coming in Y.3)
|
|
32
|
+
vdx publish <patch|minor|major> [--dry-run] [--force]
|
|
33
|
+
|
|
34
|
+
# MCP stdio server consumed by the Claude Code plugin
|
|
35
|
+
vdx-mcp --project <path>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Three quick examples**
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# 1. Score the project I'm standing in
|
|
42
|
+
vdx audit .
|
|
43
|
+
|
|
44
|
+
# 2. Wire native scripts into the 6 lifecycle verbs, then run one
|
|
45
|
+
vdx init . --stack node
|
|
46
|
+
vdx test # → mise run test (which calls `vitest run` or whatever was detected)
|
|
47
|
+
|
|
48
|
+
# 3. Ship a new minor release of a Node lib
|
|
49
|
+
vdx publish minor # bump → npm publish (OTP prompt) → git commit + tag (no push)
|
|
25
50
|
```
|
|
26
51
|
|
|
27
52
|
By default the bundled `rubric/vdx-rubric.yaml` is used (a mirror of canonical
|
|
@@ -48,6 +73,9 @@ npm run typecheck
|
|
|
48
73
|
- `src/evaluator.ts` — recursive evaluator + sugar notation
|
|
49
74
|
- `src/scoring.ts` — delta-style levels, flags for orthogonal axes
|
|
50
75
|
- `src/audit.ts` — orchestrator: overrides + applies_to filter + subpackage-ctx
|
|
76
|
+
- `src/init.ts` — `vdx init` planner (`selectVerbTask` + mise.toml/AGENTS.md renderers)
|
|
77
|
+
- `src/run.ts` — `resolveLifecycleVerb` + error renderer (pure logic for `vdx <verb>`)
|
|
78
|
+
- `src/publish.ts` — `planPublish` (pre-flight) + `executePublish` (bump → npm → git)
|
|
51
79
|
- `src/report.ts` — markdown / JSON output
|
|
52
80
|
- `src/index.ts` — CLI entry
|
|
53
81
|
- `src/mcp-server.ts` — MCP stdio server (9 tools)
|
|
@@ -60,4 +88,8 @@ npm run typecheck
|
|
|
60
88
|
- Baseline loading from a git ref is documented but evaluator still reads file
|
|
61
89
|
paths only — `baseline:` in `mise.toml` is recorded but does not auto-fetch.
|
|
62
90
|
- Watermark drift (phase 2 of `drift-algorithm.md`).
|
|
63
|
-
-
|
|
91
|
+
- `vdx publish` only ships for Node (MVP). PHP/Python in Y.3; Cargo/Ruby/Go/Java
|
|
92
|
+
in Y.4.
|
|
93
|
+
- Open items: O25 (mock-infra delta for Node), O26 (TOML round-trip), O27 (real
|
|
94
|
+
shared-infra precheck), O32 (multi-subpackage monorepo). See
|
|
95
|
+
[docs/decisions.md](../docs/decisions.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vodmal/vdx-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "vdx — unified lifecycle interface (up/down/build/test/check/fix) + versioned maturity audit with drift detection. Pairs with the vdx Claude Code plugin.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vdx",
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
3
4
|
import { loadRubric } from './rubric.ts';
|
|
4
5
|
import { loadManifest, loadOverrides } from './manifest.ts';
|
|
5
6
|
import { autoDetectStack, type Ctx } from './facts.ts';
|
|
7
|
+
import {
|
|
8
|
+
LIFECYCLE_VERBS,
|
|
9
|
+
type LifecycleVerb,
|
|
10
|
+
resolveLifecycleVerb,
|
|
11
|
+
renderResolveError,
|
|
12
|
+
} from './run.ts';
|
|
6
13
|
import { audit } from './audit.ts';
|
|
7
14
|
import { reportMarkdown, reportJson } from './report.ts';
|
|
8
15
|
import { planInit, writeInit, renderPlanSummary } from './init.ts';
|
|
@@ -20,6 +27,7 @@ const DEFAULT_RUBRIC = resolveDefaultRubric();
|
|
|
20
27
|
function usage(): never {
|
|
21
28
|
process.stderr.write(
|
|
22
29
|
`Usage:
|
|
30
|
+
vdx <up|down|build|test|check|fix> run lifecycle verb (via mise run <verb>)
|
|
23
31
|
vdx audit <project_path> [--rubric <path>] [--stack <stack>] [--json]
|
|
24
32
|
vdx init <project_path> [--stack <id>] [--baseline <ref>] [--dry-run] [--force]
|
|
25
33
|
vdx publish <patch|minor|major> [--dry-run] [--force]
|
|
@@ -176,8 +184,32 @@ function cmdPublish(opts: ParsedArgs): void {
|
|
|
176
184
|
}
|
|
177
185
|
}
|
|
178
186
|
|
|
187
|
+
function cmdRun(verb: LifecycleVerb): void {
|
|
188
|
+
const projectRoot = process.cwd();
|
|
189
|
+
const res = resolveLifecycleVerb(projectRoot, verb);
|
|
190
|
+
if (!res.ok) {
|
|
191
|
+
process.stderr.write(renderResolveError(res, projectRoot, verb));
|
|
192
|
+
process.exit(2);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
execFileSync('mise', ['run', verb], { cwd: projectRoot, stdio: 'inherit' });
|
|
197
|
+
} catch (e: any) {
|
|
198
|
+
if (e?.code === 'ENOENT') {
|
|
199
|
+
process.stderr.write(
|
|
200
|
+
`vdx: \`mise\` binary not found on PATH\n` +
|
|
201
|
+
`hint: install mise — https://mise.jdx.dev/getting-started.html\n`,
|
|
202
|
+
);
|
|
203
|
+
process.exit(127);
|
|
204
|
+
}
|
|
205
|
+
process.exit(typeof e?.status === 'number' ? e.status : 1);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
179
209
|
const parsed = parseArgs(process.argv);
|
|
180
210
|
if (parsed.cmd === 'audit') cmdAudit(parsed);
|
|
181
211
|
else if (parsed.cmd === 'init') cmdInit(parsed);
|
|
182
212
|
else if (parsed.cmd === 'publish') cmdPublish(parsed);
|
|
213
|
+
else if ((LIFECYCLE_VERBS as readonly string[]).includes(parsed.cmd))
|
|
214
|
+
cmdRun(parsed.cmd as LifecycleVerb);
|
|
183
215
|
else usage();
|
package/src/run.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as TOML from 'smol-toml';
|
|
4
|
+
|
|
5
|
+
export const LIFECYCLE_VERBS = ['up', 'down', 'build', 'test', 'check', 'fix'] as const;
|
|
6
|
+
export type LifecycleVerb = (typeof LIFECYCLE_VERBS)[number];
|
|
7
|
+
|
|
8
|
+
export type VerbResolveResult =
|
|
9
|
+
| { ok: true; miseTomlPath: string }
|
|
10
|
+
| {
|
|
11
|
+
ok: false;
|
|
12
|
+
reason: 'no-mise-toml' | 'parse-error' | 'no-task';
|
|
13
|
+
detail?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function resolveLifecycleVerb(
|
|
17
|
+
projectRoot: string,
|
|
18
|
+
verb: string,
|
|
19
|
+
): VerbResolveResult {
|
|
20
|
+
const miseTomlPath = path.join(projectRoot, 'mise.toml');
|
|
21
|
+
if (!fs.existsSync(miseTomlPath)) {
|
|
22
|
+
return { ok: false, reason: 'no-mise-toml' };
|
|
23
|
+
}
|
|
24
|
+
let parsed: unknown;
|
|
25
|
+
try {
|
|
26
|
+
parsed = TOML.parse(fs.readFileSync(miseTomlPath, 'utf8'));
|
|
27
|
+
} catch (e: any) {
|
|
28
|
+
return { ok: false, reason: 'parse-error', detail: e?.message ?? String(e) };
|
|
29
|
+
}
|
|
30
|
+
const tasks = (parsed as { tasks?: Record<string, unknown> })?.tasks;
|
|
31
|
+
if (!tasks || typeof tasks !== 'object' || !(verb in tasks)) {
|
|
32
|
+
return { ok: false, reason: 'no-task' };
|
|
33
|
+
}
|
|
34
|
+
return { ok: true, miseTomlPath };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function renderResolveError(
|
|
38
|
+
res: Exclude<VerbResolveResult, { ok: true }>,
|
|
39
|
+
projectRoot: string,
|
|
40
|
+
verb: string,
|
|
41
|
+
): string {
|
|
42
|
+
switch (res.reason) {
|
|
43
|
+
case 'no-mise-toml':
|
|
44
|
+
return (
|
|
45
|
+
`vdx: no mise.toml in ${projectRoot}\n` +
|
|
46
|
+
`hint: run \`vdx init .\` to generate one, or define it manually.\n`
|
|
47
|
+
);
|
|
48
|
+
case 'parse-error':
|
|
49
|
+
return `vdx: failed to parse mise.toml: ${res.detail ?? 'unknown error'}\n`;
|
|
50
|
+
case 'no-task':
|
|
51
|
+
return (
|
|
52
|
+
`vdx: no \`[tasks.${verb}]\` in mise.toml\n` +
|
|
53
|
+
`hint: run \`vdx init . --force\` to re-detect, or add the task manually.\n`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|