@praeviso/code-env-switch 0.1.4 → 0.1.6

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.
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_MODEL_PRICING = void 0;
4
+ exports.resolvePricingForProfile = resolvePricingForProfile;
5
+ exports.calculateUsageCost = calculateUsageCost;
6
+ exports.formatUsdAmount = formatUsdAmount;
7
+ const TOKENS_PER_MILLION = 1000000;
8
+ exports.DEFAULT_MODEL_PRICING = {
9
+ "Claude Sonnet 4.5": {
10
+ input: 3.0,
11
+ output: 15.0,
12
+ cacheWrite: 3.75,
13
+ cacheRead: 0.3,
14
+ description: "Balanced performance and speed for daily use.",
15
+ },
16
+ "Sonnet 4.5": {
17
+ input: 3.0,
18
+ output: 15.0,
19
+ cacheWrite: 3.75,
20
+ cacheRead: 0.3,
21
+ description: "Balanced performance and speed for daily use.",
22
+ },
23
+ "Claude Opus 4.5": {
24
+ input: 5.0,
25
+ output: 25.0,
26
+ cacheWrite: 6.25,
27
+ cacheRead: 0.5,
28
+ description: "Most capable model for agents and coding.",
29
+ },
30
+ "Opus 4.5": {
31
+ input: 5.0,
32
+ output: 25.0,
33
+ cacheWrite: 6.25,
34
+ cacheRead: 0.5,
35
+ description: "Most capable model for agents and coding.",
36
+ },
37
+ "Claude Haiku 4.5": {
38
+ input: 1.0,
39
+ output: 5.0,
40
+ cacheWrite: 1.25,
41
+ cacheRead: 0.1,
42
+ description: "Fast responses for lightweight tasks.",
43
+ },
44
+ "Haiku 4.5": {
45
+ input: 1.0,
46
+ output: 5.0,
47
+ cacheWrite: 1.25,
48
+ cacheRead: 0.1,
49
+ description: "Fast responses for lightweight tasks.",
50
+ },
51
+ "claude-opus-4-5-20251101": {
52
+ input: 5.0,
53
+ output: 25.0,
54
+ cacheWrite: 6.25,
55
+ cacheRead: 0.5,
56
+ description: "Most capable model for agents and coding.",
57
+ },
58
+ "claude-sonnet-4-5-20251022": {
59
+ input: 3.0,
60
+ output: 15.0,
61
+ cacheWrite: 3.75,
62
+ cacheRead: 0.3,
63
+ description: "Balanced performance and speed for daily use.",
64
+ },
65
+ "claude-haiku-4-5-20251022": {
66
+ input: 1.0,
67
+ output: 5.0,
68
+ cacheWrite: 1.25,
69
+ cacheRead: 0.1,
70
+ description: "Fast responses for lightweight tasks.",
71
+ },
72
+ "gpt-5.1": {
73
+ input: 1.25,
74
+ output: 10.0,
75
+ cacheRead: 0.125,
76
+ description: "Base model for daily development work.",
77
+ },
78
+ "gpt-5.1-codex": {
79
+ input: 1.25,
80
+ output: 10.0,
81
+ cacheRead: 0.125,
82
+ description: "Code-focused model for programming workflows.",
83
+ },
84
+ "gpt-5.1-codex-max": {
85
+ input: 1.25,
86
+ output: 10.0,
87
+ cacheRead: 0.125,
88
+ description: "Flagship code model for complex projects.",
89
+ },
90
+ "gpt-5.2": {
91
+ input: 1.75,
92
+ output: 14.0,
93
+ cacheRead: 0.175,
94
+ description: "Latest flagship model with improved performance.",
95
+ },
96
+ "gpt-5.2-codex": {
97
+ input: 1.75,
98
+ output: 14.0,
99
+ cacheRead: 0.175,
100
+ description: "Latest flagship code model.",
101
+ },
102
+ };
103
+ function normalizeModelKey(value) {
104
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
105
+ }
106
+ function parsePriceValue(value) {
107
+ if (value === null || value === undefined)
108
+ return null;
109
+ if (typeof value === "number" && Number.isFinite(value))
110
+ return value;
111
+ if (typeof value === "string") {
112
+ const match = value.replace(/,/g, "").match(/-?\d+(?:\.\d+)?/);
113
+ if (match) {
114
+ const parsed = Number(match[0]);
115
+ return Number.isFinite(parsed) ? parsed : null;
116
+ }
117
+ }
118
+ return null;
119
+ }
120
+ function compactPricing(value) {
121
+ if (!value || typeof value !== "object")
122
+ return null;
123
+ const input = parsePriceValue(value.input);
124
+ const output = parsePriceValue(value.output);
125
+ const cacheRead = parsePriceValue(value.cacheRead);
126
+ const cacheWrite = parsePriceValue(value.cacheWrite);
127
+ const description = typeof value.description === "string" && value.description.trim()
128
+ ? value.description.trim()
129
+ : undefined;
130
+ const pricing = {};
131
+ let hasNumber = false;
132
+ if (input !== null) {
133
+ pricing.input = input;
134
+ hasNumber = true;
135
+ }
136
+ if (output !== null) {
137
+ pricing.output = output;
138
+ hasNumber = true;
139
+ }
140
+ if (cacheRead !== null) {
141
+ pricing.cacheRead = cacheRead;
142
+ hasNumber = true;
143
+ }
144
+ if (cacheWrite !== null) {
145
+ pricing.cacheWrite = cacheWrite;
146
+ hasNumber = true;
147
+ }
148
+ if (description)
149
+ pricing.description = description;
150
+ return hasNumber ? pricing : null;
151
+ }
152
+ function resolveMultiplier(value) {
153
+ const parsed = parsePriceValue(value);
154
+ if (parsed === null)
155
+ return null;
156
+ if (parsed < 0)
157
+ return null;
158
+ return parsed;
159
+ }
160
+ function applyMultiplier(pricing, multiplier) {
161
+ if (!pricing)
162
+ return null;
163
+ if (multiplier === null)
164
+ return pricing;
165
+ const scaled = {};
166
+ if (pricing.input !== undefined)
167
+ scaled.input = pricing.input * multiplier;
168
+ if (pricing.output !== undefined)
169
+ scaled.output = pricing.output * multiplier;
170
+ if (pricing.cacheRead !== undefined)
171
+ scaled.cacheRead = pricing.cacheRead * multiplier;
172
+ if (pricing.cacheWrite !== undefined)
173
+ scaled.cacheWrite = pricing.cacheWrite * multiplier;
174
+ if (pricing.description)
175
+ scaled.description = pricing.description;
176
+ return scaled;
177
+ }
178
+ function buildModelIndex(models) {
179
+ const index = new Map();
180
+ if (!models)
181
+ return index;
182
+ for (const [model, pricing] of Object.entries(models)) {
183
+ const key = normalizeModelKey(model);
184
+ if (!key)
185
+ continue;
186
+ const cleaned = compactPricing(pricing);
187
+ if (!cleaned)
188
+ continue;
189
+ index.set(key, { model, pricing: cleaned });
190
+ }
191
+ return index;
192
+ }
193
+ function resolveModelPricing(config, model) {
194
+ var _a;
195
+ if (!model)
196
+ return null;
197
+ const key = normalizeModelKey(model);
198
+ if (!key)
199
+ return null;
200
+ const configIndex = buildModelIndex((_a = config.pricing) === null || _a === void 0 ? void 0 : _a.models);
201
+ const fromConfig = configIndex.get(key);
202
+ if (fromConfig)
203
+ return fromConfig;
204
+ const defaultsIndex = buildModelIndex(exports.DEFAULT_MODEL_PRICING);
205
+ return defaultsIndex.get(key) || null;
206
+ }
207
+ function mergePricing(base, override) {
208
+ if (!base && !override)
209
+ return null;
210
+ return compactPricing({ ...(base || {}), ...(override || {}) });
211
+ }
212
+ function getProfilePricing(profile) {
213
+ var _a;
214
+ if (!profile || !profile.pricing || typeof profile.pricing !== "object") {
215
+ return { model: null, pricing: null, multiplier: null };
216
+ }
217
+ const raw = profile.pricing;
218
+ const model = typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() : null;
219
+ return {
220
+ model,
221
+ pricing: compactPricing(raw),
222
+ multiplier: (_a = raw.multiplier) !== null && _a !== void 0 ? _a : null,
223
+ };
224
+ }
225
+ function resolvePricingForProfile(config, profile, model) {
226
+ var _a, _b;
227
+ const profilePricing = getProfilePricing(profile);
228
+ const baseFromProfileModel = profilePricing.model
229
+ ? resolveModelPricing(config, profilePricing.model)
230
+ : null;
231
+ const mergedProfile = mergePricing(baseFromProfileModel ? baseFromProfileModel.pricing : null, profilePricing.pricing);
232
+ const resolvedPricing = mergedProfile ||
233
+ (baseFromProfileModel ? baseFromProfileModel.pricing : null) ||
234
+ ((_b = (_a = resolveModelPricing(config, model)) === null || _a === void 0 ? void 0 : _a.pricing) !== null && _b !== void 0 ? _b : null);
235
+ if (!resolvedPricing)
236
+ return null;
237
+ const multiplier = resolveMultiplier(profilePricing.multiplier);
238
+ return applyMultiplier(resolvedPricing, multiplier);
239
+ }
240
+ function toFiniteNumber(value) {
241
+ if (value === null || value === undefined)
242
+ return null;
243
+ const num = Number(value);
244
+ if (!Number.isFinite(num))
245
+ return null;
246
+ return num;
247
+ }
248
+ function calculateUsageCost(usage, pricing) {
249
+ var _a, _b, _c, _d, _e, _f;
250
+ if (!usage || !pricing)
251
+ return null;
252
+ const inputTokens = toFiniteNumber(usage.inputTokens);
253
+ const outputTokens = toFiniteNumber(usage.outputTokens);
254
+ const cacheReadTokens = toFiniteNumber(usage.cacheReadTokens);
255
+ const cacheWriteTokens = toFiniteNumber(usage.cacheWriteTokens);
256
+ if (inputTokens === null &&
257
+ outputTokens === null &&
258
+ cacheReadTokens === null &&
259
+ cacheWriteTokens === null) {
260
+ return null;
261
+ }
262
+ const tokens = {
263
+ input: Math.max(0, inputTokens || 0),
264
+ output: Math.max(0, outputTokens || 0),
265
+ cacheRead: Math.max(0, cacheReadTokens || 0),
266
+ cacheWrite: Math.max(0, cacheWriteTokens || 0),
267
+ };
268
+ const knownTotal = toFiniteNumber((_b = (_a = usage.todayTokens) !== null && _a !== void 0 ? _a : usage.totalTokens) !== null && _b !== void 0 ? _b : null);
269
+ const breakdownTotal = tokens.input + tokens.output + tokens.cacheRead + tokens.cacheWrite;
270
+ if (breakdownTotal === 0 && knownTotal !== null && knownTotal > 0) {
271
+ return null;
272
+ }
273
+ if (tokens.input > 0 && pricing.input === undefined)
274
+ return null;
275
+ if (tokens.output > 0 && pricing.output === undefined)
276
+ return null;
277
+ if (tokens.cacheRead > 0 && pricing.cacheRead === undefined)
278
+ return null;
279
+ if (tokens.cacheWrite > 0 && pricing.cacheWrite === undefined)
280
+ return null;
281
+ const total = (tokens.input * ((_c = pricing.input) !== null && _c !== void 0 ? _c : 0) +
282
+ tokens.output * ((_d = pricing.output) !== null && _d !== void 0 ? _d : 0) +
283
+ tokens.cacheRead * ((_e = pricing.cacheRead) !== null && _e !== void 0 ? _e : 0) +
284
+ tokens.cacheWrite * ((_f = pricing.cacheWrite) !== null && _f !== void 0 ? _f : 0)) /
285
+ TOKENS_PER_MILLION;
286
+ return Number.isFinite(total) ? total : null;
287
+ }
288
+ function formatUsdAmount(amount) {
289
+ if (amount === null || !Number.isFinite(amount))
290
+ return "-";
291
+ const normalized = Math.abs(amount) < 1e-12 ? 0 : amount;
292
+ const abs = Math.abs(normalized);
293
+ let decimals = 2;
294
+ if (abs < 1)
295
+ decimals = 4;
296
+ if (abs < 0.1)
297
+ decimals = 5;
298
+ if (abs < 0.01)
299
+ decimals = 6;
300
+ let text = normalized.toFixed(decimals);
301
+ text = text.replace(/\.?0+$/, "");
302
+ return `$${text}`;
303
+ }
@@ -15,6 +15,61 @@
15
15
  "type": "command",
