ai-developer-skill-os 10.1.0 → 10.2.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.
Files changed (98) hide show
  1. package/.agents/AGENTS.md +13 -0
  2. package/.agents/LICENSE +21 -21
  3. package/.agents/docs/ARCHITECTURE.md +56 -120
  4. package/.agents/docs/GOVERNANCE.md +3 -3
  5. package/.agents/docs/SPEC.md +137 -60
  6. package/.agents/docs/VERSIONING.md +25 -57
  7. package/.agents/docs/adr/0005-v10-platform-consolidation.md +50 -0
  8. package/.agents/docs/schemas/learning.schema.yml +22 -57
  9. package/.agents/docs/schemas/skill.schema.yml +116 -161
  10. package/.agents/docs/schemas/workflow.schema.yml +51 -26
  11. package/.agents/docs/skill-classification.md +1 -1
  12. package/.agents/registry/graph.json +171 -77
  13. package/.agents/registry/index.yaml +68 -35
  14. package/.agents/rules/coding.md +1 -1
  15. package/.agents/rules/command-safety.md +1 -1
  16. package/.agents/rules/global.md +1 -1
  17. package/.agents/rules/prompt-compiler.md +1 -1
  18. package/.agents/rules/safety.md +1 -1
  19. package/.agents/rules/security.md +1 -1
  20. package/.agents/rules/skill-quality.md +18 -3
  21. package/.agents/skills/_template/SKILL.md +238 -88
  22. package/.agents/skills/qk-api-data-discovery/SKILL.md +176 -190
  23. package/.agents/skills/qk-api-data-discovery/references/bronze-record-format.md +47 -0
  24. package/.agents/skills/qk-api-data-discovery/references/data-contract-yaml.md +72 -0
  25. package/.agents/skills/qk-api-data-discovery/references/discovery-report-template.md +110 -0
  26. package/.agents/skills/qk-backend-data/SKILL.md +150 -1
  27. package/.agents/skills/qk-bug-resolution/SKILL.md +153 -1
  28. package/.agents/skills/qk-bug-resolution/evals/scorecard.yaml +1 -1
  29. package/.agents/skills/qk-code-cleaner/SKILL.md +150 -1
  30. package/.agents/skills/qk-code-review/SKILL.md +195 -1
  31. package/.agents/skills/qk-code-review/evals/scorecard.yaml +1 -1
  32. package/.agents/skills/qk-code-review/references/ai/{v8-schema-validation.md → schema-validation.md} +2 -2
  33. package/.agents/skills/qk-code-review/references/cross-cutting/async-concurrency-patterns.md +515 -515
  34. package/.agents/skills/qk-code-review/references/cross-cutting/error-handling-principles.md +492 -492
  35. package/.agents/skills/qk-code-review/references/cross-cutting/n-plus-one-queries.md +309 -309
  36. package/.agents/skills/qk-code-review/references/cross-cutting/sql-injection-prevention.md +307 -307
  37. package/.agents/skills/qk-code-review/references/cross-cutting/xss-prevention.md +263 -263
  38. package/.agents/skills/qk-code-review/references/languages/angular.md +768 -768
  39. package/.agents/skills/qk-code-review/references/languages/c.md +890 -890
  40. package/.agents/skills/qk-code-review/references/languages/cpp.md +893 -893
  41. package/.agents/skills/qk-code-review/references/languages/css-less-sass.md +661 -661
  42. package/.agents/skills/qk-code-review/references/languages/django.md +985 -985
  43. package/.agents/skills/qk-code-review/references/languages/fastapi.md +580 -580
  44. package/.agents/skills/qk-code-review/references/languages/go.md +993 -993
  45. package/.agents/skills/qk-code-review/references/languages/java.md +409 -409
  46. package/.agents/skills/qk-code-review/references/languages/java8.md +586 -586
  47. package/.agents/skills/qk-code-review/references/languages/kotlin.md +1018 -1018
  48. package/.agents/skills/qk-code-review/references/languages/nestjs.md +593 -593
  49. package/.agents/skills/qk-code-review/references/languages/php.md +684 -684
  50. package/.agents/skills/qk-code-review/references/languages/python.md +1073 -1073
  51. package/.agents/skills/qk-code-review/references/languages/qt.md +757 -757
  52. package/.agents/skills/qk-code-review/references/languages/react.md +871 -871
  53. package/.agents/skills/qk-code-review/references/languages/ruby.md +964 -964
  54. package/.agents/skills/qk-code-review/references/languages/rust.md +846 -846
  55. package/.agents/skills/qk-code-review/references/languages/svelte.md +1064 -1064
  56. package/.agents/skills/qk-code-review/references/languages/swift.md +936 -936
  57. package/.agents/skills/qk-code-review/references/languages/typescript.md +1016 -1016
  58. package/.agents/skills/qk-code-review/references/languages/vue.md +924 -924
  59. package/.agents/skills/qk-code-review/references/languages/zig.md +440 -440
  60. package/.agents/skills/qk-devops-release/SKILL.md +150 -1
  61. package/.agents/skills/qk-feature-delivery/SKILL.md +153 -1
  62. package/.agents/skills/qk-feature-delivery/evals/scorecard.yaml +1 -1
  63. package/.agents/skills/qk-orchestrator/SKILL.md +187 -2
  64. package/.agents/skills/qk-orchestrator/evals/scorecard.yaml +1 -1
  65. package/.agents/skills/qk-orchestrator/references/routing-table.md +1 -1
  66. package/.agents/skills/qk-product-spec/SKILL.md +150 -1
  67. package/.agents/skills/qk-prompt-compiler/SKILL.md +188 -65
  68. package/.agents/skills/qk-ui-engineer/SKILL.md +150 -1
  69. package/.agents/workflows/_schema.yml +146 -146
  70. package/.agents/workflows/bug-resolution.yml +155 -121
  71. package/.agents/workflows/code-review.yml +127 -93
  72. package/.agents/workflows/context-discovery.yml +128 -94
  73. package/.agents/workflows/documentation.yml +124 -90
  74. package/.agents/workflows/feature-delivery.yml +158 -124
  75. package/.agents/workflows/production-release.yml +207 -173
  76. package/.agents/workflows/prompt-compilation.yml +126 -0
  77. package/.agents/workflows/refactor.yml +136 -102
  78. package/.agents/workflows/security-audit.yml +149 -115
  79. package/.agents/workflows/shared/quality-gate.yml +3 -1
  80. package/.agents/workflows/skin-governance.yml +149 -115
  81. package/.agents/workflows/spec-driven-development.yml +78 -71
  82. package/CHANGELOG.md +61 -0
  83. package/README.md +3 -3
  84. package/bin/install.js +38 -51
  85. package/package.json +68 -74
  86. package/tooling/build-registry.js +226 -208
  87. package/tooling/run-aar.js +55 -126
  88. package/tooling/sync-versions.js +2 -2
  89. package/tooling/validate-graph.js +100 -87
  90. package/tooling/validate-skills.js +32 -14
  91. package/.agents/docs/decisions/ADR-001-v8-migration.md +0 -58
  92. package/.agents/docs/decisions/ADR-002-workflow-separation.md +0 -50
  93. package/.agents/docs/decisions/ADR-003-registry-generated.md +0 -54
  94. package/.agents/docs/decisions/ADR-008-skill-boundary-review.md +0 -27
  95. package/.agents/registry/capability-graph.yml +0 -390
  96. package/.agents/registry/skills-index.yml +0 -305
  97. package/.agents/skills/_template/capability.yaml +0 -34
  98. package/.agents/skills/_template/evals/scorecard.yaml +0 -19
