hacklab 0.10.3 → 0.10.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -10
- package/dist/cli.js +1 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +27 -2
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/{demon.d.ts → daemon.d.ts} +5 -5
- package/dist/commands/daemon.d.ts.map +1 -0
- package/dist/commands/{demon.js → daemon.js} +8 -8
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/join.d.ts.map +1 -1
- package/dist/commands/join.js +2 -2
- package/dist/commands/join.js.map +1 -1
- package/dist/commands/sync.d.ts +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +37 -8
- package/dist/commands/sync.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/prompt-consent.d.ts +61 -0
- package/dist/prompt-consent.d.ts.map +1 -0
- package/dist/prompt-consent.js +167 -0
- package/dist/prompt-consent.js.map +1 -0
- package/dist/prompt-stats.d.ts +72 -0
- package/dist/prompt-stats.d.ts.map +1 -0
- package/dist/prompt-stats.js +244 -0
- package/dist/prompt-stats.js.map +1 -0
- package/dist/registry.d.ts +0 -6
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +3 -8
- package/dist/registry.js.map +1 -1
- package/dist/resolve-command.d.ts +1 -5
- package/dist/resolve-command.d.ts.map +1 -1
- package/dist/resolve-command.js +3 -19
- package/dist/resolve-command.js.map +1 -1
- package/dist/sync.d.ts +14 -0
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +25 -0
- package/dist/sync.js.map +1 -1
- package/package.json +1 -1
- package/dist/commands/demon.d.ts.map +0 -1
- package/dist/commands/demon.js.map +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consent for uploading anything derived from the user's Claude Code
|
|
3
|
+
* conversations.
|
|
4
|
+
*
|
|
5
|
+
* Nothing conversation-derived leaves the machine until someone has answered
|
|
6
|
+
* this explicitly. The tiers are additive:
|
|
7
|
+
*
|
|
8
|
+
* none token counts only — exactly what the CLI did before this existed
|
|
9
|
+
* stats + prompt-length histogram and per-project prompt counts (numbers
|
|
10
|
+
* only; no prompt text ever leaves the machine)
|
|
11
|
+
* full + a sample of the raw prompt text, so the backend can score how
|
|
12
|
+
* technical the prompting is. It scores the sample and discards it.
|
|
13
|
+
*
|
|
14
|
+
* The answer is remembered in ~/.hacklab/config.json so the question is asked
|
|
15
|
+
* once, and can be changed or revoked at any time with
|
|
16
|
+
* `hacklab config prompt-stats <tier>`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const PROMPT_CONSENT_TIERS: readonly ["none", "stats", "full"];
|
|
19
|
+
export type PromptConsentTier = (typeof PROMPT_CONSENT_TIERS)[number];
|
|
20
|
+
export declare function isPromptConsentTier(value: unknown): value is PromptConsentTier;
|
|
21
|
+
/** The stored answer, or null when the user has never been asked. */
|
|
22
|
+
export declare function loadPromptConsent(): Promise<PromptConsentTier | null>;
|
|
23
|
+
export declare function savePromptConsent(tier: PromptConsentTier): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Read `--share-prompt-stats[=<tier>]` out of a command's args.
|
|
26
|
+
*
|
|
27
|
+
* This is the agent-friendly path: it answers the question up front so an
|
|
28
|
+
* unattended run never blocks on a prompt it can't see. Bare
|
|
29
|
+
* `--share-prompt-stats` means the numbers-only tier — the safe reading of an
|
|
30
|
+
* unqualified yes, since sending prompt *text* should always be deliberate.
|
|
31
|
+
* `--no-share-prompt-stats` is an explicit refusal.
|
|
32
|
+
*
|
|
33
|
+
* Returns the chosen tier, or null when the flag is absent. `rest` is the args
|
|
34
|
+
* with the flag removed so per-command parsing never sees it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parsePromptStatsFlag(args: string[]): {
|
|
37
|
+
tier: PromptConsentTier | null;
|
|
38
|
+
rest: string[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Ask a yes/no question with no default: Enter alone re-asks, and only `y` or
|
|
42
|
+
* `n` settles it. Deliberately not @clack/prompts' confirm, which ships a
|
|
43
|
+
* highlighted default that can be accepted with a stray Enter — consent to
|
|
44
|
+
* upload conversation data should never be something you agree to by
|
|
45
|
+
* reflex.
|
|
46
|
+
*
|
|
47
|
+
* Returns null on a non-TTY stdin or EOF, which callers treat as "unanswered"
|
|
48
|
+
* rather than as a yes.
|
|
49
|
+
*/
|
|
50
|
+
export declare function askYesNo(question: string): Promise<boolean | null>;
|
|
51
|
+
/**
|
|
52
|
+
* Resolve the consent tier for this run.
|
|
53
|
+
*
|
|
54
|
+
* Precedence: an explicit flag wins (and is remembered), then the stored
|
|
55
|
+
* answer, then the interactive questions. An unattended run that has never
|
|
56
|
+
* answered gets `none` — silence is never consent.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolvePromptConsent(flagTier: PromptConsentTier | null, opts?: {
|
|
59
|
+
interactive?: boolean;
|
|
60
|
+
}): Promise<PromptConsentTier>;
|
|
61
|
+
//# sourceMappingURL=prompt-consent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-consent.d.ts","sourceRoot":"","sources":["../src/prompt-consent.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,oBAAoB,oCAAqC,CAAA;AACtE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AAErE,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,iBAAiB,CAK5B;AAED,qEAAqE;AACrE,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAK3E;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IACpD,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC9B,IAAI,EAAE,MAAM,EAAE,CAAA;CACf,CAsBA;AAED;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAoBxE;AA+BD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,EAClC,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACnC,OAAO,CAAC,iBAAiB,CAAC,CAyC5B"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
import { loadConfig, saveConfig } from './config.js';
|
|
3
|
+
import { bold, dim, info } from './ui.js';
|
|
4
|
+
/**
|
|
5
|
+
* Consent for uploading anything derived from the user's Claude Code
|
|
6
|
+
* conversations.
|
|
7
|
+
*
|
|
8
|
+
* Nothing conversation-derived leaves the machine until someone has answered
|
|
9
|
+
* this explicitly. The tiers are additive:
|
|
10
|
+
*
|
|
11
|
+
* none token counts only — exactly what the CLI did before this existed
|
|
12
|
+
* stats + prompt-length histogram and per-project prompt counts (numbers
|
|
13
|
+
* only; no prompt text ever leaves the machine)
|
|
14
|
+
* full + a sample of the raw prompt text, so the backend can score how
|
|
15
|
+
* technical the prompting is. It scores the sample and discards it.
|
|
16
|
+
*
|
|
17
|
+
* The answer is remembered in ~/.hacklab/config.json so the question is asked
|
|
18
|
+
* once, and can be changed or revoked at any time with
|
|
19
|
+
* `hacklab config prompt-stats <tier>`.
|
|
20
|
+
*/
|
|
21
|
+
export const PROMPT_CONSENT_TIERS = ['none', 'stats', 'full'];
|
|
22
|
+
export function isPromptConsentTier(value) {
|
|
23
|
+
return (typeof value === 'string' &&
|
|
24
|
+
PROMPT_CONSENT_TIERS.includes(value));
|
|
25
|
+
}
|
|
26
|
+
/** The stored answer, or null when the user has never been asked. */
|
|
27
|
+
export async function loadPromptConsent() {
|
|
28
|
+
const config = await loadConfig();
|
|
29
|
+
return isPromptConsentTier(config.promptStatsConsent)
|
|
30
|
+
? config.promptStatsConsent
|
|
31
|
+
: null;
|
|
32
|
+
}
|
|
33
|
+
export async function savePromptConsent(tier) {
|
|
34
|
+
const config = await loadConfig();
|
|
35
|
+
config.promptStatsConsent = tier;
|
|
36
|
+
await saveConfig(config);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read `--share-prompt-stats[=<tier>]` out of a command's args.
|
|
40
|
+
*
|
|
41
|
+
* This is the agent-friendly path: it answers the question up front so an
|
|
42
|
+
* unattended run never blocks on a prompt it can't see. Bare
|
|
43
|
+
* `--share-prompt-stats` means the numbers-only tier — the safe reading of an
|
|
44
|
+
* unqualified yes, since sending prompt *text* should always be deliberate.
|
|
45
|
+
* `--no-share-prompt-stats` is an explicit refusal.
|
|
46
|
+
*
|
|
47
|
+
* Returns the chosen tier, or null when the flag is absent. `rest` is the args
|
|
48
|
+
* with the flag removed so per-command parsing never sees it.
|
|
49
|
+
*/
|
|
50
|
+
export function parsePromptStatsFlag(args) {
|
|
51
|
+
const rest = [];
|
|
52
|
+
let tier = null;
|
|
53
|
+
for (const arg of args) {
|
|
54
|
+
if (arg === '--no-share-prompt-stats') {
|
|
55
|
+
tier = 'none';
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (arg === '--share-prompt-stats') {
|
|
59
|
+
tier = 'stats';
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (arg.startsWith('--share-prompt-stats=')) {
|
|
63
|
+
const value = arg.slice('--share-prompt-stats='.length);
|
|
64
|
+
tier = isPromptConsentTier(value) ? value : 'stats';
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
rest.push(arg);
|
|
68
|
+
}
|
|
69
|
+
return { tier, rest };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Ask a yes/no question with no default: Enter alone re-asks, and only `y` or
|
|
73
|
+
* `n` settles it. Deliberately not @clack/prompts' confirm, which ships a
|
|
74
|
+
* highlighted default that can be accepted with a stray Enter — consent to
|
|
75
|
+
* upload conversation data should never be something you agree to by
|
|
76
|
+
* reflex.
|
|
77
|
+
*
|
|
78
|
+
* Returns null on a non-TTY stdin or EOF, which callers treat as "unanswered"
|
|
79
|
+
* rather than as a yes.
|
|
80
|
+
*/
|
|
81
|
+
export async function askYesNo(question) {
|
|
82
|
+
if (!process.stdin.isTTY)
|
|
83
|
+
return null;
|
|
84
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
85
|
+
try {
|
|
86
|
+
for (;;) {
|
|
87
|
+
const answer = await new Promise((resolve) => {
|
|
88
|
+
rl.question(`${question} ${dim('[y/n]')} `, (value) => resolve(value));
|
|
89
|
+
rl.once('close', () => resolve(null));
|
|
90
|
+
});
|
|
91
|
+
if (answer === null)
|
|
92
|
+
return null;
|
|
93
|
+
const normalized = answer.trim().toLowerCase();
|
|
94
|
+
if (normalized === 'y' || normalized === 'yes')
|
|
95
|
+
return true;
|
|
96
|
+
if (normalized === 'n' || normalized === 'no')
|
|
97
|
+
return false;
|
|
98
|
+
// Anything else (including a bare Enter) is not an answer.
|
|
99
|
+
console.log(dim(' please press y or n.'));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
rl.close();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** The disclosure. Printed verbatim before the first question. */
|
|
107
|
+
function printDisclosure() {
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log(bold(' share prompt stats?'));
|
|
110
|
+
console.log('');
|
|
111
|
+
info(' hacklab can read your local Claude Code history to chart how you prompt.');
|
|
112
|
+
info(` it is read on this machine, from ${dim('~/.claude/projects')}.`);
|
|
113
|
+
console.log('');
|
|
114
|
+
info(` ${bold('what would be uploaded')}`);
|
|
115
|
+
info(' how many prompts you have sent, and how long they are in words');
|
|
116
|
+
info(' a prompt count per project, matched by its git remote');
|
|
117
|
+
console.log('');
|
|
118
|
+
info(` ${bold('what would not')}`);
|
|
119
|
+
info(' the text of your prompts, unless you separately say yes below');
|
|
120
|
+
info(' anything from a project without a git remote');
|
|
121
|
+
console.log('');
|
|
122
|
+
info(dim(' hacklab stores the numbers on your profile. you can change or revoke'));
|
|
123
|
+
info(dim(' this later with `hacklab config prompt-stats <none|stats|full>`.'));
|
|
124
|
+
console.log('');
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolve the consent tier for this run.
|
|
128
|
+
*
|
|
129
|
+
* Precedence: an explicit flag wins (and is remembered), then the stored
|
|
130
|
+
* answer, then the interactive questions. An unattended run that has never
|
|
131
|
+
* answered gets `none` — silence is never consent.
|
|
132
|
+
*/
|
|
133
|
+
export async function resolvePromptConsent(flagTier, opts = {}) {
|
|
134
|
+
if (flagTier) {
|
|
135
|
+
await savePromptConsent(flagTier);
|
|
136
|
+
return flagTier;
|
|
137
|
+
}
|
|
138
|
+
const stored = await loadPromptConsent();
|
|
139
|
+
if (stored)
|
|
140
|
+
return stored;
|
|
141
|
+
if (!opts.interactive || !process.stdin.isTTY)
|
|
142
|
+
return 'none';
|
|
143
|
+
printDisclosure();
|
|
144
|
+
const shareNumbers = await askYesNo(' share these numbers with hacklab?');
|
|
145
|
+
if (shareNumbers === null)
|
|
146
|
+
return 'none';
|
|
147
|
+
if (!shareNumbers) {
|
|
148
|
+
await savePromptConsent('none');
|
|
149
|
+
console.log('');
|
|
150
|
+
info(dim(' no problem — syncing token counts only, as before.'));
|
|
151
|
+
return 'none';
|
|
152
|
+
}
|
|
153
|
+
console.log('');
|
|
154
|
+
info(' optional: a sample of your prompt text (up to 20k characters) can be');
|
|
155
|
+
info(' sent so an LLM can score how technical your prompting is. hacklab');
|
|
156
|
+
info(` scores it and ${bold('discards it')} — the text is never stored.`);
|
|
157
|
+
console.log('');
|
|
158
|
+
const shareText = await askYesNo(' also send a sample of your prompt text?');
|
|
159
|
+
const tier = shareText === true ? 'full' : 'stats';
|
|
160
|
+
await savePromptConsent(tier);
|
|
161
|
+
console.log('');
|
|
162
|
+
info(dim(tier === 'full'
|
|
163
|
+
? ' sharing prompt stats and a text sample.'
|
|
164
|
+
: ' sharing prompt stats. numbers only, no text.'));
|
|
165
|
+
return tier;
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=prompt-consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-consent.js","sourceRoot":"","sources":["../src/prompt-consent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Buckets are `1..bucketMax`, so the axis stays readable at any prompt length. */
|
|
2
|
+
export declare const PROMPT_LENGTH_BUCKET_MIN = 10;
|
|
3
|
+
export declare const PROMPT_LENGTH_BUCKET_MAX = 100;
|
|
4
|
+
/** The percentile that sets `bucketMax`; everything above lands in the overflow. */
|
|
5
|
+
export declare const PROMPT_LENGTH_OVERFLOW_PERCENTILE = 0.9;
|
|
6
|
+
/** Matches the backend's cap — anything longer is truncated before upload. */
|
|
7
|
+
export declare const CONVERSATION_SAMPLE_MAX_CHARS = 20000;
|
|
8
|
+
export type PromptStatsProject = {
|
|
9
|
+
repoUrl: string;
|
|
10
|
+
promptCount: number;
|
|
11
|
+
lastActiveAt: string;
|
|
12
|
+
};
|
|
13
|
+
export type PromptStats = {
|
|
14
|
+
totalPrompts: number;
|
|
15
|
+
bucketMax: number;
|
|
16
|
+
histogram: {
|
|
17
|
+
length: number;
|
|
18
|
+
count: number;
|
|
19
|
+
}[];
|
|
20
|
+
projects: PromptStatsProject[];
|
|
21
|
+
/** Only ever set under the `full` consent tier. */
|
|
22
|
+
conversationSample?: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The user's own prompt text from one transcript line, or null when the line
|
|
26
|
+
* isn't one.
|
|
27
|
+
*
|
|
28
|
+
* A transcript's `user` entries cover more than typed prompts: tool results
|
|
29
|
+
* come back as synthetic user turns too. Those carry `tool_result` content
|
|
30
|
+
* blocks, so a line only counts when its content is a plain string or an array
|
|
31
|
+
* of nothing but `text` blocks. Sidechain entries (subagent conversations) are
|
|
32
|
+
* the agent talking to itself, not the person typing, so they're excluded.
|
|
33
|
+
*/
|
|
34
|
+
export declare function promptTextFrom(entry: unknown): string | null;
|
|
35
|
+
/** Whitespace-separated word count. Zero-word prompts are dropped by callers. */
|
|
36
|
+
export declare function countWords(text: string): number;
|
|
37
|
+
/**
|
|
38
|
+
* The overflow threshold for this user's own distribution: their p90 prompt
|
|
39
|
+
* length rounded up to a multiple of 10, clamped to [10, 100].
|
|
40
|
+
*
|
|
41
|
+
* Per-user rather than fixed because prompt length varies enormously between
|
|
42
|
+
* people — a fixed axis would either crush a terse user's histogram into the
|
|
43
|
+
* first two buckets or run a verbose one off the end.
|
|
44
|
+
*/
|
|
45
|
+
export declare function bucketMaxFor(wordCounts: number[]): number;
|
|
46
|
+
/**
|
|
47
|
+
* Bucket the word counts. Buckets `1..bucketMax-1` are exact word counts; the
|
|
48
|
+
* bucket at `bucketMax` is the overflow, holding every prompt that long or
|
|
49
|
+
* longer. Empty buckets are omitted — the chart fills the gaps.
|
|
50
|
+
*/
|
|
51
|
+
export declare function buildHistogram(wordCounts: number[], bucketMax: number): {
|
|
52
|
+
length: number;
|
|
53
|
+
count: number;
|
|
54
|
+
}[];
|
|
55
|
+
/**
|
|
56
|
+
* The `origin` remote of the repo at `cwd`, or null when there isn't one (not
|
|
57
|
+
* a repo, no origin, no git binary, or the directory is gone). The URL is sent
|
|
58
|
+
* as-is; the backend normalizes it before matching against the user's own
|
|
59
|
+
* projects.
|
|
60
|
+
*/
|
|
61
|
+
export declare function gitOriginUrl(cwd: string): Promise<string | null>;
|
|
62
|
+
/**
|
|
63
|
+
* Scan the local Claude Code transcripts.
|
|
64
|
+
*
|
|
65
|
+
* `includeSample` gates the raw prompt text: only the `full` consent tier
|
|
66
|
+
* passes true, and the sample never touches disk here — it goes straight into
|
|
67
|
+
* the upload payload the caller builds.
|
|
68
|
+
*/
|
|
69
|
+
export declare function scanPromptStats(options?: {
|
|
70
|
+
includeSample?: boolean;
|
|
71
|
+
}): Promise<PromptStats | null>;
|
|
72
|
+
//# sourceMappingURL=prompt-stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-stats.d.ts","sourceRoot":"","sources":["../src/prompt-stats.ts"],"names":[],"mappings":"AA6BA,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,KAAK,CAAA;AAC1C,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAC3C,oFAAoF;AACpF,eAAO,MAAM,iCAAiC,MAAM,CAAA;AACpD,8EAA8E;AAC9E,eAAO,MAAM,6BAA6B,QAAS,CAAA;AAEnD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC9C,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,mDAAmD;IACnD,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAwB5D;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CAazD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAAE,EACpB,SAAS,EAAE,MAAM,GAChB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAUrC;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYtE;AAQD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAoF7B"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
import { findFiles } from './scanners/util.js';
|
|
7
|
+
/**
|
|
8
|
+
* Prompt statistics, computed entirely on this machine from the local Claude
|
|
9
|
+
* Code transcripts and uploaded only under an explicit consent tier (see
|
|
10
|
+
* prompt-consent.ts).
|
|
11
|
+
*
|
|
12
|
+
* Two numbers come out of a scan:
|
|
13
|
+
*
|
|
14
|
+
* - a histogram of how long the user's own prompts are, in words
|
|
15
|
+
* - a per-project prompt count, keyed by the project's git origin
|
|
16
|
+
*
|
|
17
|
+
* and, under the `full` tier only, a sample of the raw prompt text so the
|
|
18
|
+
* backend can score "how technical is this person's prompting". The backend
|
|
19
|
+
* scores that sample and throws it away; it is never stored.
|
|
20
|
+
*
|
|
21
|
+
* Everything here is deliberately best-effort: an unreadable transcript, a
|
|
22
|
+
* project that isn't a git repo, a missing `git` binary — each drops its own
|
|
23
|
+
* contribution and the rest of the scan still uploads.
|
|
24
|
+
*/
|
|
25
|
+
const execFileAsync = promisify(execFile);
|
|
26
|
+
/** Buckets are `1..bucketMax`, so the axis stays readable at any prompt length. */
|
|
27
|
+
export const PROMPT_LENGTH_BUCKET_MIN = 10;
|
|
28
|
+
export const PROMPT_LENGTH_BUCKET_MAX = 100;
|
|
29
|
+
/** The percentile that sets `bucketMax`; everything above lands in the overflow. */
|
|
30
|
+
export const PROMPT_LENGTH_OVERFLOW_PERCENTILE = 0.9;
|
|
31
|
+
/** Matches the backend's cap — anything longer is truncated before upload. */
|
|
32
|
+
export const CONVERSATION_SAMPLE_MAX_CHARS = 20_000;
|
|
33
|
+
/**
|
|
34
|
+
* The user's own prompt text from one transcript line, or null when the line
|
|
35
|
+
* isn't one.
|
|
36
|
+
*
|
|
37
|
+
* A transcript's `user` entries cover more than typed prompts: tool results
|
|
38
|
+
* come back as synthetic user turns too. Those carry `tool_result` content
|
|
39
|
+
* blocks, so a line only counts when its content is a plain string or an array
|
|
40
|
+
* of nothing but `text` blocks. Sidechain entries (subagent conversations) are
|
|
41
|
+
* the agent talking to itself, not the person typing, so they're excluded.
|
|
42
|
+
*/
|
|
43
|
+
export function promptTextFrom(entry) {
|
|
44
|
+
if (!entry || typeof entry !== 'object')
|
|
45
|
+
return null;
|
|
46
|
+
const line = entry;
|
|
47
|
+
if (line.type !== 'user')
|
|
48
|
+
return null;
|
|
49
|
+
if (line.isSidechain === true)
|
|
50
|
+
return null;
|
|
51
|
+
const content = line.message?.content;
|
|
52
|
+
if (typeof content === 'string')
|
|
53
|
+
return content;
|
|
54
|
+
if (!Array.isArray(content))
|
|
55
|
+
return null;
|
|
56
|
+
const texts = [];
|
|
57
|
+
for (const block of content) {
|
|
58
|
+
if (!block || typeof block !== 'object')
|
|
59
|
+
return null;
|
|
60
|
+
const { type, text } = block;
|
|
61
|
+
// One non-text block (a tool_result, an image) disqualifies the whole
|
|
62
|
+
// entry: it isn't a prompt the person typed.
|
|
63
|
+
if (type !== 'text' || typeof text !== 'string')
|
|
64
|
+
return null;
|
|
65
|
+
texts.push(text);
|
|
66
|
+
}
|
|
67
|
+
return texts.length > 0 ? texts.join('\n') : null;
|
|
68
|
+
}
|
|
69
|
+
/** Whitespace-separated word count. Zero-word prompts are dropped by callers. */
|
|
70
|
+
export function countWords(text) {
|
|
71
|
+
const matches = text.match(/\S+/g);
|
|
72
|
+
return matches ? matches.length : 0;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The overflow threshold for this user's own distribution: their p90 prompt
|
|
76
|
+
* length rounded up to a multiple of 10, clamped to [10, 100].
|
|
77
|
+
*
|
|
78
|
+
* Per-user rather than fixed because prompt length varies enormously between
|
|
79
|
+
* people — a fixed axis would either crush a terse user's histogram into the
|
|
80
|
+
* first two buckets or run a verbose one off the end.
|
|
81
|
+
*/
|
|
82
|
+
export function bucketMaxFor(wordCounts) {
|
|
83
|
+
if (wordCounts.length === 0)
|
|
84
|
+
return PROMPT_LENGTH_BUCKET_MIN;
|
|
85
|
+
const sorted = [...wordCounts].sort((a, b) => a - b);
|
|
86
|
+
const index = Math.min(sorted.length - 1, Math.floor(sorted.length * PROMPT_LENGTH_OVERFLOW_PERCENTILE));
|
|
87
|
+
const p90 = sorted[index] ?? PROMPT_LENGTH_BUCKET_MIN;
|
|
88
|
+
const rounded = Math.ceil(p90 / 10) * 10;
|
|
89
|
+
return Math.min(PROMPT_LENGTH_BUCKET_MAX, Math.max(PROMPT_LENGTH_BUCKET_MIN, rounded));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Bucket the word counts. Buckets `1..bucketMax-1` are exact word counts; the
|
|
93
|
+
* bucket at `bucketMax` is the overflow, holding every prompt that long or
|
|
94
|
+
* longer. Empty buckets are omitted — the chart fills the gaps.
|
|
95
|
+
*/
|
|
96
|
+
export function buildHistogram(wordCounts, bucketMax) {
|
|
97
|
+
const counts = new Map();
|
|
98
|
+
for (const words of wordCounts) {
|
|
99
|
+
if (words <= 0)
|
|
100
|
+
continue;
|
|
101
|
+
const bucket = words >= bucketMax ? bucketMax : words;
|
|
102
|
+
counts.set(bucket, (counts.get(bucket) ?? 0) + 1);
|
|
103
|
+
}
|
|
104
|
+
return [...counts.entries()]
|
|
105
|
+
.map(([length, count]) => ({ length, count }))
|
|
106
|
+
.sort((a, b) => a.length - b.length);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The `origin` remote of the repo at `cwd`, or null when there isn't one (not
|
|
110
|
+
* a repo, no origin, no git binary, or the directory is gone). The URL is sent
|
|
111
|
+
* as-is; the backend normalizes it before matching against the user's own
|
|
112
|
+
* projects.
|
|
113
|
+
*/
|
|
114
|
+
export async function gitOriginUrl(cwd) {
|
|
115
|
+
try {
|
|
116
|
+
const { stdout } = await execFileAsync('git', ['-C', cwd, 'remote', 'get-url', 'origin'], { timeout: 5_000 });
|
|
117
|
+
const url = stdout.trim();
|
|
118
|
+
return url.length > 0 ? url : null;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Scan the local Claude Code transcripts.
|
|
126
|
+
*
|
|
127
|
+
* `includeSample` gates the raw prompt text: only the `full` consent tier
|
|
128
|
+
* passes true, and the sample never touches disk here — it goes straight into
|
|
129
|
+
* the upload payload the caller builds.
|
|
130
|
+
*/
|
|
131
|
+
export async function scanPromptStats(options = {}) {
|
|
132
|
+
const root = join(homedir(), '.claude', 'projects');
|
|
133
|
+
const files = await findFiles(root, '.jsonl');
|
|
134
|
+
if (files.length === 0)
|
|
135
|
+
return null;
|
|
136
|
+
const wordCounts = [];
|
|
137
|
+
const sampleParts = [];
|
|
138
|
+
let sampleChars = 0;
|
|
139
|
+
// Keyed by the transcript's project directory, which is Claude Code's own
|
|
140
|
+
// grouping. The directory name is a lossy encoding of the path, so the real
|
|
141
|
+
// working directory comes from the `cwd` recorded inside the entries.
|
|
142
|
+
const byProjectDir = new Map();
|
|
143
|
+
for (const filePath of files) {
|
|
144
|
+
const projectDir = dirname(filePath);
|
|
145
|
+
let project = byProjectDir.get(projectDir);
|
|
146
|
+
if (!project) {
|
|
147
|
+
project = { cwd: null, promptCount: 0, lastActiveAt: 0 };
|
|
148
|
+
byProjectDir.set(projectDir, project);
|
|
149
|
+
}
|
|
150
|
+
let content;
|
|
151
|
+
try {
|
|
152
|
+
content = await readFile(filePath, 'utf8');
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
for (const line of content.split('\n')) {
|
|
158
|
+
if (!line.trim())
|
|
159
|
+
continue;
|
|
160
|
+
let parsed;
|
|
161
|
+
try {
|
|
162
|
+
parsed = JSON.parse(line);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const entry = parsed;
|
|
168
|
+
if (!project.cwd && typeof entry.cwd === 'string' && entry.cwd) {
|
|
169
|
+
project.cwd = entry.cwd;
|
|
170
|
+
}
|
|
171
|
+
const text = promptTextFrom(parsed);
|
|
172
|
+
if (text === null)
|
|
173
|
+
continue;
|
|
174
|
+
const words = countWords(text);
|
|
175
|
+
if (words <= 0)
|
|
176
|
+
continue;
|
|
177
|
+
wordCounts.push(words);
|
|
178
|
+
project.promptCount += 1;
|
|
179
|
+
if (typeof entry.timestamp === 'string') {
|
|
180
|
+
const at = Date.parse(entry.timestamp);
|
|
181
|
+
if (Number.isFinite(at) && at > project.lastActiveAt) {
|
|
182
|
+
project.lastActiveAt = at;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (options.includeSample &&
|
|
186
|
+
sampleChars < CONVERSATION_SAMPLE_MAX_CHARS) {
|
|
187
|
+
sampleParts.push(text);
|
|
188
|
+
sampleChars += text.length + 2;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (wordCounts.length === 0)
|
|
193
|
+
return null;
|
|
194
|
+
const bucketMax = bucketMaxFor(wordCounts);
|
|
195
|
+
const stats = {
|
|
196
|
+
totalPrompts: wordCounts.length,
|
|
197
|
+
bucketMax,
|
|
198
|
+
histogram: buildHistogram(wordCounts, bucketMax),
|
|
199
|
+
projects: await resolveProjects(byProjectDir),
|
|
200
|
+
};
|
|
201
|
+
if (options.includeSample && sampleParts.length > 0) {
|
|
202
|
+
stats.conversationSample = sampleParts
|
|
203
|
+
.join('\n\n')
|
|
204
|
+
.slice(0, CONVERSATION_SAMPLE_MAX_CHARS);
|
|
205
|
+
}
|
|
206
|
+
return stats;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Turn the per-directory tallies into repo-keyed entries. Directories with no
|
|
210
|
+
* prompts, no recorded `cwd`, or no git origin drop out — the backend can only
|
|
211
|
+
* match a project by its repo URL, so an entry without one is dead weight.
|
|
212
|
+
*
|
|
213
|
+
* Two transcript directories can resolve to the same repo (the same project
|
|
214
|
+
* opened at different paths), so counts are summed per origin.
|
|
215
|
+
*/
|
|
216
|
+
async function resolveProjects(byProjectDir) {
|
|
217
|
+
const byRepo = new Map();
|
|
218
|
+
for (const project of byProjectDir.values()) {
|
|
219
|
+
if (project.promptCount === 0 || !project.cwd)
|
|
220
|
+
continue;
|
|
221
|
+
const repoUrl = await gitOriginUrl(project.cwd);
|
|
222
|
+
if (!repoUrl)
|
|
223
|
+
continue;
|
|
224
|
+
const existing = byRepo.get(repoUrl);
|
|
225
|
+
if (existing) {
|
|
226
|
+
existing.promptCount += project.promptCount;
|
|
227
|
+
existing.lastActiveAt = Math.max(existing.lastActiveAt, project.lastActiveAt);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
byRepo.set(repoUrl, {
|
|
231
|
+
promptCount: project.promptCount,
|
|
232
|
+
lastActiveAt: project.lastActiveAt,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return [...byRepo.entries()].map(([repoUrl, entry]) => ({
|
|
237
|
+
repoUrl,
|
|
238
|
+
promptCount: entry.promptCount,
|
|
239
|
+
// A transcript with no usable timestamp still happened; dating it now is
|
|
240
|
+
// more honest than dropping the project or claiming the epoch.
|
|
241
|
+
lastActiveAt: new Date(entry.lastActiveAt > 0 ? entry.lastActiveAt : Date.now()).toISOString(),
|
|
242
|
+
}));
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=prompt-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-stats.js","sourceRoot":"","sources":["../src/prompt-stats.ts"],"names":[],"mappings":"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"}
|
package/dist/registry.d.ts
CHANGED
|
@@ -5,17 +5,11 @@ export type CommandSpec = {
|
|
|
5
5
|
args?: string;
|
|
6
6
|
/** One-line description shown in `hacklab --help`. */
|
|
7
7
|
summary: string;
|
|
8
|
-
/** Extra names that resolve to this command but aren't listed in help. For
|
|
9
|
-
* spellings people's fingers reach for that aren't the command's own name. */
|
|
10
|
-
aliases?: string[];
|
|
11
8
|
/** Run the command with the per-command args (the global `--env` is already stripped). */
|
|
12
9
|
run: (args: string[]) => Promise<void> | void;
|
|
13
10
|
};
|
|
14
11
|
export declare const COMMANDS: CommandSpec[];
|
|
15
12
|
/** Every command name, in registry order — the source of truth for resolution. */
|
|
16
13
|
export declare const COMMAND_NAMES: string[];
|
|
17
|
-
/** Alias → the canonical command it stands for. Kept out of `COMMAND_NAMES` so
|
|
18
|
-
* aliases never show up in `--help` or as their own ambiguity candidate. */
|
|
19
|
-
export declare const COMMAND_ALIASES: Record<string, string>;
|
|
20
14
|
export declare function findCommand(name: string): CommandSpec | undefined;
|
|
21
15
|
//# sourceMappingURL=registry.d.ts.map
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,WAAW,GAAG;IACxB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAA;IACZ,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAA;IACf
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,WAAW,GAAG;IACxB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAA;IACZ,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAA;IACf,0FAA0F;IAC1F,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CAC9C,CAAA;AAED,eAAO,MAAM,QAAQ,EAAE,WAAW,EA4HjC,CAAA;AAED,kFAAkF;AAClF,eAAO,MAAM,aAAa,UAA8B,CAAA;AAExD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAEjE"}
|
package/dist/registry.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { book, parseBookArgs } from './commands/book.js';
|
|
2
2
|
import { chat } from './commands/chat.js';
|
|
3
3
|
import { configCommand } from './commands/config.js';
|
|
4
|
-
import {
|
|
4
|
+
import { daemon } from './commands/daemon.js';
|
|
5
5
|
import { drop, parseDropArgs } from './commands/drop.js';
|
|
6
6
|
import { essay } from './commands/essay.js';
|
|
7
7
|
import { hackathon } from './commands/hackathon.js';
|
|
@@ -31,12 +31,10 @@ export const COMMANDS = [
|
|
|
31
31
|
run: (args) => sync(args),
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
|
-
name: '
|
|
34
|
+
name: 'daemon',
|
|
35
35
|
args: '[off]',
|
|
36
36
|
summary: 'summon the daily background sync (off tears it down)',
|
|
37
|
-
|
|
38
|
-
aliases: ['daemon'],
|
|
39
|
-
run: (args) => demon(args),
|
|
37
|
+
run: (args) => daemon(args),
|
|
40
38
|
},
|
|
41
39
|
{
|
|
42
40
|
name: 'whoami',
|
|
@@ -143,9 +141,6 @@ export const COMMANDS = [
|
|
|
143
141
|
];
|
|
144
142
|
/** Every command name, in registry order — the source of truth for resolution. */
|
|
145
143
|
export const COMMAND_NAMES = COMMANDS.map((c) => c.name);
|
|
146
|
-
/** Alias → the canonical command it stands for. Kept out of `COMMAND_NAMES` so
|
|
147
|
-
* aliases never show up in `--help` or as their own ambiguity candidate. */
|
|
148
|
-
export const COMMAND_ALIASES = Object.fromEntries(COMMANDS.flatMap((c) => (c.aliases ?? []).map((alias) => [alias, c.name])));
|
|
149
144
|
export function findCommand(name) {
|
|
150
145
|
return COMMANDS.find((c) => c.name === name);
|
|
151
146
|
}
|
package/dist/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|