selftune 0.2.30 → 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/registry/github-install.ts +256 -0
- package/cli/selftune/registry/index.ts +1 -1
- package/cli/selftune/registry/install.ts +58 -7
- 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/packages/dashboard-core/src/routes/manifest.ts +2 -2
- package/packages/ui/src/components/SkillReportPanels.tsx +7 -7
- package/packages/ui/src/primitives/button.tsx +5 -0
- 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/Registry.md +19 -13
- 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-BcXquWFB.css +0 -1
- package/apps/local-dashboard/dist/assets/index-Coq42hE4.js +0 -15
- package/apps/local-dashboard/dist/assets/vendor-ui-B0H8s1mP.js +0 -1
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
|
|
4
|
+
import type { CreatePackageEvaluationResult } from "./create/package-evaluator.js";
|
|
5
|
+
import { formatCreatePackageBenchmarkReport } from "./create/package-evaluator.js";
|
|
6
|
+
import { runCreateReport } from "./create/report.js";
|
|
7
|
+
import { computeCreateCheckResult, formatCreateCheckResult } from "./create/readiness.js";
|
|
8
|
+
import { PUBLIC_COMMAND_SURFACES, renderCommandHelp } from "./command-surface.js";
|
|
9
|
+
import type { CreateCheckResult } from "./types.js";
|
|
10
|
+
import { handleCLIError } from "./utils/cli-error.js";
|
|
11
|
+
|
|
12
|
+
export interface VerifyResult {
|
|
13
|
+
skill: string;
|
|
14
|
+
skill_path: string;
|
|
15
|
+
readiness_state: CreateCheckResult["state"];
|
|
16
|
+
verified: boolean;
|
|
17
|
+
next_command: string | null;
|
|
18
|
+
readiness: CreateCheckResult;
|
|
19
|
+
report: CreatePackageEvaluationResult | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface RunVerifyOptions {
|
|
23
|
+
skillPath: string;
|
|
24
|
+
agent?: string;
|
|
25
|
+
evalSetPath?: string;
|
|
26
|
+
autoFix?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface RunVerifyDeps {
|
|
30
|
+
computeCreateCheckResult?: typeof computeCreateCheckResult;
|
|
31
|
+
runCreateReport?: typeof runCreateReport;
|
|
32
|
+
runSelftuneSubCommand?: (command: string[]) => {
|
|
33
|
+
exitCode: number | null;
|
|
34
|
+
stdout: string;
|
|
35
|
+
stderr: string;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const MAX_AUTO_FIX_ITERATIONS = 4;
|
|
40
|
+
|
|
41
|
+
function runSelftuneSubCommand(command: string[]): {
|
|
42
|
+
exitCode: number | null;
|
|
43
|
+
stdout: string;
|
|
44
|
+
stderr: string;
|
|
45
|
+
} {
|
|
46
|
+
const indexPath = join(import.meta.dir, "index.ts");
|
|
47
|
+
const result = Bun.spawnSync(["bun", "run", indexPath, ...command], {
|
|
48
|
+
stdout: "pipe",
|
|
49
|
+
stderr: "pipe",
|
|
50
|
+
env: process.env,
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
exitCode: result.exitCode,
|
|
54
|
+
stdout: Buffer.from(result.stdout).toString("utf-8"),
|
|
55
|
+
stderr: Buffer.from(result.stderr).toString("utf-8"),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function withPublishRecommendation(
|
|
60
|
+
result: CreatePackageEvaluationResult,
|
|
61
|
+
): CreatePackageEvaluationResult {
|
|
62
|
+
if (!result.summary.evaluation_passed) return result;
|
|
63
|
+
return {
|
|
64
|
+
...result,
|
|
65
|
+
summary: {
|
|
66
|
+
...result.summary,
|
|
67
|
+
next_command: `selftune publish --skill-path ${result.summary.skill_path}`,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function buildAutoFixCommand(
|
|
73
|
+
readiness: CreateCheckResult,
|
|
74
|
+
options: Pick<RunVerifyOptions, "evalSetPath">,
|
|
75
|
+
): string[] | null {
|
|
76
|
+
switch (readiness.state) {
|
|
77
|
+
case "needs_evals":
|
|
78
|
+
return [
|
|
79
|
+
"eval",
|
|
80
|
+
"generate",
|
|
81
|
+
"--skill",
|
|
82
|
+
readiness.skill,
|
|
83
|
+
"--skill-path",
|
|
84
|
+
readiness.skill_path,
|
|
85
|
+
"--auto-synthetic",
|
|
86
|
+
];
|
|
87
|
+
case "needs_unit_tests":
|
|
88
|
+
return [
|
|
89
|
+
"eval",
|
|
90
|
+
"unit-test",
|
|
91
|
+
"--skill",
|
|
92
|
+
readiness.skill,
|
|
93
|
+
"--generate",
|
|
94
|
+
...(options.evalSetPath ? ["--eval-set", options.evalSetPath] : []),
|
|
95
|
+
"--skill-path",
|
|
96
|
+
readiness.skill_path,
|
|
97
|
+
];
|
|
98
|
+
case "needs_routing_replay":
|
|
99
|
+
return ["create", "replay", "--skill-path", readiness.skill_path];
|
|
100
|
+
case "needs_baseline":
|
|
101
|
+
return ["create", "baseline", "--skill-path", readiness.skill_path];
|
|
102
|
+
default:
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function runVerify(
|
|
108
|
+
options: RunVerifyOptions,
|
|
109
|
+
deps: RunVerifyDeps = {},
|
|
110
|
+
): Promise<VerifyResult> {
|
|
111
|
+
const computeReadiness = deps.computeCreateCheckResult ?? computeCreateCheckResult;
|
|
112
|
+
const buildReport = deps.runCreateReport ?? runCreateReport;
|
|
113
|
+
const execSubCommand = deps.runSelftuneSubCommand ?? runSelftuneSubCommand;
|
|
114
|
+
let readiness = await computeReadiness(options.skillPath);
|
|
115
|
+
const autoFix = options.autoFix !== false;
|
|
116
|
+
|
|
117
|
+
if (!readiness.ok && autoFix) {
|
|
118
|
+
for (let i = 0; i < MAX_AUTO_FIX_ITERATIONS; i++) {
|
|
119
|
+
const command = buildAutoFixCommand(readiness, options);
|
|
120
|
+
if (!command) break;
|
|
121
|
+
|
|
122
|
+
process.stderr.write(`[verify] Auto-fixing: selftune ${command.join(" ")}\n`);
|
|
123
|
+
|
|
124
|
+
const result = execSubCommand(command);
|
|
125
|
+
if (result.exitCode !== 0) {
|
|
126
|
+
process.stderr.write(`[verify] Sub-command exited ${result.exitCode}, stopping auto-fix\n`);
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// eslint-disable-next-line no-await-in-loop -- each remediation step changes the next readiness state
|
|
131
|
+
readiness = await computeReadiness(options.skillPath);
|
|
132
|
+
if (readiness.ok) break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!readiness.ok) {
|
|
137
|
+
return {
|
|
138
|
+
skill: readiness.skill,
|
|
139
|
+
skill_path: readiness.skill_path,
|
|
140
|
+
readiness_state: readiness.state,
|
|
141
|
+
verified: false,
|
|
142
|
+
next_command: readiness.next_command,
|
|
143
|
+
readiness,
|
|
144
|
+
report: null,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const report = withPublishRecommendation(
|
|
149
|
+
await buildReport({
|
|
150
|
+
skillPath: readiness.skill_path,
|
|
151
|
+
agent: options.agent,
|
|
152
|
+
evalSetPath: options.evalSetPath,
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
skill: readiness.skill,
|
|
158
|
+
skill_path: readiness.skill_path,
|
|
159
|
+
readiness_state: readiness.state,
|
|
160
|
+
verified: report.summary.evaluation_passed,
|
|
161
|
+
next_command: report.summary.next_command,
|
|
162
|
+
readiness,
|
|
163
|
+
report,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export async function cliMain(): Promise<void> {
|
|
168
|
+
const { values } = parseArgs({
|
|
169
|
+
options: {
|
|
170
|
+
"skill-path": { type: "string" },
|
|
171
|
+
agent: { type: "string" },
|
|
172
|
+
"eval-set": { type: "string" },
|
|
173
|
+
"no-auto-fix": { type: "boolean", default: false },
|
|
174
|
+
json: { type: "boolean", default: false },
|
|
175
|
+
help: { type: "boolean", short: "h", default: false },
|
|
176
|
+
},
|
|
177
|
+
strict: true,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
if (values.help) {
|
|
181
|
+
console.log(renderCommandHelp(PUBLIC_COMMAND_SURFACES.verify));
|
|
182
|
+
process.exit(0);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const result = await runVerify({
|
|
186
|
+
skillPath: values["skill-path"] ?? "",
|
|
187
|
+
agent: values.agent,
|
|
188
|
+
evalSetPath: values["eval-set"],
|
|
189
|
+
autoFix: !values["no-auto-fix"],
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (values.json || !process.stdout.isTTY) {
|
|
193
|
+
console.log(JSON.stringify(result, null, 2));
|
|
194
|
+
} else if (result.report) {
|
|
195
|
+
console.log(formatCreatePackageBenchmarkReport(result.report));
|
|
196
|
+
} else {
|
|
197
|
+
console.log(formatCreateCheckResult(result.readiness));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
process.exit(result.verified ? 0 : 1);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (import.meta.main) {
|
|
204
|
+
cliMain().catch(handleCLIError);
|
|
205
|
+
}
|
|
@@ -129,7 +129,7 @@ export function discoverWorkflowSkillProposals(
|
|
|
129
129
|
|
|
130
130
|
const summary = buildWorkflowProposalSummary(workflow, draft);
|
|
131
131
|
const currentValue = `No dedicated workflow skill exists for ${workflow.skills.join(" -> ")}.`;
|
|
132
|
-
const proposedValue = `Create ${draft.skill_name} at ${draft.
|
|
132
|
+
const proposedValue = `Create package ${draft.skill_name} at ${draft.skill_dir}`;
|
|
133
133
|
const queryClause = workflow.representative_query.trim()
|
|
134
134
|
? ` Common trigger: "${workflow.representative_query.trim()}".`
|
|
135
135
|
: "";
|
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* skill-scaffold.ts
|
|
3
3
|
*
|
|
4
|
-
* Builds draft workflow
|
|
5
|
-
* The draft is preview-first by default so agents can review the
|
|
6
|
-
* writing it into a local skill registry.
|
|
4
|
+
* Builds draft workflow skill packages from repeated telemetry-discovered
|
|
5
|
+
* workflows. The draft is preview-first by default so agents can review the
|
|
6
|
+
* scaffold before writing it into a local skill registry.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
11
|
+
import type { CreateSkillDraft } from "../create/templates.js";
|
|
12
|
+
import { buildCreateSkillDraft } from "../create/templates.js";
|
|
11
13
|
import type { DiscoveredWorkflow } from "../types.js";
|
|
12
14
|
import { findGitRepositoryRoot } from "../utils/skill-discovery.js";
|
|
13
15
|
|
|
14
|
-
export interface WorkflowSkillDraft {
|
|
16
|
+
export interface WorkflowSkillDraft extends CreateSkillDraft {
|
|
15
17
|
title: string;
|
|
16
|
-
skill_name: string;
|
|
17
|
-
description: string;
|
|
18
|
-
output_dir: string;
|
|
19
|
-
skill_dir: string;
|
|
20
|
-
skill_path: string;
|
|
21
18
|
content: string;
|
|
22
19
|
source_workflow: {
|
|
23
20
|
workflow_id: string;
|
|
@@ -33,6 +30,7 @@ export interface WorkflowSkillDraftOptions {
|
|
|
33
30
|
skillName?: string;
|
|
34
31
|
description?: string;
|
|
35
32
|
cwd?: string;
|
|
33
|
+
generatedBy?: string;
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
const STOPWORDS = new Set([
|
|
@@ -85,6 +83,11 @@ function deriveBaseLabel(workflow: DiscoveredWorkflow): string {
|
|
|
85
83
|
return `${workflow.skills.join(" ")} workflow`;
|
|
86
84
|
}
|
|
87
85
|
|
|
86
|
+
export function getDefaultWorkflowSkillOutputDir(cwd: string = process.cwd()): string {
|
|
87
|
+
const repoRoot = findGitRepositoryRoot(cwd);
|
|
88
|
+
return join(repoRoot ?? cwd, ".agents", "skills");
|
|
89
|
+
}
|
|
90
|
+
|
|
88
91
|
function formatList(items: string[]): string {
|
|
89
92
|
if (items.length === 0) return "";
|
|
90
93
|
if (items.length === 1) return items[0];
|
|
@@ -92,30 +95,6 @@ function formatList(items: string[]): string {
|
|
|
92
95
|
return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
function wrapFoldedScalar(value: string, width = 78): string[] {
|
|
96
|
-
const words = value.split(/\s+/).filter(Boolean);
|
|
97
|
-
const lines: string[] = [];
|
|
98
|
-
let current = "";
|
|
99
|
-
|
|
100
|
-
for (const word of words) {
|
|
101
|
-
const candidate = current.length === 0 ? word : `${current} ${word}`;
|
|
102
|
-
if (candidate.length > width && current.length > 0) {
|
|
103
|
-
lines.push(` ${current}`);
|
|
104
|
-
current = word;
|
|
105
|
-
} else {
|
|
106
|
-
current = candidate;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (current.length > 0) lines.push(` ${current}`);
|
|
111
|
-
return lines.length > 0 ? lines : [" "];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function getDefaultWorkflowSkillOutputDir(cwd: string = process.cwd()): string {
|
|
115
|
-
const repoRoot = findGitRepositoryRoot(cwd);
|
|
116
|
-
return join(repoRoot ?? cwd, ".agents", "skills");
|
|
117
|
-
}
|
|
118
|
-
|
|
119
98
|
export function buildWorkflowSkillDescription(
|
|
120
99
|
workflow: DiscoveredWorkflow,
|
|
121
100
|
override?: string,
|
|
@@ -131,16 +110,15 @@ export function buildWorkflowSkillDescription(
|
|
|
131
110
|
return `Use when the task consistently needs ${chain} in sequence.`;
|
|
132
111
|
}
|
|
133
112
|
|
|
134
|
-
|
|
113
|
+
function buildWorkflowSkillContent(
|
|
135
114
|
workflow: DiscoveredWorkflow,
|
|
136
115
|
title: string,
|
|
137
116
|
skillName: string,
|
|
138
117
|
description: string,
|
|
118
|
+
generatedBy: string,
|
|
139
119
|
): string {
|
|
140
|
-
const workflowName = title.endsWith("Workflow") ? title : `${title} Workflow`;
|
|
141
120
|
const chain = workflow.skills.join(" → ");
|
|
142
121
|
const query = workflow.representative_query.trim();
|
|
143
|
-
const foldedDescription = wrapFoldedScalar(description).join("\n");
|
|
144
122
|
|
|
145
123
|
const whenToUseLines =
|
|
146
124
|
query.length > 0
|
|
@@ -150,49 +128,121 @@ export function buildWorkflowSkillContent(
|
|
|
150
128
|
]
|
|
151
129
|
: [`- The request repeatedly needs this skill chain: ${chain}`];
|
|
152
130
|
|
|
153
|
-
const executionPlanLines = workflow.skills.map(
|
|
154
|
-
(skill, index) =>
|
|
155
|
-
`${index + 1}. Invoke \`${skill}\` in its established role for this workflow.`,
|
|
156
|
-
);
|
|
157
|
-
|
|
158
131
|
return `---
|
|
159
132
|
name: ${skillName}
|
|
160
133
|
description: >
|
|
161
|
-
${
|
|
134
|
+
${description}
|
|
162
135
|
metadata:
|
|
163
136
|
author: selftune-autogen
|
|
164
137
|
version: 0.1.0
|
|
165
|
-
category:
|
|
166
|
-
generated_by:
|
|
138
|
+
category: custom
|
|
139
|
+
generated_by: ${generatedBy}
|
|
167
140
|
source_workflow_id: ${workflow.workflow_id}
|
|
168
141
|
---
|
|
169
142
|
|
|
170
143
|
# ${title}
|
|
171
144
|
|
|
172
|
-
This draft skill was scaffolded by selftune from repeated workflow
|
|
173
|
-
Review the routing language and
|
|
145
|
+
This draft skill package was scaffolded by selftune from repeated workflow
|
|
146
|
+
telemetry. Review the routing language and package contents before broad
|
|
147
|
+
distribution.
|
|
174
148
|
|
|
175
149
|
## When to Use
|
|
176
150
|
|
|
177
151
|
${whenToUseLines.join("\n")}
|
|
178
152
|
|
|
179
|
-
##
|
|
153
|
+
## Workflow Routing
|
|
180
154
|
|
|
181
|
-
|
|
155
|
+
| Trigger | Workflow | File |
|
|
156
|
+
| --- | --- | --- |
|
|
157
|
+
| Default execution path | Default | workflows/default.md |
|
|
182
158
|
|
|
183
|
-
##
|
|
159
|
+
## Package Resources
|
|
184
160
|
|
|
185
|
-
|
|
186
|
-
-
|
|
187
|
-
|
|
161
|
+
- \`workflows/default.md\` contains the ordered execution steps for the workflow.
|
|
162
|
+
- \`references/overview.md\` preserves the observed provenance and trigger
|
|
163
|
+
evidence from telemetry.
|
|
164
|
+
- \`selftune.create.json\` records the package-level readiness hints for future
|
|
165
|
+
validation.
|
|
188
166
|
|
|
189
167
|
## Notes
|
|
190
168
|
|
|
191
|
-
- This is a proposal scaffold, not a silently published
|
|
169
|
+
- This is a proposal scaffold, not a silently published skill.
|
|
170
|
+
- Source workflow: ${chain}
|
|
192
171
|
- Add tighter scope boundaries and richer examples before publishing.
|
|
193
172
|
`;
|
|
194
173
|
}
|
|
195
174
|
|
|
175
|
+
function buildWorkflowDefaultContent(workflow: DiscoveredWorkflow, title: string): string {
|
|
176
|
+
const query = workflow.representative_query.trim();
|
|
177
|
+
const chain = workflow.skills.join(" → ");
|
|
178
|
+
const executionPlanLines = workflow.skills.map(
|
|
179
|
+
(skill, index) =>
|
|
180
|
+
`${index + 1}. Invoke \`${skill}\` in its established role for this workflow.`,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return `# ${title} Default Workflow
|
|
184
|
+
|
|
185
|
+
## When to Use
|
|
186
|
+
|
|
187
|
+
Use this after \`../SKILL.md\` has already matched the request.${query.length > 0 ? ` The representative trigger was "${query}".` : ""}
|
|
188
|
+
|
|
189
|
+
## Goal
|
|
190
|
+
|
|
191
|
+
Coordinate this repeated multi-skill chain without making the top-level router
|
|
192
|
+
carry all of the execution detail: ${chain}.
|
|
193
|
+
|
|
194
|
+
## Steps
|
|
195
|
+
|
|
196
|
+
${executionPlanLines.join("\n")}
|
|
197
|
+
|
|
198
|
+
## Notes
|
|
199
|
+
|
|
200
|
+
- Provenance lives in \`../references/overview.md\`.
|
|
201
|
+
- Replace these placeholders with concrete execution mechanics before shipping.
|
|
202
|
+
`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function buildWorkflowReferenceContent(workflow: DiscoveredWorkflow, title: string): string {
|
|
206
|
+
const query = workflow.representative_query.trim();
|
|
207
|
+
const chain = workflow.skills.join(" → ");
|
|
208
|
+
|
|
209
|
+
return `# ${title} Overview
|
|
210
|
+
|
|
211
|
+
This package was scaffolded from observed workflow telemetry.
|
|
212
|
+
|
|
213
|
+
## Provenance
|
|
214
|
+
|
|
215
|
+
- Workflow ID: ${workflow.workflow_id}
|
|
216
|
+
- Skills: ${chain}
|
|
217
|
+
- Observed sessions: ${workflow.occurrence_count}
|
|
218
|
+
- Synergy score: ${workflow.synergy_score.toFixed(2)}
|
|
219
|
+
- Sequence consistency: ${Math.round(workflow.sequence_consistency * 100)}%
|
|
220
|
+
- Completion rate: ${Math.round(workflow.completion_rate * 100)}%
|
|
221
|
+
${query.length > 0 ? `- Representative trigger: ${query}` : "- Representative trigger: not captured"}
|
|
222
|
+
|
|
223
|
+
## Authoring Notes
|
|
224
|
+
|
|
225
|
+
- Keep the router lean in \`../SKILL.md\`.
|
|
226
|
+
- Move detailed steps into \`../workflows/default.md\`.
|
|
227
|
+
- Expand this file with examples, vocabulary, and edge cases as the skill matures.
|
|
228
|
+
`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function replaceDraftFile(
|
|
232
|
+
draft: CreateSkillDraft,
|
|
233
|
+
relativePath: string,
|
|
234
|
+
content: string,
|
|
235
|
+
): CreateSkillDraft["files"] {
|
|
236
|
+
return draft.files.map((file) =>
|
|
237
|
+
file.relative_path === relativePath
|
|
238
|
+
? {
|
|
239
|
+
...file,
|
|
240
|
+
content,
|
|
241
|
+
}
|
|
242
|
+
: file,
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
196
246
|
export function buildWorkflowSkillDraft(
|
|
197
247
|
workflow: DiscoveredWorkflow,
|
|
198
248
|
options: WorkflowSkillDraftOptions = {},
|
|
@@ -201,18 +251,38 @@ export function buildWorkflowSkillDraft(
|
|
|
201
251
|
const skillName = slugifyWorkflowSkillName(baseLabel);
|
|
202
252
|
const title = titleCase(baseLabel) || titleCase(`${workflow.skills.join(" ")} workflow`);
|
|
203
253
|
const description = buildWorkflowSkillDescription(workflow, options.description);
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
254
|
+
const generatedBy = options.generatedBy?.trim() || "selftune workflows scaffold";
|
|
255
|
+
const baseDraft = buildCreateSkillDraft({
|
|
256
|
+
name: title,
|
|
257
|
+
description,
|
|
258
|
+
outputDir: options.outputDir,
|
|
259
|
+
cwd: options.cwd,
|
|
260
|
+
});
|
|
261
|
+
const skillContent = buildWorkflowSkillContent(
|
|
262
|
+
workflow,
|
|
263
|
+
title,
|
|
264
|
+
skillName,
|
|
265
|
+
description,
|
|
266
|
+
generatedBy,
|
|
267
|
+
);
|
|
268
|
+
const workflowContent = buildWorkflowDefaultContent(workflow, title);
|
|
269
|
+
const referenceContent = buildWorkflowReferenceContent(workflow, title);
|
|
270
|
+
const files = replaceDraftFile(baseDraft, "SKILL.md", skillContent).map((file) => {
|
|
271
|
+
if (file.relative_path === "workflows/default.md") {
|
|
272
|
+
return { ...file, content: workflowContent };
|
|
273
|
+
}
|
|
274
|
+
if (file.relative_path === "references/overview.md") {
|
|
275
|
+
return { ...file, content: referenceContent };
|
|
276
|
+
}
|
|
277
|
+
return file;
|
|
278
|
+
});
|
|
207
279
|
|
|
208
|
-
|
|
280
|
+
const draft: WorkflowSkillDraft = {
|
|
281
|
+
...baseDraft,
|
|
209
282
|
title,
|
|
210
283
|
skill_name: skillName,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
skill_dir: skillDir,
|
|
214
|
-
skill_path: skillPath,
|
|
215
|
-
content: buildWorkflowSkillContent(workflow, title, skillName, description),
|
|
284
|
+
files,
|
|
285
|
+
content: "",
|
|
216
286
|
source_workflow: {
|
|
217
287
|
workflow_id: workflow.workflow_id,
|
|
218
288
|
skills: workflow.skills,
|
|
@@ -221,6 +291,9 @@ export function buildWorkflowSkillDraft(
|
|
|
221
291
|
representative_query: workflow.representative_query,
|
|
222
292
|
},
|
|
223
293
|
};
|
|
294
|
+
|
|
295
|
+
draft.content = formatWorkflowSkillDraft(draft);
|
|
296
|
+
return draft;
|
|
224
297
|
}
|
|
225
298
|
|
|
226
299
|
export function formatWorkflowSkillDraft(draft: WorkflowSkillDraft): string {
|
|
@@ -236,6 +309,11 @@ export function formatWorkflowSkillDraft(draft: WorkflowSkillDraft): string {
|
|
|
236
309
|
lines.push(`Representative query: "${draft.source_workflow.representative_query.trim()}"`);
|
|
237
310
|
}
|
|
238
311
|
|
|
239
|
-
lines.push(""
|
|
312
|
+
lines.push("");
|
|
313
|
+
for (const file of draft.files) {
|
|
314
|
+
lines.push(`=== ${file.relative_path} ===`, file.content.trimEnd(), "");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
lines.push("Empty directories:", " - scripts/", " - assets/");
|
|
240
318
|
return lines.join("\n");
|
|
241
319
|
}
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
* - cliMain() (reads logs, discovers workflows, prints output or saves)
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { existsSync,
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { parseArgs } from "node:util";
|
|
13
13
|
|
|
14
|
+
import { writeCreateSkillDraft } from "../create/init.js";
|
|
14
15
|
import { getDb } from "../localdb/db.js";
|
|
15
16
|
import { querySessionTelemetry, querySkillUsageRecords } from "../localdb/queries.js";
|
|
16
17
|
import type {
|
|
@@ -242,13 +243,12 @@ Options:
|
|
|
242
243
|
);
|
|
243
244
|
}
|
|
244
245
|
|
|
245
|
-
|
|
246
|
-
writeFileSync(draft.skill_path, draft.content, "utf-8");
|
|
246
|
+
writeCreateSkillDraft(draft, { force: values.force });
|
|
247
247
|
|
|
248
248
|
if (values.json || !process.stdout.isTTY) {
|
|
249
249
|
console.log(JSON.stringify({ ...draft, written: true }, null, 2));
|
|
250
250
|
} else {
|
|
251
|
-
console.log(`Scaffolded skill "${draft.skill_name}" to ${draft.
|
|
251
|
+
console.log(`Scaffolded skill package "${draft.skill_name}" to ${draft.skill_dir}`);
|
|
252
252
|
}
|
|
253
253
|
return;
|
|
254
254
|
}
|
package/package.json
CHANGED
|
@@ -217,7 +217,7 @@ export const DASHBOARD_ROUTE_MANIFEST: readonly DashboardRouteManifestEntry[] =
|
|
|
217
217
|
icon: PackageIcon,
|
|
218
218
|
feature: "registry",
|
|
219
219
|
discoverableFeature: "registry",
|
|
220
|
-
lockedTitle: "Cloud Registry lives in
|
|
220
|
+
lockedTitle: "Cloud Registry lives in selftune Cloud",
|
|
221
221
|
lockedBody:
|
|
222
222
|
"Publish versioned skills, watch installations across projects, and roll back bad versions from a single cloud workspace.",
|
|
223
223
|
lockedHighlights: [
|
|
@@ -255,7 +255,7 @@ export const DASHBOARD_ROUTE_MANIFEST: readonly DashboardRouteManifestEntry[] =
|
|
|
255
255
|
icon: UsersIcon,
|
|
256
256
|
feature: "signals",
|
|
257
257
|
discoverableFeature: "signals",
|
|
258
|
-
lockedTitle: "Contributor signals run through
|
|
258
|
+
lockedTitle: "Contributor signals run through selftune Cloud",
|
|
259
259
|
lockedBody:
|
|
260
260
|
"See anonymized contributor signals, compare bundle submissions, and turn real-world usage into proposals without leaving the shared dashboard.",
|
|
261
261
|
lockedHighlights: [
|
|
@@ -379,7 +379,7 @@ function narrativeObservedText({
|
|
|
379
379
|
promptLinkRate != null
|
|
380
380
|
? ` It could link ${formatRate(promptLinkRate)} of those checks back to prompts.`
|
|
381
381
|
: "";
|
|
382
|
-
return `
|
|
382
|
+
return `selftune watched ${checks} skill checks across ${sessions} sessions.${promptClause}`;
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
function narrativeDiagnosisText({
|
|
@@ -411,17 +411,17 @@ function narrativeDecisionText({
|
|
|
411
411
|
}) {
|
|
412
412
|
switch (trustState) {
|
|
413
413
|
case "validated":
|
|
414
|
-
return `
|
|
414
|
+
return `selftune found a candidate that looks promising, but it has not been deployed yet. ${nextActionText}`;
|
|
415
415
|
case "deployed":
|
|
416
|
-
return `A change has already been deployed for this skill.
|
|
416
|
+
return `A change has already been deployed for this skill. selftune is now watching for regressions in real use.`;
|
|
417
417
|
case "rolled_back":
|
|
418
418
|
return `A previous change was rolled back, so the live skill is back on the safer version while selftune keeps observing.`;
|
|
419
419
|
case "watch":
|
|
420
|
-
return `
|
|
420
|
+
return `selftune sees enough signal to keep a close eye on this skill, but not enough to blindly change it. ${nextActionText}`;
|
|
421
421
|
case "observed":
|
|
422
|
-
return `
|
|
422
|
+
return `selftune is still learning how people use this skill before making stronger recommendations.`;
|
|
423
423
|
case "low_sample":
|
|
424
|
-
return `There is not enough evidence yet to trust a big change here.
|
|
424
|
+
return `There is not enough evidence yet to trust a big change here. selftune is still collecting examples.`;
|
|
425
425
|
default:
|
|
426
426
|
return latestAction
|
|
427
427
|
? `The latest automated decision for this skill was ${latestAction}. ${nextActionText}`
|
|
@@ -552,7 +552,7 @@ export function SkillTrustNarrativePanel({
|
|
|
552
552
|
/>
|
|
553
553
|
</div>
|
|
554
554
|
<div className="rounded-xl border border-border/10 bg-muted/15 px-4 py-3 text-sm text-muted-foreground">
|
|
555
|
-
If a proposal is rejected or still pending, your live skill has not changed yet.
|
|
555
|
+
If a proposal is rejected or still pending, your live skill has not changed yet. selftune
|
|
556
556
|
only earns trust by testing changes before deployment.
|
|
557
557
|
</div>
|
|
558
558
|
</CardContent>
|
|
@@ -18,6 +18,10 @@ const buttonVariants = cva(
|
|
|
18
18
|
destructive:
|
|
19
19
|
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
20
20
|
link: "text-primary underline-offset-4 hover:underline",
|
|
21
|
+
"glass-primary":
|
|
22
|
+
"border-cyan-300 bg-background/75 text-foreground shadow-[0_10px_28px_rgba(34,211,238,0.14),inset_0_1px_0_rgba(255,255,255,0.09)] hover:border-cyan-200 hover:bg-background/85 hover:shadow-[0_14px_34px_rgba(34,211,238,0.18),inset_0_1px_0_rgba(255,255,255,0.12)]",
|
|
23
|
+
"glass-secondary":
|
|
24
|
+
"border-border/70 bg-background/60 text-foreground shadow-[inset_0_1px_0_rgba(255,255,255,0.06)] hover:border-cyan-400/20 hover:bg-background/75 hover:shadow-[0_10px_24px_rgba(34,211,238,0.08),inset_0_1px_0_rgba(255,255,255,0.1)]",
|
|
21
25
|
},
|
|
22
26
|
size: {
|
|
23
27
|
default:
|
|
@@ -31,6 +35,7 @@ const buttonVariants = cva(
|
|
|
31
35
|
"icon-sm":
|
|
32
36
|
"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
33
37
|
"icon-lg": "size-9",
|
|
38
|
+
glass: "gap-2 px-4 py-2 backdrop-blur-sm",
|
|
34
39
|
},
|
|
35
40
|
},
|
|
36
41
|
defaultVariants: {
|