@@ -1,208 +1,226 @@
1
- /**
2
- * build-registry.js
3
- * V8.2 Governed Capability Metadata & Eval Platform
4
- * Generates registry/index.yaml (lightweight lookup) and registry/graph.json (O(1) graph & cycle check)
5
- *
6
- * Usage: node tooling/build-registry.js
7
- */
8
-
9
- import fs from 'fs';
10
- import path from 'path';
11
- import { fileURLToPath } from 'url';
12
- import yaml from 'js-yaml';
13
-
14
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
-
16
- const SKILLS_DIR = path.join(__dirname, '../.agents/skills');
17
- const REGISTRY_DIR = path.join(__dirname, '../.agents/registry');
18
- const INDEX_YAML = path.join(REGISTRY_DIR, 'index.yaml');
19
- const GRAPH_JSON = path.join(REGISTRY_DIR, 'graph.json');
20
-
21
- function parseYamlFile(filePath) {
22
- try {
23
- let content = fs.readFileSync(filePath, 'utf8');
24
- if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1);
25
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
26
- if (match) {
27
- return yaml.load(match[1]);
28
- }
29
- return yaml.load(content);
30
- } catch (e) {
31
- console.error(`[WARN] Failed to parse YAML from ${filePath}: ${e.message}`);
32
- return null;
33
- }
34
- }
35
-
36
- function buildRegistry() {
37
- console.log('🔄 Building V8.2 Governed Capability Registry & O(1) Runtime Graph...');
38
-
39
- if (!fs.existsSync(SKILLS_DIR)) {
40
- console.error('❌ Skills directory not found:', SKILLS_DIR);
41
- process.exit(1);
42
- }
43
-
44
- fs.mkdirSync(REGISTRY_DIR, { recursive: true });
45
-
46
- const dirs = fs.readdirSync(SKILLS_DIR).filter(d =>
47
- !d.startsWith('_') && fs.statSync(path.join(SKILLS_DIR, d)).isDirectory()
48
- );
49
-
50
- const capabilities = {};
51
- const adjacency = {};
52
- const reverseAdjacency = {};
53
-
54
- for (const dir of dirs) {
55
- const capYamlPath = path.join(SKILLS_DIR, dir, 'capability.yaml');
56
- const skillMdPath = path.join(SKILLS_DIR, dir, 'SKILL.md');
57
-
58
- let meta = null;
59
- let fromCapabilityYaml = false;
60
-
61
- let skillMdMeta = null;
62
- if (fs.existsSync(skillMdPath)) {
63
- skillMdMeta = parseYamlFile(skillMdPath);
64
- }
65
-
66
- if (fs.existsSync(capYamlPath)) {
67
- meta = parseYamlFile(capYamlPath);
68
- fromCapabilityYaml = true;
69
- } else if (skillMdMeta) {
70
- meta = skillMdMeta;
71
- }
72
-
73
- if (!meta) continue;
74
-
75
- const id = meta.id || meta.name || dir;
76
- const version = meta.version || skillMdMeta?.version || '8.2.0';
77
- const status = meta.status || skillMdMeta?.status || 'stable';
78
- const description = meta.description || skillMdMeta?.description || '';
79
-
80
- // Normalize tags
81
- let tags = meta.tags || meta.keywords || [];
82
- if (!Array.isArray(tags) || tags.length === 0) {
83
- // Derive simple tags from dir words
84
- tags = dir.replace(/^qk-/, '').split('-');
85
- }
86
-
87
- // Normalize dependencies
88
- let dependencies = meta.dependencies || [];
89
- if (!fromCapabilityYaml) {
90
- // Derive from V8.1 skill frontmatter decision_boundary / workflow
91
- const deps = new Set();
92
- if (meta.workflow && typeof meta.workflow === 'string') deps.add(meta.workflow);
93
- if (meta.decision_boundary?.delegates_to) {
94
- meta.decision_boundary.delegates_to.forEach(d => deps.add(d));
95
- }
96
- dependencies = Array.from(deps);
97
- }
98
-
99
- capabilities[id] = {
100
- path: `.agents/skills/${dir}`,
101
- version,
102
- status,
103
- tags,
104
- dependencies
105
- };
106
-
107
- adjacency[id] = dependencies;
108
- if (!reverseAdjacency[id]) reverseAdjacency[id] = [];
109
- }
110
-
111
- // Populate reverse adjacency for orphan detection
112
- for (const [id, deps] of Object.entries(adjacency)) {
113
- for (const dep of deps) {
114
- if (!reverseAdjacency[dep]) reverseAdjacency[dep] = [];
115
- reverseAdjacency[dep].push(id);
116
- }
117
- }
118
-
119
- // Detect cycles using DFS
120
- let hasCycles = false;
121
- const visited = {};
122
- const recursionStack = {};
123
-
124
- function detectCycle(node) {
125
- visited[node] = true;
126
- recursionStack[node] = true;
127
-
128
- const deps = adjacency[node] || [];
129
- for (const dep of deps) {
130
- if (!visited[dep]) {
131
- if (detectCycle(dep)) return true;
132
- } else if (recursionStack[dep]) {
133
- return true;
134
- }
135
- }
136
-
137
- recursionStack[node] = false;
138
- return false;
139
- }
140
-
141
- for (const node of Object.keys(adjacency)) {
142
- if (!visited[node]) {
143
- if (detectCycle(node)) {
144
- hasCycles = true;
145
- break;
146
- }
147
- }
148
- }
149
-
150
- // Build Graph JSON (O(1) Runtime lookups)
151
- const graphNodes = {};
152
- for (const [id, data] of Object.entries(capabilities)) {
153
- const dependents = reverseAdjacency[id] || [];
154
- const isOrphan = data.dependencies.length === 0 && dependents.length === 0 && id !== 'qk-orchestrator' && id !== 'qk-help';
155
-
156
- graphNodes[id] = {
157
- path: data.path,
158
- version: data.version,
159
- tags: data.tags,
160
- dependencies: data.dependencies,
161
- dependents,
162
- is_orphan: isOrphan
163
- };
164
- }
165
-
166
- const allIds = Object.keys(capabilities);
167
- const activeSkills = allIds.filter(id => capabilities[id].status === 'stable' || capabilities[id].status === 'experimental');
168
- const archivedSkills = allIds.filter(id => capabilities[id].status === 'archived');
169
- const relocatedSkills = allIds.filter(id => capabilities[id].status === 'relocated');
170
-
171
- const graphJsonData = {
172
- schema_version: 1,
173
- manifest_version: '8.2',
174
- generated_at: new Date().toISOString(),
175
- nodes: graphNodes,
176
- adjacency,
177
- stats: {
178
- total_scanned: allIds.length,
179
- active: activeSkills.length,
180
- archived: archivedSkills.length,
181
- relocated: relocatedSkills.length,
182
- has_cycles: hasCycles
183
- }
184
- };
185
-
186
- // Build Registry Index YAML (Lightweight lookup)
187
- const indexYamlContent = `# AUTO-GENERATED BY registry-builder (tooling/build-registry.js)\n# DO NOT EDIT MANUALLY. THIS IS A GENERATED RUNTIME ARTIFACT.\n` +
188
- yaml.dump({
189
- schema_version: 1,
190
- manifest_version: '8.2',
191
- generated_at: new Date().toISOString(),
192
- stats: {
193
- total_scanned: allIds.length,
194
- active: activeSkills.length,
195
- archived: archivedSkills.length,
196
- relocated: relocatedSkills.length
197
- },
198
- capabilities
199
- }, { indent: 2 });
200
-
201
- fs.writeFileSync(INDEX_YAML, indexYamlContent, 'utf8');
202
- fs.writeFileSync(GRAPH_JSON, JSON.stringify(graphJsonData, null, 2), 'utf8');
203
-
204
- console.log(`✅ Generated lightweight registry index at .agents/registry/index.yaml (${allIds.length} scanned: ${activeSkills.length} active, ${archivedSkills.length} archived, ${relocatedSkills.length} relocated)`);
205
- console.log(`✅ Generated O(1) adjacency graph at .agents/registry/graph.json (active: ${activeSkills.length}, has_cycles: ${hasCycles})`);
206
- }
207
-
208
- buildRegistry();
1
+ /**
2
+ * build-registry.js
3
+ * V10.2 Governed Capability Metadata & Eval Platform
4
+ * Generates registry/index.yaml (lightweight lookup) and registry/graph.json (O(1) graph & cycle check)
5
+ *
6
+ * Usage: node tooling/build-registry.js
7
+ */
8
+
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+ import { fileURLToPath } from 'url';
12
+ import yaml from 'js-yaml';
13
+
14
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
+
16
+ const SKILLS_DIR = path.join(__dirname, '../.agents/skills');
17
+ const REGISTRY_DIR = path.join(__dirname, '../.agents/registry');
18
+ const INDEX_YAML = path.join(REGISTRY_DIR, 'index.yaml');
19
+ const GRAPH_JSON = path.join(REGISTRY_DIR, 'graph.json');
20
+
21
+ function parseYamlFile(filePath) {
22
+ try {
23
+ let content = fs.readFileSync(filePath, 'utf8');
24
+ if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1);
25
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
26
+ if (match) {
27
+ return yaml.load(match[1]);
28
+ }
29
+ return yaml.load(content);
30
+ } catch (e) {
31
+ console.error(`[WARN] Failed to parse YAML from ${filePath}: ${e.message}`);
32
+ return null;
33
+ }
34
+ }
35
+
36
+ function buildRegistry() {
37
+ console.log('🔄 Building V10.2 Governed Capability Registry & O(1) Runtime Graph...');
38
+
39
+ if (!fs.existsSync(SKILLS_DIR)) {
40
+ console.error('❌ Skills directory not found:', SKILLS_DIR);
41
+ process.exit(1);
42
+ }
43
+
44
+ fs.mkdirSync(REGISTRY_DIR, { recursive: true });
45
+
46
+ const dirs = fs.readdirSync(SKILLS_DIR).filter(d =>
47
+ !d.startsWith('_') && fs.statSync(path.join(SKILLS_DIR, d)).isDirectory()
48
+ );
49
+
50
+ const capabilities = {};
51
+ const adjacency = {};
52
+ const reverseAdjacency = {};
53
+
54
+ for (const dir of dirs) {
55
+ const capYamlPath = path.join(SKILLS_DIR, dir, 'capability.yaml');
56
+ const skillMdPath = path.join(SKILLS_DIR, dir, 'SKILL.md');
57
+
58
+ let meta = null;
59
+ let fromCapabilityYaml = false;
60
+
61
+ let skillMdMeta = null;
62
+ if (fs.existsSync(skillMdPath)) {
63
+ skillMdMeta = parseYamlFile(skillMdPath);
64
+ }
65
+
66
+ if (fs.existsSync(capYamlPath)) {
67
+ meta = parseYamlFile(capYamlPath);
68
+ fromCapabilityYaml = true;
69
+ } else if (skillMdMeta) {
70
+ meta = skillMdMeta;
71
+ }
72
+
73
+ if (!meta) continue;
74
+
75
+ const id = meta.id || meta.name || dir;
76
+ const version = meta.version || skillMdMeta?.version || '8.2.0';
77
+ const status = meta.status || skillMdMeta?.status || 'stable';
78
+ const description = meta.description || skillMdMeta?.description || '';
79
+
80
+ // Normalize tags
81
+ let tags = meta.tags || meta.keywords || [];
82
+ if (!Array.isArray(tags) || tags.length === 0) {
83
+ // Derive simple tags from dir words
84
+ tags = dir.replace(/^qk-/, '').split('-');
85
+ }
86
+
87
+ // Normalize dependencies - only use explicit skill-to-skill dependencies
88
+ let dependencies = meta.dependencies || [];
89
+ if (!fromCapabilityYaml) {
90
+ // Only use explicit dependencies, not workflow names
91
+ // workflow field identifies the primary workflow, not a dependency
92
+ const extraDeps = meta.decision_boundary?.delegates_to || [];
93
+ if (extraDeps.length > 0) {
94
+ const depSet = new Set([...dependencies, ...extraDeps]);
95
+ dependencies = Array.from(depSet);
96
+ }
97
+ }
98
+
99
+ capabilities[id] = {
100
+ path: `.agents/skills/${dir}`,
101
+ version,
102
+ status,
103
+ platforms: meta.platforms || ["antigravity", "claude", "opencode"],
104
+ tags,
105
+ dependencies
106
+ };
107
+
108
+ adjacency[id] = dependencies;
109
+ if (!reverseAdjacency[id]) reverseAdjacency[id] = [];
110
+ }
111
+
112
+ // ── Hardcoded Cross-Skill Edges (V10.2) ─────
113
+ // Directed acyclic graph: qk-orchestrator is the root hub
114
+ // It routes to all other skills. No other skill routes back to it.
115
+ const crossEdges = {
116
+ 'qk-orchestrator': ['qk-prompt-compiler', 'qk-product-spec', 'qk-feature-delivery', 'qk-bug-resolution', 'qk-code-cleaner', 'qk-code-review', 'qk-ui-engineer', 'qk-backend-data', 'qk-devops-release', 'qk-api-data-discovery']
117
+ };
118
+
119
+ for (const [skillId, deps] of Object.entries(crossEdges)) {
120
+ if (adjacency[skillId]) {
121
+ adjacency[skillId] = [...new Set([...adjacency[skillId], ...deps])];
122
+ } else {
123
+ adjacency[skillId] = deps;
124
+ }
125
+ }
126
+
127
+ // Populate reverse adjacency for orphan detection
128
+ for (const [id, deps] of Object.entries(adjacency)) {
129
+ for (const dep of deps) {
130
+ if (!reverseAdjacency[dep]) reverseAdjacency[dep] = [];
131
+ reverseAdjacency[dep].push(id);
132
+ }
133
+ }
134
+
135
+ // Detect cycles using DFS
136
+ let hasCycles = false;
137
+ const visited = {};
138
+ const recursionStack = {};
139
+
140
+ function detectCycle(node) {
141
+ visited[node] = true;
142
+ recursionStack[node] = true;
143
+
144
+ const deps = adjacency[node] || [];
145
+ for (const dep of deps) {
146
+ if (!visited[dep]) {
147
+ if (detectCycle(dep)) return true;
148
+ } else if (recursionStack[dep]) {
149
+ return true;
150
+ }
151
+ }
152
+
153
+ recursionStack[node] = false;
154
+ return false;
155
+ }
156
+
157
+ for (const node of Object.keys(adjacency)) {
158
+ if (!visited[node]) {
159
+ if (detectCycle(node)) {
160
+ hasCycles = true;
161
+ break;
162
+ }
163
+ }
164
+ }
165
+
166
+ // Build Graph JSON (O(1) Runtime lookups)
167
+ const graphNodes = {};
168
+ for (const [id, data] of Object.entries(capabilities)) {
169
+ const dependents = reverseAdjacency[id] || [];
170
+ const isOrphan = data.dependencies.length === 0 && dependents.length === 0 && id !== 'qk-orchestrator' && id !== 'qk-help';
171
+
172
+ graphNodes[id] = {
173
+ path: data.path,
174
+ version: data.version,
175
+ platforms: data.platforms || ["antigravity", "claude", "opencode"],
176
+ tags: data.tags,
177
+ dependencies: data.dependencies,
178
+ dependents,
179
+ is_orphan: isOrphan
180
+ };
181
+ }
182
+
183
+ const allIds = Object.keys(capabilities);
184
+ const activeSkills = allIds.filter(id => capabilities[id].status === 'stable' || capabilities[id].status === 'experimental');
185
+ const archivedSkills = allIds.filter(id => capabilities[id].status === 'archived');
186
+ const relocatedSkills = allIds.filter(id => capabilities[id].status === 'relocated');
187
+
188
+ const graphJsonData = {
189
+ schema_version: 1,
190
+ manifest_version: '10.2',
191
+ generated_at: new Date().toISOString(),
192
+ nodes: graphNodes,
193
+ edges: Object.entries(adjacency).flatMap(([from, toList]) => toList.map(to => ({ from, to, relation: 'depends_on' }))),
194
+ adjacency,
195
+ stats: {
196
+ total_scanned: allIds.length,
197
+ active: activeSkills.length,
198
+ archived: archivedSkills.length,
199
+ relocated: relocatedSkills.length,
200
+ has_cycles: hasCycles
201
+ }
202
+ };
203
+
204
+ // Build Registry Index YAML (Lightweight lookup)
205
+ const indexYamlContent = `# AUTO-GENERATED BY registry-builder (tooling/build-registry.js)\n# DO NOT EDIT MANUALLY. THIS IS A GENERATED RUNTIME ARTIFACT.\n` +
206
+ yaml.dump({
207
+ schema_version: 1,
208
+ manifest_version: '10.2',
209
+ generated_at: new Date().toISOString(),
210
+ stats: {
211
+ total_scanned: allIds.length,
212
+ active: activeSkills.length,
213
+ archived: archivedSkills.length,
214
+ relocated: relocatedSkills.length
215
+ },
216
+ capabilities
217
+ }, { indent: 2 });
218
+
219
+ fs.writeFileSync(INDEX_YAML, indexYamlContent, 'utf8');
220
+ fs.writeFileSync(GRAPH_JSON, JSON.stringify(graphJsonData, null, 2), 'utf8');
221
+
222
+ console.log(`✅ Generated lightweight registry index at .agents/registry/index.yaml (${allIds.length} scanned: ${activeSkills.length} active, ${archivedSkills.length} archived, ${relocatedSkills.length} relocated)`);
223
+ console.log(`✅ Generated O(1) adjacency graph at .agents/registry/graph.json (active: ${activeSkills.length}, has_cycles: ${hasCycles})`);
224
+ }
225
+
226
+ buildRegistry();
@@ -1,143 +1,72 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import YAML from 'js-yaml';
4
3
  import { fileURLToPath } from 'url';
