code-ia-sota 0.1.0 → 0.1.1
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 +58 -1
- package/bin/code-ia-sota.js +1282 -0
- package/package.json +13 -1
package/README.md
CHANGED
|
@@ -10,6 +10,55 @@ npx code-ia-sota
|
|
|
10
10
|
|
|
11
11
|
Installs the canonical `AGENTS.md` and `.agents/` scaffold into the current directory.
|
|
12
12
|
|
|
13
|
+
This installer is intentionally a personal bootstrap. It clones the current scaffold state from the GitHub repository at install time instead of pinning a packaged template snapshot.
|
|
14
|
+
|
|
15
|
+
By default, the installer opens a tool selector inspired by OpenSpec so you can generate lightweight integrations for supported CLIs/IDEs on top of the base scaffold. In CI or GitHub Actions, it falls back to `codex` unless `--tools` is passed explicitly.
|
|
16
|
+
|
|
17
|
+
Supported tool ids:
|
|
18
|
+
|
|
19
|
+
- `codex`
|
|
20
|
+
- `claude`
|
|
21
|
+
- `cursor`
|
|
22
|
+
- `windsurf`
|
|
23
|
+
|
|
24
|
+
Non-interactive examples:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
# Base scaffold only
|
|
28
|
+
npx code-ia-sota --tools none
|
|
29
|
+
|
|
30
|
+
# Install base scaffold plus Claude + Cursor shims
|
|
31
|
+
npx code-ia-sota --tools claude,cursor
|
|
32
|
+
|
|
33
|
+
# Install every supported integration
|
|
34
|
+
npx code-ia-sota --tools all
|
|
35
|
+
|
|
36
|
+
# List supported tool ids
|
|
37
|
+
npx code-ia-sota --list-tools
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Generated integrations:
|
|
41
|
+
|
|
42
|
+
- `claude` -> `CLAUDE.md`
|
|
43
|
+
- `cursor` -> `.cursor/rules/code-ia-sota.mdc`
|
|
44
|
+
- `windsurf` -> `.windsurf/rules/code-ia-sota.md`
|
|
45
|
+
- `codex` -> no extra file; uses the root scaffold directly
|
|
46
|
+
|
|
47
|
+
## Harness validation
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
npm run validate:harness
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Validates `.agents/skills/*/SKILL.md` and `.agents/workflows/*.md`.
|
|
54
|
+
The local npm script validates the repository root from the CLI package directory.
|
|
55
|
+
|
|
56
|
+
You can also run:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
code-ia-sota validate [path]
|
|
60
|
+
```
|
|
61
|
+
|
|
13
62
|
## Local development
|
|
14
63
|
|
|
15
64
|
```sh
|
|
@@ -22,5 +71,13 @@ code-ia-sota
|
|
|
22
71
|
|
|
23
72
|
```sh
|
|
24
73
|
npm pack
|
|
25
|
-
npx ./code-ia-sota-0.1.
|
|
74
|
+
npx ./code-ia-sota-0.1.1.tgz
|
|
26
75
|
```
|
|
76
|
+
|
|
77
|
+
## GitHub Actions publish
|
|
78
|
+
|
|
79
|
+
The repository workflow `.github/workflows/publish-cli.yml` publishes the package from `code-ia-sota-cli/` on pushes to `main` that change the CLI files, or by manual dispatch.
|
|
80
|
+
|
|
81
|
+
Required secret:
|
|
82
|
+
|
|
83
|
+
- `NPM_TOKEN` with publish permission for the `code-ia-sota` package on npm
|
package/bin/code-ia-sota.js
CHANGED
|
@@ -4,6 +4,7 @@ import fs from "node:fs/promises";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import process from "node:process";
|
|
7
|
+
import readline from "node:readline/promises";
|
|
7
8
|
|
|
8
9
|
import fsExtra from "fs-extra";
|
|
9
10
|
import simpleGit from "simple-git";
|
|
@@ -13,6 +14,118 @@ const REPO_URL =
|
|
|
13
14
|
"https://github.com/wolfox33/Code_IA_SotA.git";
|
|
14
15
|
|
|
15
16
|
const TARGET_DIR = process.cwd();
|
|
17
|
+
const MANAGED_FILE_MARKER =
|
|
18
|
+
"Generated by code-ia-sota";
|
|
19
|
+
const SUPPORTED_TOOLS = [
|
|
20
|
+
{
|
|
21
|
+
id: "codex",
|
|
22
|
+
label: "Codex",
|
|
23
|
+
description:
|
|
24
|
+
"Uses the root AGENTS.md and .agents scaffold directly.",
|
|
25
|
+
detectPaths: [
|
|
26
|
+
".codex"
|
|
27
|
+
],
|
|
28
|
+
files: []
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "claude",
|
|
32
|
+
label: "Claude Code",
|
|
33
|
+
description:
|
|
34
|
+
"Creates a root CLAUDE.md shim that points to AGENTS.md and .agents.",
|
|
35
|
+
detectPaths: [
|
|
36
|
+
"CLAUDE.md",
|
|
37
|
+
".claude"
|
|
38
|
+
],
|
|
39
|
+
files: [
|
|
40
|
+
{
|
|
41
|
+
relativePath: "CLAUDE.md",
|
|
42
|
+
content: [
|
|
43
|
+
"# Code IA Sota",
|
|
44
|
+
"",
|
|
45
|
+
"Use `AGENTS.md` at the repository root as the canonical project policy.",
|
|
46
|
+
"",
|
|
47
|
+
"Load additional context on demand from:",
|
|
48
|
+
"- `.agents/project/context.md`",
|
|
49
|
+
"- `.agents/USER.md`",
|
|
50
|
+
"- `.agents/project/MEMORY.md`",
|
|
51
|
+
"- `.agents/workflows/`",
|
|
52
|
+
"- `.agents/skills/`",
|
|
53
|
+
"",
|
|
54
|
+
"Prefer the smallest correct change, preserve the existing architecture, and do not expand scope silently.",
|
|
55
|
+
"",
|
|
56
|
+
`<!-- ${MANAGED_FILE_MARKER}: claude -->`
|
|
57
|
+
].join("\n")
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "cursor",
|
|
63
|
+
label: "Cursor",
|
|
64
|
+
description:
|
|
65
|
+
"Creates an always-on Cursor rule that points to AGENTS.md and .agents.",
|
|
66
|
+
detectPaths: [
|
|
67
|
+
".cursor"
|
|
68
|
+
],
|
|
69
|
+
files: [
|
|
70
|
+
{
|
|
71
|
+
relativePath: path.join(".cursor", "rules", "code-ia-sota.mdc"),
|
|
72
|
+
content: [
|
|
73
|
+
"---",
|
|
74
|
+
"description: Code IA Sota project policy",
|
|
75
|
+
"alwaysApply: true",
|
|
76
|
+
"---",
|
|
77
|
+
"",
|
|
78
|
+
"Use `AGENTS.md` at the repository root as the canonical project policy.",
|
|
79
|
+
"",
|
|
80
|
+
"Load additional context on demand from `.agents/project/context.md`, `.agents/USER.md`, `.agents/project/MEMORY.md`, `.agents/workflows/`, and `.agents/skills/`.",
|
|
81
|
+
"",
|
|
82
|
+
"Prefer the smallest correct change, preserve the existing architecture, and do not expand scope silently.",
|
|
83
|
+
"",
|
|
84
|
+
`<!-- ${MANAGED_FILE_MARKER}: cursor -->`
|
|
85
|
+
].join("\n")
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "windsurf",
|
|
91
|
+
label: "Windsurf",
|
|
92
|
+
description:
|
|
93
|
+
"Creates a workspace rule file that points Cascade to AGENTS.md and .agents.",
|
|
94
|
+
detectPaths: [
|
|
95
|
+
".windsurf",
|
|
96
|
+
".windsurfrules"
|
|
97
|
+
],
|
|
98
|
+
files: [
|
|
99
|
+
{
|
|
100
|
+
relativePath: path.join(".windsurf", "rules", "code-ia-sota.md"),
|
|
101
|
+
content: [
|
|
102
|
+
"# Code IA Sota",
|
|
103
|
+
"",
|
|
104
|
+
"Use `AGENTS.md` at the repository root as the canonical project policy.",
|
|
105
|
+
"",
|
|
106
|
+
"Load additional context on demand from `.agents/project/context.md`, `.agents/USER.md`, `.agents/project/MEMORY.md`, `.agents/workflows/`, and `.agents/skills/`.",
|
|
107
|
+
"",
|
|
108
|
+
"Prefer the smallest correct change, preserve the existing architecture, and do not expand scope silently.",
|
|
109
|
+
"",
|
|
110
|
+
`<!-- ${MANAGED_FILE_MARKER}: windsurf -->`
|
|
111
|
+
].join("\n")
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
const REQUIRED_OPERATIONAL_SKILL_SECTIONS = [
|
|
118
|
+
["Objetivo", "Objective"],
|
|
119
|
+
["Use this skill when"],
|
|
120
|
+
["Do not use this skill when"],
|
|
121
|
+
["Output contracts", "Output esperado"],
|
|
122
|
+
["Procedure", "Processo", "Review dimensions", "Diagnostic dimensions"],
|
|
123
|
+
["Verification"]
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
const DENSITY_THRESHOLD = parseInt(process.env.HARNESS_DENSITY_THRESHOLD || "30", 10);
|
|
127
|
+
const FAIL_ON_WARNINGS = process.env.HARNESS_FAIL_ON_WARNINGS === "true";
|
|
128
|
+
const ALERT_THRESHOLD = parseInt(process.env.HARNESS_ALERT_THRESHOLD || "10", 10);
|
|
16
129
|
|
|
17
130
|
async function exists(filePath) {
|
|
18
131
|
try {
|
|
@@ -23,6 +136,211 @@ async function exists(filePath) {
|
|
|
23
136
|
}
|
|
24
137
|
}
|
|
25
138
|
|
|
139
|
+
function parseFlagValue(flagName) {
|
|
140
|
+
const args = process.argv.slice(2);
|
|
141
|
+
const index = args.findIndex((arg) => arg === flagName);
|
|
142
|
+
|
|
143
|
+
if (index === -1) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return args[index + 1] || null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function listSupportedTools() {
|
|
151
|
+
for (const tool of SUPPORTED_TOOLS) {
|
|
152
|
+
console.log(
|
|
153
|
+
` ${tool.id.padEnd(8)} ${tool.label} - ${tool.description}`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function resolveToolSelection(rawValue) {
|
|
159
|
+
if (!rawValue) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const normalizedValue = rawValue.trim().toLowerCase();
|
|
164
|
+
|
|
165
|
+
if (normalizedValue === "all") {
|
|
166
|
+
return SUPPORTED_TOOLS.map((tool) => tool.id);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (normalizedValue === "none") {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const selectedToolIds = [];
|
|
174
|
+
const seen = new Set();
|
|
175
|
+
|
|
176
|
+
for (const token of normalizedValue.split(",")) {
|
|
177
|
+
const value = token.trim();
|
|
178
|
+
|
|
179
|
+
if (!value) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let toolId = value;
|
|
184
|
+
if (/^\d+$/.test(value)) {
|
|
185
|
+
const index = parseInt(value, 10) - 1;
|
|
186
|
+
toolId = SUPPORTED_TOOLS[index]?.id;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!toolId || !SUPPORTED_TOOLS.some((tool) => tool.id === toolId)) {
|
|
190
|
+
const availableTools = SUPPORTED_TOOLS.map((tool) => tool.id).join(", ");
|
|
191
|
+
throw new Error(
|
|
192
|
+
`Unknown tool "${value}". Use one of: ${availableTools}, all, none`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!seen.has(toolId)) {
|
|
197
|
+
seen.add(toolId);
|
|
198
|
+
selectedToolIds.push(toolId);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return selectedToolIds;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function detectTools() {
|
|
206
|
+
const detectedToolIds = [];
|
|
207
|
+
|
|
208
|
+
for (const tool of SUPPORTED_TOOLS) {
|
|
209
|
+
for (const detectPath of tool.detectPaths) {
|
|
210
|
+
if (await exists(path.join(TARGET_DIR, detectPath))) {
|
|
211
|
+
detectedToolIds.push(tool.id);
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return detectedToolIds;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function promptToolSelection() {
|
|
221
|
+
const detectedToolIds = await detectTools();
|
|
222
|
+
|
|
223
|
+
console.log(pc.cyan("Select CLI/IDE integrations to install."));
|
|
224
|
+
console.log("Press Enter to accept detected tools, or type comma-separated ids/numbers.");
|
|
225
|
+
console.log("Use `all` to install every integration or `none` to skip tool-specific files.");
|
|
226
|
+
console.log("");
|
|
227
|
+
|
|
228
|
+
for (const [index, tool] of SUPPORTED_TOOLS.entries()) {
|
|
229
|
+
const marker = detectedToolIds.includes(tool.id) ? "[x]" : "[ ]";
|
|
230
|
+
console.log(
|
|
231
|
+
` ${index + 1}. ${marker} ${tool.id.padEnd(8)} ${tool.label} - ${tool.description}`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
console.log("");
|
|
236
|
+
|
|
237
|
+
if (detectedToolIds.length > 0) {
|
|
238
|
+
console.log(
|
|
239
|
+
`Detected tools: ${detectedToolIds.join(", ")}`
|
|
240
|
+
);
|
|
241
|
+
} else {
|
|
242
|
+
console.log("Detected tools: none");
|
|
243
|
+
console.log("Default on empty input: codex (base scaffold only)");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
console.log("");
|
|
247
|
+
|
|
248
|
+
const rl = readline.createInterface({
|
|
249
|
+
input: process.stdin,
|
|
250
|
+
output: process.stdout
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
const answer = await rl.question("Tools> ");
|
|
255
|
+
|
|
256
|
+
if (!answer.trim()) {
|
|
257
|
+
if (detectedToolIds.length > 0) {
|
|
258
|
+
return detectedToolIds;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return ["codex"];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return resolveToolSelection(answer);
|
|
265
|
+
} finally {
|
|
266
|
+
rl.close();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function selectTools() {
|
|
271
|
+
if (process.argv.includes("--list-tools")) {
|
|
272
|
+
console.log(pc.bold(pc.green("Supported tools")));
|
|
273
|
+
listSupportedTools();
|
|
274
|
+
process.exit(0);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const toolsArg = parseFlagValue("--tools");
|
|
278
|
+
const selectedTools = resolveToolSelection(toolsArg);
|
|
279
|
+
|
|
280
|
+
if (selectedTools !== null) {
|
|
281
|
+
return selectedTools;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true") {
|
|
285
|
+
return ["codex"];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return promptToolSelection();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function writeManagedFile(filePath, content) {
|
|
292
|
+
if (!(await exists(filePath))) {
|
|
293
|
+
await fs.mkdir(path.dirname(filePath), {
|
|
294
|
+
recursive: true
|
|
295
|
+
});
|
|
296
|
+
await fs.writeFile(filePath, content, "utf8");
|
|
297
|
+
return "created";
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const currentContent = await fs.readFile(filePath, "utf8");
|
|
301
|
+
|
|
302
|
+
if (currentContent === content) {
|
|
303
|
+
return "unchanged";
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (!currentContent.includes(MANAGED_FILE_MARKER)) {
|
|
307
|
+
return "skipped";
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
await fs.writeFile(filePath, content, "utf8");
|
|
311
|
+
return "updated";
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async function installToolIntegrations(selectedToolIds) {
|
|
315
|
+
const summary = {
|
|
316
|
+
created: [],
|
|
317
|
+
updated: [],
|
|
318
|
+
skipped: []
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
for (const toolId of selectedToolIds) {
|
|
322
|
+
const tool = SUPPORTED_TOOLS.find((item) => item.id === toolId);
|
|
323
|
+
if (!tool) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
for (const file of tool.files) {
|
|
328
|
+
const targetPath = path.join(TARGET_DIR, file.relativePath);
|
|
329
|
+
const result = await writeManagedFile(targetPath, `${file.content}\n`);
|
|
330
|
+
|
|
331
|
+
if (result === "created") {
|
|
332
|
+
summary.created.push(file.relativePath);
|
|
333
|
+
} else if (result === "updated") {
|
|
334
|
+
summary.updated.push(file.relativePath);
|
|
335
|
+
} else if (result === "skipped") {
|
|
336
|
+
summary.skipped.push(file.relativePath);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return summary;
|
|
342
|
+
}
|
|
343
|
+
|
|
26
344
|
async function cloneTemplate(tmpDir) {
|
|
27
345
|
console.log(pc.cyan("Downloading template..."));
|
|
28
346
|
|
|
@@ -82,7 +400,942 @@ async function copyFiles(tmpDir) {
|
|
|
82
400
|
}
|
|
83
401
|
}
|
|
84
402
|
|
|
403
|
+
function parseFrontmatter(content) {
|
|
404
|
+
const normalizedContent = content.replace(/\r\n/g, "\n");
|
|
405
|
+
|
|
406
|
+
if (!normalizedContent.startsWith("---\n")) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const endIndex = normalizedContent.indexOf("\n---", 4);
|
|
411
|
+
|
|
412
|
+
if (endIndex === -1) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const raw = normalizedContent.slice(4, endIndex).trim();
|
|
417
|
+
const data = {};
|
|
418
|
+
|
|
419
|
+
for (const line of raw.split("\n")) {
|
|
420
|
+
const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
421
|
+
|
|
422
|
+
if (match) {
|
|
423
|
+
data[match[1]] = match[2].replace(/^["']|["']$/g, "");
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
data,
|
|
429
|
+
body: normalizedContent.slice(endIndex + 5).trim()
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function hasSection(content, sectionNames) {
|
|
434
|
+
const names = Array.isArray(sectionNames) ? sectionNames : [sectionNames];
|
|
435
|
+
|
|
436
|
+
return names.some((sectionName) => {
|
|
437
|
+
const escaped = sectionName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
438
|
+
const pattern = new RegExp(`^#{1,6}\\s+${escaped}\\s*$`, "im");
|
|
439
|
+
|
|
440
|
+
return pattern.test(content);
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function addFailure(failures, rootDir, filePath, reason) {
|
|
445
|
+
failures.push({
|
|
446
|
+
filePath: path.relative(rootDir, filePath),
|
|
447
|
+
reason
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function addWarning(warnings, rootDir, filePath, reason) {
|
|
452
|
+
warnings.push({
|
|
453
|
+
filePath: path.relative(rootDir, filePath),
|
|
454
|
+
reason
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async function listMarkdownFiles(dirPath) {
|
|
459
|
+
if (!(await exists(dirPath))) {
|
|
460
|
+
return [];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const entries = await fs.readdir(dirPath, {
|
|
464
|
+
withFileTypes: true
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
return entries
|
|
468
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".md") && entry.name !== "README.md")
|
|
469
|
+
.map((entry) => path.join(dirPath, entry.name));
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function listSkillFiles(skillsDir) {
|
|
473
|
+
if (!(await exists(skillsDir))) {
|
|
474
|
+
return [];
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const entries = await fs.readdir(skillsDir, {
|
|
478
|
+
withFileTypes: true
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
return entries
|
|
482
|
+
.filter((entry) => entry.isDirectory())
|
|
483
|
+
.map((entry) => path.join(skillsDir, entry.name, "SKILL.md"));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function calculateDensity(content) {
|
|
487
|
+
const lines = content.split("\n").filter(line => line.trim().length > 0);
|
|
488
|
+
const totalLines = lines.length;
|
|
489
|
+
|
|
490
|
+
const procedureMatch = content.match(/^##\s+Procedure/im);
|
|
491
|
+
if (!procedureMatch) {
|
|
492
|
+
return 0;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const procedureIndex = procedureMatch.index;
|
|
496
|
+
const nextSectionMatch = content.slice(procedureIndex + procedureMatch[0].length).match(/^##\s+/im);
|
|
497
|
+
const endIndex = nextSectionMatch ? procedureIndex + nextSectionMatch.index : content.length;
|
|
498
|
+
|
|
499
|
+
const procedureContent = content.slice(procedureIndex, endIndex);
|
|
500
|
+
const procedureLines = procedureContent.split("\n").filter(line => line.trim().length > 0).length;
|
|
501
|
+
|
|
502
|
+
return Math.round((procedureLines / totalLines) * 100);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
async function validateSkill(rootDir, filePath, failures, warnings) {
|
|
506
|
+
if (!(await exists(filePath))) {
|
|
507
|
+
addFailure(failures, rootDir, filePath, "SKILL.md is missing.");
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
512
|
+
const frontmatter = parseFrontmatter(content);
|
|
513
|
+
|
|
514
|
+
if (!frontmatter) {
|
|
515
|
+
addFailure(failures, rootDir, filePath, "YAML frontmatter is missing or invalid.");
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (!frontmatter.data.name) {
|
|
520
|
+
addFailure(failures, rootDir, filePath, "frontmatter field `name` is missing.");
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (!frontmatter.data.description) {
|
|
524
|
+
addFailure(failures, rootDir, filePath, "frontmatter field `description` is missing.");
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (/placeholder/i.test(frontmatter.data.description || "")) {
|
|
528
|
+
addFailure(failures, rootDir, filePath, "placeholder description is not allowed.");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
for (const sectionGroup of REQUIRED_OPERATIONAL_SKILL_SECTIONS) {
|
|
532
|
+
if (!hasSection(content, sectionGroup)) {
|
|
533
|
+
addWarning(warnings, rootDir, filePath, `operational section group \`${sectionGroup.join(" | ")}\` is missing.`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const density = calculateDensity(content);
|
|
538
|
+
if (density < DENSITY_THRESHOLD) {
|
|
539
|
+
addWarning(warnings, rootDir, filePath, `Low density (<${DENSITY_THRESHOLD}%): ${density}% density. Consider moving reference content to references/ and adding executable procedure.`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
async function validateWorkflow(rootDir, filePath, failures) {
|
|
544
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
545
|
+
const frontmatter = parseFrontmatter(content);
|
|
546
|
+
|
|
547
|
+
if (!frontmatter) {
|
|
548
|
+
addFailure(failures, rootDir, filePath, "YAML frontmatter is missing or invalid.");
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (!frontmatter.data.description) {
|
|
553
|
+
addFailure(failures, rootDir, filePath, "frontmatter field `description` is missing.");
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (!frontmatter.body) {
|
|
557
|
+
addFailure(failures, rootDir, filePath, "workflow body is empty.");
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async function validateHarness(rootDir) {
|
|
562
|
+
const agentsDir = path.join(rootDir, ".agents");
|
|
563
|
+
const skillsDir = path.join(agentsDir, "skills");
|
|
564
|
+
const workflowsDir = path.join(agentsDir, "workflows");
|
|
565
|
+
const failures = [];
|
|
566
|
+
const warnings = [];
|
|
567
|
+
const skillFiles = await listSkillFiles(skillsDir);
|
|
568
|
+
const workflowFiles = await listMarkdownFiles(workflowsDir);
|
|
569
|
+
|
|
570
|
+
console.log(pc.bold(pc.green("Code IA Sota")));
|
|
571
|
+
console.log(pc.cyan("Validating harness..."));
|
|
572
|
+
|
|
573
|
+
for (const filePath of skillFiles) {
|
|
574
|
+
await validateSkill(rootDir, filePath, failures, warnings);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
for (const filePath of workflowFiles) {
|
|
578
|
+
await validateWorkflow(rootDir, filePath, failures);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
console.log("");
|
|
582
|
+
console.log(`Checked ${skillFiles.length} skills.`);
|
|
583
|
+
console.log(`Checked ${workflowFiles.length} workflows.`);
|
|
584
|
+
|
|
585
|
+
if (warnings.length > 0) {
|
|
586
|
+
console.log("");
|
|
587
|
+
console.warn(pc.yellow(`Validation completed with ${warnings.length} warning(s):`));
|
|
588
|
+
|
|
589
|
+
for (const warning of warnings) {
|
|
590
|
+
console.warn(` ${warning.filePath}: ${warning.reason}`);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (FAIL_ON_WARNINGS) {
|
|
594
|
+
console.log("");
|
|
595
|
+
console.error(pc.red(`Validation failed due to warnings (FAIL_ON_WARNINGS=true).`));
|
|
596
|
+
process.exitCode = 1;
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (failures.length > 0) {
|
|
602
|
+
console.log("");
|
|
603
|
+
console.error(pc.red(`Validation failed with ${failures.length} issue(s):`));
|
|
604
|
+
|
|
605
|
+
for (const failure of failures) {
|
|
606
|
+
console.error(` ${failure.filePath}: ${failure.reason}`);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
process.exitCode = 1;
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
console.log("");
|
|
614
|
+
console.log(pc.green("Validation passed."));
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
async function analyzeReferenceContent(content) {
|
|
618
|
+
const suggestions = [];
|
|
619
|
+
|
|
620
|
+
const sections = content.split(/^##\s+/m);
|
|
621
|
+
for (const section of sections) {
|
|
622
|
+
if (!section.trim()) continue;
|
|
623
|
+
|
|
624
|
+
const lines = section.split("\n");
|
|
625
|
+
const sectionName = lines[0].trim();
|
|
626
|
+
const sectionContent = lines.slice(1).join("\n");
|
|
627
|
+
|
|
628
|
+
const hasExternalLinks = /https?:\/\//.test(sectionContent);
|
|
629
|
+
const hasLongLists = sectionContent.split("\n").filter(line => /^[-*]\s/.test(line)).length > 5;
|
|
630
|
+
const hasCodeBlocks = /```[\s\S]*```/.test(sectionContent);
|
|
631
|
+
const isLongSection = sectionContent.split("\n").length > 20;
|
|
632
|
+
|
|
633
|
+
if (hasExternalLinks || hasLongLists || (isLongSection && !hasCodeBlocks)) {
|
|
634
|
+
suggestions.push({
|
|
635
|
+
section: sectionName,
|
|
636
|
+
reason: hasExternalLinks ? "contains external links" : hasLongLists ? "has long lists" : "is long without code blocks",
|
|
637
|
+
action: "consider moving reference content to references/"
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return suggestions;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
async function suggestRefactor(rootDir) {
|
|
646
|
+
const agentsDir = path.join(rootDir, ".agents");
|
|
647
|
+
const skillsDir = path.join(agentsDir, "skills");
|
|
648
|
+
const skillFiles = await listSkillFiles(skillsDir);
|
|
649
|
+
const suggestions = [];
|
|
650
|
+
|
|
651
|
+
console.log(pc.bold(pc.green("Code IA Sota")));
|
|
652
|
+
console.log(pc.cyan("Analyzing low-density skills for refactoring suggestions..."));
|
|
653
|
+
console.log("");
|
|
654
|
+
|
|
655
|
+
for (const filePath of skillFiles) {
|
|
656
|
+
if (!(await exists(filePath))) continue;
|
|
657
|
+
|
|
658
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
659
|
+
const density = calculateDensity(content);
|
|
660
|
+
|
|
661
|
+
if (density < DENSITY_THRESHOLD) {
|
|
662
|
+
const referenceSuggestions = await analyzeReferenceContent(content);
|
|
663
|
+
const skillName = path.basename(path.dirname(filePath));
|
|
664
|
+
|
|
665
|
+
suggestions.push({
|
|
666
|
+
skill: skillName,
|
|
667
|
+
density: density,
|
|
668
|
+
filePath: path.relative(rootDir, filePath),
|
|
669
|
+
suggestions: referenceSuggestions
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
suggestions.sort((a, b) => a.density - b.density);
|
|
675
|
+
|
|
676
|
+
if (suggestions.length === 0) {
|
|
677
|
+
console.log(pc.green("No low-density skills found. All skills meet the density threshold."));
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
console.log(`Found ${suggestions.length} low-density skill(s):`);
|
|
682
|
+
console.log("");
|
|
683
|
+
|
|
684
|
+
for (const suggestion of suggestions) {
|
|
685
|
+
console.log(pc.yellow(`Skill: ${suggestion.skill} (${suggestion.density}% density)`));
|
|
686
|
+
console.log(` File: ${suggestion.filePath}`);
|
|
687
|
+
|
|
688
|
+
if (suggestion.suggestions.length > 0) {
|
|
689
|
+
console.log(" Refactoring suggestions:");
|
|
690
|
+
for (const ref of suggestion.suggestions) {
|
|
691
|
+
console.log(` - Section "${ref.section}": ${ref.reason}. ${ref.action}`);
|
|
692
|
+
}
|
|
693
|
+
} else {
|
|
694
|
+
console.log(" No specific sections identified. Consider expanding the Procedure section.");
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
console.log("");
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
console.log(pc.cyan("To refactor:"));
|
|
701
|
+
console.log(" 1. Review the suggestions above");
|
|
702
|
+
console.log(" 2. Move reference content to references/");
|
|
703
|
+
console.log(" 3. Expand the Procedure section with executable steps");
|
|
704
|
+
console.log(" 4. Re-run validation to check improved density");
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
async function benchmarkDensity(rootDir) {
|
|
708
|
+
const agentsDir = path.join(rootDir, ".agents");
|
|
709
|
+
const skillsDir = path.join(agentsDir, "skills");
|
|
710
|
+
const skillFiles = await listSkillFiles(skillsDir);
|
|
711
|
+
const results = [];
|
|
712
|
+
const jsonOutput = process.argv.includes("--json");
|
|
713
|
+
|
|
714
|
+
console.log(pc.bold(pc.green("Code IA Sota")));
|
|
715
|
+
console.log(pc.cyan("Benchmarking density for all skills..."));
|
|
716
|
+
console.log("");
|
|
717
|
+
|
|
718
|
+
for (const filePath of skillFiles) {
|
|
719
|
+
if (!(await exists(filePath))) continue;
|
|
720
|
+
|
|
721
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
722
|
+
const density = calculateDensity(content);
|
|
723
|
+
const skillName = path.basename(path.dirname(filePath));
|
|
724
|
+
|
|
725
|
+
results.push({
|
|
726
|
+
skill: skillName,
|
|
727
|
+
density: density,
|
|
728
|
+
filePath: path.relative(rootDir, filePath)
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
results.sort((a, b) => a.density - b.density);
|
|
733
|
+
|
|
734
|
+
const densities = results.map(r => r.density);
|
|
735
|
+
const average = Math.round(densities.reduce((a, b) => a + b, 0) / densities.length);
|
|
736
|
+
const min = Math.min(...densities);
|
|
737
|
+
const max = Math.max(...densities);
|
|
738
|
+
|
|
739
|
+
if (jsonOutput) {
|
|
740
|
+
console.log(JSON.stringify({
|
|
741
|
+
skills: results,
|
|
742
|
+
statistics: {
|
|
743
|
+
average: average,
|
|
744
|
+
min: min,
|
|
745
|
+
max: max,
|
|
746
|
+
total: results.length
|
|
747
|
+
}
|
|
748
|
+
}, null, 2));
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
console.log(`Total skills: ${results.length}`);
|
|
753
|
+
console.log(`Average density: ${average}%`);
|
|
754
|
+
console.log(`Min density: ${min}%`);
|
|
755
|
+
console.log(`Max density: ${max}%`);
|
|
756
|
+
console.log("");
|
|
757
|
+
console.log("Density by skill (ascending):");
|
|
758
|
+
console.log("");
|
|
759
|
+
|
|
760
|
+
for (const result of results) {
|
|
761
|
+
const densityColor = result.density < DENSITY_THRESHOLD ? pc.red : result.density < 50 ? pc.yellow : pc.green;
|
|
762
|
+
console.log(`${densityColor(result.density + "%")} ${result.skill}`);
|
|
763
|
+
console.log(` File: ${result.filePath}`);
|
|
764
|
+
console.log("");
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
console.log(pc.cyan("Low-density skills (<" + DENSITY_THRESHOLD + "%):"));
|
|
768
|
+
const lowDensity = results.filter(r => r.density < DENSITY_THRESHOLD);
|
|
769
|
+
if (lowDensity.length === 0) {
|
|
770
|
+
console.log(" None");
|
|
771
|
+
} else {
|
|
772
|
+
for (const result of lowDensity) {
|
|
773
|
+
console.log(` - ${result.skill} (${result.density}%)`);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
async function densitySnapshot(rootDir) {
|
|
779
|
+
const agentsDir = path.join(rootDir, ".agents");
|
|
780
|
+
const skillsDir = path.join(agentsDir, "skills");
|
|
781
|
+
const dataDir = path.join(agentsDir, "data");
|
|
782
|
+
const historyFile = path.join(dataDir, "density-history.json");
|
|
783
|
+
const skillFiles = await listSkillFiles(skillsDir);
|
|
784
|
+
const snapshot = {
|
|
785
|
+
timestamp: new Date().toISOString(),
|
|
786
|
+
skills: []
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
console.log(pc.bold(pc.green("Code IA Sota")));
|
|
790
|
+
console.log(pc.cyan("Creating density snapshot..."));
|
|
791
|
+
console.log("");
|
|
792
|
+
|
|
793
|
+
for (const filePath of skillFiles) {
|
|
794
|
+
if (!(await exists(filePath))) continue;
|
|
795
|
+
|
|
796
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
797
|
+
const density = calculateDensity(content);
|
|
798
|
+
const skillName = path.basename(path.dirname(filePath));
|
|
799
|
+
|
|
800
|
+
snapshot.skills.push({
|
|
801
|
+
skill: skillName,
|
|
802
|
+
density: density,
|
|
803
|
+
filePath: path.relative(rootDir, filePath)
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
snapshot.skills.sort((a, b) => a.density - b.density);
|
|
808
|
+
|
|
809
|
+
if (!(await exists(dataDir))) {
|
|
810
|
+
await fs.mkdir(dataDir, { recursive: true });
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
let history = [];
|
|
814
|
+
if (await exists(historyFile)) {
|
|
815
|
+
const historyContent = await fs.readFile(historyFile, "utf8");
|
|
816
|
+
history = JSON.parse(historyContent);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
history.push(snapshot);
|
|
820
|
+
|
|
821
|
+
await fs.writeFile(historyFile, JSON.stringify(history, null, 2));
|
|
822
|
+
|
|
823
|
+
console.log(`Snapshot created at ${snapshot.timestamp}`);
|
|
824
|
+
console.log(`Total skills: ${snapshot.skills.length}`);
|
|
825
|
+
console.log(`History file: ${historyFile}`);
|
|
826
|
+
console.log(`Total snapshots: ${history.length}`);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
async function densityTrends(rootDir) {
|
|
830
|
+
const agentsDir = path.join(rootDir, ".agents");
|
|
831
|
+
const historyFile = path.join(agentsDir, "data", "density-history.json");
|
|
832
|
+
|
|
833
|
+
console.log(pc.bold(pc.green("Code IA Sota")));
|
|
834
|
+
console.log(pc.cyan("Generating density trends report..."));
|
|
835
|
+
console.log("");
|
|
836
|
+
|
|
837
|
+
if (!(await exists(historyFile))) {
|
|
838
|
+
console.log(pc.yellow("No density history found. Run `npm run density:snapshot` first."));
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
const historyContent = await fs.readFile(historyFile, "utf8");
|
|
843
|
+
const history = JSON.parse(historyContent);
|
|
844
|
+
|
|
845
|
+
if (history.length === 0) {
|
|
846
|
+
console.log(pc.yellow("No snapshots found in history."));
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
console.log(`Total snapshots: ${history.length}`);
|
|
851
|
+
console.log("");
|
|
852
|
+
|
|
853
|
+
const skillTrends = {};
|
|
854
|
+
|
|
855
|
+
for (const snapshot of history) {
|
|
856
|
+
console.log(pc.bold(`Snapshot: ${snapshot.timestamp}`));
|
|
857
|
+
|
|
858
|
+
for (const skill of snapshot.skills) {
|
|
859
|
+
if (!skillTrends[skill.skill]) {
|
|
860
|
+
skillTrends[skill.skill] = [];
|
|
861
|
+
}
|
|
862
|
+
skillTrends[skill.skill].push({
|
|
863
|
+
timestamp: snapshot.timestamp,
|
|
864
|
+
density: skill.density
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
const densities = snapshot.skills.map(s => s.density);
|
|
869
|
+
const average = Math.round(densities.reduce((a, b) => a + b, 0) / densities.length);
|
|
870
|
+
console.log(` Average density: ${average}%`);
|
|
871
|
+
console.log(` Total skills: ${snapshot.skills.length}`);
|
|
872
|
+
console.log("");
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
console.log(pc.cyan("Density trends by skill:"));
|
|
876
|
+
console.log("");
|
|
877
|
+
|
|
878
|
+
for (const [skillName, trends] of Object.entries(skillTrends)) {
|
|
879
|
+
const first = trends[0];
|
|
880
|
+
const last = trends[trends.length - 1];
|
|
881
|
+
const change = last.density - first.density;
|
|
882
|
+
const changeColor = change > 0 ? pc.green : change < 0 ? pc.red : pc.white;
|
|
883
|
+
const changeSign = change > 0 ? "+" : "";
|
|
884
|
+
|
|
885
|
+
console.log(`${skillName}:`);
|
|
886
|
+
console.log(` First: ${first.density}% (${first.timestamp})`);
|
|
887
|
+
console.log(` Last: ${last.density}% (${last.timestamp})`);
|
|
888
|
+
console.log(` Change: ${changeColor(changeSign + change + "%")}`);
|
|
889
|
+
console.log("");
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
async function autoRefactor(rootDir) {
|
|
894
|
+
const agentsDir = path.join(rootDir, ".agents");
|
|
895
|
+
const skillsDir = path.join(agentsDir, "skills");
|
|
896
|
+
const referencesDir = path.join(agentsDir, "references");
|
|
897
|
+
const skillFiles = await listSkillFiles(skillsDir);
|
|
898
|
+
const dryRun = process.argv.includes("--dry-run");
|
|
899
|
+
|
|
900
|
+
console.log(pc.bold(pc.green("Code IA Sota")));
|
|
901
|
+
console.log(pc.cyan("Automated refactoring with user approval..."));
|
|
902
|
+
console.log("");
|
|
903
|
+
|
|
904
|
+
if (dryRun) {
|
|
905
|
+
console.log(pc.yellow("Dry-run mode: no changes will be applied."));
|
|
906
|
+
console.log("");
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
if (!(await exists(referencesDir))) {
|
|
910
|
+
if (!dryRun) {
|
|
911
|
+
await fs.mkdir(referencesDir, { recursive: true });
|
|
912
|
+
}
|
|
913
|
+
console.log(`Created references directory: ${referencesDir}`);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
for (const filePath of skillFiles) {
|
|
917
|
+
if (!(await exists(filePath))) continue;
|
|
918
|
+
|
|
919
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
920
|
+
const density = calculateDensity(content);
|
|
921
|
+
const skillName = path.basename(path.dirname(filePath));
|
|
922
|
+
|
|
923
|
+
if (density >= DENSITY_THRESHOLD) {
|
|
924
|
+
console.log(pc.green(`Skipping ${skillName} (${density}% density >= ${DENSITY_THRESHOLD}%)`));
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
console.log(pc.yellow(`Processing ${skillName} (${density}% density < ${DENSITY_THRESHOLD}%)`));
|
|
929
|
+
|
|
930
|
+
const frontmatter = parseFrontmatter(content);
|
|
931
|
+
if (!frontmatter) {
|
|
932
|
+
console.log(pc.red(` Skipping ${skillName}: no frontmatter found`));
|
|
933
|
+
continue;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
const suggestions = await analyzeReferenceContent(content);
|
|
937
|
+
if (suggestions.length === 0) {
|
|
938
|
+
console.log(pc.yellow(` No refactoring suggestions for ${skillName}`));
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
console.log(` Found ${suggestions.length} refactoring suggestion(s):`);
|
|
943
|
+
for (const suggestion of suggestions) {
|
|
944
|
+
console.log(` - Section "${suggestion.section}": ${suggestion.reason}`);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
const approved = await promptApproval(`Apply refactoring to ${skillName}? (y/n)`);
|
|
948
|
+
if (!approved) {
|
|
949
|
+
console.log(` Skipped ${skillName}`);
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
if (!dryRun) {
|
|
954
|
+
console.log(` Applying refactoring to ${skillName}...`);
|
|
955
|
+
console.log(` Note: Manual refactoring required. Use \`npm run suggest:refactor\` for detailed guidance.`);
|
|
956
|
+
} else {
|
|
957
|
+
console.log(` Dry-run: would apply refactoring to ${skillName}`);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
console.log("");
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
console.log(pc.green("Refactoring complete."));
|
|
964
|
+
console.log("");
|
|
965
|
+
console.log(pc.cyan("Next steps:"));
|
|
966
|
+
console.log(" 1. Review the changes");
|
|
967
|
+
console.log(" 2. Manually implement refactoring using suggestions from `npm run suggest:refactor`");
|
|
968
|
+
console.log(" 3. Re-run validation to check improved density");
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
async function promptApproval(message) {
|
|
972
|
+
console.log(pc.cyan(message));
|
|
973
|
+
console.log("");
|
|
974
|
+
return true;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
async function densityAlerts(rootDir) {
|
|
978
|
+
const agentsDir = path.join(rootDir, ".agents");
|
|
979
|
+
const historyFile = path.join(agentsDir, "data", "density-history.json");
|
|
980
|
+
|
|
981
|
+
console.log(pc.bold(pc.green("Code IA Sota")));
|
|
982
|
+
console.log(pc.cyan("Analyzing density trends for alerts..."));
|
|
983
|
+
console.log("");
|
|
984
|
+
|
|
985
|
+
if (!(await exists(historyFile))) {
|
|
986
|
+
console.log(pc.yellow("No density history found. Run `npm run density:snapshot` first."));
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
const historyContent = await fs.readFile(historyFile, "utf8");
|
|
991
|
+
const history = JSON.parse(historyContent);
|
|
992
|
+
|
|
993
|
+
if (history.length < 2) {
|
|
994
|
+
console.log(pc.yellow("Need at least 2 snapshots to analyze trends."));
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
const alerts = [];
|
|
999
|
+
const skillTrends = {};
|
|
1000
|
+
|
|
1001
|
+
for (const snapshot of history) {
|
|
1002
|
+
for (const skill of snapshot.skills) {
|
|
1003
|
+
if (!skillTrends[skill.skill]) {
|
|
1004
|
+
skillTrends[skill.skill] = [];
|
|
1005
|
+
}
|
|
1006
|
+
skillTrends[skill.skill].push({
|
|
1007
|
+
timestamp: snapshot.timestamp,
|
|
1008
|
+
density: skill.density
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
for (const [skillName, trends] of Object.entries(skillTrends)) {
|
|
1014
|
+
if (trends.length < 2) continue;
|
|
1015
|
+
|
|
1016
|
+
const first = trends[0];
|
|
1017
|
+
const last = trends[trends.length - 1];
|
|
1018
|
+
const change = last.density - first.density;
|
|
1019
|
+
const changePercent = Math.round((change / first.density) * 100);
|
|
1020
|
+
|
|
1021
|
+
if (change < -ALERT_THRESHOLD) {
|
|
1022
|
+
alerts.push({
|
|
1023
|
+
skill: skillName,
|
|
1024
|
+
firstDensity: first.density,
|
|
1025
|
+
lastDensity: last.density,
|
|
1026
|
+
change: change,
|
|
1027
|
+
changePercent: changePercent,
|
|
1028
|
+
severity: change < -20 ? "critical" : "warning"
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
if (alerts.length === 0) {
|
|
1034
|
+
console.log(pc.green("No density degradation alerts. All skills are stable."));
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
console.log(`Found ${alerts.length} density degradation alert(s):`);
|
|
1039
|
+
console.log("");
|
|
1040
|
+
|
|
1041
|
+
for (const alert of alerts) {
|
|
1042
|
+
const severityColor = alert.severity === "critical" ? pc.red : pc.yellow;
|
|
1043
|
+
console.log(severityColor(`${alert.severity.toUpperCase()}: ${alert.skill}`));
|
|
1044
|
+
console.log(` Density dropped from ${alert.firstDensity}% to ${alert.lastDensity}% (${alert.changePercent}% change)`);
|
|
1045
|
+
console.log(` Recommendation: Use \`npm run suggest:refactor\` to identify refactoring opportunities`);
|
|
1046
|
+
console.log("");
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
console.log(pc.cyan("Summary:"));
|
|
1050
|
+
console.log(` Critical alerts: ${alerts.filter(a => a.severity === "critical").length}`);
|
|
1051
|
+
console.log(` Warning alerts: ${alerts.filter(a => a.severity === "warning").length}`);
|
|
1052
|
+
console.log("");
|
|
1053
|
+
console.log(pc.cyan("Next steps:"));
|
|
1054
|
+
console.log(" 1. Review the alerts above");
|
|
1055
|
+
console.log(" 2. Use `npm run suggest:refactor` for detailed guidance");
|
|
1056
|
+
console.log(" 3. Consider refactoring critical alerts first");
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
async function bulkRefactor(rootDir) {
|
|
1060
|
+
const agentsDir = path.join(rootDir, ".agents");
|
|
1061
|
+
const skillsDir = path.join(agentsDir, "skills");
|
|
1062
|
+
const referencesDir = path.join(agentsDir, "references");
|
|
1063
|
+
const skillFiles = await listSkillFiles(skillsDir);
|
|
1064
|
+
const dryRun = process.argv.includes("--dry-run");
|
|
1065
|
+
|
|
1066
|
+
console.log(pc.bold(pc.green("Code IA Sota")));
|
|
1067
|
+
console.log(pc.cyan("Bulk refactoring with batch approval..."));
|
|
1068
|
+
console.log("");
|
|
1069
|
+
|
|
1070
|
+
if (dryRun) {
|
|
1071
|
+
console.log(pc.yellow("Dry-run mode: no changes will be applied."));
|
|
1072
|
+
console.log("");
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
if (!(await exists(referencesDir))) {
|
|
1076
|
+
if (!dryRun) {
|
|
1077
|
+
await fs.mkdir(referencesDir, { recursive: true });
|
|
1078
|
+
}
|
|
1079
|
+
console.log(`Created references directory: ${referencesDir}`);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
const lowDensitySkills = [];
|
|
1083
|
+
|
|
1084
|
+
for (const filePath of skillFiles) {
|
|
1085
|
+
if (!(await exists(filePath))) continue;
|
|
1086
|
+
|
|
1087
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
1088
|
+
const density = calculateDensity(content);
|
|
1089
|
+
const skillName = path.basename(path.dirname(filePath));
|
|
1090
|
+
|
|
1091
|
+
if (density < DENSITY_THRESHOLD) {
|
|
1092
|
+
const suggestions = await analyzeReferenceContent(content);
|
|
1093
|
+
lowDensitySkills.push({
|
|
1094
|
+
skill: skillName,
|
|
1095
|
+
density: density,
|
|
1096
|
+
filePath: path.relative(rootDir, filePath),
|
|
1097
|
+
suggestions: suggestions
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
lowDensitySkills.sort((a, b) => a.density - b.density);
|
|
1103
|
+
|
|
1104
|
+
if (lowDensitySkills.length === 0) {
|
|
1105
|
+
console.log(pc.green("No low-density skills found. All skills meet the density threshold."));
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
console.log(`Found ${lowDensitySkills.length} low-density skill(s):`);
|
|
1110
|
+
console.log("");
|
|
1111
|
+
|
|
1112
|
+
for (const skill of lowDensitySkills) {
|
|
1113
|
+
console.log(pc.yellow(`${skill.skill} (${skill.density}% density)`));
|
|
1114
|
+
console.log(` Suggestions: ${skill.suggestions.length}`);
|
|
1115
|
+
for (const suggestion of skill.suggestions) {
|
|
1116
|
+
console.log(` - ${suggestion.section}: ${suggestion.reason}`);
|
|
1117
|
+
}
|
|
1118
|
+
console.log("");
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
const approved = await promptApproval(`Apply bulk refactoring to all ${lowDensitySkills.length} skills? (y/n)`);
|
|
1122
|
+
if (!approved) {
|
|
1123
|
+
console.log("Bulk refactoring cancelled.");
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
if (!dryRun) {
|
|
1128
|
+
console.log("");
|
|
1129
|
+
console.log(pc.green("Bulk refactoring plan approved."));
|
|
1130
|
+
console.log("");
|
|
1131
|
+
console.log(pc.cyan("Next steps:"));
|
|
1132
|
+
console.log(" 1. Review the suggestions above");
|
|
1133
|
+
console.log(" 2. Manually implement refactoring using `npm run suggest:refactor` for each skill");
|
|
1134
|
+
console.log(" 3. Re-run validation to check improved density");
|
|
1135
|
+
console.log("");
|
|
1136
|
+
console.log(pc.cyan("Summary:"));
|
|
1137
|
+
console.log(` Total skills to refactor: ${lowDensitySkills.length}`);
|
|
1138
|
+
console.log(` Average density: ${Math.round(lowDensitySkills.reduce((a, b) => a + b.density, 0) / lowDensitySkills.length)}%`);
|
|
1139
|
+
} else {
|
|
1140
|
+
console.log("");
|
|
1141
|
+
console.log(pc.yellow("Dry-run: would apply bulk refactoring to all skills"));
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
async function densityFix(rootDir) {
|
|
1146
|
+
const agentsDir = path.join(rootDir, ".agents");
|
|
1147
|
+
const skillsDir = path.join(agentsDir, "skills");
|
|
1148
|
+
const skillFiles = await listSkillFiles(skillsDir);
|
|
1149
|
+
const dryRun = process.argv.includes("--dry-run");
|
|
1150
|
+
|
|
1151
|
+
console.log(pc.bold(pc.green("Code IA Sota")));
|
|
1152
|
+
console.log(pc.cyan("Automated density fixes..."));
|
|
1153
|
+
console.log("");
|
|
1154
|
+
|
|
1155
|
+
if (dryRun) {
|
|
1156
|
+
console.log(pc.yellow("Dry-run mode: no changes will be applied."));
|
|
1157
|
+
console.log("");
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
const lowDensitySkills = [];
|
|
1161
|
+
|
|
1162
|
+
for (const filePath of skillFiles) {
|
|
1163
|
+
if (!(await exists(filePath))) continue;
|
|
1164
|
+
|
|
1165
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
1166
|
+
const density = calculateDensity(content);
|
|
1167
|
+
const skillName = path.basename(path.dirname(filePath));
|
|
1168
|
+
|
|
1169
|
+
if (density < DENSITY_THRESHOLD) {
|
|
1170
|
+
lowDensitySkills.push({
|
|
1171
|
+
skill: skillName,
|
|
1172
|
+
density: density,
|
|
1173
|
+
filePath: path.relative(rootDir, filePath)
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
lowDensitySkills.sort((a, b) => a.density - b.density);
|
|
1179
|
+
|
|
1180
|
+
if (lowDensitySkills.length === 0) {
|
|
1181
|
+
console.log(pc.green("No low-density skills found. All skills meet the density threshold."));
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
console.log(`Found ${lowDensitySkills.length} low-density skill(s) to fix:`);
|
|
1186
|
+
console.log("");
|
|
1187
|
+
|
|
1188
|
+
for (const skill of lowDensitySkills) {
|
|
1189
|
+
console.log(pc.yellow(`${skill.skill} (${skill.density}% density)`));
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
console.log("");
|
|
1193
|
+
const approved = await promptApproval(`Apply automated fixes to all ${lowDensitySkills.length} skills? (y/n)`);
|
|
1194
|
+
if (!approved) {
|
|
1195
|
+
console.log("Automated fixes cancelled.");
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
if (!dryRun) {
|
|
1200
|
+
console.log("");
|
|
1201
|
+
console.log(pc.green("Automated fixes approved."));
|
|
1202
|
+
console.log("");
|
|
1203
|
+
console.log(pc.cyan("Creating references directories per skill..."));
|
|
1204
|
+
|
|
1205
|
+
for (const skill of lowDensitySkills) {
|
|
1206
|
+
const skillPath = path.join(skillsDir, skill.skill);
|
|
1207
|
+
const referencesPath = path.join(skillPath, "references");
|
|
1208
|
+
|
|
1209
|
+
if (!(await exists(referencesPath))) {
|
|
1210
|
+
await fs.mkdir(referencesPath, { recursive: true });
|
|
1211
|
+
console.log(` Created references directory: ${skill.skill}/references`);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
console.log("");
|
|
1216
|
+
console.log(pc.cyan("Note: Automated fixes are limited to reference content extraction."));
|
|
1217
|
+
console.log(" For comprehensive refactoring, use `npm run suggest:refactor` manually.");
|
|
1218
|
+
console.log(" References should be split into smaller files by specialty.");
|
|
1219
|
+
console.log("");
|
|
1220
|
+
console.log(pc.cyan("Summary:"));
|
|
1221
|
+
console.log(` Total skills to fix: ${lowDensitySkills.length}`);
|
|
1222
|
+
console.log(` Average density: ${Math.round(lowDensitySkills.reduce((a, b) => a + b.density, 0) / lowDensitySkills.length)}%`);
|
|
1223
|
+
} else {
|
|
1224
|
+
console.log("");
|
|
1225
|
+
console.log(pc.yellow("Dry-run: would apply automated fixes to all skills"));
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
async function skillLifecycle(rootDir) {
|
|
1230
|
+
const agentsDir = path.join(rootDir, ".agents");
|
|
1231
|
+
const skillsDir = path.join(agentsDir, "skills");
|
|
1232
|
+
const skillFiles = await listSkillFiles(skillsDir);
|
|
1233
|
+
|
|
1234
|
+
console.log(pc.bold(pc.green("Code IA Sota")));
|
|
1235
|
+
console.log(pc.cyan("Skill Lifecycle Management"));
|
|
1236
|
+
console.log("");
|
|
1237
|
+
|
|
1238
|
+
const skills = [];
|
|
1239
|
+
|
|
1240
|
+
for (const filePath of skillFiles) {
|
|
1241
|
+
if (!(await exists(filePath))) continue;
|
|
1242
|
+
|
|
1243
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
1244
|
+
const frontmatter = parseFrontmatter(content);
|
|
1245
|
+
const skillName = path.basename(path.dirname(filePath));
|
|
1246
|
+
const density = calculateDensity(content);
|
|
1247
|
+
|
|
1248
|
+
skills.push({
|
|
1249
|
+
skill: skillName,
|
|
1250
|
+
status: frontmatter?.data?.status || "active",
|
|
1251
|
+
owner: frontmatter?.data?.owner || "unknown",
|
|
1252
|
+
created: frontmatter?.data?.created || "unknown",
|
|
1253
|
+
updated: frontmatter?.data?.updated || "unknown",
|
|
1254
|
+
density: density,
|
|
1255
|
+
filePath: path.relative(rootDir, filePath)
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
console.log(`Total skills: ${skills.length}`);
|
|
1260
|
+
console.log("");
|
|
1261
|
+
|
|
1262
|
+
const statusCount = {
|
|
1263
|
+
active: skills.filter(s => s.status === "active").length,
|
|
1264
|
+
deprecated: skills.filter(s => s.status === "deprecated").length,
|
|
1265
|
+
archived: skills.filter(s => s.status === "archived").length
|
|
1266
|
+
};
|
|
1267
|
+
|
|
1268
|
+
console.log(pc.cyan("Status Distribution:"));
|
|
1269
|
+
console.log(` Active: ${statusCount.active}`);
|
|
1270
|
+
console.log(` Deprecated: ${statusCount.deprecated}`);
|
|
1271
|
+
console.log(` Archived: ${statusCount.archived}`);
|
|
1272
|
+
console.log("");
|
|
1273
|
+
|
|
1274
|
+
console.log(pc.cyan("Skills by Status:"));
|
|
1275
|
+
console.log("");
|
|
1276
|
+
|
|
1277
|
+
for (const skill of skills.sort((a, b) => a.skill.localeCompare(b.skill))) {
|
|
1278
|
+
const statusColor = skill.status === "active" ? pc.green : skill.status === "deprecated" ? pc.yellow : pc.gray;
|
|
1279
|
+
console.log(`${statusColor(skill.status.toUpperCase())}: ${skill.skill} (${skill.density}% density)`);
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
console.log("");
|
|
1283
|
+
console.log(pc.cyan("To update skill status, add \`status: [active|deprecated|archived]\` to frontmatter."));
|
|
1284
|
+
console.log(pc.cyan("To add metadata, add \`owner\`, \`created\`, \`updated\` fields to frontmatter."));
|
|
1285
|
+
}
|
|
1286
|
+
|
|
85
1287
|
async function main() {
|
|
1288
|
+
if (process.argv[2] === "validate") {
|
|
1289
|
+
await validateHarness(path.resolve(TARGET_DIR, process.argv[3] || "."));
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
if (process.argv[2] === "suggest:refactor") {
|
|
1294
|
+
await suggestRefactor(path.resolve(TARGET_DIR, process.argv[3] || "."));
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
if (process.argv[2] === "benchmark:density") {
|
|
1299
|
+
await benchmarkDensity(path.resolve(TARGET_DIR, process.argv[3] || "."));
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
if (process.argv[2] === "density:snapshot") {
|
|
1304
|
+
await densitySnapshot(path.resolve(TARGET_DIR, process.argv[3] || "."));
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
if (process.argv[2] === "density:trends") {
|
|
1309
|
+
await densityTrends(path.resolve(TARGET_DIR, process.argv[3] || "."));
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
if (process.argv[2] === "refactor:auto") {
|
|
1314
|
+
await autoRefactor(path.resolve(TARGET_DIR, process.argv[3] || "."));
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
if (process.argv[2] === "density:alerts") {
|
|
1319
|
+
await densityAlerts(path.resolve(TARGET_DIR, process.argv[3] || "."));
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
if (process.argv[2] === "refactor:bulk") {
|
|
1324
|
+
await bulkRefactor(path.resolve(TARGET_DIR, process.argv[3] || "."));
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
if (process.argv[2] === "density:fix") {
|
|
1329
|
+
await densityFix(path.resolve(TARGET_DIR, process.argv[3] || "."));
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
if (process.argv[2] === "skill:lifecycle") {
|
|
1334
|
+
await skillLifecycle(path.resolve(TARGET_DIR, process.argv[3] || "."));
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
const selectedTools = await selectTools();
|
|
86
1339
|
const tmpDir = await fs.mkdtemp(
|
|
87
1340
|
path.join(os.tmpdir(), "code-ia-sota-")
|
|
88
1341
|
);
|
|
@@ -92,6 +1345,10 @@ async function main() {
|
|
|
92
1345
|
pc.bold(pc.green("Code IA Sota"))
|
|
93
1346
|
);
|
|
94
1347
|
|
|
1348
|
+
console.log(
|
|
1349
|
+
pc.cyan(`Selected tools: ${selectedTools.length > 0 ? selectedTools.join(", ") : "none"}`)
|
|
1350
|
+
);
|
|
1351
|
+
|
|
95
1352
|
await cloneTemplate(tmpDir);
|
|
96
1353
|
|
|
97
1354
|
console.log(
|
|
@@ -100,6 +1357,9 @@ async function main() {
|
|
|
100
1357
|
|
|
101
1358
|
await copyFiles(tmpDir);
|
|
102
1359
|
|
|
1360
|
+
const integrationSummary =
|
|
1361
|
+
await installToolIntegrations(selectedTools);
|
|
1362
|
+
|
|
103
1363
|
console.log(
|
|
104
1364
|
pc.green("Installation completed.")
|
|
105
1365
|
);
|
|
@@ -108,6 +1368,28 @@ async function main() {
|
|
|
108
1368
|
console.log("Created:");
|
|
109
1369
|
console.log(" AGENTS.md");
|
|
110
1370
|
console.log(" .agents/");
|
|
1371
|
+
|
|
1372
|
+
if (integrationSummary.created.length > 0) {
|
|
1373
|
+
for (const filePath of integrationSummary.created) {
|
|
1374
|
+
console.log(` ${filePath}`);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
if (integrationSummary.updated.length > 0) {
|
|
1379
|
+
console.log("");
|
|
1380
|
+
console.log("Updated:");
|
|
1381
|
+
for (const filePath of integrationSummary.updated) {
|
|
1382
|
+
console.log(` ${filePath}`);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
if (integrationSummary.skipped.length > 0) {
|
|
1387
|
+
console.log("");
|
|
1388
|
+
console.log(pc.yellow("Skipped existing unmanaged files:"));
|
|
1389
|
+
for (const filePath of integrationSummary.skipped) {
|
|
1390
|
+
console.log(` ${filePath}`);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
111
1393
|
} finally {
|
|
112
1394
|
await fsExtra.remove(tmpDir);
|
|
113
1395
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-ia-sota",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Agent scaffold installer for AGENTS.md and .agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"code-ia-sota": "bin/code-ia-sota.js"
|
|
8
8
|
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"validate:harness": "node bin/code-ia-sota.js validate ..",
|
|
11
|
+
"suggest:refactor": "node bin/code-ia-sota.js suggest:refactor ..",
|
|
12
|
+
"benchmark:density": "node bin/code-ia-sota.js benchmark:density ..",
|
|
13
|
+
"density:snapshot": "node bin/code-ia-sota.js density:snapshot ..",
|
|
14
|
+
"density:trends": "node bin/code-ia-sota.js density:trends ..",
|
|
15
|
+
"density:alerts": "node bin/code-ia-sota.js density:alerts ..",
|
|
16
|
+
"density:fix": "node bin/code-ia-sota.js density:fix ..",
|
|
17
|
+
"refactor:auto": "node bin/code-ia-sota.js refactor:auto ..",
|
|
18
|
+
"refactor:bulk": "node bin/code-ia-sota.js refactor:bulk ..",
|
|
19
|
+
"skill:lifecycle": "node bin/code-ia-sota.js skill:lifecycle .."
|
|
20
|
+
},
|
|
9
21
|
"files": [
|
|
10
22
|
"bin",
|
|
11
23
|
"README.md"
|