@skillsmith/cli 0.6.2 → 0.6.4

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 (210) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/src/commands/__meta__/telemetry-coverage.test.d.ts +20 -0
  4. package/dist/src/commands/__meta__/telemetry-coverage.test.d.ts.map +1 -0
  5. package/dist/src/commands/__meta__/telemetry-coverage.test.js +118 -0
  6. package/dist/src/commands/__meta__/telemetry-coverage.test.js.map +1 -0
  7. package/dist/src/commands/ab-test.d.ts +1 -0
  8. package/dist/src/commands/ab-test.d.ts.map +1 -1
  9. package/dist/src/commands/ab-test.js +17 -8
  10. package/dist/src/commands/ab-test.js.map +1 -1
  11. package/dist/src/commands/analyze.d.ts +1 -0
  12. package/dist/src/commands/analyze.d.ts.map +1 -1
  13. package/dist/src/commands/analyze.js +32 -23
  14. package/dist/src/commands/analyze.js.map +1 -1
  15. package/dist/src/commands/audit-collisions.d.ts +1 -4
  16. package/dist/src/commands/audit-collisions.d.ts.map +1 -1
  17. package/dist/src/commands/audit-collisions.js +32 -23
  18. package/dist/src/commands/audit-collisions.js.map +1 -1
  19. package/dist/src/commands/audit-sources.action.d.ts +37 -0
  20. package/dist/src/commands/audit-sources.action.d.ts.map +1 -0
  21. package/dist/src/commands/audit-sources.action.js +300 -0
  22. package/dist/src/commands/audit-sources.action.js.map +1 -0
  23. package/dist/src/commands/audit-sources.d.ts +37 -0
  24. package/dist/src/commands/audit-sources.d.ts.map +1 -0
  25. package/dist/src/commands/audit-sources.js +55 -0
  26. package/dist/src/commands/audit-sources.js.map +1 -0
  27. package/dist/src/commands/audit-sources.test.d.ts +9 -0
  28. package/dist/src/commands/audit-sources.test.d.ts.map +1 -0
  29. package/dist/src/commands/audit-sources.test.js +228 -0
  30. package/dist/src/commands/audit-sources.test.js.map +1 -0
  31. package/dist/src/commands/audit.d.ts +2 -6
  32. package/dist/src/commands/audit.d.ts.map +1 -1
  33. package/dist/src/commands/audit.js +52 -31
  34. package/dist/src/commands/audit.js.map +1 -1
  35. package/dist/src/commands/author/index.d.ts +2 -1
  36. package/dist/src/commands/author/index.d.ts.map +1 -1
  37. package/dist/src/commands/author/index.js +4 -1
  38. package/dist/src/commands/author/index.js.map +1 -1
  39. package/dist/src/commands/author/init.action.d.ts +29 -0
  40. package/dist/src/commands/author/init.action.d.ts.map +1 -0
  41. package/dist/src/commands/author/init.action.js +131 -0
  42. package/dist/src/commands/author/init.action.js.map +1 -0
  43. package/dist/src/commands/author/init.d.ts +0 -14
  44. package/dist/src/commands/author/init.d.ts.map +1 -1
  45. package/dist/src/commands/author/init.js +3 -87
  46. package/dist/src/commands/author/init.js.map +1 -1
  47. package/dist/src/commands/author/mcp-init.d.ts +1 -0
  48. package/dist/src/commands/author/mcp-init.d.ts.map +1 -1
  49. package/dist/src/commands/author/mcp-init.js +21 -13
  50. package/dist/src/commands/author/mcp-init.js.map +1 -1
  51. package/dist/src/commands/author/subagent.d.ts +1 -0
  52. package/dist/src/commands/author/subagent.d.ts.map +1 -1
  53. package/dist/src/commands/author/subagent.js +23 -15
  54. package/dist/src/commands/author/subagent.js.map +1 -1
  55. package/dist/src/commands/author/transform.d.ts +1 -0
  56. package/dist/src/commands/author/transform.d.ts.map +1 -1
  57. package/dist/src/commands/author/transform.js +23 -15
  58. package/dist/src/commands/author/transform.js.map +1 -1
  59. package/dist/src/commands/config.d.ts +2 -0
  60. package/dist/src/commands/config.d.ts.map +1 -1
  61. package/dist/src/commands/config.js +43 -28
  62. package/dist/src/commands/config.js.map +1 -1
  63. package/dist/src/commands/create.d.ts +1 -0
  64. package/dist/src/commands/create.d.ts.map +1 -1
  65. package/dist/src/commands/create.js +28 -19
  66. package/dist/src/commands/create.js.map +1 -1
  67. package/dist/src/commands/diff.d.ts +4 -0
  68. package/dist/src/commands/diff.d.ts.map +1 -1
  69. package/dist/src/commands/diff.js +57 -42
  70. package/dist/src/commands/diff.js.map +1 -1
  71. package/dist/src/commands/diff.test.js +30 -0
  72. package/dist/src/commands/diff.test.js.map +1 -1
  73. package/dist/src/commands/import-local.d.ts +7 -0
  74. package/dist/src/commands/import-local.d.ts.map +1 -1
  75. package/dist/src/commands/import-local.js +43 -34
  76. package/dist/src/commands/import-local.js.map +1 -1
  77. package/dist/src/commands/import.d.ts +6 -0
  78. package/dist/src/commands/import.d.ts.map +1 -1
  79. package/dist/src/commands/import.js +23 -14
  80. package/dist/src/commands/import.js.map +1 -1
  81. package/dist/src/commands/index.d.ts +1 -0
  82. package/dist/src/commands/index.d.ts.map +1 -1
  83. package/dist/src/commands/index.js +2 -0
  84. package/dist/src/commands/index.js.map +1 -1
  85. package/dist/src/commands/info.d.ts +7 -0
  86. package/dist/src/commands/info.d.ts.map +1 -1
  87. package/dist/src/commands/info.js +77 -68
  88. package/dist/src/commands/info.js.map +1 -1
  89. package/dist/src/commands/install-skill.d.ts +3 -0
  90. package/dist/src/commands/install-skill.d.ts.map +1 -1
  91. package/dist/src/commands/install-skill.js +23 -15
  92. package/dist/src/commands/install-skill.js.map +1 -1
  93. package/dist/src/commands/install.d.ts +21 -0
  94. package/dist/src/commands/install.d.ts.map +1 -1
  95. package/dist/src/commands/install.js +154 -139
  96. package/dist/src/commands/install.js.map +1 -1
  97. package/dist/src/commands/login.d.ts +4 -0
  98. package/dist/src/commands/login.d.ts.map +1 -1
  99. package/dist/src/commands/login.js +56 -47
  100. package/dist/src/commands/login.js.map +1 -1
  101. package/dist/src/commands/logout.d.ts +1 -0
  102. package/dist/src/commands/logout.d.ts.map +1 -1
  103. package/dist/src/commands/logout.js +36 -27
  104. package/dist/src/commands/logout.js.map +1 -1
  105. package/dist/src/commands/manage.d.ts +3 -9
  106. package/dist/src/commands/manage.d.ts.map +1 -1
  107. package/dist/src/commands/manage.js +69 -45
  108. package/dist/src/commands/manage.js.map +1 -1
  109. package/dist/src/commands/merge.d.ts +7 -0
  110. package/dist/src/commands/merge.d.ts.map +1 -1
  111. package/dist/src/commands/merge.js +117 -108
  112. package/dist/src/commands/merge.js.map +1 -1
  113. package/dist/src/commands/pin.d.ts +2 -0
  114. package/dist/src/commands/pin.d.ts.map +1 -1
  115. package/dist/src/commands/pin.js +88 -73
  116. package/dist/src/commands/pin.js.map +1 -1
  117. package/dist/src/commands/recommend.d.ts +1 -0
  118. package/dist/src/commands/recommend.d.ts.map +1 -1
  119. package/dist/src/commands/recommend.js +35 -27
  120. package/dist/src/commands/recommend.js.map +1 -1
  121. package/dist/src/commands/search-formatters.d.ts +1 -0
  122. package/dist/src/commands/search-formatters.d.ts.map +1 -1
  123. package/dist/src/commands/search-formatters.js +3 -0
  124. package/dist/src/commands/search-formatters.js.map +1 -1
  125. package/dist/src/commands/search.action.d.ts +16 -0
  126. package/dist/src/commands/search.action.d.ts.map +1 -0
  127. package/dist/src/commands/search.action.js +384 -0
  128. package/dist/src/commands/search.action.js.map +1 -0
  129. package/dist/src/commands/search.d.ts +3 -0
  130. package/dist/src/commands/search.d.ts.map +1 -1
  131. package/dist/src/commands/search.js +5 -343
  132. package/dist/src/commands/search.js.map +1 -1
  133. package/dist/src/commands/sync.action.d.ts +39 -0
  134. package/dist/src/commands/sync.action.d.ts.map +1 -0
  135. package/dist/src/commands/sync.action.js +353 -0
  136. package/dist/src/commands/sync.action.js.map +1 -0
  137. package/dist/src/commands/sync.d.ts +3 -0
  138. package/dist/src/commands/sync.d.ts.map +1 -1
  139. package/dist/src/commands/sync.js +8 -310
  140. package/dist/src/commands/sync.js.map +1 -1
  141. package/dist/src/commands/telemetry.action.d.ts +38 -0
  142. package/dist/src/commands/telemetry.action.d.ts.map +1 -0
  143. package/dist/src/commands/telemetry.action.js +350 -0
  144. package/dist/src/commands/telemetry.action.js.map +1 -0
  145. package/dist/src/commands/telemetry.d.ts +30 -0
  146. package/dist/src/commands/telemetry.d.ts.map +1 -0
  147. package/dist/src/commands/telemetry.helpers.d.ts +58 -0
  148. package/dist/src/commands/telemetry.helpers.d.ts.map +1 -0
  149. package/dist/src/commands/telemetry.helpers.js +173 -0
  150. package/dist/src/commands/telemetry.helpers.js.map +1 -0
  151. package/dist/src/commands/telemetry.helpers.test.d.ts +15 -0
  152. package/dist/src/commands/telemetry.helpers.test.d.ts.map +1 -0
  153. package/dist/src/commands/telemetry.helpers.test.js +240 -0
  154. package/dist/src/commands/telemetry.helpers.test.js.map +1 -0
  155. package/dist/src/commands/telemetry.js +65 -0
  156. package/dist/src/commands/telemetry.js.map +1 -0
  157. package/dist/src/commands/telemetry.test.d.ts +18 -0
  158. package/dist/src/commands/telemetry.test.d.ts.map +1 -0
  159. package/dist/src/commands/telemetry.test.js +401 -0
  160. package/dist/src/commands/telemetry.test.js.map +1 -0
  161. package/dist/src/commands/whoami.d.ts +1 -0
  162. package/dist/src/commands/whoami.d.ts.map +1 -1
  163. package/dist/src/commands/whoami.js +29 -21
  164. package/dist/src/commands/whoami.js.map +1 -1
  165. package/dist/src/index.js +3 -1
  166. package/dist/src/index.js.map +1 -1
  167. package/dist/src/utils/manifest.d.ts +75 -0
  168. package/dist/src/utils/manifest.d.ts.map +1 -1
  169. package/dist/src/utils/manifest.js +78 -0
  170. package/dist/src/utils/manifest.js.map +1 -1
  171. package/dist/src/utils/manifest.test.d.ts +20 -0
  172. package/dist/src/utils/manifest.test.d.ts.map +1 -0
  173. package/dist/src/utils/manifest.test.js +344 -0
  174. package/dist/src/utils/manifest.test.js.map +1 -0
  175. package/dist/src/utils/open-database.d.ts +10 -2
  176. package/dist/src/utils/open-database.d.ts.map +1 -1
  177. package/dist/src/utils/open-database.js +15 -2
  178. package/dist/src/utils/open-database.js.map +1 -1
  179. package/dist/src/utils/skills-directory.d.ts.map +1 -1
  180. package/dist/src/utils/skills-directory.js +5 -1
  181. package/dist/src/utils/skills-directory.js.map +1 -1
  182. package/dist/tests/e2e/audit-sources-apply.test.d.ts +13 -0
  183. package/dist/tests/e2e/audit-sources-apply.test.d.ts.map +1 -0
  184. package/dist/tests/e2e/audit-sources-apply.test.js +166 -0
  185. package/dist/tests/e2e/audit-sources-apply.test.js.map +1 -0
  186. package/dist/tests/e2e/audit-sources-recovery.test.d.ts +15 -0
  187. package/dist/tests/e2e/audit-sources-recovery.test.d.ts.map +1 -0
  188. package/dist/tests/e2e/audit-sources-recovery.test.js +179 -0
  189. package/dist/tests/e2e/audit-sources-recovery.test.js.map +1 -0
  190. package/dist/tests/e2e/audit-sources-roundtrip.test.d.ts +23 -0
  191. package/dist/tests/e2e/audit-sources-roundtrip.test.d.ts.map +1 -0
  192. package/dist/tests/e2e/audit-sources-roundtrip.test.js +107 -0
  193. package/dist/tests/e2e/audit-sources-roundtrip.test.js.map +1 -0
  194. package/dist/tests/e2e/utils/source-recovery-fixture.d.ts +49 -0
  195. package/dist/tests/e2e/utils/source-recovery-fixture.d.ts.map +1 -0
  196. package/dist/tests/e2e/utils/source-recovery-fixture.js +106 -0
  197. package/dist/tests/e2e/utils/source-recovery-fixture.js.map +1 -0
  198. package/dist/tests/embedding-probe-stdio.test.d.ts +11 -0
  199. package/dist/tests/embedding-probe-stdio.test.d.ts.map +1 -0
  200. package/dist/tests/embedding-probe-stdio.test.js +58 -0
  201. package/dist/tests/embedding-probe-stdio.test.js.map +1 -0
  202. package/dist/tests/init.test.js +2 -1
  203. package/dist/tests/init.test.js.map +1 -1
  204. package/dist/tests/install-quarantine.test.d.ts +10 -0
  205. package/dist/tests/install-quarantine.test.d.ts.map +1 -0
  206. package/dist/tests/install-quarantine.test.js +86 -0
  207. package/dist/tests/install-quarantine.test.js.map +1 -0
  208. package/package.json +5 -5
  209. package/templates/skill-telemetry.cmd +16 -0
  210. package/templates/skill-telemetry.sh +199 -0
