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.
- package/.agents/AGENTS.md +13 -0
- package/.agents/LICENSE +21 -21
- package/.agents/docs/ARCHITECTURE.md +56 -120
- package/.agents/docs/GOVERNANCE.md +3 -3
- package/.agents/docs/SPEC.md +137 -60
- package/.agents/docs/VERSIONING.md +25 -57
- package/.agents/docs/adr/0005-v10-platform-consolidation.md +50 -0
- package/.agents/docs/schemas/learning.schema.yml +22 -57
- package/.agents/docs/schemas/skill.schema.yml +116 -161
- package/.agents/docs/schemas/workflow.schema.yml +51 -26
- package/.agents/docs/skill-classification.md +1 -1
- package/.agents/registry/graph.json +171 -77
- package/.agents/registry/index.yaml +68 -35
- package/.agents/rules/coding.md +1 -1
- package/.agents/rules/command-safety.md +1 -1
- package/.agents/rules/global.md +1 -1
- package/.agents/rules/prompt-compiler.md +1 -1
- package/.agents/rules/safety.md +1 -1
- package/.agents/rules/security.md +1 -1
- package/.agents/rules/skill-quality.md +18 -3
- package/.agents/skills/_template/SKILL.md +238 -88
- package/.agents/skills/qk-api-data-discovery/SKILL.md +176 -190
- package/.agents/skills/qk-api-data-discovery/references/bronze-record-format.md +47 -0
- package/.agents/skills/qk-api-data-discovery/references/data-contract-yaml.md +72 -0
- package/.agents/skills/qk-api-data-discovery/references/discovery-report-template.md +110 -0
- package/.agents/skills/qk-backend-data/SKILL.md +150 -1
- package/.agents/skills/qk-bug-resolution/SKILL.md +153 -1
- package/.agents/skills/qk-bug-resolution/evals/scorecard.yaml +1 -1
- package/.agents/skills/qk-code-cleaner/SKILL.md +150 -1
- package/.agents/skills/qk-code-review/SKILL.md +195 -1
- package/.agents/skills/qk-code-review/evals/scorecard.yaml +1 -1
- package/.agents/skills/qk-code-review/references/ai/{v8-schema-validation.md → schema-validation.md} +2 -2
- package/.agents/skills/qk-code-review/references/cross-cutting/async-concurrency-patterns.md +515 -515
- package/.agents/skills/qk-code-review/references/cross-cutting/error-handling-principles.md +492 -492
- package/.agents/skills/qk-code-review/references/cross-cutting/n-plus-one-queries.md +309 -309
- package/.agents/skills/qk-code-review/references/cross-cutting/sql-injection-prevention.md +307 -307
- package/.agents/skills/qk-code-review/references/cross-cutting/xss-prevention.md +263 -263
- package/.agents/skills/qk-code-review/references/languages/angular.md +768 -768
- package/.agents/skills/qk-code-review/references/languages/c.md +890 -890
- package/.agents/skills/qk-code-review/references/languages/cpp.md +893 -893
- package/.agents/skills/qk-code-review/references/languages/css-less-sass.md +661 -661
- package/.agents/skills/qk-code-review/references/languages/django.md +985 -985
- package/.agents/skills/qk-code-review/references/languages/fastapi.md +580 -580
- package/.agents/skills/qk-code-review/references/languages/go.md +993 -993
- package/.agents/skills/qk-code-review/references/languages/java.md +409 -409
- package/.agents/skills/qk-code-review/references/languages/java8.md +586 -586
- package/.agents/skills/qk-code-review/references/languages/kotlin.md +1018 -1018
- package/.agents/skills/qk-code-review/references/languages/nestjs.md +593 -593
- package/.agents/skills/qk-code-review/references/languages/php.md +684 -684
- package/.agents/skills/qk-code-review/references/languages/python.md +1073 -1073
- package/.agents/skills/qk-code-review/references/languages/qt.md +757 -757
- package/.agents/skills/qk-code-review/references/languages/react.md +871 -871
- package/.agents/skills/qk-code-review/references/languages/ruby.md +964 -964
- package/.agents/skills/qk-code-review/references/languages/rust.md +846 -846
- package/.agents/skills/qk-code-review/references/languages/svelte.md +1064 -1064
- package/.agents/skills/qk-code-review/references/languages/swift.md +936 -936
- package/.agents/skills/qk-code-review/references/languages/typescript.md +1016 -1016
- package/.agents/skills/qk-code-review/references/languages/vue.md +924 -924
- package/.agents/skills/qk-code-review/references/languages/zig.md +440 -440
- package/.agents/skills/qk-devops-release/SKILL.md +150 -1
- package/.agents/skills/qk-feature-delivery/SKILL.md +153 -1
- package/.agents/skills/qk-feature-delivery/evals/scorecard.yaml +1 -1
- package/.agents/skills/qk-orchestrator/SKILL.md +187 -2
- package/.agents/skills/qk-orchestrator/evals/scorecard.yaml +1 -1
- package/.agents/skills/qk-orchestrator/references/routing-table.md +1 -1
- package/.agents/skills/qk-product-spec/SKILL.md +150 -1
- package/.agents/skills/qk-prompt-compiler/SKILL.md +188 -65
- package/.agents/skills/qk-ui-engineer/SKILL.md +150 -1
- package/.agents/workflows/_schema.yml +146 -146
- package/.agents/workflows/bug-resolution.yml +155 -121
- package/.agents/workflows/code-review.yml +127 -93
- package/.agents/workflows/context-discovery.yml +128 -94
- package/.agents/workflows/documentation.yml +124 -90
- package/.agents/workflows/feature-delivery.yml +158 -124
- package/.agents/workflows/production-release.yml +207 -173
- package/.agents/workflows/prompt-compilation.yml +126 -0
- package/.agents/workflows/refactor.yml +136 -102
- package/.agents/workflows/security-audit.yml +149 -115
- package/.agents/workflows/shared/quality-gate.yml +3 -1
- package/.agents/workflows/skin-governance.yml +149 -115
- package/.agents/workflows/spec-driven-development.yml +78 -71
- package/CHANGELOG.md +61 -0
- package/README.md +3 -3
- package/bin/install.js +38 -51
- package/package.json +68 -74
- package/tooling/build-registry.js +226 -208
- package/tooling/run-aar.js +55 -126
- package/tooling/sync-versions.js +2 -2
- package/tooling/validate-graph.js +100 -87
- package/tooling/validate-skills.js +32 -14
- package/.agents/docs/decisions/ADR-001-v8-migration.md +0 -58
- package/.agents/docs/decisions/ADR-002-workflow-separation.md +0 -50
- package/.agents/docs/decisions/ADR-003-registry-generated.md +0 -54
- package/.agents/docs/decisions/ADR-008-skill-boundary-review.md +0 -27
- package/.agents/registry/capability-graph.yml +0 -390
- package/.agents/registry/skills-index.yml +0 -305
- package/.agents/skills/_template/capability.yaml +0 -34
- package/.agents/skills/_template/evals/scorecard.yaml +0 -19
|
@@ -1,208 +1,226 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* build-registry.js
|
|
3
|
-
*
|
|
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
|
|
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
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
capabilities[id] = {
|
|
100
|
-
path: `.agents/skills/${dir}`,
|
|
101
|
-
version,
|
|
102
|
-
status,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
for (const dep of deps) {
|
|
130
|
-
if (!
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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();
|
package/tooling/run-aar.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
const
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
121
|
-
|
|
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
|
-
|
|
126
|
-
|
|
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
|
-
|
|
130
|
-
|
|
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
|
-
|
|
142
|
-
runGraphAudit();
|
|
143
|
-
runWorkflowAudit();
|
|
72
|
+
export default { runAar };
|
package/tooling/sync-versions.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
|
|
4
|
-
const targetVersion = "
|
|
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, `# ──
|
|
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') {
|