selftune 0.2.31 → 0.2.32
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 +83 -56
- package/apps/local-dashboard/dist/assets/index-B-ut4w0B.js +15 -0
- package/apps/local-dashboard/dist/assets/index-BFGfCVrL.css +1 -0
- package/apps/local-dashboard/dist/assets/vendor-ui-DfowE3Hu.js +1 -0
- package/apps/local-dashboard/dist/index.html +3 -3
- package/cli/selftune/command-surface.ts +613 -2
- package/cli/selftune/create/baseline.ts +429 -0
- package/cli/selftune/create/check.ts +35 -0
- package/cli/selftune/create/init.ts +115 -0
- package/cli/selftune/create/package-candidate-state.ts +771 -0
- package/cli/selftune/create/package-evaluator.ts +710 -0
- package/cli/selftune/create/package-fingerprint.ts +142 -0
- package/cli/selftune/create/package-search.ts +377 -0
- package/cli/selftune/create/publish.ts +431 -0
- package/cli/selftune/create/readiness.ts +495 -0
- package/cli/selftune/create/replay.ts +330 -0
- package/cli/selftune/create/report.ts +74 -0
- package/cli/selftune/create/scaffold.ts +121 -0
- package/cli/selftune/create/skills-ref-adapter.ts +177 -0
- package/cli/selftune/create/status.ts +33 -0
- package/cli/selftune/create/templates.ts +249 -0
- package/cli/selftune/cron/setup.ts +1 -1
- package/cli/selftune/dashboard-action-events.ts +4 -1
- package/cli/selftune/dashboard-action-result.ts +789 -24
- package/cli/selftune/dashboard-action-stream.ts +80 -0
- package/cli/selftune/dashboard-contract.ts +146 -3
- package/cli/selftune/dashboard-server.ts +5 -4
- package/cli/selftune/eval/hooks-to-evals.ts +58 -35
- package/cli/selftune/eval/synthetic-evals.ts +145 -17
- package/cli/selftune/evolution/bounded-mutations.ts +1045 -0
- package/cli/selftune/evolution/evolve-body.ts +9 -36
- package/cli/selftune/evolution/evolve.ts +8 -72
- package/cli/selftune/evolution/stopping-criteria.ts +5 -13
- package/cli/selftune/evolution/unblock-suggestions.ts +0 -16
- package/cli/selftune/evolution/validate-host-replay.ts +115 -15
- package/cli/selftune/improve.ts +206 -0
- package/cli/selftune/index.ts +123 -6
- package/cli/selftune/init.ts +1 -1
- package/cli/selftune/localdb/queries/dashboard.ts +30 -0
- package/cli/selftune/localdb/schema.ts +52 -0
- package/cli/selftune/monitoring/watch.ts +257 -23
- package/cli/selftune/orchestrate/execute.ts +300 -1
- package/cli/selftune/orchestrate/finalize.ts +14 -0
- package/cli/selftune/orchestrate/plan.ts +22 -5
- package/cli/selftune/orchestrate/prepare.ts +59 -4
- package/cli/selftune/orchestrate/report.ts +1 -1
- package/cli/selftune/orchestrate.ts +34 -1
- package/cli/selftune/publish.ts +35 -0
- package/cli/selftune/routes/actions.ts +81 -15
- package/cli/selftune/routes/overview.ts +1 -1
- package/cli/selftune/routes/skill-report.ts +147 -2
- package/cli/selftune/run.ts +18 -0
- package/cli/selftune/schedule.ts +3 -3
- package/cli/selftune/search-run.ts +703 -0
- package/cli/selftune/status.ts +35 -11
- package/cli/selftune/testing-readiness.ts +431 -40
- package/cli/selftune/types.ts +316 -0
- package/cli/selftune/utils/eval-readiness.ts +1 -0
- package/cli/selftune/utils/json-output.ts +11 -0
- package/cli/selftune/utils/lifecycle-surface.ts +48 -0
- package/cli/selftune/utils/query-filter.ts +82 -1
- package/cli/selftune/utils/tui.ts +85 -2
- package/cli/selftune/verify.ts +205 -0
- package/cli/selftune/workflows/proposals.ts +1 -1
- package/cli/selftune/workflows/skill-scaffold.ts +141 -63
- package/cli/selftune/workflows/workflows.ts +4 -4
- package/package.json +1 -1
- package/skill/SKILL.md +148 -85
- package/skill/references/cli-quick-reference.md +16 -1
- package/skill/references/creator-playbook.md +31 -10
- package/skill/workflows/Baseline.md +8 -9
- package/skill/workflows/Contributions.md +4 -4
- package/skill/workflows/Create.md +173 -0
- package/skill/workflows/CreateTestDeploy.md +34 -30
- package/skill/workflows/Cron.md +2 -2
- package/skill/workflows/Dashboard.md +3 -3
- package/skill/workflows/Evals.md +13 -7
- package/skill/workflows/Evolve.md +75 -32
- package/skill/workflows/EvolveBody.md +22 -15
- package/skill/workflows/Hook.md +1 -1
- package/skill/workflows/Improve.md +168 -0
- package/skill/workflows/Initialize.md +3 -3
- package/skill/workflows/Orchestrate.md +49 -12
- package/skill/workflows/Publish.md +100 -0
- package/skill/workflows/Run.md +72 -0
- package/skill/workflows/Schedule.md +2 -2
- package/skill/workflows/SearchRun.md +89 -0
- package/skill/workflows/SignalsDashboard.md +2 -2
- package/skill/workflows/UnitTest.md +13 -4
- package/skill/workflows/Verify.md +136 -0
- package/skill/workflows/Watch.md +114 -47
- package/skill/workflows/Workflows.md +13 -8
- package/apps/local-dashboard/dist/assets/index-B7v_o1WC.js +0 -15
- package/apps/local-dashboard/dist/assets/index-CrO77SVi.css +0 -1
- package/apps/local-dashboard/dist/assets/vendor-ui-B0H8s1mP.js +0 -1
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { SkillTestingReadiness } from "../dashboard-contract.js";
|
|
5
|
+
import { emitDashboardStepProgress } from "../dashboard-action-instrumentation.js";
|
|
6
|
+
import { scoreDescription } from "../evolution/description-quality.js";
|
|
7
|
+
import { getDb } from "../localdb/db.js";
|
|
8
|
+
import { getSkillTestingReadiness } from "../testing-readiness.js";
|
|
9
|
+
import type { CreateCheckReadiness, CreateCheckResult, CreateCheckState } from "../types.js";
|
|
10
|
+
import { parseFrontmatter, type SkillFrontmatter } from "../utils/frontmatter.js";
|
|
11
|
+
import { CLIError } from "../utils/cli-error.js";
|
|
12
|
+
import {
|
|
13
|
+
buildCreateSkillManifest,
|
|
14
|
+
slugifyCreateSkillName,
|
|
15
|
+
type CreateSkillManifest,
|
|
16
|
+
} from "./templates.js";
|
|
17
|
+
import { validateAgentSkill, type ValidateAgentSkillDeps } from "./skills-ref-adapter.js";
|
|
18
|
+
|
|
19
|
+
const SKILL_MD_LINE_BUDGET = 500;
|
|
20
|
+
const DESCRIPTION_CHAR_BUDGET = 1024;
|
|
21
|
+
|
|
22
|
+
export interface ComputeCreateCheckDeps {
|
|
23
|
+
validateAgentSkill?: (
|
|
24
|
+
skillDir: string,
|
|
25
|
+
deps?: ValidateAgentSkillDeps,
|
|
26
|
+
) => Promise<CreateCheckResult["spec_validation"]>;
|
|
27
|
+
getTestingReadiness?: (skillName: string, searchDirs: string[]) => SkillTestingReadiness | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ResolvedCreateSkillPath {
|
|
31
|
+
skill_dir: string;
|
|
32
|
+
skill_path: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface LoadedCreateManifest {
|
|
36
|
+
manifest: CreateSkillManifest;
|
|
37
|
+
present: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface CreateSkillContext {
|
|
41
|
+
skill_name: string;
|
|
42
|
+
skill_dir: string;
|
|
43
|
+
skill_path: string;
|
|
44
|
+
skill_content: string;
|
|
45
|
+
frontmatter: SkillFrontmatter;
|
|
46
|
+
manifest: CreateSkillManifest;
|
|
47
|
+
manifest_present: boolean;
|
|
48
|
+
testing_readiness: SkillTestingReadiness | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function resolveCreateSkillPath(skillPathArg: string): ResolvedCreateSkillPath {
|
|
52
|
+
const trimmed = skillPathArg.trim();
|
|
53
|
+
if (!trimmed) {
|
|
54
|
+
throw new CLIError(
|
|
55
|
+
"--skill-path <path> is required",
|
|
56
|
+
"MISSING_FLAG",
|
|
57
|
+
"selftune create check --skill-path /path/to/SKILL.md",
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const absolute = resolve(trimmed);
|
|
62
|
+
const stat = existsSync(absolute) ? statSync(absolute) : null;
|
|
63
|
+
|
|
64
|
+
if (stat?.isDirectory()) {
|
|
65
|
+
const skillPath = join(absolute, "SKILL.md");
|
|
66
|
+
if (!existsSync(skillPath)) {
|
|
67
|
+
throw new CLIError(
|
|
68
|
+
`SKILL.md not found under ${absolute}`,
|
|
69
|
+
"FILE_NOT_FOUND",
|
|
70
|
+
"Pass a skill directory or a direct path to SKILL.md.",
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return { skill_dir: absolute, skill_path: skillPath };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!existsSync(absolute)) {
|
|
77
|
+
throw new CLIError(
|
|
78
|
+
`Skill path not found at ${absolute}`,
|
|
79
|
+
"FILE_NOT_FOUND",
|
|
80
|
+
"Pass a skill directory or a direct path to SKILL.md.",
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (basename(absolute) !== "SKILL.md") {
|
|
85
|
+
throw new CLIError(
|
|
86
|
+
`Expected a skill directory or SKILL.md, received ${absolute}`,
|
|
87
|
+
"INVALID_FLAG",
|
|
88
|
+
"Pass --skill-path /path/to/skill-dir or /path/to/skill-dir/SKILL.md",
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { skill_dir: dirname(absolute), skill_path: absolute };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function loadCreateManifest(skillDir: string): LoadedCreateManifest {
|
|
96
|
+
const manifestPath = join(skillDir, "selftune.create.json");
|
|
97
|
+
const fallback = buildCreateSkillManifest();
|
|
98
|
+
|
|
99
|
+
if (!existsSync(manifestPath)) {
|
|
100
|
+
return { manifest: fallback, present: false };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(readFileSync(manifestPath, "utf-8")) as Partial<CreateSkillManifest>;
|
|
105
|
+
return {
|
|
106
|
+
manifest: {
|
|
107
|
+
version: 1,
|
|
108
|
+
entry_workflow:
|
|
109
|
+
typeof parsed.entry_workflow === "string" && parsed.entry_workflow.trim().length > 0
|
|
110
|
+
? parsed.entry_workflow
|
|
111
|
+
: fallback.entry_workflow,
|
|
112
|
+
supports_package_replay:
|
|
113
|
+
typeof parsed.supports_package_replay === "boolean"
|
|
114
|
+
? parsed.supports_package_replay
|
|
115
|
+
: fallback.supports_package_replay,
|
|
116
|
+
expected_resources: {
|
|
117
|
+
workflows:
|
|
118
|
+
typeof parsed.expected_resources?.workflows === "boolean"
|
|
119
|
+
? parsed.expected_resources.workflows
|
|
120
|
+
: fallback.expected_resources.workflows,
|
|
121
|
+
references:
|
|
122
|
+
typeof parsed.expected_resources?.references === "boolean"
|
|
123
|
+
? parsed.expected_resources.references
|
|
124
|
+
: fallback.expected_resources.references,
|
|
125
|
+
scripts:
|
|
126
|
+
typeof parsed.expected_resources?.scripts === "boolean"
|
|
127
|
+
? parsed.expected_resources.scripts
|
|
128
|
+
: fallback.expected_resources.scripts,
|
|
129
|
+
assets:
|
|
130
|
+
typeof parsed.expected_resources?.assets === "boolean"
|
|
131
|
+
? parsed.expected_resources.assets
|
|
132
|
+
: fallback.expected_resources.assets,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
present: true,
|
|
136
|
+
};
|
|
137
|
+
} catch {
|
|
138
|
+
return { manifest: fallback, present: false };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function hasDirectoryEntries(path: string): boolean {
|
|
143
|
+
if (!existsSync(path)) return false;
|
|
144
|
+
try {
|
|
145
|
+
return readdirSync(path).length > 0;
|
|
146
|
+
} catch {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function isCreateSkillDraft(skillPath: string): boolean {
|
|
152
|
+
return existsSync(join(dirname(skillPath), "selftune.create.json"));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function readTestingReadiness(
|
|
156
|
+
skillName: string,
|
|
157
|
+
skillDir: string,
|
|
158
|
+
deps: ComputeCreateCheckDeps,
|
|
159
|
+
): SkillTestingReadiness | null {
|
|
160
|
+
if (deps.getTestingReadiness) {
|
|
161
|
+
return deps.getTestingReadiness(skillName, [dirname(skillDir)]);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
return getSkillTestingReadiness(getDb(), skillName, [dirname(skillDir)]);
|
|
166
|
+
} catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function readCreateSkillContext(
|
|
172
|
+
skillPathArg: string,
|
|
173
|
+
deps: ComputeCreateCheckDeps = {},
|
|
174
|
+
): CreateSkillContext {
|
|
175
|
+
const { skill_dir, skill_path } = resolveCreateSkillPath(skillPathArg);
|
|
176
|
+
const skill_content = readFileSync(skill_path, "utf-8");
|
|
177
|
+
const frontmatter = parseFrontmatter(skill_content);
|
|
178
|
+
const { manifest, present: manifest_present } = loadCreateManifest(skill_dir);
|
|
179
|
+
const skill_name = frontmatter.name.trim() || slugifyCreateSkillName(basename(skill_dir));
|
|
180
|
+
const testing_readiness = readTestingReadiness(skill_name, skill_dir, deps);
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
skill_name,
|
|
184
|
+
skill_dir,
|
|
185
|
+
skill_path,
|
|
186
|
+
skill_content,
|
|
187
|
+
frontmatter,
|
|
188
|
+
manifest,
|
|
189
|
+
manifest_present,
|
|
190
|
+
testing_readiness,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function derivePackageResourcesReady(
|
|
195
|
+
manifest: CreateSkillManifest,
|
|
196
|
+
checks: CreateCheckResult["readiness"]["checks"],
|
|
197
|
+
): boolean {
|
|
198
|
+
if (!checks.skill_md) return false;
|
|
199
|
+
if (!checks.frontmatter_present) return false;
|
|
200
|
+
if (!checks.skill_name_matches_dir) return false;
|
|
201
|
+
if (!checks.description_present) return false;
|
|
202
|
+
if (!checks.description_within_budget) return false;
|
|
203
|
+
if (!checks.skill_md_within_line_budget) return false;
|
|
204
|
+
if (!checks.workflow_entry) return false;
|
|
205
|
+
if (manifest.expected_resources.references && !checks.references_present) return false;
|
|
206
|
+
if (manifest.expected_resources.scripts && !checks.scripts_present) return false;
|
|
207
|
+
if (manifest.expected_resources.assets && !checks.assets_present) return false;
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function recommendNextCommand(
|
|
212
|
+
state: CreateCheckState,
|
|
213
|
+
skillName: string,
|
|
214
|
+
skillPath: string,
|
|
215
|
+
skillDir: string,
|
|
216
|
+
specCommand: string | null,
|
|
217
|
+
): string | null {
|
|
218
|
+
switch (state) {
|
|
219
|
+
case "blocked_spec_validation":
|
|
220
|
+
return specCommand ?? `uvx skills-ref validate ${skillDir}`;
|
|
221
|
+
case "needs_spec_validation":
|
|
222
|
+
return `selftune create check --skill-path ${skillPath}`;
|
|
223
|
+
case "needs_package_resources":
|
|
224
|
+
return `selftune create check --skill-path ${skillPath}`;
|
|
225
|
+
case "needs_evals":
|
|
226
|
+
return `selftune eval generate --skill ${skillName} --skill-path ${skillPath} --auto-synthetic`;
|
|
227
|
+
case "needs_unit_tests":
|
|
228
|
+
return `selftune eval unit-test --skill ${skillName} --generate --skill-path ${skillPath}`;
|
|
229
|
+
case "needs_routing_replay":
|
|
230
|
+
return `selftune create replay --skill-path ${skillPath} --mode package`;
|
|
231
|
+
case "needs_baseline":
|
|
232
|
+
return `selftune create baseline --skill-path ${skillPath} --mode package`;
|
|
233
|
+
case "ready_to_publish":
|
|
234
|
+
return `selftune create publish --skill-path ${skillPath}`;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function summarizeState(
|
|
239
|
+
state: CreateCheckState,
|
|
240
|
+
manifestPresent: boolean,
|
|
241
|
+
testingReadiness: SkillTestingReadiness | null,
|
|
242
|
+
): string {
|
|
243
|
+
switch (state) {
|
|
244
|
+
case "blocked_spec_validation":
|
|
245
|
+
return "Agent Skills spec validation is blocking this draft. Fix validator issues before treating the package as publishable.";
|
|
246
|
+
case "needs_spec_validation":
|
|
247
|
+
return "Local package checks pass, but Agent Skills spec validation has not run yet. Run create check before publishing.";
|
|
248
|
+
case "needs_package_resources":
|
|
249
|
+
return manifestPresent
|
|
250
|
+
? "Package structure is incomplete for the declared manifest. Fill the missing routing or resource files and rerun the check."
|
|
251
|
+
: "Package structure is incomplete. selftune inferred manifest defaults because selftune.create.json is missing or invalid.";
|
|
252
|
+
case "needs_evals":
|
|
253
|
+
return "Package structure is valid enough to continue, but there is no canonical eval set for this skill yet.";
|
|
254
|
+
case "needs_unit_tests":
|
|
255
|
+
return "Routing evals exist, but deterministic unit tests are still missing.";
|
|
256
|
+
case "needs_routing_replay":
|
|
257
|
+
return testingReadiness?.replay_check_count
|
|
258
|
+
? "Replay validation data exists, but selftune could not confirm the routing dry-run state."
|
|
259
|
+
: "Evals and unit tests exist, but the creator-loop replay dry-run has not been recorded yet.";
|
|
260
|
+
case "needs_baseline":
|
|
261
|
+
return "Replay validation is recorded, but no no-skill baseline measurement exists yet.";
|
|
262
|
+
case "ready_to_publish":
|
|
263
|
+
return "Spec validation, package structure, evals, unit tests, replay, and baseline are all present. The draft is ready for the deploy step.";
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function computeCreateReadiness(
|
|
268
|
+
context: CreateSkillContext,
|
|
269
|
+
options: { specValidationOk?: boolean; specCommand?: string | null } = {},
|
|
270
|
+
): CreateCheckReadiness {
|
|
271
|
+
const descriptionQuality = scoreDescription(
|
|
272
|
+
context.frontmatter.description.trim(),
|
|
273
|
+
context.skill_name,
|
|
274
|
+
);
|
|
275
|
+
const skillLineCount = context.skill_content.split(/\r?\n/).length;
|
|
276
|
+
const workflowEntryPath = join(context.skill_dir, context.manifest.entry_workflow);
|
|
277
|
+
|
|
278
|
+
const checks: CreateCheckReadiness["checks"] = {
|
|
279
|
+
skill_md: existsSync(context.skill_path),
|
|
280
|
+
frontmatter_present:
|
|
281
|
+
context.skill_content.startsWith("---\n") || context.skill_content === "---",
|
|
282
|
+
skill_name_matches_dir: context.frontmatter.name.trim() === basename(context.skill_dir),
|
|
283
|
+
description_present: context.frontmatter.description.trim().length > 0,
|
|
284
|
+
description_within_budget:
|
|
285
|
+
context.frontmatter.description.trim().length > 0 &&
|
|
286
|
+
context.frontmatter.description.trim().length <= DESCRIPTION_CHAR_BUDGET,
|
|
287
|
+
skill_md_within_line_budget: skillLineCount <= SKILL_MD_LINE_BUDGET,
|
|
288
|
+
manifest_present: context.manifest_present,
|
|
289
|
+
workflow_entry: existsSync(workflowEntryPath),
|
|
290
|
+
references_present:
|
|
291
|
+
existsSync(join(context.skill_dir, "references")) &&
|
|
292
|
+
(existsSync(join(context.skill_dir, "references", "overview.md")) ||
|
|
293
|
+
hasDirectoryEntries(join(context.skill_dir, "references"))),
|
|
294
|
+
scripts_present:
|
|
295
|
+
existsSync(join(context.skill_dir, "scripts")) &&
|
|
296
|
+
hasDirectoryEntries(join(context.skill_dir, "scripts")),
|
|
297
|
+
assets_present:
|
|
298
|
+
existsSync(join(context.skill_dir, "assets")) &&
|
|
299
|
+
hasDirectoryEntries(join(context.skill_dir, "assets")),
|
|
300
|
+
evals_present: (context.testing_readiness?.eval_set_entries ?? 0) > 0,
|
|
301
|
+
unit_tests_present: (context.testing_readiness?.unit_test_cases ?? 0) > 0,
|
|
302
|
+
routing_replay_ready:
|
|
303
|
+
existsSync(context.skill_path) &&
|
|
304
|
+
context.frontmatter.name.trim().length > 0 &&
|
|
305
|
+
context.frontmatter.description.trim().length > 0,
|
|
306
|
+
routing_replay_recorded: (context.testing_readiness?.replay_check_count ?? 0) > 0,
|
|
307
|
+
package_replay_ready: false,
|
|
308
|
+
baseline_present: (context.testing_readiness?.baseline_sample_size ?? 0) > 0,
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const packageResourcesReady = derivePackageResourcesReady(context.manifest, checks);
|
|
312
|
+
checks.package_replay_ready =
|
|
313
|
+
context.manifest.supports_package_replay &&
|
|
314
|
+
packageResourcesReady &&
|
|
315
|
+
(!context.manifest.expected_resources.references || checks.references_present) &&
|
|
316
|
+
(!context.manifest.expected_resources.scripts || checks.scripts_present) &&
|
|
317
|
+
(!context.manifest.expected_resources.assets || checks.assets_present);
|
|
318
|
+
|
|
319
|
+
let state: CreateCheckState;
|
|
320
|
+
if (options.specValidationOk === false) {
|
|
321
|
+
state = "blocked_spec_validation";
|
|
322
|
+
} else if (!packageResourcesReady) {
|
|
323
|
+
state = "needs_package_resources";
|
|
324
|
+
} else if (!checks.evals_present) {
|
|
325
|
+
state = "needs_evals";
|
|
326
|
+
} else if (
|
|
327
|
+
context.testing_readiness?.next_step === "run_unit_tests" &&
|
|
328
|
+
checks.unit_tests_present
|
|
329
|
+
) {
|
|
330
|
+
state = "needs_unit_tests";
|
|
331
|
+
} else if (!checks.unit_tests_present) {
|
|
332
|
+
state = "needs_unit_tests";
|
|
333
|
+
} else if (
|
|
334
|
+
context.testing_readiness?.next_step === "run_replay_dry_run" &&
|
|
335
|
+
checks.routing_replay_recorded
|
|
336
|
+
) {
|
|
337
|
+
state = "needs_routing_replay";
|
|
338
|
+
} else if (!checks.routing_replay_recorded) {
|
|
339
|
+
state = "needs_routing_replay";
|
|
340
|
+
} else if (
|
|
341
|
+
context.testing_readiness?.next_step === "measure_baseline" &&
|
|
342
|
+
checks.baseline_present
|
|
343
|
+
) {
|
|
344
|
+
state = "needs_baseline";
|
|
345
|
+
} else if (!checks.baseline_present) {
|
|
346
|
+
state = "needs_baseline";
|
|
347
|
+
} else if (options.specValidationOk !== true) {
|
|
348
|
+
state = "needs_spec_validation";
|
|
349
|
+
} else {
|
|
350
|
+
state = "ready_to_publish";
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const nextCommand = recommendNextCommand(
|
|
354
|
+
state,
|
|
355
|
+
context.skill_name,
|
|
356
|
+
context.skill_path,
|
|
357
|
+
context.skill_dir,
|
|
358
|
+
options.specCommand ?? null,
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
return {
|
|
362
|
+
ok: state === "ready_to_publish",
|
|
363
|
+
state,
|
|
364
|
+
summary: summarizeState(state, context.manifest_present, context.testing_readiness),
|
|
365
|
+
next_command: nextCommand,
|
|
366
|
+
checks,
|
|
367
|
+
skill_name: context.skill_name,
|
|
368
|
+
skill_dir: context.skill_dir,
|
|
369
|
+
skill_path: context.skill_path,
|
|
370
|
+
entry_workflow: context.manifest.entry_workflow,
|
|
371
|
+
manifest_present: context.manifest_present,
|
|
372
|
+
description_quality: descriptionQuality,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export function computeCreateDashboardReadiness(
|
|
377
|
+
skillPathArg: string,
|
|
378
|
+
deps: ComputeCreateCheckDeps = {},
|
|
379
|
+
): CreateCheckReadiness {
|
|
380
|
+
const context = readCreateSkillContext(skillPathArg, deps);
|
|
381
|
+
return computeCreateReadiness(context, {
|
|
382
|
+
specCommand: `selftune create check --skill-path ${context.skill_path}`,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export async function computeCreateCheckResult(
|
|
387
|
+
skillPathArg: string,
|
|
388
|
+
deps: ComputeCreateCheckDeps = {},
|
|
389
|
+
): Promise<CreateCheckResult> {
|
|
390
|
+
emitDashboardStepProgress({
|
|
391
|
+
current: 1,
|
|
392
|
+
total: 3,
|
|
393
|
+
status: "started",
|
|
394
|
+
phase: "load_draft_package",
|
|
395
|
+
label: "Load draft package",
|
|
396
|
+
});
|
|
397
|
+
const context = readCreateSkillContext(skillPathArg, deps);
|
|
398
|
+
emitDashboardStepProgress({
|
|
399
|
+
current: 1,
|
|
400
|
+
total: 3,
|
|
401
|
+
status: "finished",
|
|
402
|
+
phase: "load_draft_package",
|
|
403
|
+
label: "Load draft package",
|
|
404
|
+
passed: true,
|
|
405
|
+
evidence: context.skill_name,
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
emitDashboardStepProgress({
|
|
409
|
+
current: 2,
|
|
410
|
+
total: 3,
|
|
411
|
+
status: "started",
|
|
412
|
+
phase: "spec_validation",
|
|
413
|
+
label: "Run Agent Skills validation",
|
|
414
|
+
});
|
|
415
|
+
const specValidation = await (deps.validateAgentSkill ?? validateAgentSkill)(context.skill_dir);
|
|
416
|
+
emitDashboardStepProgress({
|
|
417
|
+
current: 2,
|
|
418
|
+
total: 3,
|
|
419
|
+
status: "finished",
|
|
420
|
+
phase: "spec_validation",
|
|
421
|
+
label: "Run Agent Skills validation",
|
|
422
|
+
passed: specValidation.ok,
|
|
423
|
+
evidence: specValidation.command ?? specValidation.validator,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
emitDashboardStepProgress({
|
|
427
|
+
current: 3,
|
|
428
|
+
total: 3,
|
|
429
|
+
status: "started",
|
|
430
|
+
phase: "compute_create_readiness",
|
|
431
|
+
label: "Compute selftune readiness",
|
|
432
|
+
});
|
|
433
|
+
const readiness = computeCreateReadiness(context, {
|
|
434
|
+
specValidationOk: specValidation.ok,
|
|
435
|
+
specCommand: specValidation.command,
|
|
436
|
+
});
|
|
437
|
+
emitDashboardStepProgress({
|
|
438
|
+
current: 3,
|
|
439
|
+
total: 3,
|
|
440
|
+
status: "finished",
|
|
441
|
+
phase: "compute_create_readiness",
|
|
442
|
+
label: "Compute selftune readiness",
|
|
443
|
+
passed: readiness.ok && specValidation.ok,
|
|
444
|
+
evidence: readiness.summary,
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
return {
|
|
448
|
+
skill: context.skill_name,
|
|
449
|
+
skill_dir: context.skill_dir,
|
|
450
|
+
skill_path: context.skill_path,
|
|
451
|
+
ok: readiness.ok && specValidation.ok,
|
|
452
|
+
state: readiness.state,
|
|
453
|
+
next_command: readiness.next_command,
|
|
454
|
+
spec_validation: specValidation,
|
|
455
|
+
readiness,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function formatBooleanCheck(name: string, value: boolean): string {
|
|
460
|
+
return `${value ? "PASS" : "FAIL"} ${name}`;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export function formatCreateCheckResult(result: CreateCheckResult): string {
|
|
464
|
+
const checks = result.readiness.checks;
|
|
465
|
+
|
|
466
|
+
return [
|
|
467
|
+
`Skill: ${result.skill}`,
|
|
468
|
+
`Directory: ${result.skill_dir}`,
|
|
469
|
+
`State: ${result.state}`,
|
|
470
|
+
`Spec validation: ${result.spec_validation.ok ? "pass" : "blocked"}${result.spec_validation.command ? ` (${result.spec_validation.command})` : ""}`,
|
|
471
|
+
"",
|
|
472
|
+
"Checks:",
|
|
473
|
+
` ${formatBooleanCheck("skill_md", checks.skill_md)}`,
|
|
474
|
+
` ${formatBooleanCheck("frontmatter_present", checks.frontmatter_present)}`,
|
|
475
|
+
` ${formatBooleanCheck("skill_name_matches_dir", checks.skill_name_matches_dir)}`,
|
|
476
|
+
` ${formatBooleanCheck("description_present", checks.description_present)}`,
|
|
477
|
+
` ${formatBooleanCheck("description_within_budget", checks.description_within_budget)}`,
|
|
478
|
+
` ${formatBooleanCheck("skill_md_within_line_budget", checks.skill_md_within_line_budget)}`,
|
|
479
|
+
` ${formatBooleanCheck("manifest_present", checks.manifest_present)}`,
|
|
480
|
+
` ${formatBooleanCheck("workflow_entry", checks.workflow_entry)}`,
|
|
481
|
+
` ${formatBooleanCheck("references_present", checks.references_present)}`,
|
|
482
|
+
` ${formatBooleanCheck("scripts_present", checks.scripts_present)}`,
|
|
483
|
+
` ${formatBooleanCheck("assets_present", checks.assets_present)}`,
|
|
484
|
+
` ${formatBooleanCheck("evals_present", checks.evals_present)}`,
|
|
485
|
+
` ${formatBooleanCheck("unit_tests_present", checks.unit_tests_present)}`,
|
|
486
|
+
` ${formatBooleanCheck("routing_replay_ready", checks.routing_replay_ready)}`,
|
|
487
|
+
` ${formatBooleanCheck("routing_replay_recorded", checks.routing_replay_recorded)}`,
|
|
488
|
+
` ${formatBooleanCheck("package_replay_ready", checks.package_replay_ready)}`,
|
|
489
|
+
` ${formatBooleanCheck("baseline_present", checks.baseline_present)}`,
|
|
490
|
+
"",
|
|
491
|
+
`Description quality: ${Math.round(result.readiness.description_quality.composite * 100)}%`,
|
|
492
|
+
`Summary: ${result.readiness.summary}`,
|
|
493
|
+
result.next_command ? `Next command: ${result.next_command}` : "Next command: none",
|
|
494
|
+
].join("\n");
|
|
495
|
+
}
|