archgen-skill 0.0.2 → 0.0.4

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.
Files changed (34) hide show
  1. package/README.md +29 -3
  2. package/bin/archgen.mjs +146 -12
  3. package/lib/block.js +197 -8
  4. package/lib/config.js +96 -0
  5. package/lib/doctor.js +283 -0
  6. package/lib/init.js +130 -27
  7. package/lib/install.js +105 -14
  8. package/lib/migrate.js +88 -0
  9. package/lib/migrations/001-stamp-provenance.mjs +77 -0
  10. package/lib/migrations/index.mjs +170 -0
  11. package/lib/restore.js +239 -0
  12. package/lib/store.js +161 -0
  13. package/lib/uninstall-project.js +95 -0
  14. package/lib/version-stamp.js +47 -0
  15. package/lib/version.js +23 -0
  16. package/package.json +37 -9
  17. package/vendor/skills/archgen/SKILL.md +252 -73
  18. package/vendor/skills/archgen/archgen.config.json +16 -0
  19. package/vendor/skills/archgen/assets/skill-registry.json +4 -3
  20. package/vendor/skills/archgen/references/artifact-templates.md +251 -7
  21. package/vendor/skills/archgen/references/interview.md +180 -16
  22. package/vendor/skills/archgen/references/mcp-and-skills.md +88 -17
  23. package/vendor/skills/archgen/references/orchestration.md +102 -39
  24. package/vendor/skills/archgen/references/platforms.md +18 -14
  25. package/vendor/skills/archgen/scripts/doc-index.mjs +457 -0
  26. package/vendor/skills/archgen/scripts/impact.mjs +14 -1
  27. package/vendor/skills/archgen/scripts/lib/graph.mjs +82 -3
  28. package/vendor/skills/archgen/scripts/lib/scope.mjs +65 -0
  29. package/vendor/skills/archgen/scripts/lib/yaml.mjs +19 -3
  30. package/vendor/skills/archgen/scripts/plan-graph.mjs +329 -0
  31. package/vendor/skills/archgen/scripts/set-status.mjs +26 -6
  32. package/vendor/skills/archgen/scripts/update-agents.mjs +204 -0
  33. package/vendor/skills/archgen/scripts/validate.mjs +18 -1
  34. package/vendor/skills/archgen/scripts/verify-plan.mjs +8 -3
@@ -11,10 +11,18 @@ import { buildGraph, findCycle, GraphError } from './lib/graph.mjs';
11
11
  const STATUSES = ['pending', 'ready', 'running', 'blocked', 'done', 'failed'];
12
12
  const REQUIRED = ['id', 'title', 'file_ownership'];
13
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._-]*$/;
14
17
 
15
18
  const [, , file, ...rest] = process.argv;
16
19
  if (!file) { console.error('usage: validate.mjs <tasks.yaml> [--plan <dir>]'); process.exit(1); }
17
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
+ }
18
26
  const planDir = planDirIdx >= 0 ? rest[planDirIdx + 1] : null;
19
27
 
20
28
  /** @type {string[]} errors */ const errors = [];
@@ -42,7 +50,16 @@ for (const [i, t] of tasks.entries()) {
42
50
  const where = `tasks[${i}] (${t?.id ?? '<no-id>'})`;
43
51
  if (t === null || typeof t !== 'object' || Array.isArray(t)) { errors.push(`${where}: not a mapping`); continue; }
44
52
  for (const k of Object.keys(t)) if (!KNOWN_KEYS.has(k)) warnings.push(`${where}: unknown key '${k}' (ignored)`);
45
- for (const r of REQUIRED) if (t[r] === undefined) errors.push(`${where}: missing required field '${r}'`);
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
+ }
46
63
  if (t.status !== undefined && !STATUSES.includes(t.status)) errors.push(`${where}: invalid status '${t.status}' (allowed: ${STATUSES.join('|')})`);
47
64
  if (Array.isArray(t.file_ownership) && t.file_ownership.length === 0) errors.push(`${where}: file_ownership must be non-empty`);
48
65
  if (t.depends_on !== undefined && !Array.isArray(t.depends_on)) errors.push(`${where}: depends_on must be a list`);
@@ -11,7 +11,7 @@ import { checkPlanCoverage } from './lib/plan-coverage.mjs';
11
11
 
12
12
  const [, , file, ...rest] = process.argv;
13
13
  const planIdx = rest.indexOf('--plan');
14
- if (!file || planIdx < 0) {
14
+ if (!file || planIdx < 0 || !rest[planIdx + 1]) {
15
15
  console.error('usage: verify-plan.mjs <tasks.yaml> --plan <plans-dir>');
16
16
  process.exit(2);
17
17
  }
@@ -35,10 +35,13 @@ if (issues.length === 0) {
35
35
  if (cycle.length) issues.push(`dependency cycle: ${cycle.join(' -> ')}`);
36
36
 
37
37
  // Every task must carry acceptance criteria — the verifier refuses work
38
- // that cannot be objectively checked later.
38
+ // that cannot be objectively checked later. Entries must be non-blank
39
+ // strings: whitespace-only or empty entries are placeholders, not criteria.
39
40
  for (const t of tasks) {
40
41
  if (!Array.isArray(t.acceptance) || t.acceptance.length === 0) {
41
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`);
42
45
  }
43
46
  if (!Array.isArray(t.file_ownership) || t.file_ownership.length === 0) {
44
47
  issues.push(`task '${t.id}' has empty file_ownership`);
@@ -55,8 +58,10 @@ if (issues.length === 0) {
55
58
 
56
59
  issues.push(...checkPlanCoverage(planDir, tasks));
57
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.
58
63
  if (e instanceof GraphError) issues.push(e.message);
59
- else throw e;
64
+ else issues.push(`internal check failure: ${e?.message ?? String(e)}`);
60
65
  }
61
66
  }
62
67