selftune 0.2.0 → 0.2.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.
Files changed (122) hide show
  1. package/.claude/agents/diagnosis-analyst.md +20 -10
  2. package/.claude/agents/evolution-reviewer.md +14 -1
  3. package/.claude/agents/integration-guide.md +18 -6
  4. package/.claude/agents/pattern-analyst.md +18 -5
  5. package/CHANGELOG.md +12 -4
  6. package/README.md +43 -35
  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/cli/selftune/badge/badge-data.ts +1 -1
  20. package/cli/selftune/badge/badge.ts +4 -8
  21. package/cli/selftune/canonical-export.ts +183 -0
  22. package/cli/selftune/constants.ts +28 -0
  23. package/cli/selftune/contribute/contribute.ts +1 -1
  24. package/cli/selftune/cron/setup.ts +17 -17
  25. package/cli/selftune/dashboard-contract.ts +202 -0
  26. package/cli/selftune/dashboard-server.ts +653 -186
  27. package/cli/selftune/dashboard.ts +41 -176
  28. package/cli/selftune/eval/baseline.ts +5 -4
  29. package/cli/selftune/eval/composability-v2.ts +273 -0
  30. package/cli/selftune/eval/hooks-to-evals.ts +34 -15
  31. package/cli/selftune/eval/unit-test-cli.ts +1 -1
  32. package/cli/selftune/evolution/evidence.ts +26 -0
  33. package/cli/selftune/evolution/evolve-body.ts +105 -11
  34. package/cli/selftune/evolution/evolve.ts +371 -25
  35. package/cli/selftune/evolution/extract-patterns.ts +87 -29
  36. package/cli/selftune/evolution/rollback.ts +2 -2
  37. package/cli/selftune/grading/auto-grade.ts +200 -0
  38. package/cli/selftune/grading/grade-session.ts +448 -97
  39. package/cli/selftune/grading/results.ts +42 -0
  40. package/cli/selftune/hooks/prompt-log.ts +172 -2
  41. package/cli/selftune/hooks/session-stop.ts +123 -3
  42. package/cli/selftune/hooks/skill-eval.ts +119 -3
  43. package/cli/selftune/index.ts +395 -116
  44. package/cli/selftune/ingestors/claude-replay.ts +140 -114
  45. package/cli/selftune/ingestors/codex-rollout.ts +345 -46
  46. package/cli/selftune/ingestors/codex-wrapper.ts +207 -39
  47. package/cli/selftune/ingestors/openclaw-ingest.ts +141 -8
  48. package/cli/selftune/ingestors/opencode-ingest.ts +193 -17
  49. package/cli/selftune/init.ts +227 -14
  50. package/cli/selftune/last.ts +14 -5
  51. package/cli/selftune/localdb/db.ts +63 -0
  52. package/cli/selftune/localdb/materialize.ts +428 -0
  53. package/cli/selftune/localdb/queries.ts +376 -0
  54. package/cli/selftune/localdb/schema.ts +204 -0
  55. package/cli/selftune/monitoring/watch.ts +66 -15
  56. package/cli/selftune/normalization.ts +682 -0
  57. package/cli/selftune/observability.ts +19 -44
  58. package/cli/selftune/orchestrate.ts +1073 -0
  59. package/cli/selftune/quickstart.ts +203 -0
  60. package/cli/selftune/repair/skill-usage.ts +576 -0
  61. package/cli/selftune/schedule.ts +561 -0
  62. package/cli/selftune/status.ts +48 -26
  63. package/cli/selftune/sync.ts +627 -0
  64. package/cli/selftune/types.ts +148 -0
  65. package/cli/selftune/utils/canonical-log.ts +45 -0
  66. package/cli/selftune/utils/hooks.ts +41 -0
  67. package/cli/selftune/utils/html.ts +27 -0
  68. package/cli/selftune/utils/llm-call.ts +78 -20
  69. package/cli/selftune/utils/math.ts +10 -0
  70. package/cli/selftune/utils/query-filter.ts +139 -0
  71. package/cli/selftune/utils/skill-discovery.ts +340 -0
  72. package/cli/selftune/utils/skill-log.ts +68 -0
  73. package/cli/selftune/utils/skill-usage-confidence.ts +18 -0
  74. package/cli/selftune/utils/transcript.ts +272 -26
  75. package/cli/selftune/workflows/discover.ts +254 -0
  76. package/cli/selftune/workflows/skill-md-writer.ts +288 -0
  77. package/cli/selftune/workflows/workflows.ts +188 -0
  78. package/package.json +21 -8
  79. package/packages/telemetry-contract/README.md +11 -0
  80. package/packages/telemetry-contract/fixtures/golden.json +87 -0
  81. package/packages/telemetry-contract/fixtures/golden.test.ts +42 -0
  82. package/packages/telemetry-contract/index.ts +1 -0
  83. package/packages/telemetry-contract/package.json +19 -0
  84. package/packages/telemetry-contract/src/index.ts +2 -0
  85. package/packages/telemetry-contract/src/types.ts +163 -0
  86. package/packages/telemetry-contract/src/validators.ts +109 -0
  87. package/skill/SKILL.md +84 -53
  88. package/skill/Workflows/AutoActivation.md +17 -16
  89. package/skill/Workflows/Badge.md +6 -0
  90. package/skill/Workflows/Baseline.md +46 -23
  91. package/skill/Workflows/Composability.md +12 -5
  92. package/skill/Workflows/Contribute.md +17 -14
  93. package/skill/Workflows/Cron.md +56 -79
  94. package/skill/Workflows/Dashboard.md +45 -34
  95. package/skill/Workflows/Doctor.md +30 -17
  96. package/skill/Workflows/Evals.md +64 -40
  97. package/skill/Workflows/EvolutionMemory.md +2 -0
  98. package/skill/Workflows/Evolve.md +102 -47
  99. package/skill/Workflows/EvolveBody.md +6 -6
  100. package/skill/Workflows/Grade.md +36 -31
  101. package/skill/Workflows/ImportSkillsBench.md +11 -5
  102. package/skill/Workflows/Ingest.md +43 -36
  103. package/skill/Workflows/Initialize.md +44 -30
  104. package/skill/Workflows/Orchestrate.md +139 -0
  105. package/skill/Workflows/Replay.md +39 -18
  106. package/skill/Workflows/Rollback.md +3 -3
  107. package/skill/Workflows/Schedule.md +61 -0
  108. package/skill/Workflows/Sync.md +88 -0
  109. package/skill/Workflows/UnitTest.md +34 -22
  110. package/skill/Workflows/Watch.md +14 -4
  111. package/skill/Workflows/Workflows.md +129 -0
  112. package/skill/assets/activation-rules-default.json +26 -0
  113. package/skill/assets/multi-skill-settings.json +63 -0
  114. package/skill/assets/single-skill-settings.json +57 -0
  115. package/skill/references/invocation-taxonomy.md +2 -2
  116. package/skill/references/logs.md +164 -2
  117. package/skill/references/setup-patterns.md +65 -0
  118. package/skill/references/version-history.md +40 -0
  119. package/skill/settings_snippet.json +1 -1
  120. package/templates/multi-skill-settings.json +7 -7
  121. package/templates/single-skill-settings.json +6 -6
  122. package/dashboard/index.html +0 -1680
