selftune 0.1.4 → 0.2.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.
Files changed (153) hide show
  1. package/.claude/agents/diagnosis-analyst.md +156 -0
  2. package/.claude/agents/evolution-reviewer.md +180 -0
  3. package/.claude/agents/integration-guide.md +212 -0
  4. package/.claude/agents/pattern-analyst.md +160 -0
  5. package/CHANGELOG.md +46 -1
  6. package/README.md +105 -257
  7. package/apps/local-dashboard/dist/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
  8. package/apps/local-dashboard/dist/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
  9. package/apps/local-dashboard/dist/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
  10. package/apps/local-dashboard/dist/assets/index-C4EOTFZ2.js +15 -0
  11. package/apps/local-dashboard/dist/assets/index-bl-Webyd.css +1 -0
  12. package/apps/local-dashboard/dist/assets/vendor-react-U7zYD9Rg.js +60 -0
  13. package/apps/local-dashboard/dist/assets/vendor-table-B7VF2Ipl.js +26 -0
  14. package/apps/local-dashboard/dist/assets/vendor-ui-D7_zX_qy.js +346 -0
  15. package/apps/local-dashboard/dist/favicon.png +0 -0
  16. package/apps/local-dashboard/dist/index.html +17 -0
  17. package/apps/local-dashboard/dist/logo.png +0 -0
  18. package/apps/local-dashboard/dist/logo.svg +9 -0
  19. package/assets/BeforeAfter.gif +0 -0
  20. package/assets/FeedbackLoop.gif +0 -0
  21. package/assets/logo.svg +9 -0
  22. package/assets/skill-health-badge.svg +20 -0
  23. package/cli/selftune/activation-rules.ts +171 -0
  24. package/cli/selftune/badge/badge-data.ts +108 -0
  25. package/cli/selftune/badge/badge-svg.ts +212 -0
  26. package/cli/selftune/badge/badge.ts +99 -0
  27. package/cli/selftune/canonical-export.ts +183 -0
  28. package/cli/selftune/constants.ts +103 -1
  29. package/cli/selftune/contribute/bundle.ts +314 -0
  30. package/cli/selftune/contribute/contribute.ts +214 -0
  31. package/cli/selftune/contribute/sanitize.ts +162 -0
  32. package/cli/selftune/cron/setup.ts +266 -0
  33. package/cli/selftune/dashboard-contract.ts +202 -0
  34. package/cli/selftune/dashboard-server.ts +1049 -0
  35. package/cli/selftune/dashboard.ts +43 -156
  36. package/cli/selftune/eval/baseline.ts +248 -0
  37. package/cli/selftune/eval/composability-v2.ts +273 -0
  38. package/cli/selftune/eval/composability.ts +117 -0
  39. package/cli/selftune/eval/generate-unit-tests.ts +143 -0
  40. package/cli/selftune/eval/hooks-to-evals.ts +101 -16
  41. package/cli/selftune/eval/import-skillsbench.ts +221 -0
  42. package/cli/selftune/eval/synthetic-evals.ts +172 -0
  43. package/cli/selftune/eval/unit-test-cli.ts +152 -0
  44. package/cli/selftune/eval/unit-test.ts +196 -0
  45. package/cli/selftune/evolution/deploy-proposal.ts +142 -1
  46. package/cli/selftune/evolution/evidence.ts +26 -0
  47. package/cli/selftune/evolution/evolve-body.ts +586 -0
  48. package/cli/selftune/evolution/evolve.ts +825 -116
  49. package/cli/selftune/evolution/extract-patterns.ts +105 -16
  50. package/cli/selftune/evolution/pareto.ts +314 -0
  51. package/cli/selftune/evolution/propose-body.ts +171 -0
  52. package/cli/selftune/evolution/propose-description.ts +100 -2
  53. package/cli/selftune/evolution/propose-routing.ts +166 -0
  54. package/cli/selftune/evolution/refine-body.ts +141 -0
  55. package/cli/selftune/evolution/rollback.ts +21 -4
  56. package/cli/selftune/evolution/validate-body.ts +254 -0
  57. package/cli/selftune/evolution/validate-proposal.ts +257 -35
  58. package/cli/selftune/evolution/validate-routing.ts +177 -0
  59. package/cli/selftune/grading/auto-grade.ts +200 -0
  60. package/cli/selftune/grading/grade-session.ts +513 -42
  61. package/cli/selftune/grading/pre-gates.ts +104 -0
  62. package/cli/selftune/grading/results.ts +42 -0
  63. package/cli/selftune/hooks/auto-activate.ts +185 -0
  64. package/cli/selftune/hooks/evolution-guard.ts +165 -0
  65. package/cli/selftune/hooks/prompt-log.ts +172 -2
  66. package/cli/selftune/hooks/session-stop.ts +123 -3
  67. package/cli/selftune/hooks/skill-change-guard.ts +112 -0
  68. package/cli/selftune/hooks/skill-eval.ts +119 -3
  69. package/cli/selftune/index.ts +415 -48
  70. package/cli/selftune/ingestors/claude-replay.ts +377 -0
  71. package/cli/selftune/ingestors/codex-rollout.ts +345 -46
  72. package/cli/selftune/ingestors/codex-wrapper.ts +207 -39
  73. package/cli/selftune/ingestors/openclaw-ingest.ts +573 -0
  74. package/cli/selftune/ingestors/opencode-ingest.ts +193 -17
  75. package/cli/selftune/init.ts +376 -16
  76. package/cli/selftune/last.ts +14 -5
  77. package/cli/selftune/localdb/db.ts +63 -0
  78. package/cli/selftune/localdb/materialize.ts +428 -0
  79. package/cli/selftune/localdb/queries.ts +376 -0
  80. package/cli/selftune/localdb/schema.ts +204 -0
  81. package/cli/selftune/memory/writer.ts +447 -0
  82. package/cli/selftune/monitoring/watch.ts +90 -16
  83. package/cli/selftune/normalization.ts +682 -0
  84. package/cli/selftune/observability.ts +19 -44
  85. package/cli/selftune/orchestrate.ts +1073 -0
  86. package/cli/selftune/quickstart.ts +203 -0
  87. package/cli/selftune/repair/skill-usage.ts +576 -0
  88. package/cli/selftune/schedule.ts +561 -0
  89. package/cli/selftune/status.ts +59 -33
  90. package/cli/selftune/sync.ts +627 -0
  91. package/cli/selftune/types.ts +525 -5
  92. package/cli/selftune/utils/canonical-log.ts +45 -0
  93. package/cli/selftune/utils/frontmatter.ts +217 -0
  94. package/cli/selftune/utils/hooks.ts +41 -0
  95. package/cli/selftune/utils/html.ts +27 -0
  96. package/cli/selftune/utils/llm-call.ts +103 -19
  97. package/cli/selftune/utils/math.ts +10 -0
  98. package/cli/selftune/utils/query-filter.ts +139 -0
  99. package/cli/selftune/utils/skill-discovery.ts +340 -0
  100. package/cli/selftune/utils/skill-log.ts +68 -0
  101. package/cli/selftune/utils/skill-usage-confidence.ts +18 -0
  102. package/cli/selftune/utils/transcript.ts +307 -26
  103. package/cli/selftune/utils/trigger-check.ts +89 -0
  104. package/cli/selftune/utils/tui.ts +156 -0
  105. package/cli/selftune/workflows/discover.ts +254 -0
  106. package/cli/selftune/workflows/skill-md-writer.ts +288 -0
  107. package/cli/selftune/workflows/workflows.ts +188 -0
  108. package/package.json +28 -11
  109. package/packages/telemetry-contract/README.md +11 -0
  110. package/packages/telemetry-contract/fixtures/golden.json +87 -0
  111. package/packages/telemetry-contract/fixtures/golden.test.ts +42 -0
  112. package/packages/telemetry-contract/index.ts +1 -0
  113. package/packages/telemetry-contract/package.json +19 -0
  114. package/packages/telemetry-contract/src/index.ts +2 -0
  115. package/packages/telemetry-contract/src/types.ts +163 -0
  116. package/packages/telemetry-contract/src/validators.ts +109 -0
  117. package/skill/SKILL.md +180 -33
  118. package/skill/Workflows/AutoActivation.md +145 -0
  119. package/skill/Workflows/Badge.md +124 -0
  120. package/skill/Workflows/Baseline.md +144 -0
  121. package/skill/Workflows/Composability.md +107 -0
  122. package/skill/Workflows/Contribute.md +94 -0
  123. package/skill/Workflows/Cron.md +132 -0
  124. package/skill/Workflows/Dashboard.md +214 -0
  125. package/skill/Workflows/Doctor.md +63 -14
  126. package/skill/Workflows/Evals.md +110 -18
  127. package/skill/Workflows/EvolutionMemory.md +154 -0
  128. package/skill/Workflows/Evolve.md +181 -21
  129. package/skill/Workflows/EvolveBody.md +159 -0
  130. package/skill/Workflows/Grade.md +36 -31
  131. package/skill/Workflows/ImportSkillsBench.md +117 -0
  132. package/skill/Workflows/Ingest.md +142 -21
  133. package/skill/Workflows/Initialize.md +91 -23
  134. package/skill/Workflows/Orchestrate.md +139 -0
  135. package/skill/Workflows/Replay.md +91 -0
  136. package/skill/Workflows/Rollback.md +23 -4
  137. package/skill/Workflows/Schedule.md +61 -0
  138. package/skill/Workflows/Sync.md +88 -0
  139. package/skill/Workflows/UnitTest.md +150 -0
  140. package/skill/Workflows/Watch.md +33 -1
  141. package/skill/Workflows/Workflows.md +129 -0
  142. package/skill/assets/activation-rules-default.json +26 -0
  143. package/skill/assets/multi-skill-settings.json +63 -0
  144. package/skill/assets/single-skill-settings.json +57 -0
  145. package/skill/references/invocation-taxonomy.md +2 -2
  146. package/skill/references/logs.md +164 -2
  147. package/skill/references/setup-patterns.md +65 -0
  148. package/skill/references/version-history.md +40 -0
  149. package/skill/settings_snippet.json +23 -0
  150. package/templates/activation-rules-default.json +27 -0
  151. package/templates/multi-skill-settings.json +64 -0
  152. package/templates/single-skill-settings.json +58 -0
  153. package/dashboard/index.html +0 -1119
