@skillsmith/cli 0.6.2 → 0.6.3
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/CHANGELOG.md +13 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/commands/index.d.ts +1 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +2 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/search.d.ts.map +1 -1
- package/dist/src/commands/search.js +11 -0
- package/dist/src/commands/search.js.map +1 -1
- package/dist/src/commands/telemetry.d.ts +38 -0
- package/dist/src/commands/telemetry.d.ts.map +1 -0
- package/dist/src/commands/telemetry.helpers.d.ts +58 -0
- package/dist/src/commands/telemetry.helpers.d.ts.map +1 -0
- package/dist/src/commands/telemetry.helpers.js +173 -0
- package/dist/src/commands/telemetry.helpers.js.map +1 -0
- package/dist/src/commands/telemetry.helpers.test.d.ts +15 -0
- package/dist/src/commands/telemetry.helpers.test.d.ts.map +1 -0
- package/dist/src/commands/telemetry.helpers.test.js +240 -0
- package/dist/src/commands/telemetry.helpers.test.js.map +1 -0
- package/dist/src/commands/telemetry.js +354 -0
- package/dist/src/commands/telemetry.js.map +1 -0
- package/dist/src/commands/telemetry.test.d.ts +18 -0
- package/dist/src/commands/telemetry.test.d.ts.map +1 -0
- package/dist/src/commands/telemetry.test.js +401 -0
- package/dist/src/commands/telemetry.test.js.map +1 -0
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/utils/manifest.d.ts +75 -0
- package/dist/src/utils/manifest.d.ts.map +1 -1
- package/dist/src/utils/manifest.js +78 -0
- package/dist/src/utils/manifest.js.map +1 -1
- package/dist/src/utils/manifest.test.d.ts +20 -0
- package/dist/src/utils/manifest.test.d.ts.map +1 -0
- package/dist/src/utils/manifest.test.js +344 -0
- package/dist/src/utils/manifest.test.js.map +1 -0
- package/dist/tests/embedding-probe-stdio.test.d.ts +11 -0
- package/dist/tests/embedding-probe-stdio.test.d.ts.map +1 -0
- package/dist/tests/embedding-probe-stdio.test.js +58 -0
- package/dist/tests/embedding-probe-stdio.test.js.map +1 -0
- package/package.json +4 -3
- package/templates/skill-telemetry.cmd +16 -0
- package/templates/skill-telemetry.sh +199 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for telemetry.helpers.ts — atomic settings.json merger.
|
|
3
|
+
* @module @skillsmith/cli/commands/telemetry.helpers.test
|
|
4
|
+
* @see SMI-5021 Wave 3 Step 2 — plan line 717
|
|
5
|
+
*
|
|
6
|
+
* Covers:
|
|
7
|
+
* - addSkillHookEntries: empty settings → well-formed hooks block
|
|
8
|
+
* - addSkillHookEntries: idempotent (second add is a no-op)
|
|
9
|
+
* - addSkillHookEntries: throws on foreign Skill matcher command path
|
|
10
|
+
* - removeSkillHookEntries: removes only Skillsmith entries
|
|
11
|
+
* - removeSkillHookEntries: on empty settings → no-op
|
|
12
|
+
* - writeClaudeSettings: atomic temp-file → rename correctness
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// fs mock — intercepts node:fs (used by telemetry.helpers.ts)
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
const memfs = {};
|
|
19
|
+
let existsResult = false;
|
|
20
|
+
vi.mock('node:fs', () => ({
|
|
21
|
+
existsSync: vi.fn((_p) => existsResult),
|
|
22
|
+
readFileSync: vi.fn((path) => {
|
|
23
|
+
const content = memfs[path];
|
|
24
|
+
if (content === undefined)
|
|
25
|
+
throw Object.assign(new Error(`ENOENT: ${path}`), { code: 'ENOENT' });
|
|
26
|
+
return content;
|
|
27
|
+
}),
|
|
28
|
+
writeFileSync: vi.fn((path, content) => {
|
|
29
|
+
memfs[path] = content;
|
|
30
|
+
}),
|
|
31
|
+
renameSync: vi.fn((src, dst) => {
|
|
32
|
+
const content = memfs[src];
|
|
33
|
+
if (content === undefined)
|
|
34
|
+
throw new Error(`ENOENT: ${src}`);
|
|
35
|
+
memfs[dst] = content;
|
|
36
|
+
delete memfs[src];
|
|
37
|
+
}),
|
|
38
|
+
mkdirSync: vi.fn(() => undefined),
|
|
39
|
+
chmodSync: vi.fn(() => undefined),
|
|
40
|
+
}));
|
|
41
|
+
import { loadClaudeSettings, addSkillHookEntries, removeSkillHookEntries, writeClaudeSettings, TelemetryHookError, } from './telemetry.helpers.js';
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Helpers
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
const HOOK_PATH = '/home/test/.skillsmith/hooks/skill-telemetry.sh';
|
|
46
|
+
function emptySettings() {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
function settingsWithSkillsmithHooks() {
|
|
50
|
+
return {
|
|
51
|
+
hooks: {
|
|
52
|
+
PreToolUse: [
|
|
53
|
+
{
|
|
54
|
+
matcher: 'Skill',
|
|
55
|
+
hooks: [{ type: 'command', command: `${HOOK_PATH} pre` }],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
PostToolUse: [
|
|
59
|
+
{
|
|
60
|
+
matcher: 'Skill',
|
|
61
|
+
hooks: [{ type: 'command', command: `${HOOK_PATH} post` }],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function settingsWithForeignSkillHook() {
|
|
68
|
+
return {
|
|
69
|
+
hooks: {
|
|
70
|
+
PreToolUse: [
|
|
71
|
+
{
|
|
72
|
+
matcher: 'Skill',
|
|
73
|
+
hooks: [{ type: 'command', command: '/other/tool/skill-hook.sh pre' }],
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
PostToolUse: [],
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Setup / teardown
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
for (const k of Object.keys(memfs))
|
|
85
|
+
delete memfs[k];
|
|
86
|
+
existsResult = false;
|
|
87
|
+
vi.clearAllMocks();
|
|
88
|
+
});
|
|
89
|
+
afterEach(() => {
|
|
90
|
+
for (const k of Object.keys(memfs))
|
|
91
|
+
delete memfs[k];
|
|
92
|
+
});
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// loadClaudeSettings
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
describe('loadClaudeSettings', () => {
|
|
97
|
+
it('returns empty object when file does not exist', () => {
|
|
98
|
+
existsResult = false;
|
|
99
|
+
const result = loadClaudeSettings('user');
|
|
100
|
+
expect(result).toEqual({});
|
|
101
|
+
});
|
|
102
|
+
it('returns parsed JSON when file exists and is valid', async () => {
|
|
103
|
+
existsResult = true;
|
|
104
|
+
const { existsSync, readFileSync } = await import('node:fs');
|
|
105
|
+
existsSync.mockReturnValueOnce(true);
|
|
106
|
+
readFileSync.mockReturnValueOnce(JSON.stringify({ hooks: { PreToolUse: [] } }));
|
|
107
|
+
const result = loadClaudeSettings('user');
|
|
108
|
+
expect(result).toHaveProperty('hooks');
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// addSkillHookEntries
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
describe('addSkillHookEntries', () => {
|
|
115
|
+
it('adds PreToolUse and PostToolUse Skill matchers to empty settings', () => {
|
|
116
|
+
const result = addSkillHookEntries(emptySettings(), HOOK_PATH);
|
|
117
|
+
expect(result.hooks?.PreToolUse).toHaveLength(1);
|
|
118
|
+
expect(result.hooks?.PreToolUse?.[0]?.matcher).toBe('Skill');
|
|
119
|
+
expect(result.hooks?.PreToolUse?.[0]?.hooks?.[0]?.command).toBe(`${HOOK_PATH} pre`);
|
|
120
|
+
expect(result.hooks?.PostToolUse).toHaveLength(1);
|
|
121
|
+
expect(result.hooks?.PostToolUse?.[0]?.matcher).toBe('Skill');
|
|
122
|
+
expect(result.hooks?.PostToolUse?.[0]?.hooks?.[0]?.command).toBe(`${HOOK_PATH} post`);
|
|
123
|
+
});
|
|
124
|
+
it('is idempotent — calling add twice produces only one entry per array', () => {
|
|
125
|
+
const once = addSkillHookEntries(emptySettings(), HOOK_PATH);
|
|
126
|
+
const twice = addSkillHookEntries(once, HOOK_PATH);
|
|
127
|
+
expect(twice.hooks?.PreToolUse).toHaveLength(1);
|
|
128
|
+
expect(twice.hooks?.PostToolUse).toHaveLength(1);
|
|
129
|
+
});
|
|
130
|
+
it('preserves existing non-Skill hook entries alongside new Skill entry', () => {
|
|
131
|
+
const existing = {
|
|
132
|
+
hooks: {
|
|
133
|
+
PreToolUse: [{ matcher: 'Bash', hooks: [{ type: 'command', command: 'echo pre-bash' }] }],
|
|
134
|
+
PostToolUse: [],
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
const result = addSkillHookEntries(existing, HOOK_PATH);
|
|
138
|
+
// Should have both Bash and Skill entries
|
|
139
|
+
expect(result.hooks?.PreToolUse).toHaveLength(2);
|
|
140
|
+
expect(result.hooks?.PreToolUse?.some((e) => e.matcher === 'Bash')).toBe(true);
|
|
141
|
+
expect(result.hooks?.PreToolUse?.some((e) => e.matcher === 'Skill')).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
it('throws TelemetryHookError with code hook.foreign_skill_matcher when a foreign Skill hook exists', () => {
|
|
144
|
+
const settings = settingsWithForeignSkillHook();
|
|
145
|
+
expect(() => addSkillHookEntries(settings, HOOK_PATH)).toThrow(TelemetryHookError);
|
|
146
|
+
expect(() => addSkillHookEntries(settings, HOOK_PATH)).toThrow(expect.objectContaining({ code: 'hook.foreign_skill_matcher' }));
|
|
147
|
+
});
|
|
148
|
+
it('error message from foreign Skill matcher includes removal instructions', () => {
|
|
149
|
+
const settings = settingsWithForeignSkillHook();
|
|
150
|
+
let caught;
|
|
151
|
+
try {
|
|
152
|
+
addSkillHookEntries(settings, HOOK_PATH);
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
caught = e;
|
|
156
|
+
}
|
|
157
|
+
expect(caught).toBeInstanceOf(TelemetryHookError);
|
|
158
|
+
const err = caught;
|
|
159
|
+
expect(err.message).toContain('Remove it manually');
|
|
160
|
+
expect(err.message).toContain('skillsmith telemetry install-hook');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// removeSkillHookEntries
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
describe('removeSkillHookEntries', () => {
|
|
167
|
+
it('removes Skillsmith Skill entries from both PreToolUse and PostToolUse', () => {
|
|
168
|
+
const settings = settingsWithSkillsmithHooks();
|
|
169
|
+
const result = removeSkillHookEntries(settings, HOOK_PATH);
|
|
170
|
+
expect(result.hooks?.PreToolUse).toHaveLength(0);
|
|
171
|
+
expect(result.hooks?.PostToolUse).toHaveLength(0);
|
|
172
|
+
});
|
|
173
|
+
it('does not touch foreign hooks when removing Skillsmith entries', () => {
|
|
174
|
+
const settings = {
|
|
175
|
+
hooks: {
|
|
176
|
+
PreToolUse: [
|
|
177
|
+
{ matcher: 'Bash', hooks: [{ type: 'command', command: 'echo bash' }] },
|
|
178
|
+
{ matcher: 'Skill', hooks: [{ type: 'command', command: `${HOOK_PATH} pre` }] },
|
|
179
|
+
],
|
|
180
|
+
PostToolUse: [
|
|
181
|
+
{ matcher: 'Skill', hooks: [{ type: 'command', command: `${HOOK_PATH} post` }] },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
const result = removeSkillHookEntries(settings, HOOK_PATH);
|
|
186
|
+
// Bash hook preserved; Skill hook removed
|
|
187
|
+
expect(result.hooks?.PreToolUse).toHaveLength(1);
|
|
188
|
+
expect(result.hooks?.PreToolUse?.[0]?.matcher).toBe('Bash');
|
|
189
|
+
expect(result.hooks?.PostToolUse).toHaveLength(0);
|
|
190
|
+
});
|
|
191
|
+
it('is a no-op when settings has no hooks key', () => {
|
|
192
|
+
const result = removeSkillHookEntries({}, HOOK_PATH);
|
|
193
|
+
expect(result.hooks).toBeUndefined();
|
|
194
|
+
});
|
|
195
|
+
it('is a no-op when no Skill entries exist to remove', () => {
|
|
196
|
+
const settings = {
|
|
197
|
+
hooks: {
|
|
198
|
+
PreToolUse: [{ matcher: 'Bash', hooks: [{ type: 'command', command: 'echo bash' }] }],
|
|
199
|
+
PostToolUse: [],
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
const result = removeSkillHookEntries(settings, HOOK_PATH);
|
|
203
|
+
expect(result.hooks?.PreToolUse).toHaveLength(1);
|
|
204
|
+
expect(result.hooks?.PostToolUse).toHaveLength(0);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// writeClaudeSettings — atomic write correctness
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
describe('writeClaudeSettings', () => {
|
|
211
|
+
it('writes the settings to a .tmp file then renames to final path', async () => {
|
|
212
|
+
const { writeFileSync, renameSync } = await import('node:fs');
|
|
213
|
+
const settings = { hooks: { PreToolUse: [] } };
|
|
214
|
+
writeClaudeSettings('user', settings);
|
|
215
|
+
expect(writeFileSync).toHaveBeenCalledWith(expect.stringContaining('.tmp'), expect.stringContaining('"PreToolUse"'), expect.anything());
|
|
216
|
+
expect(renameSync).toHaveBeenCalledWith(expect.stringContaining('.tmp'), expect.stringContaining('settings.json'));
|
|
217
|
+
});
|
|
218
|
+
it('second write fully replaces first without partial state', async () => {
|
|
219
|
+
// Simulate sequential write correctness (mirrors manifest.test.ts pattern)
|
|
220
|
+
const first = { hooks: { PreToolUse: [] } };
|
|
221
|
+
const second = {
|
|
222
|
+
hooks: {
|
|
223
|
+
PreToolUse: [
|
|
224
|
+
{ matcher: 'Skill', hooks: [{ type: 'command', command: `${HOOK_PATH} pre` }] },
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
writeClaudeSettings('user', first);
|
|
229
|
+
writeClaudeSettings('user', second);
|
|
230
|
+
// After both writes, memfs should contain the second write's content
|
|
231
|
+
const { resolveSettingsPath } = await import('./telemetry.helpers.js');
|
|
232
|
+
const finalPath = resolveSettingsPath('user');
|
|
233
|
+
const raw = memfs[finalPath];
|
|
234
|
+
expect(raw).toBeDefined();
|
|
235
|
+
const parsed = JSON.parse(raw);
|
|
236
|
+
expect(parsed.hooks?.PreToolUse).toHaveLength(1);
|
|
237
|
+
expect(parsed.hooks?.PreToolUse?.[0]?.matcher).toBe('Skill');
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
//# sourceMappingURL=telemetry.helpers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.helpers.test.js","sourceRoot":"","sources":["../../../src/commands/telemetry.helpers.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,354 @@
|
|
|
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
|
+
*
|
|
6
|
+
* Subcommands:
|
|
7
|
+
* skillsmith telemetry enable
|
|
8
|
+
* skillsmith telemetry disable
|
|
9
|
+
* skillsmith telemetry status
|
|
10
|
+
* skillsmith telemetry install-hook [--scope user|project] [--endpoint <url>]
|
|
11
|
+
* skillsmith telemetry uninstall-hook [--scope user|project]
|
|
12
|
+
* skillsmith telemetry reset-id
|
|
13
|
+
*
|
|
14
|
+
* Privacy invariants (plan line 719):
|
|
15
|
+
* - anonymousId is NEVER printed in full. Only the last 8 hex chars appear in stdout.
|
|
16
|
+
* - The full SHA-256 hex travels only to the events endpoint in hook payloads.
|
|
17
|
+
*
|
|
18
|
+
* Shared-state coordination (plan line 717):
|
|
19
|
+
* - install-hook reads then writes ~/.claude/settings.json via telemetry.helpers.ts
|
|
20
|
+
* - Refuses on foreign Skill matcher (security gate)
|
|
21
|
+
* - manifest read/write always via loadManifest / saveManifest (atomic rename)
|
|
22
|
+
*/
|
|
23
|
+
import { existsSync, copyFileSync, chmodSync, mkdirSync } from 'node:fs';
|
|
24
|
+
import { homedir } from 'node:os';
|
|
25
|
+
import { join, dirname } from 'node:path';
|
|
26
|
+
import { fileURLToPath } from 'node:url';
|
|
27
|
+
import { readdirSync, unlinkSync, statSync } from 'node:fs';
|
|
28
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
import { Command } from 'commander';
|
|
30
|
+
import chalk from 'chalk';
|
|
31
|
+
import { loadManifest, saveManifest, generateAnonymousId, shouldRotateAnonymousId, rotateAnonymousId, sweepExpiredPreviousId, } from '../utils/manifest.js';
|
|
32
|
+
import { loadClaudeSettings, addSkillHookEntries, removeSkillHookEntries, writeClaudeSettings, TelemetryHookError, } from './telemetry.helpers.js';
|
|
33
|
+
import { sanitizeError } from '../utils/sanitize.js';
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Constants
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
const PRIVACY_URL = 'https://skillsmith.app/privacy#telemetry';
|
|
38
|
+
const DEFAULT_ENDPOINT = 'https://vrcnzpmndtroqxxoqkzy.supabase.co/functions/v1/events';
|
|
39
|
+
const ORPHAN_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
40
|
+
// All path helpers resolved at call time so test harnesses that swap HOME work.
|
|
41
|
+
function hookScriptPath() {
|
|
42
|
+
return join(homedir(), '.skillsmith', 'hooks', 'skill-telemetry.sh');
|
|
43
|
+
}
|
|
44
|
+
function runDir() {
|
|
45
|
+
return join(homedir(), '.skillsmith', 'run');
|
|
46
|
+
}
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Privacy helper — NEVER print full anonymousId
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
function idTail(id) {
|
|
51
|
+
if (!id)
|
|
52
|
+
return '(none)';
|
|
53
|
+
return `...${id.slice(-8)}`;
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Orphan GC (plan line 714)
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
function gcOrphanRunFiles() {
|
|
59
|
+
try {
|
|
60
|
+
const dir = runDir();
|
|
61
|
+
if (!existsSync(dir))
|
|
62
|
+
return;
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
for (const f of readdirSync(dir)) {
|
|
65
|
+
if (!f.startsWith('skill-'))
|
|
66
|
+
continue;
|
|
67
|
+
const fp = join(dir, f);
|
|
68
|
+
try {
|
|
69
|
+
const st = statSync(fp);
|
|
70
|
+
if (now - st.mtimeMs > ORPHAN_TTL_MS)
|
|
71
|
+
unlinkSync(fp);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// best-effort
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// never throw from GC
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// enable
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
async function runEnable() {
|
|
86
|
+
const manifest = await loadManifest();
|
|
87
|
+
const t = manifest.telemetry ?? { enabled: false };
|
|
88
|
+
if (t.enabled) {
|
|
89
|
+
console.log(chalk.green('Telemetry is already enabled.'));
|
|
90
|
+
console.log(chalk.dim(` Anonymous ID tail: ${idTail(t.anonymousId)}`));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const now = new Date().toISOString();
|
|
94
|
+
const updated = {
|
|
95
|
+
...t,
|
|
96
|
+
enabled: true,
|
|
97
|
+
anonymousId: t.anonymousId ?? generateAnonymousId(),
|
|
98
|
+
anonymousIdCreatedAt: t.anonymousIdCreatedAt ?? now,
|
|
99
|
+
scope: t.scope ?? 'personal',
|
|
100
|
+
endpoint: t.endpoint ?? DEFAULT_ENDPOINT,
|
|
101
|
+
};
|
|
102
|
+
await saveManifest({ ...manifest, telemetry: updated });
|
|
103
|
+
console.log(chalk.green('Telemetry enabled.'));
|
|
104
|
+
console.log();
|
|
105
|
+
console.log('Skillsmith telemetry collects anonymous skill-invocation counts to help improve\n' +
|
|
106
|
+
'the registry and surface stale skills. No personal data, file contents, or\n' +
|
|
107
|
+
`paths are captured. Full privacy policy: ${chalk.cyan(PRIVACY_URL)}`);
|
|
108
|
+
console.log();
|
|
109
|
+
console.log(chalk.dim(` Anonymous ID tail: ${idTail(updated.anonymousId)}`));
|
|
110
|
+
console.log(chalk.dim(' Disable at any time: ') + chalk.cyan('skillsmith telemetry disable'));
|
|
111
|
+
}
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// disable
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
async function runDisable() {
|
|
116
|
+
const manifest = await loadManifest();
|
|
117
|
+
const t = manifest.telemetry ?? { enabled: false };
|
|
118
|
+
if (!t.enabled) {
|
|
119
|
+
console.log(chalk.dim('Telemetry is already disabled.'));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
await saveManifest({ ...manifest, telemetry: { ...t, enabled: false } });
|
|
123
|
+
console.log(chalk.yellow('Telemetry disabled.'));
|
|
124
|
+
console.log(chalk.dim(' The anonymous ID is retained in the local manifest for continuity if you re-enable.\n' +
|
|
125
|
+
' To fully clear it: skillsmith telemetry reset-id'));
|
|
126
|
+
}
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// status
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
async function runStatus() {
|
|
131
|
+
let manifest = await loadManifest();
|
|
132
|
+
let rotationTriggered = false;
|
|
133
|
+
// Annual auto-rotation (plan line 706, 713)
|
|
134
|
+
if (shouldRotateAnonymousId(manifest)) {
|
|
135
|
+
const rotated = rotateAnonymousId(manifest);
|
|
136
|
+
manifest = { ...manifest, telemetry: rotated };
|
|
137
|
+
await saveManifest(manifest);
|
|
138
|
+
rotationTriggered = true;
|
|
139
|
+
}
|
|
140
|
+
// Sweep expired previous id
|
|
141
|
+
const swept = sweepExpiredPreviousId(manifest);
|
|
142
|
+
if (swept !== manifest.telemetry) {
|
|
143
|
+
manifest = { ...manifest, telemetry: swept };
|
|
144
|
+
await saveManifest(manifest);
|
|
145
|
+
}
|
|
146
|
+
// GC orphan run files (plan line 714)
|
|
147
|
+
gcOrphanRunFiles();
|
|
148
|
+
const t = manifest.telemetry ?? { enabled: false };
|
|
149
|
+
const endpoint = t.endpoint ?? DEFAULT_ENDPOINT;
|
|
150
|
+
let ageDays = null;
|
|
151
|
+
if (t.anonymousIdCreatedAt) {
|
|
152
|
+
ageDays = Math.floor((Date.now() - new Date(t.anonymousIdCreatedAt).getTime()) / 86_400_000);
|
|
153
|
+
}
|
|
154
|
+
console.log(chalk.bold('Skillsmith Telemetry Status'));
|
|
155
|
+
console.log();
|
|
156
|
+
console.log(` Enabled: ${t.enabled ? chalk.green('yes') : chalk.dim('no')}`);
|
|
157
|
+
console.log(` Endpoint: ${chalk.dim(endpoint)}`);
|
|
158
|
+
console.log(` Anonymous ID: ${chalk.dim(idTail(t.anonymousId))}`);
|
|
159
|
+
console.log(` ID age (days): ${ageDays !== null ? String(ageDays) : chalk.dim('n/a')}`);
|
|
160
|
+
console.log(` Scope: ${t.scope ?? chalk.dim('personal')}`);
|
|
161
|
+
if (t.installedAt) {
|
|
162
|
+
console.log(` Hook installed: ${chalk.dim(t.installedAt)}`);
|
|
163
|
+
}
|
|
164
|
+
if (rotationTriggered) {
|
|
165
|
+
console.log();
|
|
166
|
+
console.log(chalk.yellow(' Annual rotation triggered.') +
|
|
167
|
+
chalk.dim(' New ID tail: ') +
|
|
168
|
+
chalk.cyan(idTail(t.anonymousId)));
|
|
169
|
+
if (t.previousAnonymousId) {
|
|
170
|
+
console.log(chalk.dim(` Previous ID retained for 7-day overlap window (retired: ${t.previousAnonymousIdRetiredAt ?? 'unknown'})`));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// install-hook
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
async function runInstallHook(options) {
|
|
178
|
+
// Resolve the template source — relative to this file's compiled output.
|
|
179
|
+
// src/commands/telemetry.ts → ../../templates/skill-telemetry.sh (via dist/commands)
|
|
180
|
+
// and one level up for the src layout during tests.
|
|
181
|
+
const templateCandidates = [
|
|
182
|
+
join(__dirname, '..', '..', 'templates', 'skill-telemetry.sh'),
|
|
183
|
+
join(__dirname, '..', 'templates', 'skill-telemetry.sh'),
|
|
184
|
+
];
|
|
185
|
+
const templateSrc = templateCandidates.find((p) => existsSync(p));
|
|
186
|
+
if (!templateSrc) {
|
|
187
|
+
throw new Error('skill-telemetry.sh template not found. ' +
|
|
188
|
+
'Ensure the CLI package is fully built: npm run build');
|
|
189
|
+
}
|
|
190
|
+
// Load + validate settings.json — throws TelemetryHookError on foreign Skill matcher
|
|
191
|
+
const scope = options.scope;
|
|
192
|
+
const settings = loadClaudeSettings(scope);
|
|
193
|
+
const hookPath = hookScriptPath();
|
|
194
|
+
const updated = addSkillHookEntries(settings, hookPath);
|
|
195
|
+
// Copy hook script to ~/.skillsmith/hooks/
|
|
196
|
+
const destPath = hookScriptPath();
|
|
197
|
+
const hooksDir = dirname(destPath);
|
|
198
|
+
mkdirSync(hooksDir, { recursive: true, mode: 0o700 });
|
|
199
|
+
copyFileSync(templateSrc, destPath);
|
|
200
|
+
try {
|
|
201
|
+
chmodSync(destPath, 0o755);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// best-effort on Windows
|
|
205
|
+
}
|
|
206
|
+
// Persist settings.json atomically
|
|
207
|
+
writeClaudeSettings(scope, updated);
|
|
208
|
+
// Record installation timestamp in manifest
|
|
209
|
+
const manifest = await loadManifest();
|
|
210
|
+
const t = manifest.telemetry ?? { enabled: false };
|
|
211
|
+
await saveManifest({ ...manifest, telemetry: { ...t, installedAt: new Date().toISOString() } });
|
|
212
|
+
// Update endpoint in manifest if provided
|
|
213
|
+
if (options.endpoint) {
|
|
214
|
+
const m2 = await loadManifest();
|
|
215
|
+
const t2 = m2.telemetry ?? { enabled: false };
|
|
216
|
+
await saveManifest({ ...m2, telemetry: { ...t2, endpoint: options.endpoint } });
|
|
217
|
+
}
|
|
218
|
+
const scopeLabel = scope === 'user' ? '~/.claude/settings.json' : './.claude/settings.json';
|
|
219
|
+
console.log(chalk.green('Skillsmith telemetry hook installed.'));
|
|
220
|
+
console.log(chalk.dim(` Hook script: ${destPath}`));
|
|
221
|
+
console.log(chalk.dim(` Settings file: ${scopeLabel}`));
|
|
222
|
+
console.log();
|
|
223
|
+
console.log(chalk.dim('Enable telemetry to start collecting: ') + chalk.cyan('skillsmith telemetry enable'));
|
|
224
|
+
}
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
// uninstall-hook
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
async function runUninstallHook(options) {
|
|
229
|
+
const scope = options.scope;
|
|
230
|
+
const hookPath = hookScriptPath();
|
|
231
|
+
const settings = loadClaudeSettings(scope);
|
|
232
|
+
const updated = removeSkillHookEntries(settings, hookPath);
|
|
233
|
+
writeClaudeSettings(scope, updated);
|
|
234
|
+
// Optionally remove the script file
|
|
235
|
+
try {
|
|
236
|
+
const scriptPath = hookScriptPath();
|
|
237
|
+
if (existsSync(scriptPath))
|
|
238
|
+
unlinkSync(scriptPath);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
// best-effort
|
|
242
|
+
}
|
|
243
|
+
const scopeLabel = scope === 'user' ? '~/.claude/settings.json' : './.claude/settings.json';
|
|
244
|
+
console.log(chalk.yellow('Skillsmith telemetry hook removed.'));
|
|
245
|
+
console.log(chalk.dim(` Removed from: ${scopeLabel}`));
|
|
246
|
+
console.log(chalk.dim(' Foreign hooks (if any) were not touched.'));
|
|
247
|
+
}
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
// reset-id
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
async function runResetId() {
|
|
252
|
+
const manifest = await loadManifest();
|
|
253
|
+
const rotated = rotateAnonymousId(manifest); // unconditional rotation
|
|
254
|
+
await saveManifest({ ...manifest, telemetry: rotated });
|
|
255
|
+
console.log(chalk.green('Anonymous ID rotated.'));
|
|
256
|
+
console.log(chalk.dim(` New ID tail: ${idTail(rotated.anonymousId)}`));
|
|
257
|
+
if (rotated.previousAnonymousId) {
|
|
258
|
+
console.log(chalk.dim(` Previous ID tail: ${idTail(rotated.previousAnonymousId)}`));
|
|
259
|
+
console.log(chalk.dim(` Previous ID retirement: ${rotated.previousAnonymousIdRetiredAt ?? 'unknown'} (7-day overlap window)`));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
// Command factory
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
export function createTelemetryCommand() {
|
|
266
|
+
const telemetry = new Command('telemetry').description('Manage Skillsmith telemetry preferences and Claude Code hook installation');
|
|
267
|
+
telemetry
|
|
268
|
+
.command('enable')
|
|
269
|
+
.description('Opt in to anonymous skill-invocation telemetry')
|
|
270
|
+
.action(async () => {
|
|
271
|
+
try {
|
|
272
|
+
await runEnable();
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
console.error(chalk.red('Error:'), sanitizeError(err));
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
telemetry
|
|
280
|
+
.command('disable')
|
|
281
|
+
.description('Opt out of telemetry (anonymous ID is retained for re-enable continuity)')
|
|
282
|
+
.action(async () => {
|
|
283
|
+
try {
|
|
284
|
+
await runDisable();
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
console.error(chalk.red('Error:'), sanitizeError(err));
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
telemetry
|
|
292
|
+
.command('status')
|
|
293
|
+
.description('Show current telemetry state; triggers annual ID rotation if due')
|
|
294
|
+
.action(async () => {
|
|
295
|
+
try {
|
|
296
|
+
await runStatus();
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
console.error(chalk.red('Error:'), sanitizeError(err));
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
telemetry
|
|
304
|
+
.command('install-hook')
|
|
305
|
+
.description('Install the Skill telemetry hook into ~/.claude/settings.json')
|
|
306
|
+
.option('--scope <scope>', 'Settings scope: user (~/.claude/settings.json) or project (./.claude/settings.json)', 'user')
|
|
307
|
+
.option('--endpoint <url>', 'Override the telemetry endpoint (default: prod Supabase events)')
|
|
308
|
+
.action(async (options) => {
|
|
309
|
+
const scope = options.scope === 'project' ? 'project' : 'user';
|
|
310
|
+
try {
|
|
311
|
+
await runInstallHook(options.endpoint !== undefined ? { scope, endpoint: options.endpoint } : { scope });
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
if (err instanceof TelemetryHookError) {
|
|
315
|
+
console.error(chalk.red(`Error [${err.code}]:`));
|
|
316
|
+
console.error(err.message);
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
console.error(chalk.red('Error:'), sanitizeError(err));
|
|
320
|
+
}
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
telemetry
|
|
325
|
+
.command('uninstall-hook')
|
|
326
|
+
.description('Remove the Skillsmith Skill hook entries from settings.json')
|
|
327
|
+
.option('--scope <scope>', 'Settings scope: user or project', 'user')
|
|
328
|
+
.action(async (options) => {
|
|
329
|
+
const scope = options.scope === 'project' ? 'project' : 'user';
|
|
330
|
+
try {
|
|
331
|
+
await runUninstallHook({ scope });
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
console.error(chalk.red('Error:'), sanitizeError(err));
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
telemetry
|
|
339
|
+
.command('reset-id')
|
|
340
|
+
.description('Immediately rotate the anonymous ID (previous ID kept for 7-day overlap window)')
|
|
341
|
+
.action(async () => {
|
|
342
|
+
try {
|
|
343
|
+
await runResetId();
|
|
344
|
+
}
|
|
345
|
+
catch (err) {
|
|
346
|
+
console.error(chalk.red('Error:'), sanitizeError(err));
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
return telemetry;
|
|
351
|
+
}
|
|
352
|
+
// Internal exports for tests
|
|
353
|
+
export { runEnable, runDisable, runStatus, runInstallHook, runUninstallHook, runResetId, idTail };
|
|
354
|
+
//# sourceMappingURL=telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../../src/commands/telemetry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for `skillsmith telemetry` subcommands.
|
|
3
|
+
* @module @skillsmith/cli/commands/telemetry.test
|
|
4
|
+
* @see SMI-5021 Wave 3 Step 2 — plan lines 700, 703, 706
|
|
5
|
+
*
|
|
6
|
+
* Covers:
|
|
7
|
+
* enable: fresh manifest → generates id, sets enabled=true; idempotent on re-run
|
|
8
|
+
* disable: enabled manifest → sets enabled=false; anonymousId preserved
|
|
9
|
+
* status: prints expected shape (ID tail only); detects + triggers rotation when backdated >365d
|
|
10
|
+
* reset-id: rotates unconditionally; new id distinct; previous id populated
|
|
11
|
+
* install-hook (mocked): adds PreToolUse+PostToolUse; idempotent; throws on foreign Skill matcher
|
|
12
|
+
* uninstall-hook: removes only Skillsmith entries; preserves foreign hooks
|
|
13
|
+
* install-hook --scope project: writes to ./.claude/settings.json
|
|
14
|
+
*
|
|
15
|
+
* Privacy invariant: idTail() — last 8 chars only, never full id.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=telemetry.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.test.d.ts","sourceRoot":"","sources":["../../../src/commands/telemetry.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|