@@ -8,14 +8,14 @@
8
8
  * - Hook installation checks
9
9
  */
10
10
 
11
- import { execSync } from "node:child_process";
12
11
  import { existsSync, readFileSync } from "node:fs";
13
12
  import { homedir } from "node:os";
14
13
  import { join } from "node:path";
15
14
  import { LOG_DIR, REQUIRED_FIELDS, SELFTUNE_CONFIG_PATH } from "./constants.js";
16
15
  import type { DoctorResult, HealthCheck, HealthStatus, SelftuneConfig } from "./types.js";
16
+ import { missingClaudeCodeHookKeys } from "./utils/hooks.js";
17
17
 
18
- const VALID_AGENT_TYPES = new Set(["claude_code", "codex", "opencode", "unknown"]);
18
+ const VALID_AGENT_TYPES = new Set(["claude_code", "codex", "opencode", "openclaw", "unknown"]);
19
19
  const VALID_LLM_MODES = new Set(["agent"]);
20
20
 
21
21
  const LOG_FILES: Record<string, string> = {
@@ -25,12 +25,20 @@ const LOG_FILES: Record<string, string> = {
25
25
  evolution_audit: join(LOG_DIR, "evolution_audit_log.jsonl"),
26
26
  };
27
27
 
28
- const HOOK_FILES = ["prompt-log.ts", "session-stop.ts", "skill-eval.ts"];
28
+ /**
29
+ * Maximum number of lines to validate in a JSONL health check.
30
+ * Large log files (60k+ lines) can take many seconds to fully parse,
31
+ * so we sample the first N lines for the health check.
32
+ */
33
+ const MAX_VALIDATION_LINES = 500;
29
34
 
30
35
  /**
31
36
  * Validate a JSONL file: parse each line as JSON and check that all
32
37
  * `requiredFields` are present. Returns a status/message pair suitable
33
38
  * for embedding in a {@link HealthCheck}.
39
+ *
40
+ * For performance, only the first {@link MAX_VALIDATION_LINES} non-blank
41
+ * lines are validated. The total line count still reflects the full file.
34
42
  */
35
43
  function validateJsonlFile(
36
44
  filePath: string,
@@ -39,12 +47,15 @@ function validateJsonlFile(
39
47
  let lineCount = 0;
40
48
  let parseErrors = 0;
41
49
  let schemaErrors = 0;
50
+ let validatedCount = 0;
42
51
 
43
52
  const content = readFileSync(filePath, "utf-8");
44
53
  for (const line of content.split("\n")) {
45
54
  const trimmed = line.trim();
46
55
  if (!trimmed) continue;
47
56
  lineCount++;
57
+ if (validatedCount >= MAX_VALIDATION_LINES) continue;
58
+ validatedCount++;
48
59
  try {
49
60
  const record = JSON.parse(trimmed);
50
61
  const keys = new Set(Object.keys(record));
@@ -62,7 +73,7 @@ function validateJsonlFile(
62
73
  if (parseErrors > 0 || schemaErrors > 0) {
63
74
  return {
64
75
  status: "fail",
65
- message: `${lineCount} records, ${parseErrors} parse errors, ${schemaErrors} schema errors`,
76
+ message: `${lineCount} records (${validatedCount} validated), ${parseErrors} parse errors, ${schemaErrors} schema errors`,
66
77
  };
67
78
  }
68
79
  return { status: "pass", message: `${lineCount} records, all valid` };
@@ -92,37 +103,9 @@ export function checkLogHealth(): HealthCheck[] {
92
103
  export function checkHookInstallation(): HealthCheck[] {
93
104
  const checks: HealthCheck[] = [];
94
105
 
95
- // Resolve the repository root so we check the actual active hooks, not bundled source files
96
- let repoRoot: string;
97
- try {
98
- repoRoot = execSync("git rev-parse --show-toplevel", {
99
- encoding: "utf-8",
100
- timeout: 5000,
101
- }).trim();
102
- } catch {
103
- // Not inside a git repo -- fall back to cwd
104
- repoRoot = process.cwd();
105
- }
106
-
107
- for (const hook of HOOK_FILES) {
108
- const hookPath = join(repoRoot, ".git", "hooks", hook);
109
- const check: HealthCheck = {
110
- name: `hook_${hook}`,
111
- path: hookPath,
112
- status: "pass",
113
- message: "",
114
- };
115
- if (existsSync(hookPath)) {
116
- check.status = "pass";
117
- check.message = "Hook file present";
118
- } else {
119
- check.status = "fail";
120
- check.message = "Hook file missing";
121
- }
122
- checks.push(check);
123
- }
124
-
125
- // Also check if hooks are configured in Claude Code settings
106
+ // Check if hooks are configured in Claude Code settings.json
107
+ // Claude Code uses hook keys: UserPromptSubmit, PreToolUse, PostToolUse, Stop
108
+ // (not the old kebab-case names like prompt-submit, post-tool-use, session-stop)
126
109
  const settingsPath = join(homedir(), ".claude", "settings.json");
127
110
  const settingsCheck: HealthCheck = {
128
111
  name: "hook_settings",
@@ -142,15 +125,7 @@ export function checkHookInstallation(): HealthCheck[] {
142
125
  settingsCheck.status = "warn";
143
126
  settingsCheck.message = "No hooks section in settings.json";
144
127
  } else {
145
- const hookKeys = ["prompt-submit", "post-tool-use", "session-stop"];
146
- const missing = hookKeys.filter((k) => {
147
- const entries = hooks[k];
148
- if (!Array.isArray(entries) || entries.length === 0) return true;
149
- return !entries.some(
150
- (e: { command?: string }) =>
151
- typeof e.command === "string" && e.command.includes("selftune"),
152
- );
153
- });
128
+ const missing = missingClaudeCodeHookKeys(hooks as Record<string, unknown>);
154
129
  if (missing.length > 0) {
155
130
  settingsCheck.status = "warn";
156
131
  settingsCheck.message = `Selftune hooks not configured for: ${missing.join(", ")}`;