16
16
  "padding": 0
17
17
  },
18
+ "pricing": {
19
+ "models": {
20
+ "Claude Sonnet 4.5": {
21
+ "input": 3.0,
22
+ "output": 15.0,
23
+ "cacheWrite": 3.75,
24
+ "cacheRead": 0.3,
25
+ "description": "Balanced performance and speed for daily use."
26
+ },
27
+ "Claude Opus 4.5": {
28
+ "input": 5.0,
29
+ "output": 25.0,
30
+ "cacheWrite": 6.25,
31
+ "cacheRead": 0.5,
32
+ "description": "Most capable model for agents and coding."
33
+ },
34
+ "Claude Haiku 4.5": {
35
+ "input": 1.0,
36
+ "output": 5.0,
37
+ "cacheWrite": 1.25,
38
+ "cacheRead": 0.1,
39
+ "description": "Fast responses for lightweight tasks."
40
+ },
41
+ "gpt-5.1": {
42
+ "input": 1.25,
43
+ "output": 10.0,
44
+ "cacheRead": 0.125,
45
+ "description": "Base model for daily development work."
46
+ },
47
+ "gpt-5.1-codex": {
48
+ "input": 1.25,
49
+ "output": 10.0,
50
+ "cacheRead": 0.125,
51
+ "description": "Code-focused model for programming workflows."
52
+ },
53
+ "gpt-5.1-codex-max": {
54
+ "input": 1.25,
55
+ "output": 10.0,
56
+ "cacheRead": 0.125,
57
+ "description": "Flagship code model for complex projects."
58
+ },
59
+ "gpt-5.2": {
60
+ "input": 1.75,
61
+ "output": 14.0,
62
+ "cacheRead": 0.175,
63
+ "description": "Latest flagship model with improved performance."
64
+ },
65
+ "gpt-5.2-codex": {
66
+ "input": 1.75,
67
+ "output": 14.0,
68
+ "cacheRead": 0.175,
69
+ "description": "Latest flagship code model."
70
+ }
71
+ }
72
+ },
18
73
  "profiles": {
19
74
  "p_a1b2c3": {
20
75
  "name": "primary",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praeviso/code-env-switch",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Switch between Claude Code and Codex environment variables from a single CLI",
5
5
  "bin": {
6
6
  "codenv": "bin/index.js"
package/src/cli/args.ts CHANGED
@@ -5,6 +5,7 @@ import type {
5
5
  ParsedArgs,
6
6
  InitArgs,
7
7
  AddArgs,
8
+ UsageResetArgs,
8
9
  ProfileType,
9
10
  StatuslineArgs,
10
11
  } from "../types";
@@ -156,6 +157,19 @@ export function parseAddArgs(args: string[]): AddArgs {
156
157
  return result;
157
158
  }
158
159
 
160
+ export function parseUsageResetArgs(args: string[]): UsageResetArgs {
161
+ const result: UsageResetArgs = { yes: false };
162
+ for (let i = 0; i < args.length; i++) {
163
+ const arg = args[i];
164
+ if (arg === "-y" || arg === "--yes") {
165
+ result.yes = true;
166
+ continue;
167
+ }
168
+ throw new Error(`Unknown usage-reset argument: ${arg}`);
169
+ }
170
+ return result;
171
+ }
172
+
159
173
  function parseNumberFlag(value: string | null | undefined, flag: string): number {
160
174
  if (value === null || value === undefined || value === "") {
161
175
  throw new Error(`Missing value for ${flag}.`);
package/src/cli/help.ts CHANGED
@@ -27,6 +27,7 @@ Usage:
27
27
  codenv launch <codex|claude> [--] [args...]
28
28
  codenv init
29
29
  codenv statusline [options]
30
+ codenv usage-reset [--yes]
30
31
 
31
32
  Options:
32
33
  -c, --config <path> Path to config JSON
@@ -57,6 +58,9 @@ Statusline options:
57
58
  --usage-output <n> Set output token usage
58
59
  --sync-usage Sync usage from sessions before reading
59
60
 
61
+ Usage reset options:
62
+ -y, --yes Skip confirmation prompt
63
+
60
64
  Examples:
61
65
  codenv init
62
66
  codenv use codex primary
@@ -67,6 +71,7 @@ Examples:
67
71
  codenv remove --all
68
72
  codenv launch codex -- --help
69
73
  codenv statusline --format json
74
+ codenv usage-reset --yes
70
75
  CODE_ENV_CONFIG=~/.config/code-env/config.json codenv use claude default
71
76
  codenv add --type codex primary OPENAI_BASE_URL=https://api.example.com/v1 OPENAI_API_KEY=YOUR_API_KEY
72
77
  codenv add
package/src/cli/index.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  /**
2
2
  * CLI module exports
3
3
  */
4
- export { parseArgs, parseInitArgs, parseAddArgs, parseStatuslineArgs } from "./args";
4
+ export {
5
+ parseArgs,
6
+ parseInitArgs,
7
+ parseAddArgs,
8
+ parseUsageResetArgs,
9
+ parseStatuslineArgs,
10
+ } from "./args";
5
11
  export { printHelp } from "./help";
@@ -8,3 +8,4 @@ export { printShow } from "./show";
8
8
  export { printUnset } from "./unset";
9
9
  export { runLaunch } from "./launch";
10
10
  export { printStatusline } from "./statusline";
11
+ export { runUsageReset } from "./usage";
@@ -6,9 +6,12 @@ import { buildListRows } from "../profile/display";
6
6
  import { getResolvedDefaultProfileKeys } from "../config/defaults";
7
7
  import {
8
8
  formatTokenCount,
9
+ readUsageCostIndex,
9
10
  readUsageTotalsIndex,
11
+ resolveUsageCostForProfile,
10
12
  resolveUsageTotalsForProfile,
11
13
  } from "../usage";
14
+ import { formatUsdAmount } from "../usage/pricing";
12
15
 
13
16
  export function printList(config: Config, configPath: string | null): void {
14
17
  const rows = buildListRows(config, getResolvedDefaultProfileKeys);
@@ -18,6 +21,7 @@ export function printList(config: Config, configPath: string | null): void {
18
21
  }
19
22
  try {
20
23
  const usageTotals = readUsageTotalsIndex(config, configPath, true);
24
+ const usageCosts = readUsageCostIndex(config, configPath, false);
21
25
  if (usageTotals) {
22
26
  for (const row of rows) {
23
27
  if (!row.usageType) continue;
@@ -32,6 +36,22 @@ export function printList(config: Config, configPath: string | null): void {
32
36
  row.totalTokens = usage.total;
33
37
  }
34
38
  }
39
+ if (usageCosts) {
40
+ for (const row of rows) {
41
+ if (!row.usageType) continue;
42
+ const cost = resolveUsageCostForProfile(
43
+ usageCosts,
44
+ row.usageType,
45
+ row.key,
46
+ row.name
47
+ );
48
+ if (!cost) continue;
49
+ row.todayCost = cost.today;
50
+ row.totalCost = cost.total;
51
+ row.todayBilledTokens = cost.todayTokens;
52
+ row.totalBilledTokens = cost.totalTokens;
53
+ }
54
+ }
35
55
  } catch {
36
56
  // ignore usage sync errors
37
57
  }
@@ -40,8 +60,20 @@ export function printList(config: Config, configPath: string | null): void {
40
60
  const headerToday = "TODAY";
41
61
  const headerTotal = "TOTAL";
42
62
  const headerNote = "NOTE";
43
- const todayTexts = rows.map((row) => formatTokenCount(row.todayTokens));
44
- const totalTexts = rows.map((row) => formatTokenCount(row.totalTokens));
63
+ const todayTexts = rows.map((row) =>
64
+ formatUsageWithCost(
65
+ row.todayTokens,
66
+ row.todayBilledTokens ?? null,
67
+ row.todayCost ?? null
68
+ )
69
+ );
70
+ const totalTexts = rows.map((row) =>
71
+ formatUsageWithCost(
72
+ row.totalTokens,
73
+ row.totalBilledTokens ?? null,
74
+ row.totalCost ?? null
75
+ )
76
+ );
45
77
  const nameWidth = Math.max(
46
78
  headerName.length,
47
79
  ...rows.map((row) => row.name.length)
@@ -67,7 +99,15 @@ export function printList(config: Config, configPath: string | null): void {
67
99
  todayWidth
68
100
  )} ${total.padStart(totalWidth)} ${note.padEnd(noteWidth)}`;
69
101
 
70
- console.log(formatRow(headerName, headerType, headerToday, headerTotal, headerNote));
102
+ console.log(
103
+ formatRow(
104
+ headerName,
105
+ headerType,
106
+ headerToday,
107
+ headerTotal,
108
+ headerNote
109
+ )
110
+ );
71
111
  console.log(
72
112
  formatRow(
73
113
  "-".repeat(nameWidth),
@@ -81,7 +121,13 @@ export function printList(config: Config, configPath: string | null): void {
81
121
  const row = rows[i];
82
122
  const todayText = todayTexts[i] || "-";
83
123
  const totalText = totalTexts[i] || "-";
84
- const line = formatRow(row.name, row.type, todayText, totalText, row.note);
124
+ const line = formatRow(
125
+ row.name,
126
+ row.type,
127
+ todayText,
128
+ totalText,
129
+ row.note
130
+ );
85
131
  if (row.active) {
86
132
  console.log(`\x1b[32m${line}\x1b[0m`);
87
133
  } else {
@@ -89,3 +135,27 @@ export function printList(config: Config, configPath: string | null): void {
89
135
  }
90
136
  }
91
137
  }
138
+
139
+ function formatUsageWithCost(
140
+ tokens: number | null | undefined,
141
+ billedTokens: number | null,
142
+ cost: number | null
143
+ ): string {
144
+ const tokenText = formatTokenCount(tokens ?? null);
145
+ if (tokenText === "-") return tokenText;
146
+ if (cost === null || !Number.isFinite(cost)) return tokenText;
147
+ if (tokens === null || tokens === undefined || !Number.isFinite(tokens)) {
148
+ return tokenText;
149
+ }
150
+ if (billedTokens === null || !Number.isFinite(billedTokens)) {
151
+ return `${tokenText} (${formatUsdAmount(cost)})`;
152
+ }
153
+ if (billedTokens >= tokens) {
154
+ return `${tokenText} (${formatUsdAmount(cost)})`;
155
+ }
156
+ const billedText = formatTokenCount(billedTokens);
157
+ if (billedText === "-") {
158
+ return `${tokenText} (${formatUsdAmount(cost)})`;
159
+ }
160
+ return `${tokenText} (billed ${billedText}, ${formatUsdAmount(cost)})`;
161
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Usage history reset command
3
+ */
4
+ import type { Config, UsageResetArgs } from "../types";
5
+ import { clearUsageHistory } from "../usage";
6
+ import { askConfirm, createReadline } from "../ui";
7
+
8
+ export async function runUsageReset(
9
+ config: Config,
10
+ configPath: string | null,
11
+ args: UsageResetArgs
12
+ ): Promise<void> {
13
+ if (!args.yes) {
14
+ const rl = createReadline();
15
+ try {
16
+ const confirmed = await askConfirm(
17
+ rl,
18
+ "Clear all usage history files? This cannot be undone. (y/N): "
19
+ );
20
+ if (!confirmed) return;
21
+ } finally {
22
+ rl.close();
23
+ }
24
+ }
25
+
26
+ const result = clearUsageHistory(config, configPath);
27
+ const removed = result.removed.sort();
28
+ const missing = result.missing.sort();
29
+ const failed = result.failed.sort((a, b) => a.path.localeCompare(b.path));
30
+
31
+ if (removed.length === 0 && failed.length === 0) {
32
+ console.log("No usage files found.");
33
+ return;
34
+ }
35
+
36
+ if (removed.length > 0) {
37
+ console.log(`Removed ${removed.length} file(s):`);
38
+ for (const filePath of removed) {
39
+ console.log(`- ${filePath}`);
40
+ }
41
+ }
42
+
43
+ if (missing.length > 0) {
44
+ console.log(`Skipped ${missing.length} missing file(s).`);
45
+ }
46
+
47
+ if (failed.length > 0) {
48
+ for (const failure of failed) {
49
+ console.error(`Failed to remove ${failure.path}: ${failure.error}`);
50
+ }
51
+ process.exitCode = 1;
52
+ }
53
+ }
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  parseArgs,
11
11
  parseInitArgs,
12
12
  parseAddArgs,
13
+ parseUsageResetArgs,
13
14
  parseStatuslineArgs,
14
15
  printHelp,
15
16
  } from "./cli";
@@ -33,6 +34,7 @@ import {
33
34
  printUnset,
34
35
  runLaunch,
35
36
  printStatusline,
37
+ runUsageReset,
36
38
  } from "./commands";
37
39
  import { logProfileUse } from "./usage";
38
40
  import { createReadline, askConfirm, runInteractiveAdd, runInteractiveUse } from "./ui";
@@ -152,6 +154,15 @@ async function main() {
152
154
  return;
153
155
  }
154
156
 
157
+ if (cmd === "usage-reset" || cmd === "reset-usage") {
158
+ const resetArgs = parseUsageResetArgs(args.slice(1));
159
+ const configPath =
160
+ process.env.CODE_ENV_CONFIG_PATH || findConfigPath(parsed.configPath);
161
+ const config = readConfigIfExists(configPath);
162
+ await runUsageReset(config, configPath, resetArgs);
163
+ return;
164
+ }
165
+
155
166
  const configPath = findConfigPath(parsed.configPath);
156
167
  const config = readConfig(configPath!);
157
168
 
@@ -16,7 +16,7 @@ function resolveDefaultConfigDir(configPath: string | null): string {
16
16
  return path.join(os.homedir(), ".config", "code-env");
17
17
  }
18
18
 
19
- function getStatuslineDebugPath(configPath: string | null): string {
19
+ export function getStatuslineDebugPath(configPath: string | null): string {
20
20
  const envPath = resolvePath(process.env.CODE_ENV_STATUSLINE_DEBUG_PATH);
21
21
  if (envPath) return envPath;
22
22
  return path.join(resolveDefaultConfigDir(configPath), "statusline-debug.jsonl");