archgen-skill 0.0.5 → 0.0.7
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 +20 -7
- package/bin/archgen.mjs +142 -15
- package/lib/block.js +52 -11
- package/lib/bootstrap.js +107 -0
- package/lib/config.js +3 -2
- package/lib/doctor.js +197 -77
- package/lib/init.js +77 -19
- package/lib/install.js +79 -7
- package/lib/mcp-server.js +241 -0
- package/lib/migrations/001-stamp-provenance.mjs +15 -8
- package/lib/migrations/index.mjs +19 -8
- package/lib/restore.js +52 -16
- package/lib/spawn-npm.js +99 -0
- package/lib/store.js +175 -28
- package/lib/uninstall-project.js +38 -7
- package/lib/version.js +20 -4
- package/package.json +3 -3
- package/vendor/skills/archgen/SKILL.md +363 -213
- package/vendor/skills/archgen/archgen.config.json +1 -1
- package/vendor/skills/archgen/references/code-standards.md +2 -2
- package/vendor/skills/archgen/assets/mcp-template.json +0 -42
- package/vendor/skills/archgen/assets/skill-registry.json +0 -24
- package/vendor/skills/archgen/references/artifact-templates.md +0 -406
- package/vendor/skills/archgen/references/interview.md +0 -275
- package/vendor/skills/archgen/references/mcp-and-skills.md +0 -117
- package/vendor/skills/archgen/references/orchestration.md +0 -292
- package/vendor/skills/archgen/references/platforms.md +0 -138
- package/vendor/skills/archgen/scripts/doc-index.mjs +0 -457
- package/vendor/skills/archgen/scripts/impact.mjs +0 -77
- package/vendor/skills/archgen/scripts/lib/graph.mjs +0 -222
- package/vendor/skills/archgen/scripts/lib/plan-coverage.mjs +0 -32
- package/vendor/skills/archgen/scripts/lib/scope.mjs +0 -65
- package/vendor/skills/archgen/scripts/lib/yaml.mjs +0 -492
- package/vendor/skills/archgen/scripts/next-tasks.mjs +0 -55
- package/vendor/skills/archgen/scripts/plan-graph.mjs +0 -329
- package/vendor/skills/archgen/scripts/set-status.mjs +0 -82
- package/vendor/skills/archgen/scripts/update-agents.mjs +0 -213
- package/vendor/skills/archgen/scripts/validate.mjs +0 -87
- package/vendor/skills/archgen/scripts/verify-plan.mjs +0 -70
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// validate.mjs — schema/link/cycle validator for tasks.yaml.
|
|
3
|
-
// Usage: node validate.mjs <tasks.yaml> [--plan <plans-dir>]
|
|
4
|
-
// Semantics: warnings -> stderr, exit 0. Errors -> stderr, exit 1 (itemized).
|
|
5
|
-
// --plan delegates cross-checking to verify-plan.mjs internals (single impl).
|
|
6
|
-
import { readFileSync, readdirSync } from 'node:fs';
|
|
7
|
-
import { join } from 'node:path';
|
|
8
|
-
import { parseYaml } from './lib/yaml.mjs';
|
|
9
|
-
import { buildGraph, findCycle, GraphError } from './lib/graph.mjs';
|
|
10
|
-
|
|
11
|
-
const STATUSES = ['pending', 'ready', 'running', 'blocked', 'done', 'failed'];
|
|
12
|
-
const REQUIRED = ['id', 'title', 'file_ownership'];
|
|
13
|
-
const KNOWN_KEYS = new Set(['id', 'title', 'status', 'depends_on', 'parallel_group', 'file_ownership', 'artifacts', 'acceptance']);
|
|
14
|
-
// Mirrors schemas/tasks.schema.json `id.pattern` — ids are strings, never
|
|
15
|
-
// numeric-coerced scalars, or set-status/next-tasks cannot address the task.
|
|
16
|
-
const ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
17
|
-
|
|
18
|
-
const [, , file, ...rest] = process.argv;
|
|
19
|
-
if (!file) { console.error('usage: validate.mjs <tasks.yaml> [--plan <dir>]'); process.exit(1); }
|
|
20
|
-
const planDirIdx = rest.indexOf('--plan');
|
|
21
|
-
if (planDirIdx >= 0 && rest[planDirIdx + 1] === undefined) {
|
|
22
|
-
// '--plan' with no value must never silently skip coverage checks.
|
|
23
|
-
console.error('usage: validate.mjs <tasks.yaml> [--plan <dir>] (--plan requires a directory value)');
|
|
24
|
-
process.exit(2);
|
|
25
|
-
}
|
|
26
|
-
const planDir = planDirIdx >= 0 ? rest[planDirIdx + 1] : null;
|
|
27
|
-
|
|
28
|
-
/** @type {string[]} errors */ const errors = [];
|
|
29
|
-
/** @type {string[]} warnings */ const warnings = [];
|
|
30
|
-
|
|
31
|
-
let data;
|
|
32
|
-
try {
|
|
33
|
-
({ data } = parseYaml(readFileSync(file, 'utf8'), { filename: file }));
|
|
34
|
-
} catch (e) {
|
|
35
|
-
console.error(`error: ${e.message}`);
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
const tasks = data?.tasks;
|
|
39
|
-
if (!Array.isArray(tasks) || tasks.length === 0) {
|
|
40
|
-
console.error('error: missing or empty `tasks:` sequence');
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
// Root-level typo guard: e.g. `task:` instead of `tasks:` would otherwise
|
|
44
|
-
// silently produce the missing-tasks error with no hint as to why.
|
|
45
|
-
for (const k of Object.keys(data ?? {})) {
|
|
46
|
-
if (k !== 'tasks') warnings.push(`unknown root key '${k}' (expected only 'tasks')`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
for (const [i, t] of tasks.entries()) {
|
|
50
|
-
const where = `tasks[${i}] (${t?.id ?? '<no-id>'})`;
|
|
51
|
-
if (t === null || typeof t !== 'object' || Array.isArray(t)) { errors.push(`${where}: not a mapping`); continue; }
|
|
52
|
-
for (const k of Object.keys(t)) if (!KNOWN_KEYS.has(k)) warnings.push(`${where}: unknown key '${k}' (ignored)`);
|
|
53
|
-
// Required means present AND non-empty: `id:` parses to null, which is not
|
|
54
|
-
// undefined — an explicit empty value must fail, not bypass the check.
|
|
55
|
-
for (const r of REQUIRED) {
|
|
56
|
-
const v = t[r];
|
|
57
|
-
if (v === undefined) errors.push(`${where}: missing required field '${r}'`);
|
|
58
|
-
else if (v === null || (typeof v === 'string' && v.trim() === '')) errors.push(`${where}: required field '${r}' is empty`);
|
|
59
|
-
}
|
|
60
|
-
if (t.id !== undefined && t.id !== null && (typeof t.id !== 'string' || !ID_PATTERN.test(t.id))) {
|
|
61
|
-
errors.push(`${where}: invalid id '${t.id}' (must be a string matching ^[A-Za-z0-9][A-Za-z0-9._-]*$)`);
|
|
62
|
-
}
|
|
63
|
-
if (t.status !== undefined && !STATUSES.includes(t.status)) errors.push(`${where}: invalid status '${t.status}' (allowed: ${STATUSES.join('|')})`);
|
|
64
|
-
if (Array.isArray(t.file_ownership) && t.file_ownership.length === 0) errors.push(`${where}: file_ownership must be non-empty`);
|
|
65
|
-
if (t.depends_on !== undefined && !Array.isArray(t.depends_on)) errors.push(`${where}: depends_on must be a list`);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const { prerequisites } = buildGraph(tasks);
|
|
70
|
-
const cycle = findCycle(prerequisites);
|
|
71
|
-
if (cycle.length) errors.push(`dependency cycle: ${cycle.join(' -> ')}`);
|
|
72
|
-
} catch (e) {
|
|
73
|
-
if (e instanceof GraphError) errors.push(e.message);
|
|
74
|
-
else throw e;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (planDir) {
|
|
78
|
-
// Delegate plan<->task coverage checks to the single shared implementation.
|
|
79
|
-
const { checkPlanCoverage } = await import('./lib/plan-coverage.mjs');
|
|
80
|
-
const findings = checkPlanCoverage(planDir, tasks);
|
|
81
|
-
for (const f of findings) errors.push(f);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
for (const w of warnings) console.error(`warn: ${w}`);
|
|
85
|
-
for (const e of errors) console.error(`error: ${e}`);
|
|
86
|
-
if (errors.length) process.exit(1);
|
|
87
|
-
console.error(`ok: ${tasks.length} tasks valid${warnings.length ? ` (${warnings.length} warning(s))` : ''}`);
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// verify-plan.mjs — the VERIFIER gate as an executable check.
|
|
3
|
-
// Usage: node verify-plan.mjs <tasks.yaml> --plan <plans-dir>
|
|
4
|
-
// stdout: JSON {verdict: "APPROVE"|"ISSUES", issues:[...]}
|
|
5
|
-
// Exit: 0 APPROVE · 1 ISSUES. Structural checks only (the agent-side verifier
|
|
6
|
-
// adds judgment on top; this script is the deterministic floor).
|
|
7
|
-
import { readFileSync } from 'node:fs';
|
|
8
|
-
import { parseYaml } from './lib/yaml.mjs';
|
|
9
|
-
import { buildGraph, findCycle, computeWaves, findOwnershipConflict, GraphError } from './lib/graph.mjs';
|
|
10
|
-
import { checkPlanCoverage } from './lib/plan-coverage.mjs';
|
|
11
|
-
|
|
12
|
-
const [, , file, ...rest] = process.argv;
|
|
13
|
-
const planIdx = rest.indexOf('--plan');
|
|
14
|
-
if (!file || planIdx < 0 || !rest[planIdx + 1]) {
|
|
15
|
-
console.error('usage: verify-plan.mjs <tasks.yaml> --plan <plans-dir>');
|
|
16
|
-
process.exit(2);
|
|
17
|
-
}
|
|
18
|
-
const planDir = rest[planIdx + 1];
|
|
19
|
-
const issues = [];
|
|
20
|
-
|
|
21
|
-
let data;
|
|
22
|
-
try {
|
|
23
|
-
({ data } = parseYaml(readFileSync(file, 'utf8'), { filename: file }));
|
|
24
|
-
} catch (e) {
|
|
25
|
-
console.log(JSON.stringify({ verdict: 'ISSUES', issues: [`unparseable tasks.yaml: ${e.message}`] }, null, 2));
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
const tasks = data?.tasks;
|
|
29
|
-
if (!Array.isArray(tasks)) { issues.push('missing tasks: sequence'); }
|
|
30
|
-
|
|
31
|
-
if (issues.length === 0) {
|
|
32
|
-
try {
|
|
33
|
-
const { byId, prerequisites } = buildGraph(tasks);
|
|
34
|
-
const cycle = findCycle(prerequisites);
|
|
35
|
-
if (cycle.length) issues.push(`dependency cycle: ${cycle.join(' -> ')}`);
|
|
36
|
-
|
|
37
|
-
// Every task must carry acceptance criteria — the verifier refuses work
|
|
38
|
-
// that cannot be objectively checked later. Entries must be non-blank
|
|
39
|
-
// strings: whitespace-only or empty entries are placeholders, not criteria.
|
|
40
|
-
for (const t of tasks) {
|
|
41
|
-
if (!Array.isArray(t.acceptance) || t.acceptance.length === 0) {
|
|
42
|
-
issues.push(`task '${t.id}' has no acceptance criteria`);
|
|
43
|
-
} else if (t.acceptance.some((a) => typeof a !== 'string' || a.trim() === '')) {
|
|
44
|
-
issues.push(`task '${t.id}' has empty or whitespace-only acceptance criteria`);
|
|
45
|
-
}
|
|
46
|
-
if (!Array.isArray(t.file_ownership) || t.file_ownership.length === 0) {
|
|
47
|
-
issues.push(`task '${t.id}' has empty file_ownership`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (!issues.length) {
|
|
52
|
-
const { waves } = computeWaves(byId, prerequisites);
|
|
53
|
-
for (const wave of waves) {
|
|
54
|
-
const conflict = findOwnershipConflict(wave);
|
|
55
|
-
if (conflict) issues.push(`same-wave ownership overlap: '${conflict.glob}' on '${conflict.a}' and '${conflict.b}'`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
issues.push(...checkPlanCoverage(planDir, tasks));
|
|
60
|
-
} catch (e) {
|
|
61
|
-
// Exotic inputs must surface as a clean ISSUES verdict, never a raw stack:
|
|
62
|
-
// the verifier is a gate, and gates report findings, not crashes.
|
|
63
|
-
if (e instanceof GraphError) issues.push(e.message);
|
|
64
|
-
else issues.push(`internal check failure: ${e?.message ?? String(e)}`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const verdict = issues.length ? 'ISSUES' : 'APPROVE';
|
|
69
|
-
console.log(JSON.stringify({ verdict, issues }, null, 2));
|
|
70
|
-
process.exit(verdict === 'APPROVE' ? 0 : 1);
|