@dzhechkov/harness-cli 0.3.199 → 0.3.200
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/LICENSE +21 -0
- package/README.md +51 -14
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +179 -17
- package/dist/cli.js.map +1 -1
- package/package.json +12 -12
- package/src/cli.ts +221 -23
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dzhechko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -376,7 +376,7 @@ dz vector import <file.rvf> [--project <dir>] [--json] # RVF import
|
|
|
376
376
|
dz vector harmonize [--apply] [--threshold <0..1>] [--json] # SEMANTIC merge of near-dups (dry-run default; --apply after a restorable backup)
|
|
377
377
|
dz teach --harmonize [--apply] [--threshold <0..1>] # alias of `dz vector harmonize`
|
|
378
378
|
dz statusline [--json] [--install] # compact Claude Code statusline: live self-learning pattern count + brain sources
|
|
379
|
-
dz usage [--json] [--project <dir>]
|
|
379
|
+
dz usage [--json] [--project <dir>] | --calibrate --session <pct> --weekly <pct> [--model fable=<pct>] # ESTIMATE Claude usage from fixed reset windows; optional per-model weekly binding; exit 0 ALWAYS
|
|
380
380
|
dz brain list [--json] # the durable cross-project knowledge brain
|
|
381
381
|
dz brain query "<q>" [--source <slug>] [--limit <N>] [--any] [--rerank] [--json] # cross-source recall (--any = OR match; --rerank reorders on-point first). Auto-broadens: if strict all-terms match yields 0 hits, it retries once as OR and labels the result "broadened" (text note + broadened:true in --json) instead of returning empty. Explicit --any is OR from the start (never labeled broadened).
|
|
382
382
|
dz brain add [--source <slug>] [--project <dir>] [--from-slice <f>|--from-pack <p>|--from-kus <f> --slug <s> --kind <k> --license <spdx> [--override]] [--json] # grow the brain
|
|
@@ -467,29 +467,66 @@ Modeled on the Agentic QE statusline pattern (e.g. AQE's `🎓 12 patterns`).
|
|
|
467
467
|
|
|
468
468
|
### Usage estimate (`dz usage`)
|
|
469
469
|
|
|
470
|
-
`dz usage` prints a READONLY, never-throw ESTIMATE of Claude SESSION
|
|
471
|
-
|
|
470
|
+
`dz usage` prints a READONLY, never-throw ESTIMATE of Claude SESSION and WEEKLY token usage,
|
|
471
|
+
aggregated from your local `~/.claude/projects/**/*.jsonl` transcripts. Weekly counts start at the
|
|
472
|
+
configured fixed local anchor (`memory.usage.weeklyResetAnchor`, for example `Wed 08:59`). Session
|
|
473
|
+
counts use the active fixed-length transcript block (`memory.usage.sessionBlockHours`, normally `5`).
|
|
472
474
|
|
|
473
475
|
```bash
|
|
474
|
-
dz usage # session ~74% (resets 19:00) · week ~
|
|
475
|
-
dz usage --json # {"sessionPct":74,"weeklyPct":
|
|
476
|
+
dz usage # session ~74% (resets 19:00) · week ~76% fable-bound (resets 08:59) · estimated
|
|
477
|
+
dz usage --json # {"sessionPct":74,"weeklyPct":76,"sessionTokens":...,"weeklyTokens":...,"resetsAt":{...},"limits":{...},"estimated":true}
|
|
476
478
|
```
|
|
477
479
|
|
|
478
480
|
**Exit code is 0 ALWAYS** (even unconfigured/error → all-null JSON) — a probe must never distinguish
|
|
479
|
-
"usage unknown" from "command failed" via a non-zero exit. Percentages are **ESTIMATES**
|
|
480
|
-
|
|
481
|
+
"usage unknown" from "command failed" via a non-zero exit. Percentages are **ESTIMATES** from local
|
|
482
|
+
transcripts; claude.ai/settings/usage is authoritative. They are `null` until you configure the
|
|
483
|
+
plan-dependent limits in `.dz/config.json`:
|
|
481
484
|
|
|
482
485
|
```json
|
|
483
|
-
{
|
|
486
|
+
{
|
|
487
|
+
"memory": {
|
|
488
|
+
"usage": {
|
|
489
|
+
"sessionTokenLimit": 200000000,
|
|
490
|
+
"weeklyTokenLimit": 1000000000,
|
|
491
|
+
"weeklyResetAnchor": "Wed 08:59",
|
|
492
|
+
"sessionBlockHours": 5,
|
|
493
|
+
"weeklyTokenLimitByModel": { "fable": 500000000 }
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
`sessionTokenLimit` and `weeklyTokenLimit` are optional; absent means that pct is `null` (unknown,
|
|
500
|
+
never `0`). `weeklyTokenLimit` is the all-model weekly limit. `weeklyTokenLimitByModel` is optional;
|
|
501
|
+
when it has at least one valid model limit (`fable`, `opus`, `sonnet`, or `haiku`), `weeklyPct` is the
|
|
502
|
+
binding per-model percentage and `--json` adds:
|
|
503
|
+
|
|
504
|
+
```json
|
|
505
|
+
{
|
|
506
|
+
"weeklyByModel": { "fable": { "tokens": 380, "pct": 76 } },
|
|
507
|
+
"limits": { "session": 200000000, "weekly": 1000000000, "weeklyByModel": { "fable": 500 } }
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
When no per-model limits are configured, the JSON shape stays at the legacy fields:
|
|
512
|
+
`sessionPct`, `weeklyPct`, `sessionTokens`, `weeklyTokens`, `resetsAt`, `limits`, `estimated`.
|
|
513
|
+
|
|
514
|
+
Calibrate from human-transcribed claude.ai percentages with one command:
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
dz usage --calibrate --session 20 --weekly 50 --model fable=76 --project .
|
|
484
518
|
```
|
|
485
519
|
|
|
486
|
-
|
|
487
|
-
|
|
520
|
+
It computes each limit as `currentTokens / (pct / 100)`, writes only `memory.usage` in
|
|
521
|
+
`.dz/config.json`, and records `calibratedAt` plus `source: "claude.ai/settings/usage"`. Invalid
|
|
522
|
+
percentages, unknown models, missing transcripts, and zero-token calibration requests are skipped with
|
|
523
|
+
exit code `0`.
|
|
524
|
+
|
|
488
525
|
This feeds `/feature-adr`'s **usage-adaptive routing** — the pipeline probes `dz usage --json` at each
|
|
489
|
-
phase boundary and pre-emptively routes the remaining stages to Codex when
|
|
490
|
-
(default 70%). **Honest caveat:** at TRUE exhaustion even the Codex
|
|
491
|
-
is a Claude wrapper), so the switch must happen BEFORE — the
|
|
492
|
-
is the real defense.
|
|
526
|
+
phase boundary and pre-emptively routes the remaining stages to Codex when `sessionPct` or the binding
|
|
527
|
+
`weeklyPct` crosses the threshold (default 70%). **Honest caveat:** at TRUE exhaustion even the Codex
|
|
528
|
+
dispatch dies (`codex:codex-rescue` is a Claude wrapper), so the switch must happen BEFORE — the
|
|
529
|
+
pre-emptive probe, not reactive detection, is the real defense.
|
|
493
530
|
|
|
494
531
|
### Targets (10 platforms — 5 lossless + Copilot + AGENTS.md + Cursor + Gemini + Windsurf)
|
|
495
532
|
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA2JH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAq5GD,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgH5E"}
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { execSync } from 'node:child_process';
|
|
|
9
9
|
import { homedir } from 'node:os';
|
|
10
10
|
import { createRequire } from 'node:module';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
|
-
import { createSkill, getSkillInfo, getWorkflow, isTargetName, listSkills, runDoctor, runInit, benchmarkSkill, benchmarkSkills, scanMcp, reconcileCapabilities, RECONCILE_BANNER, buildRegistry, discoverSkillPackDirs, checkUpstream, compareSkills, checkAllUpstream, sweepSkillDrift, syncCanonicalSkill, checkUpgrades, discoverPackages, discoverSourcePackages, fetchAllDownloads, filterByCategory, pretrain, recommend, generatePlugin, publishPackages, runSetup, runMigrate, searchRegistry, runSync, runVerify, runInitAgentsMd, runInitGeminiMd, TARGET_NAMES, WORKFLOW_NAMES, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, recallHybrid, teachGuard, mirrorPatternsToVector, vectorMirrorEnabled, vectorTierStatus, resolveVectorEngine, reindexVectorStore, harmonizeVectorStore, importRvfCheckpoint, statuslineData, writeFeatureAdrState, computeUsage, readUsageLimits, queryBookKnowledge, loadStorePatternsSync, bundleSkills, brainHome, listBrain, promoteProjectToBrain, updateBrainSource, queryBrain, groundPrompt, expandKu, reindexBrainVectors, buildPrimer, exportBrainSlice, importBrainSlice, registerKusToBrain, } from '@dzhechkov/harness-core';
|
|
12
|
+
import { createSkill, getSkillInfo, getWorkflow, isTargetName, listSkills, runDoctor, runInit, benchmarkSkill, benchmarkSkills, scanMcp, reconcileCapabilities, RECONCILE_BANNER, buildRegistry, discoverSkillPackDirs, checkUpstream, compareSkills, checkAllUpstream, sweepSkillDrift, syncCanonicalSkill, checkUpgrades, discoverPackages, discoverSourcePackages, fetchAllDownloads, filterByCategory, pretrain, recommend, generatePlugin, publishPackages, runSetup, runMigrate, searchRegistry, runSync, runVerify, runInitAgentsMd, runInitGeminiMd, TARGET_NAMES, WORKFLOW_NAMES, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, recallHybrid, teachGuard, mirrorPatternsToVector, vectorMirrorEnabled, vectorTierStatus, resolveVectorEngine, reindexVectorStore, harmonizeVectorStore, importRvfCheckpoint, statuslineData, writeFeatureAdrState, computeUsage, deriveUsageCalibration, normalizeClaudeUsageModelKey, readUsageLimits, queryBookKnowledge, loadStorePatternsSync, bundleSkills, brainHome, listBrain, promoteProjectToBrain, updateBrainSource, queryBrain, groundPrompt, expandKu, reindexBrainVectors, buildPrimer, exportBrainSlice, importBrainSlice, registerKusToBrain, } from '@dzhechkov/harness-core';
|
|
13
13
|
import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
|
|
14
14
|
import { scanGitHub, analyzeRepo, generateReport, deepAnalyze, scanAllSources, ScoutMemory } from '@dzhechkov/scout';
|
|
15
15
|
const USAGE = `dz - DZ cross-platform harness CLI
|
|
@@ -51,7 +51,7 @@ Usage:
|
|
|
51
51
|
dz brain init [--project <dir>] [--k <N>] (wire the grounding hook into .claude/settings.json — opt-in)
|
|
52
52
|
dz statusline [--json] [--install] [--project <dir>] (live self-learning panel for Claude Code's status bar; reads the CC JSON payload from STDIN)
|
|
53
53
|
dz statusline --fa-record --slug <s> --step "<label>" [--recalled <n>] [--stored <n>] [--mode <m>] (feature-adr: record live per-run learning state → 📐 panel segment)
|
|
54
|
-
dz usage [--json] [--project <dir>]
|
|
54
|
+
dz usage [--json] [--project <dir>] | dz usage --calibrate --session <pct> --weekly <pct> [--model fable=<pct>] [--project <dir>] (ESTIMATE Claude usage from fixed reset windows; optional per-model weekly binding; exit 0 ALWAYS; pct=null when limits unconfigured)
|
|
55
55
|
dz pretrain [--project <dir>]
|
|
56
56
|
dz recommend "<task description>"
|
|
57
57
|
dz compose <preset1+preset2+...> [--target <name>]
|
|
@@ -81,6 +81,7 @@ Presets: ${PRESET_NAMES.join(', ')}`;
|
|
|
81
81
|
/** Parse `<command> [--key value] [--flag]` argv. */
|
|
82
82
|
function parseArgs(argv) {
|
|
83
83
|
const options = new Map();
|
|
84
|
+
const optionLists = new Map();
|
|
84
85
|
const flags = new Set();
|
|
85
86
|
const positional = [];
|
|
86
87
|
for (let index = 0; index < argv.length; index += 1) {
|
|
@@ -90,6 +91,9 @@ function parseArgs(argv) {
|
|
|
90
91
|
const next = argv[index + 1];
|
|
91
92
|
if (next !== undefined && !next.startsWith('--')) {
|
|
92
93
|
options.set(key, next);
|
|
94
|
+
const list = optionLists.get(key) ?? [];
|
|
95
|
+
list.push(next);
|
|
96
|
+
optionLists.set(key, list);
|
|
93
97
|
index += 1;
|
|
94
98
|
}
|
|
95
99
|
else {
|
|
@@ -104,7 +108,7 @@ function parseArgs(argv) {
|
|
|
104
108
|
for (let pi = 1; pi < positional.length; pi += 1) {
|
|
105
109
|
options.set(`_positional_${pi - 1}`, positional[pi] ?? '');
|
|
106
110
|
}
|
|
107
|
-
return { command: positional[0] ?? '', options, flags };
|
|
111
|
+
return { command: positional[0] ?? '', options, optionLists, flags };
|
|
108
112
|
}
|
|
109
113
|
/**
|
|
110
114
|
* Discover skill source directories: explicit `--skills-dir` if given, else
|
|
@@ -947,18 +951,165 @@ function cmdStatusline(options, flags, cwd, write, readStdin) {
|
|
|
947
951
|
return 0;
|
|
948
952
|
}
|
|
949
953
|
}
|
|
954
|
+
function isJsonRecord(value) {
|
|
955
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
956
|
+
}
|
|
957
|
+
function usageConfigPath(projectRoot) {
|
|
958
|
+
return join(projectRoot, '.dz', 'config.json');
|
|
959
|
+
}
|
|
960
|
+
function readProjectConfigForUsage(projectRoot) {
|
|
961
|
+
const path = usageConfigPath(projectRoot);
|
|
962
|
+
try {
|
|
963
|
+
if (!existsSync(path))
|
|
964
|
+
return { config: {} };
|
|
965
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
966
|
+
if (isJsonRecord(parsed))
|
|
967
|
+
return { config: parsed };
|
|
968
|
+
return { config: {}, warning: 'existing config is not a JSON object; writing a minimal config' };
|
|
969
|
+
}
|
|
970
|
+
catch {
|
|
971
|
+
return { config: {}, warning: 'existing config could not be parsed; writing a minimal config' };
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
function applyUsageCalibrationToConfig(config, plan) {
|
|
975
|
+
const next = { ...config };
|
|
976
|
+
const memory = isJsonRecord(next['memory']) ? { ...next['memory'] } : {};
|
|
977
|
+
const usage = isJsonRecord(memory['usage']) ? { ...memory['usage'] } : {};
|
|
978
|
+
for (const change of plan.changes) {
|
|
979
|
+
if (change.key === 'session') {
|
|
980
|
+
usage['sessionTokenLimit'] = change.after;
|
|
981
|
+
}
|
|
982
|
+
else if (change.key === 'weekly') {
|
|
983
|
+
usage['weeklyTokenLimit'] = change.after;
|
|
984
|
+
}
|
|
985
|
+
else {
|
|
986
|
+
const model = normalizeClaudeUsageModelKey(change.key);
|
|
987
|
+
if (model) {
|
|
988
|
+
const existingByModel = isJsonRecord(usage['weeklyTokenLimitByModel']) ? { ...usage['weeklyTokenLimitByModel'] } : {};
|
|
989
|
+
existingByModel[model] = change.after;
|
|
990
|
+
usage['weeklyTokenLimitByModel'] = existingByModel;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
if (plan.changes.length > 0) {
|
|
995
|
+
usage['calibratedAt'] = plan.after.calibratedAt;
|
|
996
|
+
usage['source'] = plan.after.source;
|
|
997
|
+
}
|
|
998
|
+
memory['usage'] = usage;
|
|
999
|
+
next['memory'] = memory;
|
|
1000
|
+
return next;
|
|
1001
|
+
}
|
|
1002
|
+
function parseUsageModelArgs(modelArgs) {
|
|
1003
|
+
const modelPct = {};
|
|
1004
|
+
const skipped = [];
|
|
1005
|
+
for (const raw of modelArgs) {
|
|
1006
|
+
const eq = raw.indexOf('=');
|
|
1007
|
+
if (eq <= 0 || eq === raw.length - 1) {
|
|
1008
|
+
skipped.push(`model ${raw}: skipped malformed model=pct argument`);
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
const modelName = raw.slice(0, eq).trim();
|
|
1012
|
+
const model = normalizeClaudeUsageModelKey(modelName);
|
|
1013
|
+
if (!model) {
|
|
1014
|
+
skipped.push(`model ${modelName}: skipped unknown model`);
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
modelPct[model] = raw.slice(eq + 1).trim();
|
|
1018
|
+
}
|
|
1019
|
+
return { modelPct, skipped };
|
|
1020
|
+
}
|
|
1021
|
+
function writeUsageCalibrationSummary(opts) {
|
|
1022
|
+
opts.write('usage calibrate: estimated local transcript counts; claude.ai/settings/usage is authoritative');
|
|
1023
|
+
opts.write(`usage calibrate: project ${opts.projectRoot}`);
|
|
1024
|
+
if (opts.configWarning)
|
|
1025
|
+
opts.write(`usage calibrate: ${opts.configWarning}`);
|
|
1026
|
+
for (const change of opts.plan.changes) {
|
|
1027
|
+
opts.write(`usage calibrate: ${change.key} tokens=${change.tokens} pct=${change.pct}% limit ${change.before ?? 'null'} -> ${change.after}`);
|
|
1028
|
+
}
|
|
1029
|
+
const skipped = [...opts.preSkipped, ...opts.plan.skipped];
|
|
1030
|
+
for (const item of skipped)
|
|
1031
|
+
opts.write(`usage calibrate: skipped ${item}`);
|
|
1032
|
+
if (opts.wrote) {
|
|
1033
|
+
opts.write('usage calibrate: wrote .dz/config.json with source claude.ai/settings/usage');
|
|
1034
|
+
}
|
|
1035
|
+
else {
|
|
1036
|
+
opts.write('usage calibrate: no config changes written');
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
function cmdUsageCalibrate(options, optionLists, cwd, write) {
|
|
1040
|
+
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
1041
|
+
const suppliedModels = optionLists.get('model') ?? [];
|
|
1042
|
+
const parsedModels = parseUsageModelArgs(suppliedModels);
|
|
1043
|
+
const modelPct = parsedModels.modelPct;
|
|
1044
|
+
const hasModelPct = Object.keys(modelPct).length > 0;
|
|
1045
|
+
const input = {
|
|
1046
|
+
...(options.has('session') ? { sessionPct: options.get('session') } : {}),
|
|
1047
|
+
...(options.has('weekly') ? { weeklyPct: options.get('weekly') } : {}),
|
|
1048
|
+
...(hasModelPct ? { modelPct } : {}),
|
|
1049
|
+
calibratedAt: new Date().toISOString(),
|
|
1050
|
+
source: 'claude.ai/settings/usage',
|
|
1051
|
+
};
|
|
1052
|
+
const missingInputs = [];
|
|
1053
|
+
if (!options.has('session') && !options.has('weekly') && !hasModelPct) {
|
|
1054
|
+
missingInputs.push('no calibration percentages supplied');
|
|
1055
|
+
}
|
|
1056
|
+
try {
|
|
1057
|
+
const current = computeUsage(projectRoot);
|
|
1058
|
+
const before = readUsageLimits(projectRoot);
|
|
1059
|
+
const plan = deriveUsageCalibration(current, before, input);
|
|
1060
|
+
if (plan.changes.length === 0) {
|
|
1061
|
+
writeUsageCalibrationSummary({
|
|
1062
|
+
projectRoot,
|
|
1063
|
+
plan,
|
|
1064
|
+
preSkipped: [...parsedModels.skipped, ...missingInputs],
|
|
1065
|
+
wrote: false,
|
|
1066
|
+
write,
|
|
1067
|
+
});
|
|
1068
|
+
return 0;
|
|
1069
|
+
}
|
|
1070
|
+
const existing = readProjectConfigForUsage(projectRoot);
|
|
1071
|
+
const nextConfig = applyUsageCalibrationToConfig(existing.config, plan);
|
|
1072
|
+
try {
|
|
1073
|
+
mkdirSync(join(projectRoot, '.dz'), { recursive: true });
|
|
1074
|
+
writeFileSync(usageConfigPath(projectRoot), JSON.stringify(nextConfig, null, 2) + '\n');
|
|
1075
|
+
writeUsageCalibrationSummary({
|
|
1076
|
+
projectRoot,
|
|
1077
|
+
plan,
|
|
1078
|
+
preSkipped: [...parsedModels.skipped, ...missingInputs],
|
|
1079
|
+
configWarning: existing.warning,
|
|
1080
|
+
wrote: true,
|
|
1081
|
+
write,
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
catch {
|
|
1085
|
+
writeUsageCalibrationSummary({
|
|
1086
|
+
projectRoot,
|
|
1087
|
+
plan,
|
|
1088
|
+
preSkipped: [...parsedModels.skipped, ...missingInputs, 'write failed'],
|
|
1089
|
+
configWarning: existing.warning,
|
|
1090
|
+
wrote: false,
|
|
1091
|
+
write,
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
return 0;
|
|
1095
|
+
}
|
|
1096
|
+
catch {
|
|
1097
|
+
write('usage calibrate: skipped internal error; no config changes written');
|
|
1098
|
+
return 0;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
950
1101
|
/**
|
|
951
|
-
* `dz usage` — print an ESTIMATE of Claude session
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
*
|
|
1102
|
+
* `dz usage` — print an ESTIMATE of Claude session + weekly usage from fixed reset windows,
|
|
1103
|
+
* aggregated READONLY from the local transcript store (see {@link computeUsage}). `--json` emits
|
|
1104
|
+
* the single-line contract the feature-adr usage-probe agent parses; `--calibrate` is the only
|
|
1105
|
+
* write path and records human-transcribed claude.ai percentages in `.dz/config.json`.
|
|
955
1106
|
*
|
|
956
1107
|
* **Exit code is 0 ALWAYS** — including on internal error the whole body is guarded and prints the
|
|
957
1108
|
* all-null JSON, so a probe can NEVER distinguish "usage unknown" from "command failed" via a
|
|
958
|
-
* non-zero exit
|
|
959
|
-
*
|
|
1109
|
+
* non-zero exit. `--project <dir>` scopes ONLY the `.dz/config.json` read/write; measurement is
|
|
1110
|
+
* account-wide (all projects).
|
|
960
1111
|
*/
|
|
961
|
-
function cmdUsage(options, flags, cwd, write) {
|
|
1112
|
+
function cmdUsage(options, optionLists, flags, cwd, write) {
|
|
962
1113
|
const projectRoot = resolve(cwd, options.get('project') ?? '.');
|
|
963
1114
|
const nullContract = () => JSON.stringify({
|
|
964
1115
|
sessionPct: null,
|
|
@@ -970,18 +1121,28 @@ function cmdUsage(options, flags, cwd, write) {
|
|
|
970
1121
|
estimated: true,
|
|
971
1122
|
});
|
|
972
1123
|
try {
|
|
1124
|
+
if (flags.has('calibrate'))
|
|
1125
|
+
return cmdUsageCalibrate(options, optionLists, cwd, write);
|
|
973
1126
|
const u = computeUsage(projectRoot);
|
|
974
1127
|
const lim = readUsageLimits(projectRoot);
|
|
1128
|
+
const modelLimits = lim.weeklyTokenLimitByModel;
|
|
1129
|
+
const hasModelLimits = modelLimits !== undefined && Object.keys(modelLimits).length > 0;
|
|
975
1130
|
if (flags.has('json')) {
|
|
976
|
-
|
|
1131
|
+
const limitsPayload = { session: lim.sessionTokenLimit ?? null, weekly: lim.weeklyTokenLimit ?? null };
|
|
1132
|
+
if (hasModelLimits)
|
|
1133
|
+
limitsPayload.weeklyByModel = { ...modelLimits };
|
|
1134
|
+
const payload = {
|
|
977
1135
|
sessionPct: u.sessionPct,
|
|
978
1136
|
weeklyPct: u.weeklyPct,
|
|
979
1137
|
sessionTokens: u.sessionTokens,
|
|
980
1138
|
weeklyTokens: u.weeklyTokens,
|
|
981
1139
|
resetsAt: { session: u.sessionResetsAt, weekly: u.weeklyResetsAt },
|
|
982
|
-
limits:
|
|
1140
|
+
limits: limitsPayload,
|
|
983
1141
|
estimated: true,
|
|
984
|
-
}
|
|
1142
|
+
};
|
|
1143
|
+
if (hasModelLimits && u.weeklyByModel !== undefined)
|
|
1144
|
+
payload.weeklyByModel = u.weeklyByModel;
|
|
1145
|
+
write(JSON.stringify(payload));
|
|
985
1146
|
return 0;
|
|
986
1147
|
}
|
|
987
1148
|
if (u.sessionPct === null && u.weeklyPct === null) {
|
|
@@ -1000,7 +1161,8 @@ function cmdUsage(options, flags, cwd, write) {
|
|
|
1000
1161
|
}
|
|
1001
1162
|
};
|
|
1002
1163
|
const s = u.sessionPct === null ? 'n/a' : '~' + u.sessionPct + '%';
|
|
1003
|
-
const
|
|
1164
|
+
const binding = hasModelLimits && u.weeklyBindingModel !== undefined ? ' ' + u.weeklyBindingModel + '-bound' : '';
|
|
1165
|
+
const w = u.weeklyPct === null ? 'n/a' : '~' + u.weeklyPct + '%' + binding;
|
|
1004
1166
|
write('usage: session ' + s + ' (resets ' + clock(u.sessionResetsAt) + ') · week ' + w + ' (resets ' + clock(u.weeklyResetsAt) + ') · estimated');
|
|
1005
1167
|
return 0;
|
|
1006
1168
|
}
|
|
@@ -3202,7 +3364,7 @@ export async function runCli(argv, io = {}) {
|
|
|
3202
3364
|
return '';
|
|
3203
3365
|
}
|
|
3204
3366
|
};
|
|
3205
|
-
const { command, options, flags } = parseArgs(argv);
|
|
3367
|
+
const { command, options, optionLists, flags } = parseArgs(argv);
|
|
3206
3368
|
if (command === '' || command === 'help' || flags.has('help')) {
|
|
3207
3369
|
write(USAGE);
|
|
3208
3370
|
return 0;
|
|
@@ -3221,7 +3383,7 @@ export async function runCli(argv, io = {}) {
|
|
|
3221
3383
|
case 'create-skill':
|
|
3222
3384
|
return cmdCreateSkill(options, flags, cwd, write);
|
|
3223
3385
|
case 'info':
|
|
3224
|
-
return cmdInfo(options, { command, options, flags }, cwd, write);
|
|
3386
|
+
return cmdInfo(options, { command, options, optionLists, flags }, cwd, write);
|
|
3225
3387
|
case 'scout':
|
|
3226
3388
|
return await cmdScout(options, flags, cwd, write);
|
|
3227
3389
|
case 'workflow':
|
|
@@ -3247,7 +3409,7 @@ export async function runCli(argv, io = {}) {
|
|
|
3247
3409
|
case 'statusline':
|
|
3248
3410
|
return cmdStatusline(options, flags, cwd, write, readStdin);
|
|
3249
3411
|
case 'usage':
|
|
3250
|
-
return cmdUsage(options, flags, cwd, write);
|
|
3412
|
+
return cmdUsage(options, optionLists, flags, cwd, write);
|
|
3251
3413
|
case 'setup':
|
|
3252
3414
|
return await cmdSetup(options, flags, cwd, write);
|
|
3253
3415
|
case 'pretrain':
|