@@ -0,0 +1,350 @@
1
+ /**
2
+ * @fileoverview `skillsmith telemetry` action implementations + telemetry wrappers.
3
+ * @module @skillsmith/cli/commands/telemetry.action
4
+ * @see SMI-5128 batch D — sibling-split of telemetry.ts so the 6 subcommand
5
+ * handlers can be wrapped with withTelemetry without exceeding the 500-LOC
6
+ * gate. Mirrors the sync.action.ts / search.action.ts convention (SMI-5127):
7
+ * the run* business logic + the wrapped *Action exports live here; the
8
+ * commander factory stays in telemetry.ts and imports the wrapped actions.
9
+ *
10
+ * Privacy invariants (plan line 719):
11
+ * - anonymousId is NEVER printed in full. Only the last 8 hex chars appear in stdout.
12
+ * - The full SHA-256 hex travels only to the events endpoint in hook payloads.
13
+ */
14
+ import { existsSync, copyFileSync, chmodSync, mkdirSync } from 'node:fs';
15
+ import { homedir } from 'node:os';
16
+ import { join, dirname } from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+ import { readdirSync, unlinkSync, statSync } from 'node:fs';
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ import chalk from 'chalk';
21
+ import { loadManifest, saveManifest, generateAnonymousId, shouldRotateAnonymousId, rotateAnonymousId, sweepExpiredPreviousId, } from '../utils/manifest.js';
22
+ import { loadClaudeSettings, addSkillHookEntries, removeSkillHookEntries, writeClaudeSettings, TelemetryHookError, } from './telemetry.helpers.js';
23
+ import { sanitizeError } from '../utils/sanitize.js';
24
+ import { withTelemetry } from '@skillsmith/core/telemetry';
25
+ // ---------------------------------------------------------------------------
26
+ // Constants
27
+ // ---------------------------------------------------------------------------
28
+ const PRIVACY_URL = 'https://skillsmith.app/privacy#telemetry';
29
+ const DEFAULT_ENDPOINT = 'https://vrcnzpmndtroqxxoqkzy.supabase.co/functions/v1/events';
30
+ const ORPHAN_TTL_MS = 60 * 60 * 1000; // 1 hour
31
+ // All path helpers resolved at call time so test harnesses that swap HOME work.
32
+ function hookScriptPath() {
33
+ return join(homedir(), '.skillsmith', 'hooks', 'skill-telemetry.sh');
34
+ }
35
+ function runDir() {
36
+ return join(homedir(), '.skillsmith', 'run');
37
+ }
38
+ // ---------------------------------------------------------------------------
39
+ // Privacy helper — NEVER print full anonymousId
40
+ // ---------------------------------------------------------------------------
41
+ function idTail(id) {
42
+ if (!id)
43
+ return '(none)';
44
+ return `...${id.slice(-8)}`;
45
+ }
46
+ // ---------------------------------------------------------------------------
47
+ // Orphan GC (plan line 714)
48
+ // ---------------------------------------------------------------------------
49
+ function gcOrphanRunFiles() {
50
+ try {
51
+ const dir = runDir();
52
+ if (!existsSync(dir))
53
+ return;
54
+ const now = Date.now();
55
+ for (const f of readdirSync(dir)) {
56
+ if (!f.startsWith('skill-'))
57
+ continue;
58
+ const fp = join(dir, f);
59
+ try {
60
+ const st = statSync(fp);
61
+ if (now - st.mtimeMs > ORPHAN_TTL_MS)
62
+ unlinkSync(fp);
63
+ }
64
+ catch {
65
+ // best-effort
66
+ }
67
+ }
68
+ }
69
+ catch {
70
+ // never throw from GC
71
+ }
72
+ }
73
+ // ---------------------------------------------------------------------------
74
+ // enable
75
+ // ---------------------------------------------------------------------------
76
+ async function runEnable() {
77
+ const manifest = await loadManifest();
78
+ const t = manifest.telemetry ?? { enabled: false };
79
+ if (t.enabled) {
80
+ console.log(chalk.green('Telemetry is already enabled.'));
81
+ console.log(chalk.dim(` Anonymous ID tail: ${idTail(t.anonymousId)}`));
82
+ return;
83
+ }
84
+ const now = new Date().toISOString();
85
+ const updated = {
86
+ ...t,
87
+ enabled: true,
88
+ anonymousId: t.anonymousId ?? generateAnonymousId(),
89
+ anonymousIdCreatedAt: t.anonymousIdCreatedAt ?? now,
90
+ scope: t.scope ?? 'personal',
91
+ endpoint: t.endpoint ?? DEFAULT_ENDPOINT,
92
+ };
93
+ await saveManifest({ ...manifest, telemetry: updated });
94
+ console.log(chalk.green('Telemetry enabled.'));
95
+ console.log();
96
+ console.log('Skillsmith telemetry collects anonymous skill-invocation counts to help improve\n' +
97
+ 'the registry and surface stale skills. No personal data, file contents, or\n' +
98
+ `paths are captured. Full privacy policy: ${chalk.cyan(PRIVACY_URL)}`);
99
+ console.log();
100
+ console.log(chalk.dim(` Anonymous ID tail: ${idTail(updated.anonymousId)}`));
101
+ console.log(chalk.dim(' Disable at any time: ') + chalk.cyan('skillsmith telemetry disable'));
102
+ }
103
+ // ---------------------------------------------------------------------------
104
+ // disable
105
+ // ---------------------------------------------------------------------------
106
+ async function runDisable() {
107
+ const manifest = await loadManifest();
108
+ const t = manifest.telemetry ?? { enabled: false };
109
+ if (!t.enabled) {
110
+ console.log(chalk.dim('Telemetry is already disabled.'));
111
+ return;
112
+ }
113
+ await saveManifest({ ...manifest, telemetry: { ...t, enabled: false } });
114
+ console.log(chalk.yellow('Telemetry disabled.'));
115
+ console.log(chalk.dim(' The anonymous ID is retained in the local manifest for continuity if you re-enable.\n' +
116
+ ' To fully clear it: skillsmith telemetry reset-id'));
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // status
120
+ // ---------------------------------------------------------------------------
121
+ async function runStatus() {
122
+ let manifest = await loadManifest();
123
+ let rotationTriggered = false;
124
+ // Annual auto-rotation (plan line 706, 713)
125
+ if (shouldRotateAnonymousId(manifest)) {
126
+ const rotated = rotateAnonymousId(manifest);
127
+ manifest = { ...manifest, telemetry: rotated };
128
+ await saveManifest(manifest);
129
+ rotationTriggered = true;
130
+ }
131
+ // Sweep expired previous id
132
+ const swept = sweepExpiredPreviousId(manifest);
133
+ if (swept !== manifest.telemetry) {
134
+ manifest = { ...manifest, telemetry: swept };
135
+ await saveManifest(manifest);
136
+ }
137
+ // GC orphan run files (plan line 714)
138
+ gcOrphanRunFiles();
139
+ const t = manifest.telemetry ?? { enabled: false };
140
+ const endpoint = t.endpoint ?? DEFAULT_ENDPOINT;
141
+ let ageDays = null;
142
+ if (t.anonymousIdCreatedAt) {
143
+ ageDays = Math.floor((Date.now() - new Date(t.anonymousIdCreatedAt).getTime()) / 86_400_000);
144
+ }
145
+ console.log(chalk.bold('Skillsmith Telemetry Status'));
146
+ console.log();
147
+ console.log(` Enabled: ${t.enabled ? chalk.green('yes') : chalk.dim('no')}`);
148
+ console.log(` Endpoint: ${chalk.dim(endpoint)}`);
149
+ console.log(` Anonymous ID: ${chalk.dim(idTail(t.anonymousId))}`);
150
+ console.log(` ID age (days): ${ageDays !== null ? String(ageDays) : chalk.dim('n/a')}`);
151
+ console.log(` Scope: ${t.scope ?? chalk.dim('personal')}`);
152
+ if (t.installedAt) {
153
+ console.log(` Hook installed: ${chalk.dim(t.installedAt)}`);
154
+ }
155
+ if (rotationTriggered) {
156
+ console.log();
157
+ console.log(chalk.yellow(' Annual rotation triggered.') +
158
+ chalk.dim(' New ID tail: ') +
159
+ chalk.cyan(idTail(t.anonymousId)));
160
+ if (t.previousAnonymousId) {
161
+ console.log(chalk.dim(` Previous ID retained for 7-day overlap window (retired: ${t.previousAnonymousIdRetiredAt ?? 'unknown'})`));
162
+ }
163
+ }
164
+ }
165
+ // ---------------------------------------------------------------------------
166
+ // install-hook
167
+ // ---------------------------------------------------------------------------
168
+ async function runInstallHook(options) {
169
+ // Resolve the template source — relative to this file's compiled output.
170
+ // src/commands/telemetry.action.ts → ../../templates/skill-telemetry.sh (via dist/commands)
171
+ // and one level up for the src layout during tests.
172
+ const templateCandidates = [
173
+ join(__dirname, '..', '..', 'templates', 'skill-telemetry.sh'),
174
+ join(__dirname, '..', 'templates', 'skill-telemetry.sh'),
175
+ ];
176
+ const templateSrc = templateCandidates.find((p) => existsSync(p));
177
+ if (!templateSrc) {
178
+ throw new Error('skill-telemetry.sh template not found. ' +
179
+ 'Ensure the CLI package is fully built: npm run build');
180
+ }
181
+ // Load + validate settings.json — throws TelemetryHookError on foreign Skill matcher
182
+ const scope = options.scope;
183
+ const settings = loadClaudeSettings(scope);
184
+ const hookPath = hookScriptPath();
185
+ const updated = addSkillHookEntries(settings, hookPath);
186
+ // Copy hook script to ~/.skillsmith/hooks/
187
+ const destPath = hookScriptPath();
188
+ const hooksDir = dirname(destPath);
189
+ mkdirSync(hooksDir, { recursive: true, mode: 0o700 });
190
+ copyFileSync(templateSrc, destPath);
191
+ try {
192
+ chmodSync(destPath, 0o755);
193
+ }
194
+ catch {
195
+ // best-effort on Windows
196
+ }
197
+ // Persist settings.json atomically
198
+ writeClaudeSettings(scope, updated);
199
+ // Record installation timestamp in manifest
200
+ const manifest = await loadManifest();
201
+ const t = manifest.telemetry ?? { enabled: false };
202
+ await saveManifest({ ...manifest, telemetry: { ...t, installedAt: new Date().toISOString() } });
203
+ // Update endpoint in manifest if provided
204
+ if (options.endpoint) {
205
+ const m2 = await loadManifest();
206
+ const t2 = m2.telemetry ?? { enabled: false };
207
+ await saveManifest({ ...m2, telemetry: { ...t2, endpoint: options.endpoint } });
208
+ }
209
+ const scopeLabel = scope === 'user' ? '~/.claude/settings.json' : './.claude/settings.json';
210
+ console.log(chalk.green('Skillsmith telemetry hook installed.'));
211
+ console.log(chalk.dim(` Hook script: ${destPath}`));
212
+ console.log(chalk.dim(` Settings file: ${scopeLabel}`));
213
+ console.log();
214
+ console.log(chalk.dim('Enable telemetry to start collecting: ') + chalk.cyan('skillsmith telemetry enable'));
215
+ }
216
+ // ---------------------------------------------------------------------------
217
+ // uninstall-hook
218
+ // ---------------------------------------------------------------------------
219
+ async function runUninstallHook(options) {
220
+ const scope = options.scope;
221
+ const hookPath = hookScriptPath();
222
+ const settings = loadClaudeSettings(scope);
223
+ const updated = removeSkillHookEntries(settings, hookPath);
224
+ writeClaudeSettings(scope, updated);
225
+ // Optionally remove the script file
226
+ try {
227
+ const scriptPath = hookScriptPath();
228
+ if (existsSync(scriptPath))
229
+ unlinkSync(scriptPath);
230
+ }
231
+ catch {
232
+ // best-effort
233
+ }
234
+ const scopeLabel = scope === 'user' ? '~/.claude/settings.json' : './.claude/settings.json';
235
+ console.log(chalk.yellow('Skillsmith telemetry hook removed.'));
236
+ console.log(chalk.dim(` Removed from: ${scopeLabel}`));
237
+ console.log(chalk.dim(' Foreign hooks (if any) were not touched.'));
238
+ }
239
+ // ---------------------------------------------------------------------------
240
+ // reset-id
241
+ // ---------------------------------------------------------------------------
242
+ async function runResetId() {
243
+ const manifest = await loadManifest();
244
+ const rotated = rotateAnonymousId(manifest); // unconditional rotation
245
+ await saveManifest({ ...manifest, telemetry: rotated });
246
+ console.log(chalk.green('Anonymous ID rotated.'));
247
+ console.log(chalk.dim(` New ID tail: ${idTail(rotated.anonymousId)}`));
248
+ if (rotated.previousAnonymousId) {
249
+ console.log(chalk.dim(` Previous ID tail: ${idTail(rotated.previousAnonymousId)}`));
250
+ console.log(chalk.dim(` Previous ID retirement: ${rotated.previousAnonymousIdRetiredAt ?? 'unknown'} (7-day overlap window)`));
251
+ }
252
+ }
253
+ // ---------------------------------------------------------------------------
254
+ // withTelemetry-wrapped action handlers (SMI-5128)
255
+ // ---------------------------------------------------------------------------
256
+ async function telemetryEnableActionImpl() {
257
+ try {
258
+ await runEnable();
259
+ }
260
+ catch (err) {
261
+ console.error(chalk.red('Error:'), sanitizeError(err));
262
+ process.exit(1);
263
+ }
264
+ }
265
+ export const telemetryEnableAction = withTelemetry(telemetryEnableActionImpl, {
266
+ source: 'cli',
267
+ extractSkillId: () => 'telemetry enable',
268
+ extractFramework: () => 'cli',
269
+ });
270
+ async function telemetryDisableActionImpl() {
271
+ try {
272
+ await runDisable();
273
+ }
274
+ catch (err) {
275
+ console.error(chalk.red('Error:'), sanitizeError(err));
276
+ process.exit(1);
277
+ }
278
+ }
279
+ export const telemetryDisableAction = withTelemetry(telemetryDisableActionImpl, {
280
+ source: 'cli',
281
+ extractSkillId: () => 'telemetry disable',
282
+ extractFramework: () => 'cli',
283
+ });
284
+ async function telemetryStatusActionImpl() {
285
+ try {
286
+ await runStatus();
287
+ }
288
+ catch (err) {
289
+ console.error(chalk.red('Error:'), sanitizeError(err));
290
+ process.exit(1);
291
+ }
292
+ }
293
+ export const telemetryStatusAction = withTelemetry(telemetryStatusActionImpl, {
294
+ source: 'cli',
295
+ extractSkillId: () => 'telemetry status',
296
+ extractFramework: () => 'cli',
297
+ });
298
+ async function telemetryInstallHookActionImpl(options) {
299
+ const scope = options.scope === 'project' ? 'project' : 'user';
300
+ try {
301
+ await runInstallHook(options.endpoint !== undefined ? { scope, endpoint: options.endpoint } : { scope });
302
+ }
303
+ catch (err) {
304
+ if (err instanceof TelemetryHookError) {
305
+ console.error(chalk.red(`Error [${err.code}]:`));
306
+ console.error(err.message);
307
+ }
308
+ else {
309
+ console.error(chalk.red('Error:'), sanitizeError(err));
310
+ }
311
+ process.exit(1);
312
+ }
313
+ }
314
+ export const telemetryInstallHookAction = withTelemetry(telemetryInstallHookActionImpl, {
315
+ source: 'cli',
316
+ extractSkillId: () => 'telemetry install-hook',
317
+ extractFramework: () => 'cli',
318
+ });
319
+ async function telemetryUninstallHookActionImpl(options) {
320
+ const scope = options.scope === 'project' ? 'project' : 'user';
321
+ try {
322
+ await runUninstallHook({ scope });
323
+ }
324
+ catch (err) {
325
+ console.error(chalk.red('Error:'), sanitizeError(err));
326
+ process.exit(1);
327
+ }
328
+ }
329
+ export const telemetryUninstallHookAction = withTelemetry(telemetryUninstallHookActionImpl, {
330
+ source: 'cli',
331
+ extractSkillId: () => 'telemetry uninstall-hook',
332
+ extractFramework: () => 'cli',
333
+ });
334
+ async function telemetryResetIdActionImpl() {
335
+ try {
336
+ await runResetId();
337
+ }
338
+ catch (err) {
339
+ console.error(chalk.red('Error:'), sanitizeError(err));
340
+ process.exit(1);
341
+ }
342
+ }
343
+ export const telemetryResetIdAction = withTelemetry(telemetryResetIdActionImpl, {
344
+ source: 'cli',
345
+ extractSkillId: () => 'telemetry reset-id',
346
+ extractFramework: () => 'cli',
347
+ });
348
+ // Internal exports for tests (re-exported from telemetry.ts for back-compat).
349
+ export { runEnable, runDisable, runStatus, runInstallHook, runUninstallHook, runResetId, idTail };
350
+ //# sourceMappingURL=telemetry.action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.action.js","sourceRoot":"","sources":["../../../src/commands/telemetry.action.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @fileoverview `skillsmith telemetry` command group — opt-in telemetry management.
3
+ * @module @skillsmith/cli/commands/telemetry
4
+ * @see SMI-5021 Wave 3 Step 2 — CLI subcommands for telemetry (plan lines 195–196, 576)
5
+ * @see SMI-5128 batch D — action impls + run* logic moved to telemetry.action.ts
6
+ * (sibling-split convention) so the 6 subcommand handlers are withTelemetry-
7
+ * wrapped without this file exceeding the 500-LOC gate. This file retains the
8
+ * commander factory and re-exports the run* test helpers for back-compat.
9
+ *
10
+ * Subcommands:
11
+ * skillsmith telemetry enable
12
+ * skillsmith telemetry disable
13
+ * skillsmith telemetry status
14
+ * skillsmith telemetry install-hook [--scope user|project] [--endpoint <url>]
15
+ * skillsmith telemetry uninstall-hook [--scope user|project]
16
+ * skillsmith telemetry reset-id
17
+ *
18
+ * Privacy invariants (plan line 719):
19
+ * - anonymousId is NEVER printed in full. Only the last 8 hex chars appear in stdout.
20
+ * - The full SHA-256 hex travels only to the events endpoint in hook payloads.
21
+ *
22
+ * Shared-state coordination (plan line 717):
23
+ * - install-hook reads then writes ~/.claude/settings.json via telemetry.helpers.ts
24
+ * - Refuses on foreign Skill matcher (security gate)
25
+ * - manifest read/write always via loadManifest / saveManifest (atomic rename)
26
+ */
27
+ import { Command } from 'commander';
28
+ export declare function createTelemetryCommand(): Command;
29
+ export { runEnable, runDisable, runStatus, runInstallHook, runUninstallHook, runResetId, idTail, } from './telemetry.action.js';
30
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../../src/commands/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAenC,wBAAgB,sBAAsB,IAAI,OAAO,CA2ChD;AAGD,OAAO,EACL,SAAS,EACT,UAAU,EACV,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,MAAM,GACP,MAAM,uBAAuB,CAAA"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @fileoverview Atomic ~/.claude/settings.json merger for telemetry hook entries.
3
+ * @module @skillsmith/cli/commands/telemetry.helpers
4
+ * @see SMI-5021 Wave 3 Step 2 — install-hook / uninstall-hook shared-state matrix (plan line 717)
5
+ *
6
+ * Public surface:
7
+ * loadClaudeSettings — reads user or project settings.json (returns {} on missing)
8
+ * addSkillHookEntries — idempotent add; THROWS on foreign Skill matcher
9
+ * removeSkillHookEntries — removes only Skillsmith entries; never foreign hooks
10
+ * writeClaudeSettings — atomic temp-file rename (mirrors manifest.ts pattern)
11
+ *
12
+ * Hook schema (Claude Code settings.json):
13
+ * hooks.PreToolUse = Array<{ matcher: string; hooks: Array<{ type: 'command'; command: string }> }>
14
+ * hooks.PostToolUse = same shape
15
+ *
16
+ * Deduplication is by command path, not by matcher value. A foreign Skill
17
+ * matcher is one whose command path does NOT start with the Skillsmith hookPath.
18
+ */
19
+ export interface HookEntry {
20
+ type: 'command';
21
+ command: string;
22
+ }
23
+ export interface HookMatcher {
24
+ matcher: string;
25
+ hooks: HookEntry[];
26
+ }
27
+ export interface ClaudeHooks {
28
+ PreToolUse?: HookMatcher[];
29
+ PostToolUse?: HookMatcher[];
30
+ [key: string]: unknown;
31
+ }
32
+ export interface ClaudeSettings {
33
+ hooks?: ClaudeHooks;
34
+ [key: string]: unknown;
35
+ }
36
+ export declare class TelemetryHookError extends Error {
37
+ readonly code: string;
38
+ constructor(code: string, message: string);
39
+ }
40
+ export declare function resolveSettingsPath(scope: 'user' | 'project'): string;
41
+ export declare function loadClaudeSettings(scope: 'user' | 'project'): ClaudeSettings;
42
+ /**
43
+ * Adds PreToolUse + PostToolUse Skill matcher entries pointing at hookPath.
44
+ * Idempotent (dedupes by command path).
45
+ * THROWS TelemetryHookError if a foreign Skill matcher is detected.
46
+ */
47
+ export declare function addSkillHookEntries(settings: ClaudeSettings, hookPath: string): ClaudeSettings;
48
+ /**
49
+ * Removes Skillsmith Skill hook entries from settings.
50
+ * Never touches foreign hooks — only removes entries whose command starts with hookPath.
51
+ */
52
+ export declare function removeSkillHookEntries(settings: ClaudeSettings, hookPath: string): ClaudeSettings;
53
+ /**
54
+ * Atomic write of Claude settings.json: temp file → rename.
55
+ * Mirrors the manifest.ts atomic write pattern (plan line 120).
56
+ */
57
+ export declare function writeClaudeSettings(scope: 'user' | 'project', settings: ClaudeSettings): void;
58
+ //# sourceMappingURL=telemetry.helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.helpers.d.ts","sourceRoot":"","sources":["../../../src/commands/telemetry.helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAWH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,SAAS,EAAE,CAAA;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,WAAW,EAAE,CAAA;IAC1B,WAAW,CAAC,EAAE,WAAW,EAAE,CAAA;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;aAEzB,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM;CAKlB;AAMD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAMrE;AAMD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,CAW5E;AAmED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAS9F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAwBjG;AAMD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI,CAiB7F"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * @fileoverview Atomic ~/.claude/settings.json merger for telemetry hook entries.
3
+ * @module @skillsmith/cli/commands/telemetry.helpers
4
+ * @see SMI-5021 Wave 3 Step 2 — install-hook / uninstall-hook shared-state matrix (plan line 717)
5
+ *
6
+ * Public surface:
7
+ * loadClaudeSettings — reads user or project settings.json (returns {} on missing)
8
+ * addSkillHookEntries — idempotent add; THROWS on foreign Skill matcher
9
+ * removeSkillHookEntries — removes only Skillsmith entries; never foreign hooks
10
+ * writeClaudeSettings — atomic temp-file rename (mirrors manifest.ts pattern)
11
+ *
12
+ * Hook schema (Claude Code settings.json):
13
+ * hooks.PreToolUse = Array<{ matcher: string; hooks: Array<{ type: 'command'; command: string }> }>
14
+ * hooks.PostToolUse = same shape
15
+ *
16
+ * Deduplication is by command path, not by matcher value. A foreign Skill
17
+ * matcher is one whose command path does NOT start with the Skillsmith hookPath.
18
+ */
19
+ import * as crypto from 'node:crypto';
20
+ import * as fs from 'node:fs';
21
+ import { join, dirname } from 'node:path';
22
+ import { homedir } from 'node:os';
23
+ export class TelemetryHookError extends Error {
24
+ code;
25
+ constructor(code, message) {
26
+ super(message);
27
+ this.code = code;
28
+ this.name = 'TelemetryHookError';
29
+ }
30
+ }
31
+ // ---------------------------------------------------------------------------
32
+ // Path resolution
33
+ // ---------------------------------------------------------------------------
34
+ export function resolveSettingsPath(scope) {
35
+ if (scope === 'user') {
36
+ return join(homedir(), '.claude', 'settings.json');
37
+ }
38
+ // project scope: relative to cwd
39
+ return join(process.cwd(), '.claude', 'settings.json');
40
+ }
41
+ // ---------------------------------------------------------------------------
42
+ // Read
43
+ // ---------------------------------------------------------------------------
44
+ export function loadClaudeSettings(scope) {
45
+ const path = resolveSettingsPath(scope);
46
+ if (!fs.existsSync(path))
47
+ return {};
48
+ try {
49
+ const raw = fs.readFileSync(path, 'utf-8');
50
+ const parsed = JSON.parse(raw);
51
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
52
+ return {};
53
+ return parsed;
54
+ }
55
+ catch {
56
+ return {};
57
+ }
58
+ }
59
+ // ---------------------------------------------------------------------------
60
+ // Merge helpers
61
+ // ---------------------------------------------------------------------------
62
+ /**
63
+ * Returns true when a HookMatcher block uses the Skill matcher AND its first
64
+ * hook command starts with hookPath (our Skillsmith hook).
65
+ */
66
+ function isSkillsmithSkillMatcher(entry, hookPath) {
67
+ return (entry.matcher === 'Skill' &&
68
+ Array.isArray(entry.hooks) &&
69
+ entry.hooks.length > 0 &&
70
+ entry.hooks[0].command.startsWith(hookPath));
71
+ }
72
+ /**
73
+ * Returns true when a HookMatcher block uses the Skill matcher AND its first
74
+ * hook command does NOT start with hookPath — i.e. a foreign Skill matcher.
75
+ */
76
+ function isForeignSkillMatcher(entry, hookPath) {
77
+ return (entry.matcher === 'Skill' &&
78
+ Array.isArray(entry.hooks) &&
79
+ entry.hooks.length > 0 &&
80
+ !entry.hooks[0].command.startsWith(hookPath));
81
+ }
82
+ /**
83
+ * Idempotent add of Skillsmith Skill matcher entries to a hook array.
84
+ * Dedupes by command path. Throws if a foreign Skill matcher exists.
85
+ */
86
+ function mergeHookArray(existing, hookPath, command) {
87
+ const arr = Array.isArray(existing) ? [...existing] : [];
88
+ // Check for foreign Skill matcher — security gate (plan line 717)
89
+ const foreign = arr.find((e) => isForeignSkillMatcher(e, hookPath));
90
+ if (foreign) {
91
+ const foreignCmd = foreign.hooks[0]?.command ?? '(unknown)';
92
+ throw new TelemetryHookError('hook.foreign_skill_matcher', `A foreign Skill hook already exists in settings.json:\n` +
93
+ ` command: ${foreignCmd}\n\n` +
94
+ `Remove it manually before installing the Skillsmith telemetry hook:\n` +
95
+ ` 1. Open ~/.claude/settings.json (or .claude/settings.json for project scope)\n` +
96
+ ` 2. Delete the PreToolUse and PostToolUse entries with matcher "Skill"\n` +
97
+ ` 3. Re-run: skillsmith telemetry install-hook`);
98
+ }
99
+ // Check if our entry already exists (idempotent)
100
+ const alreadyPresent = arr.some((e) => isSkillsmithSkillMatcher(e, hookPath));
101
+ if (alreadyPresent)
102
+ return arr;
103
+ // Add new entry
104
+ arr.push({ matcher: 'Skill', hooks: [{ type: 'command', command }] });
105
+ return arr;
106
+ }
107
+ /**
108
+ * Adds PreToolUse + PostToolUse Skill matcher entries pointing at hookPath.
109
+ * Idempotent (dedupes by command path).
110
+ * THROWS TelemetryHookError if a foreign Skill matcher is detected.
111
+ */
112
+ export function addSkillHookEntries(settings, hookPath) {
113
+ const preCommand = `${hookPath} pre`;
114
+ const postCommand = `${hookPath} post`;
115
+ const hooks = { ...(settings.hooks ?? {}) };
116
+ hooks.PreToolUse = mergeHookArray(hooks.PreToolUse, hookPath, preCommand);
117
+ hooks.PostToolUse = mergeHookArray(hooks.PostToolUse, hookPath, postCommand);
118
+ return { ...settings, hooks };
119
+ }
120
+ /**
121
+ * Removes Skillsmith Skill hook entries from settings.
122
+ * Never touches foreign hooks — only removes entries whose command starts with hookPath.
123
+ */
124
+ export function removeSkillHookEntries(settings, hookPath) {
125
+ if (!settings.hooks)
126
+ return settings;
127
+ const filterOut = (arr) => {
128
+ if (!Array.isArray(arr))
129
+ return arr;
130
+ const filtered = arr.filter((e) => !isSkillsmithSkillMatcher(e, hookPath));
131
+ return filtered.length === 0 && arr.length > 0 ? [] : filtered;
132
+ };
133
+ const hooks = { ...settings.hooks };
134
+ const filteredPre = filterOut(hooks.PreToolUse);
135
+ if (filteredPre === undefined) {
136
+ delete hooks.PreToolUse;
137
+ }
138
+ else {
139
+ hooks.PreToolUse = filteredPre;
140
+ }
141
+ const filteredPost = filterOut(hooks.PostToolUse);
142
+ if (filteredPost === undefined) {
143
+ delete hooks.PostToolUse;
144
+ }
145
+ else {
146
+ hooks.PostToolUse = filteredPost;
147
+ }
148
+ return { ...settings, hooks };
149
+ }
150
+ // ---------------------------------------------------------------------------
151
+ // Write
152
+ // ---------------------------------------------------------------------------
153
+ /**
154
+ * Atomic write of Claude settings.json: temp file → rename.
155
+ * Mirrors the manifest.ts atomic write pattern (plan line 120).
156
+ */
157
+ export function writeClaudeSettings(scope, settings) {
158
+ const path = resolveSettingsPath(scope);
159
+ const dir = dirname(path);
160
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
161
+ const tmpSuffix = crypto.randomBytes(6).toString('hex');
162
+ const tmpPath = `${path}.${tmpSuffix}.tmp`;
163
+ const json = JSON.stringify(settings, null, 2);
164
+ fs.writeFileSync(tmpPath, json, { encoding: 'utf-8', mode: 0o600 });
165
+ fs.renameSync(tmpPath, path);
166
+ try {
167
+ fs.chmodSync(path, 0o600);
168
+ }
169
+ catch {
170
+ // Best-effort; ignore on Windows / read-only FS.
171
+ }
172
+ }
173
+ //# sourceMappingURL=telemetry.helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.helpers.js","sourceRoot":"","sources":["../../../src/commands/telemetry.helpers.ts"],"names":[],"mappings":"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"}