skills-doctor 0.5.0 → 0.5.2
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/CHANGELOG.md +16 -0
- package/README.md +2 -1
- package/dist/cli/commands/scan.js +23 -1
- package/dist/cli/utils/cleanup-handoff-to-agent.d.ts +2 -0
- package/dist/cli/utils/cleanup-handoff-to-agent.d.ts.map +1 -1
- package/dist/cli/utils/cleanup-handoff-to-agent.js +7 -0
- package/dist/cli/utils/prompts.d.ts +1 -0
- package/dist/cli/utils/prompts.d.ts.map +1 -1
- package/dist/domain/build-cleanup-handoff-prompt.d.ts +2 -0
- package/dist/domain/build-cleanup-handoff-prompt.d.ts.map +1 -1
- package/dist/domain/build-cleanup-handoff-prompt.js +5 -4
- package/dist/domain/build-report.d.ts +0 -1
- package/dist/domain/build-report.d.ts.map +1 -1
- package/dist/domain/build-report.js +1 -3
- package/dist/domain/default-report-output-root.d.ts +2 -0
- package/dist/domain/default-report-output-root.d.ts.map +1 -0
- package/dist/domain/default-report-output-root.js +19 -0
- package/dist/domain/summarize-findings.d.ts.map +1 -1
- package/dist/domain/summarize-findings.js +1 -5
- package/dist/domain/write-cleanup-directory.d.ts.map +1 -1
- package/dist/domain/write-cleanup-directory.js +4 -2
- package/dist/domain/write-findings-directory.d.ts.map +1 -1
- package/dist/domain/write-findings-directory.js +4 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.5.2] - 2026-06-20
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Let users choose which unused cleanup candidates to include in cleanup handoff prompts.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Include all unused cleanup candidates in reports and handoff selection instead of capping the detected list.
|
|
18
|
+
|
|
19
|
+
## [0.5.1] - 2026-06-20
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Store default repair and cleanup handoff reports under the OS temp directory instead of creating `.skills-doctor/reports` in the scanned project.
|
|
24
|
+
|
|
9
25
|
## [0.5.0] - 2026-06-20
|
|
10
26
|
|
|
11
27
|
### Added
|
package/README.md
CHANGED
|
@@ -104,7 +104,8 @@ available, reads local Codex usage traces from known `~/.codex` paths. It can:
|
|
|
104
104
|
3. Let you choose a repair subset: errors, errors plus warnings, all findings,
|
|
105
105
|
or selected skills.
|
|
106
106
|
4. Detect local `claude` and `codex` executables.
|
|
107
|
-
5. Write a full report under
|
|
107
|
+
5. Write a full report under the OS temp directory, for example
|
|
108
|
+
`/tmp/skills-doctor-<uid>/reports/<timestamp>/` on Linux.
|
|
108
109
|
6. Generate a compact `handoff-prompt.md` tailored to the selected findings or
|
|
109
110
|
`cleanup-prompt.md` tailored to usage cleanup.
|
|
110
111
|
7. Preview the launch command.
|
|
@@ -300,7 +300,7 @@ const reviewScan = async (report, input) => {
|
|
|
300
300
|
...(report.usage !== undefined && report.usage.topRecommendations.length > 0
|
|
301
301
|
? [
|
|
302
302
|
{
|
|
303
|
-
name: "
|
|
303
|
+
name: "Choose unused skills to disable",
|
|
304
304
|
value: "cleanup",
|
|
305
305
|
},
|
|
306
306
|
{ name: "View usage ranking", value: "usage-ranking" },
|
|
@@ -350,8 +350,14 @@ const reviewScan = async (report, input) => {
|
|
|
350
350
|
};
|
|
351
351
|
const runCleanupAgentFlow = async (report, input) => {
|
|
352
352
|
try {
|
|
353
|
+
const selectedRecommendations = await selectCleanupRecommendations(report, input);
|
|
354
|
+
if (selectedRecommendations.length === 0) {
|
|
355
|
+
input.write("Cleanup handoff cancelled.\n");
|
|
356
|
+
return undefined;
|
|
357
|
+
}
|
|
353
358
|
const handoff = await prepareCleanupHandoff({
|
|
354
359
|
report,
|
|
360
|
+
recommendations: selectedRecommendations,
|
|
355
361
|
outputRoot: input.cleanupReportOutputRoot,
|
|
356
362
|
timestamp: input.cleanupReportTimestamp,
|
|
357
363
|
});
|
|
@@ -427,6 +433,22 @@ const runCleanupAgentFlow = async (report, input) => {
|
|
|
427
433
|
throw error;
|
|
428
434
|
}
|
|
429
435
|
};
|
|
436
|
+
const selectCleanupRecommendations = async (report, input) => {
|
|
437
|
+
const usage = report.usage;
|
|
438
|
+
if (usage === undefined)
|
|
439
|
+
throw new CliInputError("Usage analysis is required before cleanup.");
|
|
440
|
+
const candidates = usage.recommendations.filter((recommendation) => recommendation.action === "disable-candidate");
|
|
441
|
+
if (candidates.length === 0)
|
|
442
|
+
return [];
|
|
443
|
+
const defaultPaths = new Set(usage.topRecommendations.map((recommendation) => recommendation.skillPath));
|
|
444
|
+
const selectedPaths = new Set(await input.prompts.checkbox("Select unused skills to disable", candidates.map((recommendation) => ({
|
|
445
|
+
name: recommendation.skillName,
|
|
446
|
+
value: recommendation.skillPath,
|
|
447
|
+
description: compactSkillPath(recommendation.skillPath),
|
|
448
|
+
checked: defaultPaths.has(recommendation.skillPath),
|
|
449
|
+
}))));
|
|
450
|
+
return candidates.filter((recommendation) => selectedPaths.has(recommendation.skillPath));
|
|
451
|
+
};
|
|
430
452
|
const runRepairAgentFlow = async (report, input) => {
|
|
431
453
|
try {
|
|
432
454
|
const handoff = await prepareRepairHandoff({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SkillCleanupRecommendation } from "../../domain/analyze-skill-usage.js";
|
|
1
2
|
import type { ScanReport } from "../../domain/build-report.js";
|
|
2
3
|
import { writeCleanupDirectory } from "../../domain/write-cleanup-directory.js";
|
|
3
4
|
export type PreparedCleanupHandoff = {
|
|
@@ -10,6 +11,7 @@ export type PreparedCleanupHandoff = {
|
|
|
10
11
|
};
|
|
11
12
|
export type PrepareCleanupHandoffInput = {
|
|
12
13
|
readonly report: ScanReport;
|
|
14
|
+
readonly recommendations?: readonly SkillCleanupRecommendation[] | undefined;
|
|
13
15
|
readonly outputRoot?: string | undefined;
|
|
14
16
|
readonly timestamp?: string | undefined;
|
|
15
17
|
readonly writeDirectory?: typeof writeCleanupDirectory | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup-handoff-to-agent.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/cleanup-handoff-to-agent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cleanup-handoff-to-agent.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/cleanup-handoff-to-agent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAK/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAGhF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,0BAA0B,EAAE,GAAG,SAAS,CAAC;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,qBAAqB,GAAG,SAAS,CAAC;CACpE,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,OAAO,0BAA0B,KAChC,OAAO,CAAC,sBAAsB,CAgDhC,CAAC"}
|
|
@@ -10,6 +10,12 @@ export const prepareCleanupHandoff = async (input) => {
|
|
|
10
10
|
if (input.report.usage.topRecommendations.length === 0) {
|
|
11
11
|
throw new CliInputError("No cleanup recommendations are available.");
|
|
12
12
|
}
|
|
13
|
+
if (input.recommendations !== undefined && input.recommendations.length === 0) {
|
|
14
|
+
throw new CliInputError("No cleanup recommendations were selected.");
|
|
15
|
+
}
|
|
16
|
+
if (input.recommendations?.some((recommendation) => recommendation.action !== "disable-candidate")) {
|
|
17
|
+
throw new CliInputError("Cleanup handoff can only disable selected unused skills.");
|
|
18
|
+
}
|
|
13
19
|
const reportResult = await tryWriteCleanupDirectory({
|
|
14
20
|
report: input.report,
|
|
15
21
|
outputRoot: input.outputRoot,
|
|
@@ -18,6 +24,7 @@ export const prepareCleanupHandoff = async (input) => {
|
|
|
18
24
|
});
|
|
19
25
|
const prompt = buildCleanupHandoffPrompt({
|
|
20
26
|
report: input.report,
|
|
27
|
+
recommendations: input.recommendations,
|
|
21
28
|
reportDirectory: reportResult.result?.directory,
|
|
22
29
|
});
|
|
23
30
|
let promptPath;
|
|
@@ -3,6 +3,7 @@ export type Choice<Value extends string> = {
|
|
|
3
3
|
readonly name: string;
|
|
4
4
|
readonly value: Value;
|
|
5
5
|
readonly description?: string;
|
|
6
|
+
readonly checked?: boolean;
|
|
6
7
|
};
|
|
7
8
|
export type PromptAdapter = {
|
|
8
9
|
readonly checkbox: <Value extends string>(message: string, choices: readonly Choice<Value>[]) => Promise<Value[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/prompts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,MAAM,MAAM,CAAC,KAAK,SAAS,MAAM,IAAI;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/prompts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,MAAM,MAAM,CAAC,KAAK,SAAS,MAAM,IAAI;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,SAAS,MAAM,EACtC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,MAAM,CAAC,KAAK,CAAC,EAAE,KAC9B,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAChF,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5E,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,SAAS,MAAM,EACpC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,MAAM,CAAC,KAAK,CAAC,EAAE,KAC9B,OAAO,CAAC,KAAK,CAAC,CAAC;CACrB,CAAC;AAEF,qBAAa,oBAAqB,SAAQ,aAAa;;CAKtD;AAED,eAAO,MAAM,qBAAqB,EAAE,aAQnC,CAAC"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { SkillCleanupRecommendation } from "./analyze-skill-usage.js";
|
|
1
2
|
import type { ScanReport } from "./build-report.js";
|
|
2
3
|
export type BuildCleanupHandoffPromptInput = {
|
|
3
4
|
readonly report: ScanReport;
|
|
5
|
+
readonly recommendations?: readonly SkillCleanupRecommendation[] | undefined;
|
|
4
6
|
readonly reportDirectory?: string | undefined;
|
|
5
7
|
};
|
|
6
8
|
export declare const buildCleanupHandoffPrompt: (input: BuildCleanupHandoffPromptInput) => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-cleanup-handoff-prompt.d.ts","sourceRoot":"","sources":["../../src/domain/build-cleanup-handoff-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC;AAErE,MAAM,MAAM,8BAA8B,GAAG;IAC3C,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,OAAO,8BAA8B,KAAG,
|
|
1
|
+
{"version":3,"file":"build-cleanup-handoff-prompt.d.ts","sourceRoot":"","sources":["../../src/domain/build-cleanup-handoff-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC;AAErE,MAAM,MAAM,8BAA8B,GAAG;IAC3C,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,0BAA0B,EAAE,GAAG,SAAS,CAAC;IAC7E,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,OAAO,8BAA8B,KAAG,MAyDjF,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const buildCleanupHandoffPrompt = (input) => {
|
|
2
2
|
const usage = requireUsage(input.report);
|
|
3
|
+
const recommendations = input.recommendations ?? usage.topRecommendations;
|
|
3
4
|
const lines = [
|
|
4
5
|
"Clean up Agent Skills context pressure using the Skills Doctor usage report.",
|
|
5
6
|
"",
|
|
@@ -15,12 +16,12 @@ export const buildCleanupHandoffPrompt = (input) => {
|
|
|
15
16
|
else {
|
|
16
17
|
lines.push("Usage report directory: unavailable; use the inline summary below.");
|
|
17
18
|
}
|
|
18
|
-
lines.push("", "Cleanup rules:", "- Inspect the usage report first.", "- Preserve frequently used and recently used skills.", "- Preserve project-local skills unless there is strong evidence and clear user intent.", "- Do not delete skills.", "- Only disable skills with a `disable-candidate` recommendation.", "- Do not modify skills recommended as keep, review, shorten-description, or merge-candidate.", "- Do not move skill directories.", "- For unused global/plugin skills, disable them the same way Codex `/skills` does: add or update `[[skills.config]]` entries in `~/.codex/config.toml` with the skill `path` and `enabled = false`.", "- Re-enable skills by using Codex `/skills` or removing the matching disabled config entry.", "- Restart Codex or start a fresh session after config changes so the disabled skill list is reloaded.", "- Do not delete skills solely because usage is unknown.", "- Do not expose raw Codex logs or transcript text.", "- Verify by rerunning `npx skills-doctor@latest` after changes.", "", "
|
|
19
|
-
for (const recommendation of
|
|
19
|
+
lines.push("", "Cleanup rules:", "- Inspect the usage report first.", "- Preserve frequently used and recently used skills.", "- Preserve project-local skills unless there is strong evidence and clear user intent.", "- Do not delete skills.", "- Only disable skills with a `disable-candidate` recommendation.", "- Disable only the selected skills listed below; leave other cleanup candidates unchanged.", "- Do not modify skills recommended as keep, review, shorten-description, or merge-candidate.", "- Do not move skill directories.", "- For unused global/plugin skills, disable them the same way Codex `/skills` does: add or update `[[skills.config]]` entries in `~/.codex/config.toml` with the skill `path` and `enabled = false`.", "- Re-enable skills by using Codex `/skills` or removing the matching disabled config entry.", "- Restart Codex or start a fresh session after config changes so the disabled skill list is reloaded.", "- Do not delete skills solely because usage is unknown.", "- Do not expose raw Codex logs or transcript text.", "- Verify by rerunning `npx skills-doctor@latest` after changes.", "", "Selected unused skills to disable:");
|
|
20
|
+
for (const recommendation of recommendations) {
|
|
20
21
|
lines.push(`- ${recommendation.action} ${recommendation.skillName}`, ` Path: ${recommendation.skillPath}`, ` Reason: ${recommendation.reason}`);
|
|
21
22
|
}
|
|
22
|
-
if (
|
|
23
|
-
lines.push("- No unused disable candidates were
|
|
23
|
+
if (recommendations.length === 0) {
|
|
24
|
+
lines.push("- No unused disable candidates were selected.");
|
|
24
25
|
}
|
|
25
26
|
lines.push("", "Make only reversible Codex skills-config disable changes, then stop and report what changed.");
|
|
26
27
|
return lines.join("\n");
|
|
@@ -57,7 +57,6 @@ export type BuildScanReportInput = {
|
|
|
57
57
|
export type BuildScanReportUsageInput = {
|
|
58
58
|
readonly analysis: SkillUsageAnalysis;
|
|
59
59
|
readonly contextPressure: ContextBudgetPressure;
|
|
60
|
-
readonly topRecommendationLimit?: number | undefined;
|
|
61
60
|
};
|
|
62
61
|
export declare const buildScanReport: (input: BuildScanReportInput) => ScanReport;
|
|
63
62
|
//# sourceMappingURL=build-report.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-report.d.ts","sourceRoot":"","sources":["../../src/domain/build-report.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"build-report.d.ts","sourceRoot":"","sources":["../../src/domain/build-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -58,9 +58,7 @@ const buildReportUsage = (input) => ({
|
|
|
58
58
|
pluginContributedSkillCount: input.analysis.pluginContributedSkillCount,
|
|
59
59
|
skillsByUsage: input.analysis.skillsByUsage,
|
|
60
60
|
recommendations: input.analysis.recommendations,
|
|
61
|
-
topRecommendations: input.analysis.recommendations
|
|
62
|
-
.filter((recommendation) => recommendation.action === "disable-candidate")
|
|
63
|
-
.slice(0, input.topRecommendationLimit ?? 10),
|
|
61
|
+
topRecommendations: input.analysis.recommendations.filter((recommendation) => recommendation.action === "disable-candidate"),
|
|
64
62
|
});
|
|
65
63
|
const countSeverity = (findings, severity) => findings.filter((finding) => finding.severity === severity).length;
|
|
66
64
|
const countDiagnosticSeverity = (diagnostics, severity) => diagnostics.filter((diagnostic) => diagnostic.severity === severity).length;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-report-output-root.d.ts","sourceRoot":"","sources":["../../src/domain/default-report-output-root.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,uBAAuB,QAAO,MACgB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { tmpdir, userInfo } from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export const defaultReportOutputRoot = () => path.join(tmpdir(), scopedTempDirectoryName(), "reports");
|
|
4
|
+
const scopedTempDirectoryName = () => {
|
|
5
|
+
const uid = process.getuid?.();
|
|
6
|
+
if (uid !== undefined) {
|
|
7
|
+
return `skills-doctor-${uid}`;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
return `skills-doctor-${safePathSegment(userInfo().username)}`;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return "skills-doctor-user";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const safePathSegment = (value) => value
|
|
17
|
+
.toLowerCase()
|
|
18
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
19
|
+
.replace(/^-+|-+$/g, "") || "user";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summarize-findings.d.ts","sourceRoot":"","sources":["../../src/domain/summarize-findings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEnD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IAC5C,QAAQ,CAAC,aAAa,EAAE,SAAS,YAAY,EAAE,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACtC,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,UAAU,SAAS,OAAO,EAAE,KAAG,cAY/D,CAAC;AAEH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,UAAU,EAClB,UAAS,mBAAwB,KAChC,CAAC,GAAG,CAQN,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,UAAU,EAClB,UAAS,yBAA8B,KACtC,
|
|
1
|
+
{"version":3,"file":"summarize-findings.d.ts","sourceRoot":"","sources":["../../src/domain/summarize-findings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEnD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IAC5C,QAAQ,CAAC,aAAa,EAAE,SAAS,YAAY,EAAE,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACtC,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,UAAU,SAAS,OAAO,EAAE,KAAG,cAY/D,CAAC;AAEH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,UAAU,EAClB,UAAS,mBAAwB,KAChC,CAAC,GAAG,CAQN,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,UAAU,EAClB,UAAS,yBAA8B,KACtC,MAmCF,CAAC"}
|
|
@@ -36,11 +36,7 @@ export const renderHumanSummary = (report, options = {}) => {
|
|
|
36
36
|
}
|
|
37
37
|
const cleanupCandidateCount = countCleanupCandidates(report);
|
|
38
38
|
if (cleanupCandidateCount > 0) {
|
|
39
|
-
|
|
40
|
-
const shownSuffix = shownCount < cleanupCandidateCount
|
|
41
|
-
? ` (${warning(String(shownCount), shouldColor)} shown in next cleanup batch)`
|
|
42
|
-
: "";
|
|
43
|
-
lines.push(`${label("Cleanup candidates", shouldColor)}: ${warning(String(cleanupCandidateCount), shouldColor)} enabled unused skills${shownSuffix}`);
|
|
39
|
+
lines.push(`${label("Cleanup candidates", shouldColor)}: ${warning(String(cleanupCandidateCount), shouldColor)} enabled unused skills`);
|
|
44
40
|
}
|
|
45
41
|
}
|
|
46
42
|
return `${lines.join("\n")}\n`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-cleanup-directory.d.ts","sourceRoot":"","sources":["../../src/domain/write-cleanup-directory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"write-cleanup-directory.d.ts","sourceRoot":"","sources":["../../src/domain/write-cleanup-directory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC;AAGrE,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,OAAO,qBAAqB,KAC3B,OAAO,CAAC,sBAAsB,CAgChC,CAAC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { defaultReportOutputRoot } from "./default-report-output-root.js";
|
|
3
4
|
export const writeCleanupDirectory = async (input) => {
|
|
4
5
|
const usage = requireUsage(input.report);
|
|
5
|
-
const
|
|
6
|
-
|
|
6
|
+
const outputRoot = input.outputRoot ?? defaultReportOutputRoot();
|
|
7
|
+
const directory = path.join(outputRoot, sanitizeTimestamp(input.timestamp ?? new Date().toISOString()));
|
|
8
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
7
9
|
const usageJsonPath = path.join(directory, "usage.json");
|
|
8
10
|
const usageMarkdownPath = path.join(directory, "usage.md");
|
|
9
11
|
await writeFile(usageJsonPath, `${JSON.stringify({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-findings-directory.d.ts","sourceRoot":"","sources":["../../src/domain/write-findings-directory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"write-findings-directory.d.ts","sourceRoot":"","sources":["../../src/domain/write-findings-directory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;IACnD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,OAAO,sBAAsB,KAC5B,OAAO,CAAC,uBAAuB,CAiDjC,CAAC"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { defaultReportOutputRoot } from "./default-report-output-root.js";
|
|
4
5
|
import { groupFindingsByKey } from "./group-findings.js";
|
|
5
6
|
export const writeFindingsDirectory = async (input) => {
|
|
6
7
|
const findings = input.findings ?? input.report.findings;
|
|
7
|
-
const
|
|
8
|
+
const outputRoot = input.outputRoot ?? defaultReportOutputRoot();
|
|
9
|
+
const directory = path.join(outputRoot, sanitizeTimestamp(input.timestamp ?? new Date().toISOString()));
|
|
8
10
|
const skillDirectory = path.join(directory, "skills");
|
|
9
|
-
await mkdir(skillDirectory, { recursive: true });
|
|
11
|
+
await mkdir(skillDirectory, { recursive: true, mode: 0o700 });
|
|
10
12
|
const findingsJsonPath = path.join(directory, "findings.json");
|
|
11
13
|
const findingsMarkdownPath = path.join(directory, "findings.md");
|
|
12
14
|
const skillReportPaths = [];
|