5
4
 
6
5
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
6
  const rootDir = path.resolve(__dirname, '..');
8
- const skillsDir = path.join(rootDir, '.agents/skills');
9
- const graphPath = path.join(rootDir, '.agents/registry/capability-graph.yml');
10
- const workflowsDir = path.join(rootDir, '.agents/workflows');
7
+ const graphPath = path.join(rootDir, '.agents/registry/graph.json');
11
8
  const reportsDir = path.join(rootDir, '.agents/reports');
12
9
 
13
- if (!fs.existsSync(reportsDir)) {
14
- fs.mkdirSync(reportsDir, { recursive: true });
15
- }
16
-
17
- function parseFrontmatter(content) {
18
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
19
- if (!match) return null;
20
- try {
21
- return YAML.load(match[1]);
22
- } catch (e) {
23
- return null;
10
+ /**
11
+ * Run After Action Review (AAR).
12
+ * Reads graph.json and generates health report.
13
+ */
14
+ export async function runAar() {
15
+ if (!fs.existsSync(graphPath)) {
16
+ console.error('❌ graph.json not found. Run: node tooling/build-registry.js');
17
+ return;
24
18
  }
25
- }
26
-
27
- // 1. Skill Audit
28
- function runSkillAudit() {
29
- const dirs = fs.readdirSync(skillsDir).filter(name => !name.startsWith('_') && fs.statSync(path.join(skillsDir, name)).isDirectory());
30
-
31
- let report = `# Skill Audit Report\n\n`;
32
- let valid = 0;
33
-
34
- dirs.forEach(skill => {
35
- const skillPath = path.join(skillsDir, skill, 'SKILL.md');
36
- if (!fs.existsSync(skillPath)) return;
37
-
38
- const content = fs.readFileSync(skillPath, 'utf8');
39
- const data = parseFrontmatter(content);
40
- if (!data) return;
41
-
42
- // Check boundary
43
- const bounds = data.decision_boundary || {};
44
- const hasOwns = bounds.owns && bounds.owns.length > 0;
45
- const hasNotOwn = bounds.does_not_own && bounds.does_not_own.length > 0;
46
- const hasConflicts = bounds.conflicts_with !== undefined; // can be empty array
47
- const hasDelegates = bounds.delegates_to !== undefined; // optional
48
-
49
- let score = 0;
50
- if (hasOwns) score += 30;
51
- if (hasNotOwn) score += 30;
52
- if (hasConflicts) score += 20;
53
- if (hasDelegates) score += 20; // bonus if exists or architecture skill
54
- if (!hasDelegates && !skill.includes('architecture') && !skill.includes('orchestrator')) {
55
- score += 20; // It's fine if implementation skill doesn't delegate
56
- }
57
-
58
- report += `## ${skill}\n`;
59
- report += `- \`owns\`: ${hasOwns ? '✅' : '❌'}\n`;
60
- report += `- \`does_not_own\`: ${hasNotOwn ? '✅' : '❌'}\n`;
61
- report += `- \`conflicts_with\`: ${hasConflicts ? '✅' : '❌'}\n`;
62
- report += `- \`delegates_to\`: ${hasDelegates ? '✅' : (skill.includes('architecture') ? '❌ (Architecture should delegate)' : '➖ (Optional)')}\n`;
63
- report += `**Boundary Health Score: ${score}/100**\n\n`;
64
-
65
- if (score >= 80) valid++;
66
- });
67
-
68
- report += `\n**Total Scanned:** ${dirs.length}\n`;
69
- report += `**Total Passed (>=80):** ${valid}\n`;
70
-
71
- fs.writeFileSync(path.join(reportsDir, 'skill-audit.md'), report);
72
- console.log('✅ Skill audit complete.');
73
- }
74
19
 
75
- // 2. Graph Audit
76
- function runGraphAudit() {
77
- if (!fs.existsSync(graphPath)) return;
78
- const graph = YAML.load(fs.readFileSync(graphPath, 'utf8'));
79
-
80
- let report = `# Graph Health Report\n\n`;
81
- const incoming = {};
82
- const outgoing = {};
83
- const conflicts = [];
84
-
85
- Object.keys(graph.nodes).forEach(n => {
86
- incoming[n] = 0;
87
- outgoing[n] = 0;
88
- });
89
-
90
- graph.edges.forEach(e => {
91
- if (outgoing[e.from] !== undefined) outgoing[e.from]++;
92
- if (incoming[e.to] !== undefined) incoming[e.to]++;
93
-
94
- if (e.relation === 'conflicts_with') {
95
- conflicts.push(`- **${e.from}** conflicts **${e.to}**\n - Reason: Semantic overlap\n - Severity: High\n - Resolution: Context loader intervention required`);
96
- }
97
- });
20
+ if (!fs.existsSync(reportsDir)) {
21
+ fs.mkdirSync(reportsDir, { recursive: true });
22
+ }
98
23
 
99
- report += `## Hub Risk Analysis\n`;
100
- const MAX_OUT = 12;
101
- const MAX_IN = 15;
102
- const ALLOWED_HUBS = ['qk-orchestrator', 'qk-context-loader'];
24
+ const graph = JSON.parse(fs.readFileSync(graphPath, 'utf8'));
25
+ const nodes = Object.keys(graph.nodes || {});
26
+ const edges = graph.edges || [];
27
+ const adjacency = graph.adjacency || {};
28
+
29
+ let report = '# After Action Review (AAR)\n\n';
30
+ report += `**Generated:** ${new Date().toISOString()}\n\n`;
31
+
32
+ // Node health
33
+ report += '## Node Health\n\n';
34
+ report += '| Node | Version | Platforms | Dependencies |\n';
35
+ report += '|------|---------|-----------|-------------|\n';
36
+ for (const [id, node] of Object.entries(graph.nodes || {})) {
37
+ const deps = (node.dependencies || []).join(', ') || '—';
38
+ report += `| ${id} | ${node.version || '—'} | ${(node.platforms || []).join(', ') || '—'} | ${deps} |\n`;
39
+ }
103
40
 
104
- Object.keys(outgoing).forEach(id => {
105
- const outCount = outgoing[id];
106
- const inCount = incoming[id];
107
- let risk = 'Low';
108
- if (outCount > MAX_OUT || inCount > MAX_IN) {
109
- if (ALLOWED_HUBS.includes(id)) {
110
- risk = 'Allowed Exception';
111
- } else {
112
- risk = 'High Risk Hub';
113
- }
114
- }
115
- if (risk !== 'Low') {
116
- report += `- **${id}**: Incoming(${inCount}), Outgoing(${outCount}) -> [${risk}]\n`;
41
+ // Edge summary
42
+ report += '\n## Edge Summary\n\n';
43
+ report += `- Total edges: ${edges.length}\n`;
44
+ report += `- Total nodes: ${nodes.length}\n`;
45
+ report += `- Orphans: ${graph.orphan_count || 0}\n`;
46
+ report += `- Has cycles: ${graph.has_cycles || false}\n`;
47
+
48
+ // Platform coverage
49
+ report += '\n## Platform Coverage\n\n';
50
+ const platformCounts = { antigravity: 0, claude: 0, opencode: 0 };
51
+ for (const node of Object.values(graph.nodes || {})) {
52
+ for (const p of node.platforms || []) {
53
+ if (platformCounts[p] !== undefined) platformCounts[p]++;
117
54
  }
118
- });
119
-
120
- report += `\n## Conflict Matrix\n`;
121
- conflicts.forEach(c => {
122
- report += `${c}\n\n`;
123
- });
55
+ }
56
+ for (const [platform, count] of Object.entries(platformCounts)) {
57
+ report += `- ${platform}: ${count}/${nodes.length} skills\n`;
58
+ }
124
59
 
125
- fs.writeFileSync(path.join(reportsDir, 'graph-health.md'), report);
126
- console.log('✅ Graph audit complete.');
127
- }
60
+ // Version compliance
61
+ report += '\n## Version Compliance\n\n';
62
+ let compliant = 0;
63
+ for (const [id, node] of Object.entries(graph.nodes || {})) {
64
+ if (node.version === '10.2.0') compliant++;
65
+ }
66
+ report += `- Version 10.2.0: ${compliant}/${nodes.length}\n`;
128
67
 
129
- // 3. Workflow Audit
130
- function runWorkflowAudit() {
131
- const workflows = fs.readdirSync(workflowsDir).filter(f => f.endsWith('.md') || f.endsWith('.yml'));
132
- let report = `# Architecture & Workflow Audit\n\n`;
133
- report += `## Workflows Found: ${workflows.length}\n`;
134
- workflows.forEach(w => {
135
- report += `- ${w}\n`;
136
- });
137
- fs.writeFileSync(path.join(reportsDir, 'architecture-audit.md'), report);
138
- console.log('✅ Workflow audit complete.');
68
+ fs.writeFileSync(path.join(reportsDir, 'aar-report.md'), report);
69
+ console.log('✅ AAR report generated.');
139
70
  }
140
71
 
141
- runSkillAudit();
142
- runGraphAudit();
143
- runWorkflowAudit();
72
+ export default { runAar };
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
 
4
- const targetVersion = "9.1.0";
4
+ const targetVersion = "10.2.0";
5
5
  const rootDir = process.cwd();
6
6
  const skillsDir = path.join(rootDir, '.agents', 'skills');
7
7
 
@@ -253,7 +253,7 @@ skills.forEach(skill => {
253
253
  if (fs.existsSync(skillMdPath)) {
254
254
  let content = fs.readFileSync(skillMdPath, 'utf8');
255
255
  let newContent = content.replace(/^version:\s*["']?[\d.]+["']?/m, `version: ${targetVersion}`);
256
- newContent = newContent.replace(/^# ── V[\d]+: Classification/m, `# ── V9: Classification`);
256
+ newContent = newContent.replace(/^# ── V[\d]+: Classification/m, `# ── V10: Classification`);
257
257
 
258
258
  // Inject or update Memory Workflow (except qk-project-memory which manages memory itself)
259
259
  if (skill !== 'qk-project-memory' && skill !== 'qk-help') {