@@ -3,48 +3,68 @@
3
3
  * selftune CLI entry point.
4
4
  *
5
5
  * Usage:
6
- * selftune init [options] Initialize agent identity and config
7
- * selftune evals [options] Generate eval sets from hook logs
8
- * selftune grade [options] Grade a skill session
9
- * selftune ingest-codex [options] Ingest Codex rollout logs
10
- * selftune ingest-opencode [options] Ingest OpenCode sessions
11
- * selftune wrap-codex [options] Wrap codex exec with telemetry
12
- * selftune evolve [options] Evolve a skill description via failure patterns
13
- * selftune rollback [options] Rollback a skill to its pre-evolution state
14
- * selftune watch [options] — Monitor post-deploy skill health
15
- * selftune doctor — Run health checks
16
- * selftune status Show skill health summary
17
- * selftune last — Show last session details
18
- * selftune dashboard [options] Open visual data dashboard
6
+ * selftune ingest <agent> Ingest agent sessions (claude, codex, opencode, openclaw, wrap-codex)
7
+ * selftune grade [mode] Grade skill sessions (auto, baseline)
8
+ * selftune evolve [target] Evolve skill descriptions (body, rollback)
9
+ * selftune eval <action> Evaluation tools (generate, unit-test, import, composability)
10
+ * selftune sync — Sync source-truth telemetry across supported agents
11
+ * selftune orchestrate Run autonomous core loop (sync → status → evolve → watch)
12
+ * selftune init Initialize agent identity and config
13
+ * selftune status Show skill health summary
14
+ * selftune watch — Monitor post-deploy skill health
15
+ * selftune doctor — Run health checks
16
+ * selftune dashboard Open visual data dashboard
17
+ * selftune last — Show last session details
18
+ * selftune cron Scheduling & automation (setup, list, remove)
19
+ * selftune badge — Generate skill health badges for READMEs
20
+ * selftune contribute — Export anonymized skill data for community
21
+ * selftune workflows — Discover and manage multi-skill workflows
22
+ * selftune quickstart — Guided onboarding: init, ingest, status, and suggestions
23
+ * selftune repair-skill-usage — Rebuild trustworthy skill usage from transcripts
24
+ * selftune export-canonical — Export canonical telemetry for downstream ingestion
25
+ * selftune hook <name> — Run a hook by name (prompt-log, session-stop, etc.)
19
26
  */
