hacklab 0.18.0 → 0.20.0
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 +43 -34
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +18 -19
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/login.d.ts +4 -2
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +6 -11
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +5 -2
- package/dist/commands/scan.js.map +1 -1
- package/dist/commands/setup-rail.d.ts +4 -3
- package/dist/commands/setup-rail.d.ts.map +1 -1
- package/dist/commands/setup-rail.js +5 -4
- package/dist/commands/setup-rail.js.map +1 -1
- package/dist/commands/setup.d.ts +2 -2
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +39 -33
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +32 -17
- package/dist/commands/sync.js.map +1 -1
- package/dist/config.d.ts +8 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/prompt-consent.d.ts +37 -23
- package/dist/prompt-consent.d.ts.map +1 -1
- package/dist/prompt-consent.js +75 -58
- package/dist/prompt-consent.js.map +1 -1
- package/dist/prompt-stats.d.ts +116 -7
- package/dist/prompt-stats.d.ts.map +1 -1
- package/dist/prompt-stats.js +205 -25
- package/dist/prompt-stats.js.map +1 -1
- package/dist/registry.js +1 -1
- package/dist/registry.js.map +1 -1
- package/dist/scanners/incremental.d.ts +114 -17
- package/dist/scanners/incremental.d.ts.map +1 -1
- package/dist/scanners/incremental.js +235 -74
- package/dist/scanners/incremental.js.map +1 -1
- package/dist/scanners/index.d.ts +7 -3
- package/dist/scanners/index.d.ts.map +1 -1
- package/dist/scanners/index.js +8 -40
- package/dist/scanners/index.js.map +1 -1
- package/dist/scanners/util.d.ts +32 -15
- package/dist/scanners/util.d.ts.map +1 -1
- package/dist/scanners/util.js +10 -34
- package/dist/scanners/util.js.map +1 -1
- package/dist/session.d.ts +4 -4
- package/dist/sync.d.ts +3 -3
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +27 -14
- package/dist/sync.js.map +1 -1
- package/package.json +1 -1
package/dist/prompt-consent.js
CHANGED
|
@@ -1,67 +1,81 @@
|
|
|
1
1
|
import { createInterface } from 'node:readline';
|
|
2
|
-
import { loadConfig,
|
|
2
|
+
import { loadConfig, updateConfig } from './config.js';
|
|
3
3
|
import { bold, dim, info } from './ui.js';
|
|
4
4
|
/**
|
|
5
|
-
* Consent for
|
|
5
|
+
* Consent for syncing anything derived from the user's Claude Code
|
|
6
6
|
* conversations.
|
|
7
7
|
*
|
|
8
8
|
* Nothing conversation-derived leaves the machine until someone has answered
|
|
9
9
|
* this explicitly. The tiers are additive:
|
|
10
10
|
*
|
|
11
|
-
* none token counts only —
|
|
12
|
-
* stats + prompt
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* none token counts only — nothing prompt-related leaves the machine
|
|
12
|
+
* stats + continuous prompt metadata: counts, word counts, timestamps and
|
|
13
|
+
* session ids, synced every minute alongside tokens, so the profile
|
|
14
|
+
* can show sessions, concurrent sessions and prompt counts. Prompt
|
|
15
|
+
* text never leaves the machine.
|
|
16
|
+
* full + a rolling sample of the most recent prompts (up to 20,000
|
|
17
|
+
* characters) sent with the daily sync, used only to estimate a
|
|
18
|
+
* technical-level score and then discarded. Never stored.
|
|
16
19
|
*
|
|
17
|
-
* The answer is remembered in ~/.hacklab/config.json so the
|
|
18
|
-
* once, and can be changed or revoked at any time with
|
|
19
|
-
* `hacklab config prompt-
|
|
20
|
+
* The answer is remembered in ~/.hacklab/config.json under `promptSync` so the
|
|
21
|
+
* question is asked once, and can be changed or revoked at any time with
|
|
22
|
+
* `hacklab config prompt-sync <tier>`.
|
|
20
23
|
*/
|
|
21
|
-
export const
|
|
22
|
-
export function
|
|
24
|
+
export const PROMPT_SYNC_TIERS = ['none', 'stats', 'full'];
|
|
25
|
+
export function isPromptSyncTier(value) {
|
|
23
26
|
return (typeof value === 'string' &&
|
|
24
|
-
|
|
27
|
+
PROMPT_SYNC_TIERS.includes(value));
|
|
25
28
|
}
|
|
26
|
-
/**
|
|
27
|
-
|
|
29
|
+
/**
|
|
30
|
+
* The stored answer, or null when the user has never been asked *this*
|
|
31
|
+
* question. The pre-continuous-sync key (`promptStatsConsent`) is deliberately
|
|
32
|
+
* not read: it answered a different question — a one-off scan, not a minutely
|
|
33
|
+
* sync of session metadata — so it can't stand in for this one.
|
|
34
|
+
*/
|
|
35
|
+
export async function loadPromptSync() {
|
|
28
36
|
const config = await loadConfig();
|
|
29
|
-
return
|
|
30
|
-
? config.promptStatsConsent
|
|
31
|
-
: null;
|
|
37
|
+
return isPromptSyncTier(config.promptSync) ? config.promptSync : null;
|
|
32
38
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Remember the answer. Read-modify-write via `updateConfig` rather than a bare
|
|
41
|
+
* `saveConfig`, because the background jobs call this too and must never
|
|
42
|
+
* flatten a config file they couldn't read. The obsolete key goes with it, so
|
|
43
|
+
* a machine is never carrying two answers to two different questions.
|
|
44
|
+
*/
|
|
45
|
+
export async function savePromptSync(tier) {
|
|
46
|
+
await updateConfig((config) => {
|
|
47
|
+
config.promptSync = tier;
|
|
48
|
+
delete config.promptStatsConsent;
|
|
49
|
+
return config;
|
|
50
|
+
});
|
|
37
51
|
}
|
|
38
52
|
/**
|
|
39
|
-
* Read `--share-prompt-
|
|
53
|
+
* Read `--share-prompt-sync[=<tier>]` out of a command's args.
|
|
40
54
|
*
|
|
41
55
|
* This is the agent-friendly path: it answers the question up front so an
|
|
42
56
|
* unattended run never blocks on a prompt it can't see. Bare
|
|
43
|
-
* `--share-prompt-
|
|
57
|
+
* `--share-prompt-sync` means the metadata-only tier — the safe reading of an
|
|
44
58
|
* unqualified yes, since sending prompt *text* should always be deliberate.
|
|
45
|
-
* `--no-share-prompt-
|
|
59
|
+
* `--no-share-prompt-sync` is an explicit refusal.
|
|
46
60
|
*
|
|
47
61
|
* Returns the chosen tier, or null when the flag is absent. `rest` is the args
|
|
48
62
|
* with the flag removed so per-command parsing never sees it.
|
|
49
63
|
*/
|
|
50
|
-
export function
|
|
64
|
+
export function parsePromptSyncFlag(args) {
|
|
51
65
|
const rest = [];
|
|
52
66
|
let tier = null;
|
|
53
67
|
for (const arg of args) {
|
|
54
|
-
if (arg === '--no-share-prompt-
|
|
68
|
+
if (arg === '--no-share-prompt-sync') {
|
|
55
69
|
tier = 'none';
|
|
56
70
|
continue;
|
|
57
71
|
}
|
|
58
|
-
if (arg === '--share-prompt-
|
|
72
|
+
if (arg === '--share-prompt-sync') {
|
|
59
73
|
tier = 'stats';
|
|
60
74
|
continue;
|
|
61
75
|
}
|
|
62
|
-
if (arg.startsWith('--share-prompt-
|
|
63
|
-
const value = arg.slice('--share-prompt-
|
|
64
|
-
tier =
|
|
76
|
+
if (arg.startsWith('--share-prompt-sync=')) {
|
|
77
|
+
const value = arg.slice('--share-prompt-sync='.length);
|
|
78
|
+
tier = isPromptSyncTier(value) ? value : 'stats';
|
|
65
79
|
continue;
|
|
66
80
|
}
|
|
67
81
|
rest.push(arg);
|
|
@@ -106,62 +120,65 @@ export async function askYesNo(question) {
|
|
|
106
120
|
/** The disclosure. Printed verbatim before the first question. */
|
|
107
121
|
function printDisclosure() {
|
|
108
122
|
console.log('');
|
|
109
|
-
console.log(bold('
|
|
123
|
+
console.log(bold(' sync your prompt activity?'));
|
|
110
124
|
console.log('');
|
|
111
|
-
info(' hacklab can
|
|
112
|
-
info(`
|
|
125
|
+
info(' hacklab can keep your profile up to date with how you work with AI,');
|
|
126
|
+
info(` reading your Claude Code history from ${dim('~/.claude/projects')}.`);
|
|
113
127
|
console.log('');
|
|
114
|
-
info(` ${bold('what would be
|
|
115
|
-
info('
|
|
116
|
-
info('
|
|
128
|
+
info(` ${bold('what would be synced, every minute:')}`);
|
|
129
|
+
info(` ${dim('-')} how many prompts you send, and how long they are in words`);
|
|
130
|
+
info(` ${dim('-')} when each session started and last ran, and its session id`);
|
|
131
|
+
info(` ${dim('-')} a prompt count per project, matched by its git remote`);
|
|
117
132
|
console.log('');
|
|
118
|
-
info(` ${bold('what
|
|
119
|
-
info('
|
|
120
|
-
info('
|
|
133
|
+
info(` ${bold('what stays on this machine:')}`);
|
|
134
|
+
info(` ${dim('-')} the text of your prompts, unless you separately say yes below`);
|
|
135
|
+
info(` ${dim('-')} anything from a project without a git remote`);
|
|
121
136
|
console.log('');
|
|
122
|
-
info(dim('
|
|
123
|
-
info(dim('
|
|
137
|
+
info(dim(' your profile shows your sessions, how many you run at once, and'));
|
|
138
|
+
info(dim(' your prompt counts. change or revoke this any time with'));
|
|
139
|
+
info(dim(' `hacklab config prompt-sync <none|stats|full>`.'));
|
|
124
140
|
console.log('');
|
|
125
141
|
}
|
|
126
142
|
/**
|
|
127
|
-
* Resolve the
|
|
143
|
+
* Resolve the prompt-sync tier for this run.
|
|
128
144
|
*
|
|
129
145
|
* Precedence: an explicit flag wins (and is remembered), then the stored
|
|
130
146
|
* answer, then the interactive questions. An unattended run that has never
|
|
131
147
|
* answered gets `none` — silence is never consent.
|
|
132
148
|
*/
|
|
133
|
-
export async function
|
|
149
|
+
export async function resolvePromptSync(flagTier, opts = {}) {
|
|
134
150
|
if (flagTier) {
|
|
135
|
-
await
|
|
151
|
+
await savePromptSync(flagTier);
|
|
136
152
|
return flagTier;
|
|
137
153
|
}
|
|
138
|
-
const stored = await
|
|
154
|
+
const stored = await loadPromptSync();
|
|
139
155
|
if (stored)
|
|
140
156
|
return stored;
|
|
141
157
|
if (!opts.interactive || !process.stdin.isTTY)
|
|
142
158
|
return 'none';
|
|
143
159
|
printDisclosure();
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
160
|
+
const shareActivity = await askYesNo(' sync this to hacklab?');
|
|
161
|
+
if (shareActivity === null)
|
|
146
162
|
return 'none';
|
|
147
|
-
if (!
|
|
148
|
-
await
|
|
163
|
+
if (!shareActivity) {
|
|
164
|
+
await savePromptSync('none');
|
|
149
165
|
console.log('');
|
|
150
|
-
info(dim(' no problem — syncing token counts only
|
|
166
|
+
info(dim(' no problem — syncing token counts only.'));
|
|
151
167
|
return 'none';
|
|
152
168
|
}
|
|
153
169
|
console.log('');
|
|
154
|
-
info(' optional: a sample of your
|
|
155
|
-
info('
|
|
156
|
-
info(` scores it and ${bold('discards it')}
|
|
170
|
+
info(' optional: a rolling sample of your most recent prompts (up to 20,000');
|
|
171
|
+
info(' characters) can go out with the daily sync, so a model can estimate');
|
|
172
|
+
info(` how technical your prompting is. hacklab scores it and ${bold('discards it')} —`);
|
|
173
|
+
info(' the text is never stored.');
|
|
157
174
|
console.log('');
|
|
158
|
-
const shareText = await askYesNo(' also send a sample of your
|
|
175
|
+
const shareText = await askYesNo(' also send a sample of your most recent prompts?');
|
|
159
176
|
const tier = shareText === true ? 'full' : 'stats';
|
|
160
|
-
await
|
|
177
|
+
await savePromptSync(tier);
|
|
161
178
|
console.log('');
|
|
162
179
|
info(dim(tier === 'full'
|
|
163
|
-
? '
|
|
164
|
-
: '
|
|
180
|
+
? ' syncing prompt activity and a text sample.'
|
|
181
|
+
: ' syncing prompt activity. numbers only, no text.'));
|
|
165
182
|
return tier;
|
|
166
183
|
}
|
|
167
184
|
//# sourceMappingURL=prompt-consent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-consent.js","sourceRoot":"","sources":["../src/prompt-consent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"prompt-consent.js","sourceRoot":"","sources":["../src/prompt-consent.ts"],"names":[],"mappings":"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"}
|
package/dist/prompt-stats.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/** Buckets are `1..bucketMax`, so the axis stays readable at any prompt length. */
|
|
2
2
|
export declare const PROMPT_LENGTH_BUCKET_MIN = 10;
|
|
3
3
|
export declare const PROMPT_LENGTH_BUCKET_MAX = 100;
|
|
4
|
-
/** The percentile that sets `bucketMax`; everything above lands in
|
|
5
|
-
export declare const
|
|
4
|
+
/** The percentile that sets `bucketMax`; everything above it lands in `tail`. */
|
|
5
|
+
export declare const PROMPT_LENGTH_AXIS_PERCENTILE = 0.9;
|
|
6
|
+
/** The server's cap on `tail` entries; longer distributions are coarsened. */
|
|
7
|
+
export declare const PROMPT_TAIL_MAX_ENTRIES = 400;
|
|
6
8
|
/** Matches the backend's cap — anything longer is truncated before upload. */
|
|
7
9
|
export declare const CONVERSATION_SAMPLE_MAX_CHARS = 20000;
|
|
8
10
|
export type PromptStatsProject = {
|
|
@@ -10,6 +12,27 @@ export type PromptStatsProject = {
|
|
|
10
12
|
promptCount: number;
|
|
11
13
|
lastActiveAt: string;
|
|
12
14
|
};
|
|
15
|
+
/** One session's running aggregate, as both the tick and a full scan build it. */
|
|
16
|
+
export type PromptSessionAggregate = {
|
|
17
|
+
/** ISO timestamp of the first prompt seen in this session. */
|
|
18
|
+
startedAt: string;
|
|
19
|
+
/** ISO timestamp of the most recent one. */
|
|
20
|
+
lastActiveAt: string;
|
|
21
|
+
promptCount: number;
|
|
22
|
+
};
|
|
23
|
+
/** One day's running tally. Cumulative for this machine, never a delta. */
|
|
24
|
+
export type PromptDayAggregate = {
|
|
25
|
+
prompts: number;
|
|
26
|
+
words: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* The whole prompt-activity aggregate, keyed for cheap merging: sessions by
|
|
30
|
+
* session id, days by YYYY-MM-DD.
|
|
31
|
+
*/
|
|
32
|
+
export type PromptActivityAggregate = {
|
|
33
|
+
sessions: Record<string, PromptSessionAggregate>;
|
|
34
|
+
daily: Record<string, PromptDayAggregate>;
|
|
35
|
+
};
|
|
13
36
|
export type PromptStats = {
|
|
14
37
|
totalPrompts: number;
|
|
15
38
|
bucketMax: number;
|
|
@@ -17,7 +40,27 @@ export type PromptStats = {
|
|
|
17
40
|
length: number;
|
|
18
41
|
count: number;
|
|
19
42
|
}[];
|
|
43
|
+
/**
|
|
44
|
+
* The exact distribution above `bucketMax`, and the only place those prompts
|
|
45
|
+
* are reported — the histogram stops at `bucketMax`. One entry per distinct
|
|
46
|
+
* word count, ascending, coarsened if there are more than
|
|
47
|
+
* `PROMPT_TAIL_MAX_ENTRIES` of them. Always present, empty when nothing
|
|
48
|
+
* exceeds `bucketMax`: the field's presence is what tells the server this
|
|
49
|
+
* histogram is exact, as opposed to a legacy snapshot whose last bar lumped
|
|
50
|
+
* everything at or above `bucketMax`.
|
|
51
|
+
*/
|
|
52
|
+
tail: {
|
|
53
|
+
length: number;
|
|
54
|
+
count: number;
|
|
55
|
+
}[];
|
|
20
56
|
projects: PromptStatsProject[];
|
|
57
|
+
/**
|
|
58
|
+
* Sessions and per-day counts for the whole local history. Not part of the
|
|
59
|
+
* `promptStats` block on the wire — the caller hands it to `stageFullScan`,
|
|
60
|
+
* which re-bases the tick's incremental state on it and works out which rows
|
|
61
|
+
* this upload still has to carry.
|
|
62
|
+
*/
|
|
63
|
+
activity: PromptActivityAggregate;
|
|
21
64
|
/** Only ever set under the `full` consent tier. */
|
|
22
65
|
conversationSample?: string;
|
|
23
66
|
};
|
|
@@ -34,9 +77,43 @@ export type PromptStats = {
|
|
|
34
77
|
export declare function promptTextFrom(entry: unknown): string | null;
|
|
35
78
|
/** Whitespace-separated word count. Zero-word prompts are dropped by callers. */
|
|
36
79
|
export declare function countWords(text: string): number;
|
|
80
|
+
/** The server's cap on a session id. Longer than this and the row is rejected. */
|
|
81
|
+
export declare const PROMPT_SESSION_ID_MAX_CHARS = 128;
|
|
82
|
+
/** One prompt, reduced to the three facts the activity aggregate needs. */
|
|
83
|
+
export type PromptLine = {
|
|
84
|
+
sessionId: string;
|
|
85
|
+
/** Canonical ISO-8601 UTC, so string order is time order. */
|
|
86
|
+
timestamp: string;
|
|
87
|
+
words: number;
|
|
88
|
+
};
|
|
37
89
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
90
|
+
* A transcript line as prompt activity, or null when it isn't one.
|
|
91
|
+
*
|
|
92
|
+
* Stricter than `promptTextFrom` on purpose: a prompt with no session id or no
|
|
93
|
+
* usable timestamp can't be placed on a session or a day, and the server's
|
|
94
|
+
* schema would reject it, so it counts towards the histogram (which needs
|
|
95
|
+
* neither) and nothing else.
|
|
96
|
+
*/
|
|
97
|
+
export declare function parsePromptLine(line: string): PromptLine | null;
|
|
98
|
+
/** An ISO-8601 UTC string, or null when the value isn't a usable instant. */
|
|
99
|
+
export declare function normalizeTimestamp(value: unknown): string | null;
|
|
100
|
+
export declare function emptyPromptActivity(): PromptActivityAggregate;
|
|
101
|
+
/**
|
|
102
|
+
* Fold one prompt into an activity aggregate, returning the session and date it
|
|
103
|
+
* landed on so an incremental caller can mark exactly those dirty.
|
|
104
|
+
*
|
|
105
|
+
* The date is the UTC day of the timestamp — the same attribution
|
|
106
|
+
* `toDateStr` gives every token daily row, so the two halves of a sync agree
|
|
107
|
+
* about which day a piece of work belongs to.
|
|
108
|
+
*/
|
|
109
|
+
export declare function addPromptToActivity(activity: PromptActivityAggregate, line: PromptLine): {
|
|
110
|
+
sessionId: string;
|
|
111
|
+
date: string;
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Where the histogram's axis ends for this user's own distribution: their p90
|
|
115
|
+
* prompt length rounded up to a multiple of 10, clamped to [10, 100]. Anything
|
|
116
|
+
* longer is reported by `buildTail` instead.
|
|
40
117
|
*
|
|
41
118
|
* Per-user rather than fixed because prompt length varies enormously between
|
|
42
119
|
* people — a fixed axis would either crush a terse user's histogram into the
|
|
@@ -44,14 +121,40 @@ export declare function countWords(text: string): number;
|
|
|
44
121
|
*/
|
|
45
122
|
export declare function bucketMaxFor(wordCounts: number[]): number;
|
|
46
123
|
/**
|
|
47
|
-
* Bucket the word counts.
|
|
48
|
-
*
|
|
49
|
-
*
|
|
124
|
+
* Bucket the word counts. Every bucket `1..bucketMax` is an exact word count —
|
|
125
|
+
* there is no overflow bar. Empty buckets are omitted; the chart fills the gaps.
|
|
126
|
+
*
|
|
127
|
+
* Prompts longer than `bucketMax` are not in here at all: they are reported by
|
|
128
|
+
* `buildTail`, and only there. So the histogram and the tail partition the
|
|
129
|
+
* scan — sum(histogram counts) + sum(tail counts) === totalPrompts, always.
|
|
50
130
|
*/
|
|
51
131
|
export declare function buildHistogram(wordCounts: number[], bucketMax: number): {
|
|
52
132
|
length: number;
|
|
53
133
|
count: number;
|
|
54
134
|
}[];
|
|
135
|
+
/**
|
|
136
|
+
* The exact distribution of everything *longer* than `bucketMax`, which the
|
|
137
|
+
* histogram does not cover at all. One entry per distinct word count,
|
|
138
|
+
* ascending; empty (never absent) when nothing exceeds `bucketMax` — the
|
|
139
|
+
* field's presence on the wire is the marker that the histogram's bars are
|
|
140
|
+
* exact.
|
|
141
|
+
*
|
|
142
|
+
* Lengths only — no prompt text is involved here, or anywhere near it.
|
|
143
|
+
*
|
|
144
|
+
* A machine with a long history can have thousands of distinct long lengths,
|
|
145
|
+
* far past what the server accepts, so an over-cap tail is coarsened: lengths
|
|
146
|
+
* are rounded to multiples of 2, then 4, 8, … until at most
|
|
147
|
+
* `PROMPT_TAIL_MAX_ENTRIES` entries remain, summing the counts that collapse
|
|
148
|
+
* together. Rounding is *up* so that a coarsened entry still sits above
|
|
149
|
+
* `bucketMax` — rounding down could claim a length inside the histogram's own
|
|
150
|
+
* exact range for a prompt the histogram never counted.
|
|
151
|
+
*
|
|
152
|
+
* Deterministic: the result depends only on the multiset of word counts.
|
|
153
|
+
*/
|
|
154
|
+
export declare function buildTail(wordCounts: number[], bucketMax: number): {
|
|
155
|
+
length: number;
|
|
156
|
+
count: number;
|
|
157
|
+
}[];
|
|
55
158
|
/**
|
|
56
159
|
* The `origin` remote of the repo at `cwd`, or null when there isn't one (not
|
|
57
160
|
* a repo, no origin, no git binary, or the directory is gone). The URL is sent
|
|
@@ -69,4 +172,10 @@ export declare function gitOriginUrl(cwd: string): Promise<string | null>;
|
|
|
69
172
|
export declare function scanPromptStats(options?: {
|
|
70
173
|
includeSample?: boolean;
|
|
71
174
|
}): Promise<PromptStats | null>;
|
|
175
|
+
/**
|
|
176
|
+
* The `promptStats` block as the server takes it. `activity` is deliberately
|
|
177
|
+
* dropped: it is local bookkeeping for the tick's incremental state, and it
|
|
178
|
+
* travels under its own top-level `promptActivity` field instead.
|
|
179
|
+
*/
|
|
180
|
+
export declare function promptStatsPayload(stats: PromptStats): Omit<PromptStats, 'activity'>;
|
|
72
181
|
//# sourceMappingURL=prompt-stats.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-stats.d.ts","sourceRoot":"","sources":["../src/prompt-stats.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prompt-stats.d.ts","sourceRoot":"","sources":["../src/prompt-stats.ts"],"names":[],"mappings":"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"}
|