@formigio/fazemos-cli 0.10.11 → 0.10.12
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/dist/index.js +132 -0
- package/dist/index.js.map +1 -1
- package/dist/manifest/checks.d.ts +6 -0
- package/dist/manifest/checks.js +356 -0
- package/dist/manifest/checks.js.map +1 -0
- package/dist/manifest/schema.d.ts +314 -0
- package/dist/manifest/schema.js +208 -0
- package/dist/manifest/schema.js.map +1 -0
- package/dist/yaml/format.d.ts +5 -0
- package/dist/yaml/format.js +41 -0
- package/dist/yaml/format.js.map +1 -0
- package/dist/yaml/load.d.ts +28 -0
- package/dist/yaml/load.js +74 -0
- package/dist/yaml/load.js.map +1 -0
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -7,6 +7,9 @@ getActiveProjectId, setActiveProjectId, clearActiveProjectId, findProjectBySlug,
|
|
|
7
7
|
import { login, signup, confirmSignup, adminLogin } from './auth.js';
|
|
8
8
|
import { api, ApiError, refreshAuthMeCache, invalidateAuthMeCache } from './api.js';
|
|
9
9
|
import { isProjectConnectionUnavailable, renderProjectConnectionUnavailableCopy, } from './connectionErrorCopy.js';
|
|
10
|
+
import { loadYaml, summarize } from './yaml/load.js';
|
|
11
|
+
import { printFindings, printJson } from './yaml/format.js';
|
|
12
|
+
import { validateManifest } from './manifest/checks.js';
|
|
10
13
|
import { execSync } from 'child_process';
|
|
11
14
|
import { readFileSync, readdirSync, writeFileSync, mkdirSync, existsSync, statSync } from 'fs';
|
|
12
15
|
import { fileURLToPath } from 'url';
|
|
@@ -7453,6 +7456,135 @@ docs
|
|
|
7453
7456
|
handleScopedError(err);
|
|
7454
7457
|
}
|
|
7455
7458
|
});
|
|
7459
|
+
// ── Spec-artifact validation (TOOL-1) ──────────────────────────────────
|
|
7460
|
+
//
|
|
7461
|
+
// Two top-level groups:
|
|
7462
|
+
// fazemos yaml validate <path> Generic YAML parse check
|
|
7463
|
+
// fazemos manifest validate <path> Architecture Design Manifest check
|
|
7464
|
+
//
|
|
7465
|
+
// Both support --json for structured output. Exit code 1 on errors, 0 on
|
|
7466
|
+
// warnings/info only. See CLAUDE.md "TOOL-1" for scope.
|
|
7467
|
+
const yamlGroup = program.command('yaml').description('Generic YAML file utilities. Offline; no API calls. Use as a cheap ' +
|
|
7468
|
+
'author-time or CI gate before committing a YAML file or feeding it into ' +
|
|
7469
|
+
'another tool. For Architecture Design Manifests, prefer `fazemos manifest ' +
|
|
7470
|
+
'validate` instead — it runs YAML parse plus structural and custom checks.');
|
|
7471
|
+
yamlGroup
|
|
7472
|
+
.command('validate')
|
|
7473
|
+
.description('Parse a YAML file and report parse errors with line/column. Cheap pre-commit / CI gate.')
|
|
7474
|
+
.argument('<path>', 'Path to a YAML file (.yaml or .yml). Absolute or relative to CWD.')
|
|
7475
|
+
.option('--json', 'Emit structured JSON output instead of human-readable text. See --help for shape.')
|
|
7476
|
+
.addHelpText('after', `
|
|
7477
|
+
Rule slugs (stable; safe to grep in --json output):
|
|
7478
|
+
read.not_found File at <path> does not exist
|
|
7479
|
+
read.not_a_file <path> exists but is not a regular file
|
|
7480
|
+
read.io_error Read failed (permissions, etc.)
|
|
7481
|
+
yaml.parse_error js-yaml could not parse the document
|
|
7482
|
+
|
|
7483
|
+
Exit codes:
|
|
7484
|
+
0 YAML parsed cleanly (no error findings)
|
|
7485
|
+
1 Parse or read error finding present
|
|
7486
|
+
|
|
7487
|
+
JSON output shape (--json):
|
|
7488
|
+
{
|
|
7489
|
+
"source": "<path>",
|
|
7490
|
+
"ok": true | false,
|
|
7491
|
+
"summary": { "errors": <int>, "warnings": <int>, "infos": <int> },
|
|
7492
|
+
"findings": [
|
|
7493
|
+
{ "severity": "error"|"warning"|"info",
|
|
7494
|
+
"rule": "<rule-slug>",
|
|
7495
|
+
"message": "<human-readable>",
|
|
7496
|
+
"path": "<dotted-path-into-doc>", // optional
|
|
7497
|
+
"line": <int>, // optional (parse errors)
|
|
7498
|
+
"column": <int> } // optional (parse errors)
|
|
7499
|
+
]
|
|
7500
|
+
}
|
|
7501
|
+
|
|
7502
|
+
Examples:
|
|
7503
|
+
fazemos yaml validate ./config.yaml
|
|
7504
|
+
fazemos yaml validate ./fixture.yml --json | jq '.findings[]'`)
|
|
7505
|
+
.action((path, opts) => {
|
|
7506
|
+
const result = loadYaml(path);
|
|
7507
|
+
const summary = summarize(result.findings);
|
|
7508
|
+
if (opts.json)
|
|
7509
|
+
printJson(result.source, result.findings, summary);
|
|
7510
|
+
else
|
|
7511
|
+
printFindings(result.source, result.findings, summary);
|
|
7512
|
+
if (summary.errors > 0)
|
|
7513
|
+
process.exit(1);
|
|
7514
|
+
});
|
|
7515
|
+
const manifestGroup = program.command('manifest').description('Architecture Design Manifest validation (the YAML companion to a tech ' +
|
|
7516
|
+
'spec, conventionally at specs/tech/<area>/<feature>-manifest.yaml). ' +
|
|
7517
|
+
'Offline; no API calls. Designed to run at three points: (1) author time ' +
|
|
7518
|
+
'before commit, (2) in agent prompts (Marco / Dex) before review tokens ' +
|
|
7519
|
+
'are spent, (3) in CI on PRs touching a manifest. Catches the recurring ' +
|
|
7520
|
+
'miss class — audit-summary math, AC traceability, duplicate IDs — ' +
|
|
7521
|
+
'without a workspace-level toolchain.');
|
|
7522
|
+
manifestGroup
|
|
7523
|
+
.command('validate')
|
|
7524
|
+
.description('Validate an Architecture Design Manifest: YAML parse + structural schema + custom checks (audit math, AC traceability, duplicate IDs).')
|
|
7525
|
+
.argument('<path>', 'Path to a manifest YAML file. Convention: specs/tech/<area>/<feature>-manifest.yaml inside a workspace clone. Absolute paths also work.')
|
|
7526
|
+
.option('--json', 'Emit structured JSON output instead of human-readable text. See --help for shape.')
|
|
7527
|
+
.addHelpText('after', `
|
|
7528
|
+
What runs (in order):
|
|
7529
|
+
1. js-yaml parse (rule slug: yaml.parse_error)
|
|
7530
|
+
2. permissive structural schema (ajv) (rule slugs: schema.*)
|
|
7531
|
+
3. custom checks (rule slugs: audit.* / trace.* / ids.* / manifest.*)
|
|
7532
|
+
|
|
7533
|
+
The schema requires feature.id + feature.title. It accepts both manifest shape
|
|
7534
|
+
variants seen in the wild — entry_points as map ({added, modified, replaced,
|
|
7535
|
+
unchanged, audit_summary}) OR as a flat list, traceability values as string OR
|
|
7536
|
+
object OR array-of-rows, rules as map OR array-of-{id, ...} entries.
|
|
7537
|
+
|
|
7538
|
+
Rule slugs (stable; safe to grep in --json output):
|
|
7539
|
+
audit.sum_mismatch audit_summary.{added+modified+replaced+unchanged} ≠ total_in_this_manifest
|
|
7540
|
+
audit.count_vs_array audit_summary declared count ≠ actual entry_points.<bucket>[] length
|
|
7541
|
+
trace.ac_task_missing traceability.AC*.task references a task ID with no match in implementation.*.tasks
|
|
7542
|
+
(recurses through implementation.sequencing.* and similar nested buckets)
|
|
7543
|
+
ids.duplicate_task same task id in multiple implementation buckets
|
|
7544
|
+
ids.duplicate duplicate id in edge_cases / risks / phase_1_gate.questions
|
|
7545
|
+
helper.callers_duplicate (warning) api.internal_helper.callers lists same caller twice
|
|
7546
|
+
schema.required required field missing (feature.id, feature.title)
|
|
7547
|
+
schema.type / schema.anyOf value has wrong shape
|
|
7548
|
+
manifest.version_missing (warning) add manifest_version: "0.1" at the top of the file
|
|
7549
|
+
manifest.version_unknown (warning) manifest_version is newer than this CLI knows; checks still ran
|
|
7550
|
+
manifest.shape root is not a YAML mapping
|
|
7551
|
+
read.not_found / read.io_error / yaml.parse_error file-level failures
|
|
7552
|
+
|
|
7553
|
+
Exit codes:
|
|
7554
|
+
0 no error-severity findings (warnings + infos OK)
|
|
7555
|
+
1 any error-severity finding present
|
|
7556
|
+
|
|
7557
|
+
JSON output shape (--json) — same as \`yaml validate --json\`:
|
|
7558
|
+
{
|
|
7559
|
+
"source": "<path>",
|
|
7560
|
+
"ok": true | false,
|
|
7561
|
+
"summary": { "errors": <int>, "warnings": <int>, "infos": <int> },
|
|
7562
|
+
"findings": [ { "severity", "rule", "message", "path"?, "line"?, "column"? } ]
|
|
7563
|
+
}
|
|
7564
|
+
|
|
7565
|
+
When to invoke:
|
|
7566
|
+
- Author time, before commit (catches the recurring miss class earliest)
|
|
7567
|
+
- Inside Marco / Dex agent prompts before review tokens are spent
|
|
7568
|
+
- In CI on any PR that touches a *-manifest.yaml file
|
|
7569
|
+
|
|
7570
|
+
Examples:
|
|
7571
|
+
fazemos manifest validate ./specs/tech/platform/I45-...-manifest.yaml
|
|
7572
|
+
fazemos manifest validate ./manifest.yaml --json | jq '.findings | map(select(.severity=="error"))'
|
|
7573
|
+
fazemos manifest validate ./manifest.yaml --json | jq -r '.findings[] | "\\(.severity) \\(.rule) \\(.message)"'`)
|
|
7574
|
+
.action((path, opts) => {
|
|
7575
|
+
const loaded = loadYaml(path);
|
|
7576
|
+
const findings = [...loaded.findings];
|
|
7577
|
+
if (loaded.ok) {
|
|
7578
|
+
findings.push(...validateManifest(loaded.value));
|
|
7579
|
+
}
|
|
7580
|
+
const summary = summarize(findings);
|
|
7581
|
+
if (opts.json)
|
|
7582
|
+
printJson(loaded.source, findings, summary);
|
|
7583
|
+
else
|
|
7584
|
+
printFindings(loaded.source, findings, summary);
|
|
7585
|
+
if (summary.errors > 0)
|
|
7586
|
+
process.exit(1);
|
|
7587
|
+
});
|
|
7456
7588
|
// Skip auto-parse only when running under Vitest (which sets process.env.VITEST).
|
|
7457
7589
|
// Tests import `program` and drive it via `program.parseAsync(...)` after mocking
|
|
7458
7590
|
// `./api.js`. In every other context — direct invocation, npx tsx, OR the bin
|