20
27
 
21
28
  const command = process.argv[2];
22
29
 
23
- if (!command || command === "--help" || command === "-h") {
30
+ if (command === "--help" || command === "-h") {
24
31
  console.log(`selftune — Skill observability and continuous improvement
25
32
 
26
33
  Usage:
27
34
  selftune <command> [options]
28
35
 
29
36
  Commands:
37
+ ingest <agent> Ingest agent sessions (claude, codex, opencode, openclaw, wrap-codex)
38
+ grade [mode] Grade skill sessions (auto, baseline)
39
+ evolve [target] Evolve skill descriptions (body, rollback)
40
+ eval <action> Evaluation tools (generate, unit-test, import, composability)
41
+ sync Sync source-truth telemetry across supported agents
42
+ orchestrate Run autonomous core loop (sync → status → evolve → watch)
30
43
  init Initialize agent identity and config
31
- evals Generate eval sets from hook logs
32
- grade Grade a skill session
33
- ingest-codex Ingest Codex rollout logs
34
- ingest-opencode Ingest OpenCode sessions
35
- wrap-codex Wrap codex exec with telemetry
36
- evolve Evolve a skill description via failure patterns
37
- rollback Rollback a skill to its pre-evolution state
44
+ status Show skill health summary
38
45
  watch Monitor post-deploy skill health
39
46
  doctor Run health checks
40
- status Show skill health summary
41
- last Show last session details
42
47
  dashboard Open visual data dashboard
48
+ last Show last session details
49
+ cron Scheduling & automation (setup, list, remove)
50
+ badge Generate skill health badges for READMEs
51
+ contribute Export anonymized skill data for community
52
+ workflows Discover and manage multi-skill workflows
53
+ quickstart Guided onboarding: init, ingest, status, and suggestions
54
+ repair-skill-usage Rebuild trustworthy skill usage from transcripts
55
+ export-canonical Export canonical telemetry for downstream ingestion
56
+ hook <name> Run a hook by name (prompt-log, session-stop, etc.)
43
57
 
44
58
  Run 'selftune <command> --help' for command-specific options.`);
45
59
  process.exit(0);
46
60
  }
47
61
 
62
+ if (!command) {
63
+ // Show status by default — same as `selftune status`
64
+ const { cliMain: statusMain } = await import("./status.js");
65
+ statusMain();
66
+ }
67
+
48
68
  // Route to the appropriate subcommand module.
49
69
  // We use dynamic imports so only the needed module is loaded.
50
70
  // Each module exports a cliMain() function that the router calls explicitly,
@@ -52,43 +72,241 @@ Run 'selftune <command> --help' for command-specific options.`);
52
72
  process.argv = [process.argv[0], process.argv[1], ...process.argv.slice(3)];
53
73
 
54
74
  switch (command) {
55
- case "init": {
56
- const { cliMain } = await import("./init.js");
57
- await cliMain();
58
- break;
59
- }
60
- case "evals": {
61
- const { cliMain } = await import("./eval/hooks-to-evals.js");
62
- cliMain();
75
+ // ── Grouped commands ──────────────────────────────────────────────────
76
+
77
+ case "ingest": {
78
+ const sub = process.argv[2];
79
+ if (!sub || sub === "--help" || sub === "-h") {
80
+ console.log(`selftune ingest — Import agent sessions into shared telemetry logs
81
+
82
+ Usage:
83
+ selftune ingest <agent> [options]
84
+
85
+ Agents:
86
+ claude Replay Claude Code transcripts into logs
87
+ codex Ingest Codex rollout logs (experimental)
88
+ opencode Ingest OpenCode sessions (experimental)
89
+ openclaw Ingest OpenClaw sessions (experimental)
90
+ wrap-codex Wrap codex exec with real-time telemetry (experimental)
91
+
92
+ Run 'selftune ingest <agent> --help' for agent-specific options.`);
93
+ process.exit(0);
94
+ }
95
+ // Strip the subcommand so downstream sees the same argv as before
96
+ process.argv = [process.argv[0], process.argv[1], ...process.argv.slice(3)];
97
+ switch (sub) {
98
+ case "claude": {
99
+ const { cliMain } = await import("./ingestors/claude-replay.js");
100
+ cliMain();
101
+ break;
102
+ }
103
+ case "codex": {
104
+ const { cliMain } = await import("./ingestors/codex-rollout.js");
105
+ cliMain();
106
+ break;
107
+ }
108
+ case "opencode": {
109
+ const { cliMain } = await import("./ingestors/opencode-ingest.js");
110
+ cliMain();
111
+ break;
112
+ }
113
+ case "openclaw": {
114
+ const { cliMain } = await import("./ingestors/openclaw-ingest.js");
115
+ cliMain();
116
+ break;
117
+ }
118
+ case "wrap-codex": {
119
+ const { cliMain } = await import("./ingestors/codex-wrapper.js");
120
+ await cliMain();
121
+ break;
122
+ }
123
+ default:
124
+ console.error(
125
+ `Unknown ingest agent: ${sub}\nRun 'selftune ingest --help' for available agents.`,
126
+ );
127
+ process.exit(1);
128
+ }
63
129
  break;
64
130
  }
131
+
65
132
  case "grade": {
66
- const { cliMain } = await import("./grading/grade-session.js");
67
- await cliMain();
68
- break;
69
- }
70
- case "ingest-codex": {
71
- const { cliMain } = await import("./ingestors/codex-rollout.js");
72
- cliMain();
133
+ const sub = process.argv[2];
134
+ if (sub === "--help" || sub === "-h") {
135
+ console.log(`selftune grade — Grade skill sessions
136
+
137
+ Usage:
138
+ selftune grade [options] Run the default session grader
139
+ selftune grade auto [options] Batch auto-grade sessions
140
+ selftune grade baseline [options] Measure baseline lift (no-skill comparison)
141
+
142
+ Run 'selftune grade <subcommand> --help' for subcommand-specific options.`);
143
+ process.exit(0);
144
+ }
145
+ // If no subcommand or starts with '-', run the default grader
146
+ if (!sub || sub.startsWith("-")) {
147
+ const { cliMain } = await import("./grading/grade-session.js");
148
+ await cliMain();
149
+ } else {
150
+ // Strip the subcommand
151
+ process.argv = [process.argv[0], process.argv[1], ...process.argv.slice(3)];
152
+ switch (sub) {
153
+ case "auto": {
154
+ const { cliMain } = await import("./grading/auto-grade.js");
155
+ await cliMain();
156
+ break;
157
+ }
158
+ case "baseline": {
159
+ const { cliMain } = await import("./eval/baseline.js");
160
+ await cliMain();
161
+ break;
162
+ }
163
+ default:
164
+ console.error(
165
+ `Unknown grade mode: ${sub}\nRun 'selftune grade --help' for available modes.`,
166
+ );
167
+ process.exit(1);
168
+ }
169
+ }
73
170
  break;
74
171
  }
75
- case "ingest-opencode": {
76
- const { cliMain } = await import("./ingestors/opencode-ingest.js");
77
- cliMain();
172
+
173
+ case "evolve": {
174
+ const sub = process.argv[2];
175
+ if (sub === "--help" || sub === "-h") {
176
+ console.log(`selftune evolve — Evolve skill descriptions
177
+
178
+ Usage:
179
+ selftune evolve [options] Run description evolution
180
+ selftune evolve body [options] Evolve full body or routing table
181
+ selftune evolve rollback [options] Rollback a previous evolution
182
+
183
+ Run 'selftune evolve <subcommand> --help' for subcommand-specific options.`);
184
+ process.exit(0);
185
+ }
186
+ // If no subcommand or starts with '-', run the default evolve
187
+ if (!sub || sub.startsWith("-")) {
188
+ const { cliMain } = await import("./evolution/evolve.js");
189
+ await cliMain();
190
+ } else {
191
+ // Strip the subcommand
192
+ process.argv = [process.argv[0], process.argv[1], ...process.argv.slice(3)];
193
+ switch (sub) {
194
+ case "body": {
195
+ const { cliMain } = await import("./evolution/evolve-body.js");
196
+ await cliMain();
197
+ break;
198
+ }
199
+ case "rollback": {
200
+ const { cliMain } = await import("./evolution/rollback.js");
201
+ await cliMain();
202
+ break;
203
+ }
204
+ default:
205
+ console.error(
206
+ `Unknown evolve target: ${sub}\nRun 'selftune evolve --help' for available targets.`,
207
+ );
208
+ process.exit(1);
209
+ }
210
+ }
78
211
  break;
79
212
  }
80
- case "wrap-codex": {
81
- const { cliMain } = await import("./ingestors/codex-wrapper.js");
82
- await cliMain();
213
+
214
+ case "eval": {
215
+ const sub = process.argv[2];
216
+ if (!sub || sub === "--help" || sub === "-h") {
217
+ console.log(`selftune eval — Evaluation and testing tools
218
+
219
+ Usage:
220
+ selftune eval <action> [options]
221
+
222
+ Actions:
223
+ generate Generate eval sets from hook logs
224
+ unit-test Run or generate skill unit tests
225
+ import Import SkillsBench task corpus as eval entries
226
+ composability Analyze skill co-occurrence conflicts
227
+
228
+ Run 'selftune eval <action> --help' for action-specific options.`);
229
+ process.exit(0);
230
+ }
231
+ // Strip the subcommand
232
+ process.argv = [process.argv[0], process.argv[1], ...process.argv.slice(3)];
233
+ switch (sub) {
234
+ case "generate": {
235
+ const { cliMain } = await import("./eval/hooks-to-evals.js");
236
+ cliMain();
237
+ break;
238
+ }
239
+ case "unit-test": {
240
+ const { cliMain } = await import("./eval/unit-test-cli.js");
241
+ await cliMain();
242
+ break;
243
+ }
244
+ case "import": {
245
+ const { cliMain } = await import("./eval/import-skillsbench.js");
246
+ cliMain();
247
+ break;
248
+ }
249
+ case "composability": {
250
+ if (process.argv[2] === "--help" || process.argv[2] === "-h") {
251
+ console.log(
252
+ "selftune eval composability --skill <name> [--window <days>] [--telemetry-log <path>]",
253
+ );
254
+ process.exit(0);
255
+ }
256
+ const { parseArgs } = await import("node:util");
257
+ const { readJsonl } = await import("./utils/jsonl.js");
258
+ const { TELEMETRY_LOG } = await import("./constants.js");
259
+ const { analyzeComposability } = await import("./eval/composability.js");
260
+ let values: ReturnType<typeof parseArgs>["values"];
261
+ try {
262
+ ({ values } = parseArgs({
263
+ options: {
264
+ skill: { type: "string" },
265
+ window: { type: "string" },
266
+ "telemetry-log": { type: "string" },
267
+ },
268
+ strict: true,
269
+ }));
270
+ } catch (error) {
271
+ const message = error instanceof Error ? error.message : String(error);
272
+ console.error(`Invalid arguments: ${message}`);
273
+ console.error("Run 'selftune eval composability --help' for usage.");
274
+ process.exit(1);
275
+ }
276
+ if (!values.skill) {
277
+ console.error("[ERROR] --skill <name> is required.");
278
+ process.exit(1);
279
+ }
280
+ const logPath = values["telemetry-log"] ?? TELEMETRY_LOG;
281
+ const telemetry = readJsonl(logPath);
282
+ const rawWindow = values.window as string | undefined;
283
+ if (rawWindow !== undefined && !/^[1-9]\d*$/.test(rawWindow)) {
284
+ console.error("Invalid --window value. Use a positive integer number of days.");
285
+ process.exit(1);
286
+ }
287
+ const windowSize = rawWindow === undefined ? undefined : Number(rawWindow);
288
+ const report = analyzeComposability(values.skill, telemetry, windowSize);
289
+ console.log(JSON.stringify(report, null, 2));
290
+ break;
291
+ }
292
+ default:
293
+ console.error(
294
+ `Unknown eval action: ${sub}\nRun 'selftune eval --help' for available actions.`,
295
+ );
296
+ process.exit(1);
297
+ }
83
298
  break;
84
299
  }
85
- case "evolve": {
86
- const { cliMain } = await import("./evolution/evolve.js");
300
+
301
+ // ── Unchanged commands ────────────────────────────────────────────────
302
+
303
+ case "init": {
304
+ const { cliMain } = await import("./init.js");
87
305
  await cliMain();
88
306
  break;
89
307
  }
90
- case "rollback": {
91
- const { cliMain } = await import("./evolution/rollback.js");
308
+ case "contribute": {
309
+ const { cliMain } = await import("./contribute/contribute.js");
92
310
  await cliMain();
93
311
  break;
94
312
  }
@@ -116,9 +334,158 @@ switch (command) {
116
334
  }
117
335
  case "dashboard": {
118
336
  const { cliMain } = await import("./dashboard.js");
337
+ await cliMain();
338
+ break;
339
+ }
340
+ case "cron":
341
+ case "schedule": {
342
+ const sub = process.argv[2];
343
+ if (sub === "--help" || sub === "-h" || (!sub && command === "cron")) {
344
+ console.log(`selftune cron — Scheduling & automation for selftune
345
+
346
+ Usage:
347
+ selftune cron <subcommand> [options]
348
+
349
+ Subcommands:
350
+ setup Auto-detect platform and install scheduled jobs (cron/launchd/systemd)
351
+ setup --platform openclaw Use OpenClaw-specific cron integration
352
+ list Show registered selftune cron jobs (OpenClaw)
353
+ remove Remove selftune cron jobs (OpenClaw)
354
+
355
+ Flags (setup):
356
+ --platform <name> Force a specific platform (openclaw, cron, launchd, systemd)
357
+ --dry-run Preview without installing
358
+ --tz <timezone> IANA timezone for job schedules (OpenClaw only)
359
+ --format, -f Alias for --platform (backward compat with schedule)
360
+ --install Write and activate artifacts (default for setup)
361
+
362
+ Aliases:
363
+ selftune schedule → selftune cron
364
+
365
+ Run 'selftune cron <subcommand> --help' for subcommand-specific options.`);
366
+ process.exit(0);
367
+ }
368
+
369
+ // If invoked as `selftune schedule` with no subcommand or with flags,
370
+ // route directly to the schedule module for backward compatibility
371
+ if (command === "schedule" && (!sub || sub.startsWith("-"))) {
372
+ const { cliMain } = await import("./schedule.js");
373
+ cliMain();
374
+ break;
375
+ }
376
+
377
+ // Strip the subcommand so downstream sees clean argv
378
+ process.argv = [process.argv[0], process.argv[1], ...process.argv.slice(3)];
379
+
380
+ switch (sub) {
381
+ case "setup": {
382
+ // Check for --platform flag to decide which setup path
383
+ const platformIdx = process.argv.indexOf("--platform");
384
+ const platformVal = platformIdx >= 0 ? process.argv[platformIdx + 1] : undefined;
385
+
386
+ if (platformVal === "openclaw") {
387
+ // Remove --platform openclaw from argv before passing to cron/setup
388
+ process.argv = process.argv.filter((_, i) => i !== platformIdx && i !== platformIdx + 1);
389
+ const { cliMain } = await import("./cron/setup.js");
390
+ await cliMain();
391
+ } else if (platformVal) {
392
+ // Map --platform to --format for the schedule module
393
+ process.argv = process.argv.filter((_, i) => i !== platformIdx && i !== platformIdx + 1);
394
+ process.argv.push("--format", platformVal, "--install");
395
+ const { cliMain } = await import("./schedule.js");
396
+ cliMain();
397
+ } else {
398
+ // Auto-detect: install schedule artifacts for the current platform
399
+ process.argv.push("--install");
400
+ const { cliMain } = await import("./schedule.js");
401
+ cliMain();
402
+ }
403
+ break;
404
+ }
405
+ case "list": {
406
+ const { cliMain } = await import("./cron/setup.js");
407
+ // Re-add 'list' so cron/setup.ts sees the subcommand
408
+ process.argv = [process.argv[0], process.argv[1], "list", ...process.argv.slice(2)];
409
+ await cliMain();
410
+ break;
411
+ }
412
+ case "remove": {
413
+ const { cliMain } = await import("./cron/setup.js");
414
+ // Re-add 'remove' so cron/setup.ts sees the subcommand
415
+ process.argv = [process.argv[0], process.argv[1], "remove", ...process.argv.slice(2)];
416
+ await cliMain();
417
+ break;
418
+ }
419
+ default:
420
+ console.error(
421
+ `Unknown cron subcommand: ${sub}\nRun 'selftune cron --help' for available subcommands.`,
422
+ );
423
+ process.exit(1);
424
+ }
425
+ break;
426
+ }
427
+ case "badge": {
428
+ const { cliMain } = await import("./badge/badge.js");
429
+ cliMain();
430
+ break;
431
+ }
432
+ case "sync": {
433
+ const { cliMain } = await import("./sync.js");
119
434
  cliMain();
120
435
  break;
121
436
  }
437
+ case "workflows": {
438
+ const { cliMain } = await import("./workflows/workflows.js");
439
+ await cliMain();
440
+ break;
441
+ }
442
+ case "quickstart": {
443
+ const { cliMain } = await import("./quickstart.js");
444
+ await cliMain();
445
+ break;
446
+ }
447
+ case "repair-skill-usage": {
448
+ const { cliMain } = await import("./repair/skill-usage.js");
449
+ cliMain();
450
+ break;
451
+ }
452
+ case "export-canonical": {
453
+ const { cliMain } = await import("./canonical-export.js");
454
+ cliMain();
455
+ break;
456
+ }
457
+ case "orchestrate": {
458
+ const { cliMain } = await import("./orchestrate.js");
459
+ await cliMain();
460
+ break;
461
+ }
462
+ case "hook": {
463
+ // Dispatch to the appropriate hook file by name.
464
+ const hookName = process.argv[2]; // argv was shifted above
465
+ const HOOK_MAP: Record<string, string> = {
466
+ "prompt-log": "prompt-log.ts",
467
+ "session-stop": "session-stop.ts",
468
+ "skill-eval": "skill-eval.ts",
469
+ "auto-activate": "auto-activate.ts",
470
+ "skill-change-guard": "skill-change-guard.ts",
471
+ "evolution-guard": "evolution-guard.ts",
472
+ };
473
+ if (!hookName || !HOOK_MAP[hookName]) {
474
+ const available = Object.keys(HOOK_MAP).join(", ");
475
+ console.error(`Unknown hook: ${hookName ?? "(none)"}\nAvailable hooks: ${available}`);
476
+ process.exit(1);
477
+ }
478
+ const { resolve, dirname } = await import("node:path");
479
+ const { fileURLToPath } = await import("node:url");
480
+ const { spawnSync } = await import("node:child_process");
481
+ const hooksDir = resolve(dirname(fileURLToPath(import.meta.url)), "hooks");
482
+ const hookFile = resolve(hooksDir, HOOK_MAP[hookName]);
483
+ const result = spawnSync("bun", ["run", hookFile], {
484
+ stdio: "inherit",
485
+ });
486
+ process.exit(result.status ?? 1);
487
+ break;
488
+ }
122
489
  default:
123
490
  console.error(`Unknown command: ${command}\nRun 'selftune --help' for available commands.`);
124
491
  process.exit(1);