@weavecode/weave 0.1.1 → 0.1.2
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/node_modules/@weavecode/cli/dist/cli.js +4 -4
- package/node_modules/@weavecode/cli/dist/commands/chpass.js +1 -1
- package/node_modules/@weavecode/cli/dist/commands/daemon-auth.js +1 -1
- package/node_modules/@weavecode/cli/dist/commands/daemon.d.ts +7 -0
- package/node_modules/@weavecode/cli/dist/commands/daemon.js +2 -2
- package/node_modules/@weavecode/cli/dist/commands/dashboard.js +2 -2
- package/node_modules/@weavecode/cli/dist/commands/doctor.js +1 -1
- package/node_modules/@weavecode/cli/dist/commands/init.js +1 -1
- package/node_modules/@weavecode/cli/dist/commands/scan.js +1 -1
- package/node_modules/@weavecode/cli/dist/commands/status.js +2 -2
- package/node_modules/@weavecode/cli/dist/commands/uninstall.js +1 -1
- package/node_modules/@weavecode/cli/dist/i18n/dict/en.js +1 -9
- package/node_modules/@weavecode/cli/dist/i18n/dict/zh.d.ts +0 -1
- package/node_modules/@weavecode/cli/dist/i18n/dict/zh.js +2 -10
- package/node_modules/@weavecode/cli/dist/i18n/index.js +1 -1
- package/node_modules/@weavecode/cli/dist/i18n/t.js +1 -1
- package/node_modules/@weavecode/cli/dist/init/claudemd-gen.d.ts +2 -3
- package/node_modules/@weavecode/cli/dist/init/claudemd-gen.js +4 -4
- package/node_modules/@weavecode/cli/dist/init/executor.js +3 -3
- package/node_modules/@weavecode/cli/dist/init/git-commit.js +2 -2
- package/node_modules/@weavecode/cli/dist/init/harnesses/claude.js +7 -7
- package/node_modules/@weavecode/cli/dist/init/harnesses/codex.js +1 -1
- package/node_modules/@weavecode/cli/dist/init/harnesses/zcode.d.ts +29 -0
- package/node_modules/@weavecode/cli/dist/init/harnesses/zcode.js +1 -0
- package/node_modules/@weavecode/cli/dist/init/instruction-link.js +1 -1
- package/node_modules/@weavecode/cli/dist/init/plugin-templates.js +1 -1
- package/node_modules/@weavecode/cli/dist/init/prompt.js +4 -4
- package/node_modules/@weavecode/cli/dist/init/uninstall.js +6 -6
- package/node_modules/@weavecode/cli/dist/init/wiring.js +1 -1
- package/node_modules/@weavecode/cli/dist/parser.js +1 -1
- package/node_modules/@weavecode/cli/dist/statusline/ansi.js +1 -1
- package/node_modules/@weavecode/cli/dist/statusline/mock-input.js +1 -1
- package/node_modules/@weavecode/cli/dist/statusline/preview.js +2 -2
- package/node_modules/@weavecode/cli/package.json +5 -5
- package/node_modules/@weavecode/core/dist/harness-checks.js +1 -1
- package/node_modules/@weavecode/core/dist/index.d.ts +1 -1
- package/node_modules/@weavecode/core/dist/index.js +1 -1
- package/node_modules/@weavecode/core/dist/platform.js +1 -1
- package/node_modules/@weavecode/core/dist/types.d.ts +0 -8
- package/node_modules/@weavecode/core/dist/types.js +1 -1
- package/node_modules/@weavecode/core/dist/utils/fs.js +1 -1
- package/node_modules/@weavecode/core/dist/utils/logger.js +1 -1
- package/node_modules/@weavecode/core/package.json +1 -1
- package/node_modules/@weavecode/harness/dist/claude/assets.d.ts +10 -0
- package/node_modules/@weavecode/harness/dist/claude/assets.js +4 -1
- package/node_modules/@weavecode/harness/dist/claude/capture.d.ts +4 -2
- package/node_modules/@weavecode/harness/dist/claude/capture.js +15 -9
- package/node_modules/@weavecode/harness/dist/claude/detect.js +1 -1
- package/node_modules/@weavecode/harness/dist/claude/event-source.js +1 -1
- package/node_modules/@weavecode/harness/dist/claude/index.d.ts +6 -1
- package/node_modules/@weavecode/harness/dist/claude/index.js +1 -1
- package/node_modules/@weavecode/harness/dist/claude/mcp-gen.js +1 -1
- package/node_modules/@weavecode/harness/dist/claude/messaging.js +2 -2
- package/node_modules/@weavecode/harness/dist/claude/model-aliases.d.ts +14 -0
- package/node_modules/@weavecode/harness/dist/claude/model-aliases.js +1 -1
- package/node_modules/@weavecode/harness/dist/claude/peers.js +1 -1
- package/node_modules/@weavecode/harness/dist/claude/plan.d.ts +2 -0
- package/node_modules/@weavecode/harness/dist/claude/plan.js +1 -0
- package/node_modules/@weavecode/harness/dist/claude/plugins.js +4 -4
- package/node_modules/@weavecode/harness/dist/claude/prompt-inbox.js +1 -1
- package/node_modules/@weavecode/harness/dist/claude/prompt.js +3 -3
- package/node_modules/@weavecode/harness/dist/claude/questions.js +2 -2
- package/node_modules/@weavecode/harness/dist/claude/settings-gen.js +1 -1
- package/node_modules/@weavecode/harness/dist/claude/side-question.d.ts +12 -0
- package/node_modules/@weavecode/harness/dist/claude/side-question.js +1 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/config.d.ts +200 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/config.js +1 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/generator.d.ts +12 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/generator.js +745 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/git-cache.d.ts +10 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/git-cache.js +1 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/surface.d.ts +20 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/surface.js +2 -0
- package/node_modules/@weavecode/harness/dist/claude/transcript.js +3 -3
- package/node_modules/@weavecode/harness/dist/codex/assets.js +1 -1
- package/node_modules/@weavecode/harness/dist/codex/detect.js +1 -1
- package/node_modules/@weavecode/harness/dist/codex/index.d.ts +1 -0
- package/node_modules/@weavecode/harness/dist/codex/index.js +1 -1
- package/node_modules/@weavecode/harness/dist/codex/mcp.d.ts +30 -0
- package/node_modules/@weavecode/harness/dist/codex/mcp.js +1 -0
- package/node_modules/@weavecode/harness/dist/codex/messaging.js +1 -1
- package/node_modules/@weavecode/harness/dist/codex/plugin-state.js +1 -1
- package/node_modules/@weavecode/harness/dist/codex/toml.js +9 -9
- package/node_modules/@weavecode/harness/dist/driver.d.ts +110 -2
- package/node_modules/@weavecode/harness/dist/driver.js +1 -1
- package/node_modules/@weavecode/harness/dist/init/generated.js +2 -2
- package/node_modules/@weavecode/harness/dist/init/pipeline.js +1 -1
- package/node_modules/@weavecode/harness/dist/registry-wiring.js +1 -1
- package/node_modules/@weavecode/harness/dist/shared/install-link.js +1 -1
- package/node_modules/@weavecode/harness/dist/zcode/assets.d.ts +8 -0
- package/node_modules/@weavecode/harness/dist/zcode/assets.js +1 -0
- package/node_modules/@weavecode/harness/dist/zcode/capture.d.ts +36 -0
- package/node_modules/@weavecode/harness/dist/zcode/capture.js +3 -0
- package/node_modules/@weavecode/harness/dist/zcode/detect.d.ts +2 -0
- package/node_modules/@weavecode/harness/dist/zcode/detect.js +1 -0
- package/node_modules/@weavecode/harness/dist/zcode/event-source.d.ts +8 -0
- package/node_modules/@weavecode/harness/dist/zcode/event-source.js +1 -0
- package/node_modules/@weavecode/harness/dist/zcode/index.d.ts +13 -0
- package/node_modules/@weavecode/harness/dist/zcode/index.js +1 -0
- package/node_modules/@weavecode/harness/dist/zcode/mcp.d.ts +6 -0
- package/node_modules/@weavecode/harness/dist/zcode/mcp.js +2 -0
- package/node_modules/@weavecode/harness/dist/zcode/messaging.d.ts +13 -0
- package/node_modules/@weavecode/harness/dist/zcode/messaging.js +1 -0
- package/node_modules/@weavecode/harness/dist/zcode/resume.d.ts +7 -0
- package/node_modules/@weavecode/harness/dist/zcode/resume.js +1 -0
- package/node_modules/@weavecode/harness/package.json +2 -2
- package/node_modules/@weavecode/server/dist/daemon/dev-entry.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/hook-pipeline.d.ts +8 -1
- package/node_modules/@weavecode/server/dist/daemon/hook-pipeline.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/hooks-routes.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/server.d.ts +7 -0
- package/node_modules/@weavecode/server/dist/daemon/server.js +8 -6
- package/node_modules/@weavecode/server/dist/daemon/session-probe.d.ts +13 -0
- package/node_modules/@weavecode/server/dist/daemon/session-probe.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/snapshot-refresher.d.ts +7 -1
- package/node_modules/@weavecode/server/dist/daemon/snapshot-refresher.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/sse-hub.d.ts +40 -0
- package/node_modules/@weavecode/server/dist/daemon/sse-hub.js +1 -0
- package/node_modules/@weavecode/server/dist/daemon/start.js +1 -1
- package/node_modules/@weavecode/server/dist/db/db.js +20 -2
- package/node_modules/@weavecode/server/dist/hooks/bus.d.ts +5 -0
- package/node_modules/@weavecode/server/dist/hooks/inbox-worker.d.ts +8 -1
- package/node_modules/@weavecode/server/dist/hooks/inbox-worker.js +3 -3
- package/node_modules/@weavecode/server/dist/hooks/message-deliverer.d.ts +29 -0
- package/node_modules/@weavecode/server/dist/hooks/message-deliverer.js +2 -2
- package/node_modules/@weavecode/server/dist/hooks/mirror.d.ts +9 -4
- package/node_modules/@weavecode/server/dist/hooks/mirror.js +4 -4
- package/node_modules/@weavecode/server/dist/hooks/session-purge.d.ts +3 -0
- package/node_modules/@weavecode/server/dist/hooks/session-purge.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/side-questions.d.ts +41 -0
- package/node_modules/@weavecode/server/dist/hooks/side-questions.js +1 -0
- package/node_modules/@weavecode/server/dist/hooks/toolkit.js +5 -5
- package/node_modules/@weavecode/server/dist/install/apply-update.js +1 -1
- package/node_modules/@weavecode/server/dist/install/capabilities.d.ts +10 -0
- package/node_modules/@weavecode/server/dist/install/capabilities.js +1 -1
- package/node_modules/@weavecode/server/dist/install/codex-toml.d.ts +8 -26
- package/node_modules/@weavecode/server/dist/install/codex-toml.js +1 -1
- package/node_modules/@weavecode/server/dist/install/global-config.d.ts +2 -4
- package/node_modules/@weavecode/server/dist/install/global-config.js +1 -2
- package/node_modules/@weavecode/server/dist/install/installer.d.ts +3 -5
- package/node_modules/@weavecode/server/dist/install/installer.js +1 -3
- package/node_modules/@weavecode/server/dist/install/mcp-rows.d.ts +5 -5
- package/node_modules/@weavecode/server/dist/install/mcp-rows.js +1 -1
- package/node_modules/@weavecode/server/dist/registry.js +2 -2
- package/node_modules/@weavecode/server/dist/repositories/hook-events.d.ts +7 -0
- package/node_modules/@weavecode/server/dist/repositories/hook-events.js +2 -2
- package/node_modules/@weavecode/server/dist/repositories/sessions.d.ts +15 -4
- package/node_modules/@weavecode/server/dist/repositories/sessions.js +2 -2
- package/node_modules/@weavecode/server/dist/repositories/side-questions.d.ts +50 -0
- package/node_modules/@weavecode/server/dist/repositories/side-questions.js +6 -0
- package/node_modules/@weavecode/server/dist/statusline/config.d.ts +4 -198
- package/node_modules/@weavecode/server/dist/statusline/config.js +1 -1
- package/node_modules/@weavecode/server/dist/statusline/generator.d.ts +5 -0
- package/node_modules/@weavecode/server/dist/statusline/generator.js +1 -717
- package/node_modules/@weavecode/server/dist/statusline/manager.d.ts +5 -5
- package/node_modules/@weavecode/server/dist/statusline/manager.js +3 -4
- package/node_modules/@weavecode/server/dist/surfaces.d.ts +11 -1
- package/node_modules/@weavecode/server/dist/surfaces.js +1 -1
- package/node_modules/@weavecode/server/package.json +4 -4
- package/node_modules/@weavecode/server/web/dist/assets/index-CHWbLAje.js +594 -0
- package/node_modules/@weavecode/server/web/dist/assets/index-D0s5v8QS.css +11 -0
- package/node_modules/@weavecode/server/web/dist/index.html +2 -2
- package/node_modules/@weavecode/templates/package.json +2 -2
- package/package.json +6 -6
- package/node_modules/@weavecode/server/web/dist/assets/index-D5WFh77i.css +0 -11
- package/node_modules/@weavecode/server/web/dist/assets/index-DJibaGnJ.js +0 -585
|
@@ -0,0 +1,745 @@
|
|
|
1
|
+
import{CONTEXT_STYLE_FORMAT as c,POWERLINE_DEFAULT_GLYPHS as e,SEGMENT_DEFAULT_FORMAT as d,SEGMENT_ORDER as p}from"./config.js";const h={gray:"90",red:"31",green:"32",yellow:"33",blue:"34",magenta:"35",cyan:"36"},f={gray:"100",red:"41",green:"42",yellow:"43",blue:"44",magenta:"45",cyan:"46"},g="\u2588",m="\u2591",v=10,E=8*1024*1024,r=5e3,y=3600,F=60,b=60,w=60,C=5;export function generateStatuslineScript(n,t){const u=JSON.stringify(n),i=JSON.stringify(t.cacheDir),s=JSON.stringify(h),a=JSON.stringify(f),o=JSON.stringify(d),l=JSON.stringify(c);return`#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* weave statusline \u2014 generated by the weave dashboard.
|
|
4
|
+
* Reads Claude Code's statusline JSON from stdin and prints a summary.
|
|
5
|
+
* Generated by weave \u2014 do not edit manually.
|
|
6
|
+
* @version weave@0.1.0
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const CONFIG = ${u};
|
|
11
|
+
const ANSI = ${s};
|
|
12
|
+
const BG = ${a};
|
|
13
|
+
const SEGMENT_FORMAT = ${o};
|
|
14
|
+
const CONTEXT_FORMAT = ${l};
|
|
15
|
+
const BAR_FILL = '${g}';
|
|
16
|
+
const BAR_EMPTY = '${m}';
|
|
17
|
+
const BAR_CELLS = ${v};
|
|
18
|
+
const GIT_IDENTITY_TTL = ${y};
|
|
19
|
+
const GIT_CHANGES_TTL = ${F};
|
|
20
|
+
const GIT_BRANCH_TTL = ${b};
|
|
21
|
+
const GIT_SYNC_TTL = ${w};
|
|
22
|
+
const GIT_RETRY_TTL = ${C};
|
|
23
|
+
const CACHE_DIR = ${i};
|
|
24
|
+
// Stamped into the row-0 logo so the running version is always visible.
|
|
25
|
+
const WEAVE_VERSION = ${JSON.stringify(t.version)};
|
|
26
|
+
// Powerline glyphs (Nerd Font), configurable via CONFIG.powerline: undefined
|
|
27
|
+
// falls back to the classic triangle set; an empty cap string disables the cap.
|
|
28
|
+
const PL = CONFIG.powerline || {};
|
|
29
|
+
const GLYPH_JOIN = PL.separator === undefined ? ${JSON.stringify(e.separator)} : PL.separator;
|
|
30
|
+
const GLYPH_START = PL.startCap === undefined ? ${JSON.stringify(e.startCap)} : PL.startCap;
|
|
31
|
+
const GLYPH_END = PL.endCap === undefined ? ${JSON.stringify(e.endCap)} : PL.endCap;
|
|
32
|
+
|
|
33
|
+
// --- colour encoding -------------------------------------------------------
|
|
34
|
+
// A colour is a named ANSI colour ('red'), an indexed value ('ansi256:196') or
|
|
35
|
+
// truecolor hex ('#rrggbb' / '#rgb'). Anything unrecognised degrades to white
|
|
36
|
+
// (fg) or no background rather than emitting a broken escape sequence.
|
|
37
|
+
|
|
38
|
+
function hexToRgb(color) {
|
|
39
|
+
var six = /^#([0-9a-fA-F]{6})$/.exec(color);
|
|
40
|
+
if (six) {
|
|
41
|
+
var s = six[1];
|
|
42
|
+
return [parseInt(s.slice(0, 2), 16), parseInt(s.slice(2, 4), 16), parseInt(s.slice(4, 6), 16)];
|
|
43
|
+
}
|
|
44
|
+
var three = /^#([0-9a-fA-F]{3})$/.exec(color);
|
|
45
|
+
if (three) {
|
|
46
|
+
var t = three[1];
|
|
47
|
+
return [parseInt(t[0] + t[0], 16), parseInt(t[1] + t[1], 16), parseInt(t[2] + t[2], 16)];
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function indexed(color) {
|
|
53
|
+
var m = /^ansi256:(\\d{1,3})$/.exec(color);
|
|
54
|
+
if (!m) return null;
|
|
55
|
+
var n = Number(m[1]);
|
|
56
|
+
return n >= 0 && n <= 255 ? n : null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function fgCode(color) {
|
|
60
|
+
if (!color) return '39';
|
|
61
|
+
if (ANSI[color]) return ANSI[color];
|
|
62
|
+
var n = indexed(color);
|
|
63
|
+
if (n !== null) return '38;5;' + n;
|
|
64
|
+
var rgb = hexToRgb(color);
|
|
65
|
+
if (rgb) return '38;2;' + rgb[0] + ';' + rgb[1] + ';' + rgb[2];
|
|
66
|
+
return '37';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function bgCode(color) {
|
|
70
|
+
if (!color) return null;
|
|
71
|
+
if (BG[color]) return BG[color];
|
|
72
|
+
var n = indexed(color);
|
|
73
|
+
if (n !== null) return '48;5;' + n;
|
|
74
|
+
var rgb = hexToRgb(color);
|
|
75
|
+
if (rgb) return '48;2;' + rgb[0] + ';' + rgb[1] + ';' + rgb[2];
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function paint(text, color, bold, bg) {
|
|
80
|
+
var fg = fgCode(color);
|
|
81
|
+
var prefix = bold ? '1;' : '';
|
|
82
|
+
var b = bgCode(bg);
|
|
83
|
+
return '\\x1b[' + prefix + fg + (b ? ';' + b : '') + 'm' + text + '\\x1b[0m';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function dim(text) {
|
|
87
|
+
return '\\x1b[2;90m' + text + '\\x1b[0m';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// --- render cache -----------------------------------------------------------
|
|
91
|
+
// Every render is a fresh process, so the file \u2014 not the process \u2014 is what
|
|
92
|
+
// carries the transcript scan across renders. One file per session under
|
|
93
|
+
// CACHE_DIR; the git segments do NOT live here (see GIT_FILE below) because
|
|
94
|
+
// they describe the worktree rather than the session. The daemon's sweeper
|
|
95
|
+
// drops expired entries and files of sessions that stopped rendering.
|
|
96
|
+
//
|
|
97
|
+
// Top level on purpose: flush() below writes it after main() returns, so a
|
|
98
|
+
// render that throws half-way still keeps what it had already scanned.
|
|
99
|
+
var CACHE_FILE = null;
|
|
100
|
+
var CACHE = null;
|
|
101
|
+
// \u53EA\u5728\u771F\u6709\u6761\u76EE\u843D\u5B9A\u65F6\u624D\u56DE\u5199\uFF1A\u7EAF\u547D\u4E2D\u7684\u6E32\u67D3\u96F6\u5199\u76D8\uFF08\u65E7\u5B9E\u73B0\u540C\u6837\u53EA\u5728\u672A\u547D\u4E2D\u65F6\u5199\uFF09\u3002
|
|
102
|
+
var CACHE_DIRTY = false;
|
|
103
|
+
|
|
104
|
+
// Git \u6BB5\u63CF\u8FF0\u7684\u662F\u5DE5\u4F5C\u533A\uFF0C\u4E0D\u662F\u4F1A\u8BDD\uFF1A\u540C\u4E00\u9879\u76EE\u7684\u6BCF\u4E2A\u4F1A\u8BDD\u90FD\u5728\u95EE\u540C\u4E00\u4E2A\u95EE\u9898\u3002\u6240\u4EE5
|
|
105
|
+
// \u5B83\u53E6\u5B58\u4E00\u4EFD\u6309\u76EE\u5F55\u5206\u7247\u7684\u6587\u4EF6\uFF08git-<cwd \u6458\u8981>.json\uFF09\uFF0C\u5404\u4F1A\u8BDD\u5171\u4EAB\u2014\u2014\u4E00\u4E2A TTL \u5185
|
|
106
|
+
// \u6574\u4E2A\u9879\u76EE\u53EA\u4ED8\u4E00\u6B21 git \u5B50\u8FDB\u7A0B\uFF0C\u6709\u591A\u5C11\u4F1A\u8BDD\u5728\u6E32\u67D3\u90FD\u4E00\u6837\u3002\u6761\u76EE\u952E\u56E0\u6B64\u53EA\u662F
|
|
107
|
+
// branch / git_user / changes \u4E09\u6837\uFF0C\u4E0D\u5E26\u76EE\u5F55\uFF08\u76EE\u5F55\u5DF2\u7ECF\u662F\u6587\u4EF6\u540D\u4E86\uFF09\u3002\u5E76\u53D1\u5199\u7528\u540C\u4E00\u5957
|
|
108
|
+
// tmp+rename\uFF0C\u540E\u5199\u7684\u6574\u4EFD\u8986\u76D6\u5148\u5199\u7684\uFF08\u4E22\u4E00\u6761\u8FC7\u671F\u6761\u76EE\u53EA\u4F1A\u8BA9\u4E0B\u6B21\u91CD\u63A2\u4E00\u6B21\uFF09\u3002
|
|
109
|
+
var GIT_FILE = null;
|
|
110
|
+
var GIT_CACHE = null;
|
|
111
|
+
var GIT_DIRTY = false;
|
|
112
|
+
|
|
113
|
+
// Session key: Claude Code's session id, then the transcript basename, then a
|
|
114
|
+
// cwd digest for hosts that send neither.
|
|
115
|
+
function cacheKeyOf(input) {
|
|
116
|
+
var path = require('path');
|
|
117
|
+
var raw = (input.session_id != null && String(input.session_id))
|
|
118
|
+
|| (typeof input.transcript_path === 'string'
|
|
119
|
+
? path.basename(input.transcript_path, '.jsonl')
|
|
120
|
+
: '');
|
|
121
|
+
if (raw) return raw.replace(/[^A-Za-z0-9._-]+/g, '_').slice(0, 64) || 'session';
|
|
122
|
+
var crypto = require('crypto');
|
|
123
|
+
var cwd = (input.workspace && input.workspace.current_dir) || process.cwd();
|
|
124
|
+
return crypto.createHash('sha1').update(cwd).digest('hex').slice(0, 16);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function initCache(input) {
|
|
128
|
+
var path = require('path');
|
|
129
|
+
var crypto = require('crypto');
|
|
130
|
+
CACHE_FILE = path.join(CACHE_DIR, cacheKeyOf(input) + '.json');
|
|
131
|
+
var cwd = (input.workspace && input.workspace.current_dir) || process.cwd();
|
|
132
|
+
// \u4E00\u4E2A\u76EE\u5F55\u4E00\u4E2A\u6587\u4EF6\uFF1A\u5927\u5C0F\u5199\u4E0D\u654F\u611F\uFF08Windows\uFF09\uFF0C\u5206\u9694\u7B26\u4E0E\u5C3E\u659C\u6760\u7684\u4E0D\u540C\u5199\u6CD5\u4E5F\u7B97\u540C\u4E00\u4E2A
|
|
133
|
+
var dirKey = crypto.createHash('sha1')
|
|
134
|
+
.update(String(cwd).toLowerCase().replace(/\\\\/g, '/').replace(/\\/+$/, ''))
|
|
135
|
+
.digest('hex').slice(0, 16);
|
|
136
|
+
GIT_FILE = path.join(CACHE_DIR, 'git-' + dirKey + '.json');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Lazily read once per render: a render that touches no cached segment must not
|
|
140
|
+
// create a file at all.
|
|
141
|
+
function cache() {
|
|
142
|
+
if (CACHE) return CACHE;
|
|
143
|
+
var fs = require('fs');
|
|
144
|
+
var loaded = null;
|
|
145
|
+
try { loaded = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')); } catch (e) {}
|
|
146
|
+
CACHE = loaded && typeof loaded === 'object' ? loaded : {};
|
|
147
|
+
CACHE.v = 1;
|
|
148
|
+
if (!CACHE.tokens || typeof CACHE.tokens !== 'object') CACHE.tokens = {};
|
|
149
|
+
return CACHE;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// \u5F62\u72B6\u4E0E sweeper \u7684\u5224\u636E\u4E00\u81F4\uFF08git \u6761\u76EE + tokens\uFF09\uFF0C\u5171\u4EAB\u6587\u4EF6\u56E0\u6B64\u4E0D\u5FC5\u53E6\u7ACB\u56DE\u6536\u89C4\u5219\uFF1A
|
|
153
|
+
// \u6761\u76EE\u5168\u90E8\u8FC7\u671F\u65F6\u5B83\u6309\u300C\u4E24\u680F\u90FD\u7A7A\u300D\u628A\u6587\u4EF6\u5220\u6389\u3002
|
|
154
|
+
function gitCache() {
|
|
155
|
+
if (GIT_CACHE) return GIT_CACHE;
|
|
156
|
+
var fs = require('fs');
|
|
157
|
+
var loaded = null;
|
|
158
|
+
try { loaded = JSON.parse(fs.readFileSync(GIT_FILE, 'utf8')); } catch (e) {}
|
|
159
|
+
GIT_CACHE = loaded && typeof loaded === 'object' ? loaded : {};
|
|
160
|
+
GIT_CACHE.v = 1;
|
|
161
|
+
if (!GIT_CACHE.git || typeof GIT_CACHE.git !== 'object') GIT_CACHE.git = {};
|
|
162
|
+
return GIT_CACHE;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function writeCacheFile(file, data) {
|
|
166
|
+
var fs = require('fs');
|
|
167
|
+
var path = require('path');
|
|
168
|
+
data.updatedAt = Date.now();
|
|
169
|
+
var tmp = file + '.' + process.pid + '.tmp';
|
|
170
|
+
var payload = JSON.stringify(data);
|
|
171
|
+
// atomic replace: renders overlap while Claude Code re-runs on a debounce
|
|
172
|
+
function put() { fs.writeFileSync(tmp, payload); fs.renameSync(tmp, file); }
|
|
173
|
+
try {
|
|
174
|
+
put();
|
|
175
|
+
} catch (e) {
|
|
176
|
+
// steady state the directory is already there \u2014 pay mkdir only when it is not
|
|
177
|
+
try { fs.mkdirSync(path.dirname(file), { recursive: true }); put(); } catch (e2) {}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function flushCache() {
|
|
182
|
+
if (!CACHE || !CACHE_FILE || !CACHE_DIRTY) return;
|
|
183
|
+
writeCacheFile(CACHE_FILE, CACHE);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function flushGitCache() {
|
|
187
|
+
if (!GIT_CACHE || !GIT_FILE || !GIT_DIRTY) return;
|
|
188
|
+
writeCacheFile(GIT_FILE, GIT_CACHE);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Git-derived segments cost a subprocess each, and Claude Code re-invokes us far
|
|
192
|
+
// more often than the value changes. A failed lookup \u2014 not a repo, git missing
|
|
193
|
+
// from PATH \u2014 is cached too, so a directory that never has git stops being
|
|
194
|
+
// re-probed on every render; but only for the short retry window, since holding
|
|
195
|
+
// a failure for an identity's full TTL would hide the segment for an hour.
|
|
196
|
+
function cachedGit(key, ttlSec, produce) {
|
|
197
|
+
var now = Date.now();
|
|
198
|
+
var store = gitCache();
|
|
199
|
+
var hit = store.git[key];
|
|
200
|
+
if (hit && hit.expires > now) return hit.value;
|
|
201
|
+
var value = produce();
|
|
202
|
+
var ttl = value == null ? Math.min(ttlSec, GIT_RETRY_TTL) : ttlSec;
|
|
203
|
+
store.git[key] = { expires: now + ttl * 1000, value: value };
|
|
204
|
+
GIT_DIRTY = true;
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function main(raw) {
|
|
209
|
+
var input = {};
|
|
210
|
+
try { input = JSON.parse(raw || ''); } catch (e) {}
|
|
211
|
+
|
|
212
|
+
var cwd = (input.workspace && input.workspace.current_dir) || process.cwd();
|
|
213
|
+
var path = require('path');
|
|
214
|
+
initCache(input);
|
|
215
|
+
|
|
216
|
+
// Normalise a used-percentage from Claude Code: value may be 0-100 or a 0-1 fraction.
|
|
217
|
+
function pctOf(value) {
|
|
218
|
+
if (value == null || isNaN(value)) return null;
|
|
219
|
+
var p = Number(value);
|
|
220
|
+
if (p <= 1 && p > 0) p = p * 100;
|
|
221
|
+
return Math.max(0, Math.min(100, Math.round(p)));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Compact a token count: 1000000 -> '1.0M', 12000 -> '12k', 960900 -> '960.9k'.
|
|
225
|
+
// M keeps one decimal (matching how context windows are quoted); k drops a
|
|
226
|
+
// trailing '.0' so whole thousands read as '12k' rather than '12.0k'.
|
|
227
|
+
function humanize(n) {
|
|
228
|
+
if (n == null || isNaN(n)) return '';
|
|
229
|
+
var v = Number(n);
|
|
230
|
+
if (v >= 1e6) return (v / 1e6).toFixed(1) + 'M';
|
|
231
|
+
if (v >= 1e3) return (v / 1e3).toFixed(1).replace(/\\.0$/, '') + 'k';
|
|
232
|
+
return String(Math.round(v));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Milliseconds to a compact duration: 45s / 12m / 2.5h / 3d. Empty when the
|
|
236
|
+
// value is missing so an absent token renders as nothing.
|
|
237
|
+
function humanizeMs(ms) {
|
|
238
|
+
if (ms == null || isNaN(ms) || Number(ms) <= 0) return '';
|
|
239
|
+
var sec = Number(ms) / 1000;
|
|
240
|
+
if (sec < 60) return Math.round(sec) + 's';
|
|
241
|
+
if (sec < 3600) return (sec / 60).toFixed(1).replace(/\\.0$/, '') + 'm';
|
|
242
|
+
if (sec < 86400) return (sec / 3600).toFixed(1).replace(/\\.0$/, '') + 'h';
|
|
243
|
+
return (sec / 86400).toFixed(1).replace(/\\.0$/, '') + 'd';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Render a progress bar of CONFIG.bar.cells cells from a 0-100 percentage.
|
|
247
|
+
function barFor(pct, bar) {
|
|
248
|
+
var cells = Math.max(1, Math.min(40, (bar && bar.cells) || BAR_CELLS));
|
|
249
|
+
var fill = (bar && bar.fill) || BAR_FILL;
|
|
250
|
+
var empty = (bar && bar.empty) || BAR_EMPTY;
|
|
251
|
+
var filled = Math.round((pct / 100) * cells);
|
|
252
|
+
// A non-zero percentage must light at least one cell: with 12 cells even 4%
|
|
253
|
+
// rounds down to 0 and the bar reads as completely empty next to "(4%)".
|
|
254
|
+
if (pct > 0 && filled === 0) filled = 1;
|
|
255
|
+
return new Array(filled + 1).join(fill) + new Array(cells - filled + 1).join(empty);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Substitute {token} placeholders; an absent token renders as nothing rather
|
|
259
|
+
// than the string 'undefined'.
|
|
260
|
+
function applyFormat(tokens, format) {
|
|
261
|
+
return String(format).replace(/\\{(\\w+)\\}/g, function (_m, key) {
|
|
262
|
+
return tokens[key] === undefined || tokens[key] === null ? '' : String(tokens[key]);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// --- data sources ----------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
// Added/deleted line counts against HEAD. execFile (not exec) so no shell is
|
|
269
|
+
// involved \u2014 the old implementation shelled out to a cmd.exe-only 'find /c'
|
|
270
|
+
// that always failed on POSIX.
|
|
271
|
+
function parseNumstat(out) {
|
|
272
|
+
var added = 0, deleted = 0, files = 0;
|
|
273
|
+
var rows = out.split('\\n');
|
|
274
|
+
for (var i = 0; i < rows.length; i++) {
|
|
275
|
+
if (!rows[i]) continue;
|
|
276
|
+
var p = rows[i].split('\\t');
|
|
277
|
+
if (p.length < 3) continue;
|
|
278
|
+
files++;
|
|
279
|
+
// binary files report '-' instead of a line count
|
|
280
|
+
if (p[0] !== '-') added += parseInt(p[0], 10) || 0;
|
|
281
|
+
if (p[1] !== '-') deleted += parseInt(p[1], 10) || 0;
|
|
282
|
+
}
|
|
283
|
+
return { added: added, deleted: deleted, files: files };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function gitNumstat(dir) {
|
|
287
|
+
var cp = require('child_process');
|
|
288
|
+
var attempts = [
|
|
289
|
+
['--no-optional-locks', 'diff', 'HEAD', '--numstat'],
|
|
290
|
+
['--no-optional-locks', 'diff', '--numstat'],
|
|
291
|
+
];
|
|
292
|
+
for (var i = 0; i < attempts.length; i++) {
|
|
293
|
+
try {
|
|
294
|
+
return parseNumstat(cp.execFileSync('git', attempts[i], {
|
|
295
|
+
cwd: dir, stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, windowsHide: true,
|
|
296
|
+
}).toString());
|
|
297
|
+
} catch (e) { /* unborn HEAD, or not a repo \u2014 try the next form */ }
|
|
298
|
+
}
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Commits the branch is ahead of / behind its upstream. A branch without an
|
|
303
|
+
// upstream (or a plain directory) just has no sync hint \u2014 not an error, so it
|
|
304
|
+
// caches as null and the token renders empty.
|
|
305
|
+
function gitAheadBehind(dir) {
|
|
306
|
+
try {
|
|
307
|
+
var out = require('child_process').execFileSync(
|
|
308
|
+
'git', ['rev-list', '--left-right', '--count', 'HEAD...@{upstream}'],
|
|
309
|
+
{ cwd: dir, stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, windowsHide: true }
|
|
310
|
+
).toString().trim();
|
|
311
|
+
var p = out.split(/\\s+/);
|
|
312
|
+
var ahead = parseInt(p[0], 10) || 0;
|
|
313
|
+
var behind = parseInt(p[1], 10) || 0;
|
|
314
|
+
if (!ahead && !behind) return null;
|
|
315
|
+
// behind first: the down arrow is "to pull", the up arrow is "to push"
|
|
316
|
+
var parts = [];
|
|
317
|
+
if (behind) parts.push(behind + '\u2193');
|
|
318
|
+
if (ahead) parts.push(ahead + '\u2191');
|
|
319
|
+
return { ahead: String(ahead), behind: String(behind), sync: '(' + parts.join('/') + ')' };
|
|
320
|
+
} catch (e) { return null; }
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// One git config value. 'git config <key>' resolves precedence itself (repo
|
|
324
|
+
// config overrides the global one), so this always yields the EFFECTIVE value.
|
|
325
|
+
function gitConfigValue(dir, key) {
|
|
326
|
+
try {
|
|
327
|
+
return require('child_process').execFileSync('git', ['config', key], {
|
|
328
|
+
cwd: dir, stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, windowsHide: true,
|
|
329
|
+
}).toString().trim();
|
|
330
|
+
} catch (e) { return ''; }
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Cumulative session tokens, read from the transcript Claude Code points us at.
|
|
334
|
+
//
|
|
335
|
+
// The scan is incremental: a tokens entry keyed by transcript path records how
|
|
336
|
+
// many bytes have been consumed and the running total, so steady-state renders
|
|
337
|
+
// read only the newly appended tail. The same assistant message is written once
|
|
338
|
+
// per content block (measured: 73 usage records for 26 distinct messages in one
|
|
339
|
+
// real session), so records are de-duplicated by message id \u2014 without that the
|
|
340
|
+
// total reads roughly three times too high.
|
|
341
|
+
function sessionTokens(transcriptPath) {
|
|
342
|
+
if (!transcriptPath) return null;
|
|
343
|
+
var fs = require('fs');
|
|
344
|
+
var stat;
|
|
345
|
+
try { stat = fs.statSync(transcriptPath); } catch (e) { return null; }
|
|
346
|
+
if (!stat.size) return null;
|
|
347
|
+
|
|
348
|
+
var store = cache();
|
|
349
|
+
var entry = store.tokens[transcriptPath];
|
|
350
|
+
if (!entry || typeof entry.offset !== 'number' || typeof entry.total !== 'number' || !Array.isArray(entry.ids)) {
|
|
351
|
+
entry = { offset: 0, total: 0, ids: [] };
|
|
352
|
+
}
|
|
353
|
+
// a shorter file means it was truncated or rewritten (/compact) \u2014 start over
|
|
354
|
+
if (stat.size < entry.offset) entry = { offset: 0, total: 0, ids: [] };
|
|
355
|
+
store.tokens[transcriptPath] = entry;
|
|
356
|
+
if (stat.size === entry.offset) return entry.total || null;
|
|
357
|
+
|
|
358
|
+
var len = Math.min(stat.size - entry.offset, ${E});
|
|
359
|
+
var buf = Buffer.alloc(len);
|
|
360
|
+
var read = 0;
|
|
361
|
+
var fd = -1;
|
|
362
|
+
try {
|
|
363
|
+
fd = fs.openSync(transcriptPath, 'r');
|
|
364
|
+
read = fs.readSync(fd, buf, 0, len, entry.offset);
|
|
365
|
+
} catch (e) {
|
|
366
|
+
return entry.total || null;
|
|
367
|
+
} finally {
|
|
368
|
+
if (fd >= 0) { try { fs.closeSync(fd); } catch (e) {} }
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
var chunk = buf.slice(0, read).toString('utf8');
|
|
372
|
+
var lastNewline = chunk.lastIndexOf('\\n');
|
|
373
|
+
// no complete line yet (or the tail is mid-write) \u2014 leave it for the next run
|
|
374
|
+
if (lastNewline < 0) return entry.total || null;
|
|
375
|
+
var consumed = lastNewline + 1;
|
|
376
|
+
|
|
377
|
+
var seen = {};
|
|
378
|
+
for (var s = 0; s < entry.ids.length; s++) seen[entry.ids[s]] = true;
|
|
379
|
+
var lines = chunk.slice(0, consumed).split('\\n');
|
|
380
|
+
for (var i = 0; i < lines.length; i++) {
|
|
381
|
+
if (!lines[i]) continue;
|
|
382
|
+
var rec;
|
|
383
|
+
try { rec = JSON.parse(lines[i]); } catch (e) { continue; }
|
|
384
|
+
var msg = rec && rec.message;
|
|
385
|
+
var usage = msg && msg.usage;
|
|
386
|
+
if (!usage) continue;
|
|
387
|
+
var id = msg.id || (rec && rec.uuid);
|
|
388
|
+
if (!id || seen[id]) continue;
|
|
389
|
+
seen[id] = true;
|
|
390
|
+
entry.ids.push(id);
|
|
391
|
+
entry.total += (usage.input_tokens || 0) + (usage.output_tokens || 0)
|
|
392
|
+
+ (usage.cache_read_input_tokens || 0) + (usage.cache_creation_input_tokens || 0);
|
|
393
|
+
}
|
|
394
|
+
entry.offset += consumed;
|
|
395
|
+
if (entry.ids.length > ${r}) {
|
|
396
|
+
entry.ids = entry.ids.slice(entry.ids.length - ${r});
|
|
397
|
+
}
|
|
398
|
+
CACHE_DIRTY = true;
|
|
399
|
+
return entry.total || null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Tokens in the context window: prefer the component breakdown, then the
|
|
403
|
+
// session's input/output totals, then derive from the percentage.
|
|
404
|
+
function contextUsed(cw, total, pct) {
|
|
405
|
+
var cu = cw.current_usage;
|
|
406
|
+
if (cu) {
|
|
407
|
+
var sum = (cu.input_tokens || 0) + (cu.output_tokens || 0)
|
|
408
|
+
+ (cu.cache_read_input_tokens || 0) + (cu.cache_creation_input_tokens || 0);
|
|
409
|
+
if (sum > 0) return sum;
|
|
410
|
+
}
|
|
411
|
+
if (cw.total_input_tokens != null) {
|
|
412
|
+
return cw.total_input_tokens + (cw.total_output_tokens || 0);
|
|
413
|
+
}
|
|
414
|
+
return Math.round((pct / 100) * (total || 0));
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Resolve a segment to its format tokens. null means "no data" \u2014 the segment is
|
|
418
|
+
// dropped from the line rather than rendering an empty shell.
|
|
419
|
+
function segmentTokens(key, seg) {
|
|
420
|
+
switch (key) {
|
|
421
|
+
case 'project': {
|
|
422
|
+
// {name} is the directory basename, {path} the full working directory \u2014
|
|
423
|
+
// the latter is what the reference layout shows.
|
|
424
|
+
var name = path.basename(cwd);
|
|
425
|
+
return cwd ? { name: name, path: cwd } : null;
|
|
426
|
+
}
|
|
427
|
+
case 'git': {
|
|
428
|
+
var branch = cachedGit('branch', GIT_BRANCH_TTL, function () {
|
|
429
|
+
try {
|
|
430
|
+
return require('child_process').execFileSync(
|
|
431
|
+
'git', ['rev-parse', '--abbrev-ref', 'HEAD'],
|
|
432
|
+
{ cwd: cwd, stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, windowsHide: true }
|
|
433
|
+
).toString().trim() || null;
|
|
434
|
+
} catch (e) { return null; }
|
|
435
|
+
});
|
|
436
|
+
if (!branch) return null;
|
|
437
|
+
// workspace.repo is optional (parsed from the git remote) \u2014 the token
|
|
438
|
+
// stays empty when absent so the default {branch} format is unaffected.
|
|
439
|
+
var repoInfo = input.workspace && input.workspace.repo;
|
|
440
|
+
var sync = cachedGit('sync', GIT_SYNC_TTL, function () { return gitAheadBehind(cwd); });
|
|
441
|
+
return {
|
|
442
|
+
branch: branch,
|
|
443
|
+
repo: repoInfo && repoInfo.owner ? repoInfo.owner + '/' + repoInfo.name : '',
|
|
444
|
+
ahead: sync ? sync.ahead : '',
|
|
445
|
+
behind: sync ? sync.behind : '',
|
|
446
|
+
sync: sync ? sync.sync : ''
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
case 'git_user':
|
|
450
|
+
return cachedGit('git_user', GIT_IDENTITY_TTL, function () {
|
|
451
|
+
// Both fields must be set \u2014 a partial identity would render 'name <>'
|
|
452
|
+
var gitUser = gitConfigValue(cwd, 'user.name');
|
|
453
|
+
var gitEmail = gitConfigValue(cwd, 'user.email');
|
|
454
|
+
return gitUser && gitEmail ? { user: gitUser, email: gitEmail } : null;
|
|
455
|
+
});
|
|
456
|
+
case 'changes':
|
|
457
|
+
return cachedGit('changes', GIT_CHANGES_TTL, function () {
|
|
458
|
+
var diff = gitNumstat(cwd);
|
|
459
|
+
return diff ? { added: diff.added, deleted: diff.deleted, files: diff.files } : null;
|
|
460
|
+
});
|
|
461
|
+
case 'model': {
|
|
462
|
+
var model = (input.model && input.model.display_name) || '';
|
|
463
|
+
return model ? { model: model } : null;
|
|
464
|
+
}
|
|
465
|
+
case 'thinking': {
|
|
466
|
+
var level = (input.effort && input.effort.level)
|
|
467
|
+
|| (input.thinking && input.thinking.enabled && 'thinking');
|
|
468
|
+
return level ? { level: level } : null;
|
|
469
|
+
}
|
|
470
|
+
case 'context': {
|
|
471
|
+
var cw = input.context_window || {};
|
|
472
|
+
var pct = pctOf(cw.used_percentage);
|
|
473
|
+
if (pct == null) return null;
|
|
474
|
+
var size = cw.context_window_size || 0;
|
|
475
|
+
var remaining = pctOf(cw.remaining_percentage);
|
|
476
|
+
return {
|
|
477
|
+
bar: barFor(pct, CONFIG.bar),
|
|
478
|
+
percent: pct,
|
|
479
|
+
used: humanize(contextUsed(cw, size, pct)),
|
|
480
|
+
total: humanize(size),
|
|
481
|
+
remaining: remaining == null ? '' : remaining,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
case 'tokens': {
|
|
485
|
+
var cw2 = input.context_window || {};
|
|
486
|
+
if (seg && seg.metric === 'context') {
|
|
487
|
+
var used = cw2.total_input_tokens != null
|
|
488
|
+
? cw2.total_input_tokens + (cw2.total_output_tokens || 0)
|
|
489
|
+
: (input.usage && input.usage.input_tokens != null ? input.usage.input_tokens : null);
|
|
490
|
+
if (used == null) return null;
|
|
491
|
+
var p = pctOf(cw2.used_percentage);
|
|
492
|
+
return { total: humanize(used), percent: p == null ? '' : p };
|
|
493
|
+
}
|
|
494
|
+
var session = sessionTokens(input.transcript_path);
|
|
495
|
+
return session == null ? null : { total: humanize(session), percent: '' };
|
|
496
|
+
}
|
|
497
|
+
case 'cost': {
|
|
498
|
+
var cost =
|
|
499
|
+
(input.cost && input.cost.total_cost_usd) ||
|
|
500
|
+
(input.session && input.session.cost_usd) ||
|
|
501
|
+
input.total_cost_usd;
|
|
502
|
+
if (cost == null) return null;
|
|
503
|
+
var costObj = input.cost || {};
|
|
504
|
+
return {
|
|
505
|
+
cost: '$' + Number(cost).toFixed(2),
|
|
506
|
+
duration: humanizeMs(costObj.total_duration_ms),
|
|
507
|
+
api_duration: humanizeMs(costObj.total_api_duration_ms),
|
|
508
|
+
lines_added: costObj.total_lines_added != null ? String(costObj.total_lines_added) : '',
|
|
509
|
+
lines_removed: costObj.total_lines_removed != null ? String(costObj.total_lines_removed) : ''
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
case 'rate': {
|
|
513
|
+
// Which window to report: five_hour (default), seven_day or spend.
|
|
514
|
+
var win = (seg && seg.window) || 'five_hour';
|
|
515
|
+
// the payload spells the third window 'spend_limit', not 'spend'
|
|
516
|
+
var rlKey = win === 'spend' ? 'spend_limit' : win;
|
|
517
|
+
var rl = input.rate_limits && input.rate_limits[rlKey];
|
|
518
|
+
var rp = rl && pctOf(rl.used_percentage);
|
|
519
|
+
if (rp == null) return null;
|
|
520
|
+
var limitLabel = win === 'seven_day' ? '7d' : win === 'spend' ? 'spend' : '5h';
|
|
521
|
+
return { percent: rp, limit: limitLabel, resets: rl.resets_at || '' };
|
|
522
|
+
}
|
|
523
|
+
case 'version':
|
|
524
|
+
return input.version ? { version: input.version } : null;
|
|
525
|
+
case 'output_style':
|
|
526
|
+
return input.output_style && input.output_style.name
|
|
527
|
+
? { style: input.output_style.name }
|
|
528
|
+
: null;
|
|
529
|
+
case 'session': {
|
|
530
|
+
// Named sessions show their name; anonymous ones fall back to the id
|
|
531
|
+
// prefix so the block always has something to render.
|
|
532
|
+
if (input.session_name) return { name: String(input.session_name).slice(0, 40) };
|
|
533
|
+
return input.session_id ? { name: String(input.session_id).slice(0, 8) } : null;
|
|
534
|
+
}
|
|
535
|
+
case 'exceeds200k':
|
|
536
|
+
return input.exceeds_200k_tokens ? { over: '200k' } : null;
|
|
537
|
+
case 'fast_mode':
|
|
538
|
+
return input.fast_mode ? { mode: 'fast' } : null;
|
|
539
|
+
case 'vim':
|
|
540
|
+
return input.vim && input.vim.mode ? { mode: input.vim.mode } : null;
|
|
541
|
+
case 'pr': {
|
|
542
|
+
var prInfo = input.pr;
|
|
543
|
+
return prInfo && prInfo.number != null
|
|
544
|
+
? {
|
|
545
|
+
number: prInfo.number,
|
|
546
|
+
state: prInfo.review_state || prInfo.kind || '',
|
|
547
|
+
url: prInfo.url || ''
|
|
548
|
+
}
|
|
549
|
+
: null;
|
|
550
|
+
}
|
|
551
|
+
case 'worktree': {
|
|
552
|
+
var wtInfo = input.worktree;
|
|
553
|
+
return wtInfo && (wtInfo.name || wtInfo.branch)
|
|
554
|
+
? { name: wtInfo.name || '', branch: wtInfo.branch || '' }
|
|
555
|
+
: null;
|
|
556
|
+
}
|
|
557
|
+
case 'agent':
|
|
558
|
+
return input.agent && input.agent.name ? { name: input.agent.name } : null;
|
|
559
|
+
case 'time':
|
|
560
|
+
return { time: new Date().toLocaleTimeString() };
|
|
561
|
+
default:
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// A segment's value template: an explicit format wins, then the legacy
|
|
567
|
+
// context.style field, then the built-in default that predates templates.
|
|
568
|
+
function formatFor(key, seg) {
|
|
569
|
+
if (seg.format) return seg.format;
|
|
570
|
+
if (key === 'context' && seg.style && CONTEXT_FORMAT[seg.style]) return CONTEXT_FORMAT[seg.style];
|
|
571
|
+
return SEGMENT_FORMAT[key] || '{value}';
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Collect the enabled, non-empty segments in the row's order as render items.
|
|
575
|
+
// An item with merge:true joins the next non-merge block (shares its
|
|
576
|
+
// background) \u2014 the same convention ccstatusline uses for icon-then-label pairs.
|
|
577
|
+
function renderParts(keys) {
|
|
578
|
+
var out = [];
|
|
579
|
+
for (var i = 0; i < keys.length; i++) {
|
|
580
|
+
var seg = CONFIG.segments[keys[i]];
|
|
581
|
+
if (!seg || !seg.enabled) continue;
|
|
582
|
+
var tokens = segmentTokens(keys[i], seg);
|
|
583
|
+
if (!tokens) continue;
|
|
584
|
+
// trim: an optional token left empty ('{branch} {sync}' with nothing to
|
|
585
|
+
// sync) must not leave a dangling separator around an otherwise good block
|
|
586
|
+
var value = applyFormat(tokens, formatFor(keys[i], seg)).trim();
|
|
587
|
+
if (value === '') continue;
|
|
588
|
+
var label = seg.label
|
|
589
|
+
? seg.label + (CONFIG.labelSeparator == null ? ': ' : CONFIG.labelSeparator)
|
|
590
|
+
: '';
|
|
591
|
+
out.push({
|
|
592
|
+
text: (seg.icon ? seg.icon + ' ' : '') + label + value,
|
|
593
|
+
fg: seg.color,
|
|
594
|
+
bold: seg.bold,
|
|
595
|
+
bg: seg.backgroundColor,
|
|
596
|
+
merge: !!seg.merge
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
return out;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function renderLine(items) {
|
|
603
|
+
var pending = [];
|
|
604
|
+
var ordered = [];
|
|
605
|
+
for (var i = 0; i < items.length; i++) {
|
|
606
|
+
if (items[i].merge) { pending.push(items[i]); continue; }
|
|
607
|
+
// the merge segments before this item join this block, sharing its bg \u2014
|
|
608
|
+
// they render flush against it (no separator of any kind)
|
|
609
|
+
for (var m = 0; m < pending.length; m++) {
|
|
610
|
+
pending[m].bg = items[i].bg;
|
|
611
|
+
pending[m].joinsNext = true;
|
|
612
|
+
}
|
|
613
|
+
ordered = ordered.concat(pending);
|
|
614
|
+
pending = [];
|
|
615
|
+
ordered.push(items[i]);
|
|
616
|
+
}
|
|
617
|
+
// trailing merge segments have no following block; drop their background
|
|
618
|
+
for (var j = 0; j < pending.length; j++) pending[j].bg = null;
|
|
619
|
+
ordered = ordered.concat(pending);
|
|
620
|
+
|
|
621
|
+
var isPower = !!(CONFIG.powerline && CONFIG.powerline.enabled);
|
|
622
|
+
|
|
623
|
+
// Caps frame the run of backgrounded blocks. An item without a background
|
|
624
|
+
// (the logo, or a segment whose bg was dropped) sits outside that run, so the
|
|
625
|
+
// caps attach to the first/last block rather than to the row's extremes.
|
|
626
|
+
var firstBg = -1;
|
|
627
|
+
var lastBg = -1;
|
|
628
|
+
for (var b = 0; b < ordered.length; b++) {
|
|
629
|
+
if (ordered[b].bg) {
|
|
630
|
+
if (firstBg < 0) firstBg = b;
|
|
631
|
+
lastBg = b;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
var parts = [];
|
|
636
|
+
var plainParts = [];
|
|
637
|
+
for (var k = 0; k < ordered.length; k++) {
|
|
638
|
+
var it = ordered[k];
|
|
639
|
+
// a merge segment joins the item after it, so no separator goes between
|
|
640
|
+
if (k > 0 && !ordered[k - 1].joinsNext) {
|
|
641
|
+
var prev = ordered[k - 1];
|
|
642
|
+
if (isPower && prev.bg && it.bg) {
|
|
643
|
+
// powerline bridge (ccstatusline): the join glyph's fg is the previous
|
|
644
|
+
// block's bg and its bg is the next block's, so the two blocks melt
|
|
645
|
+
// into one chevron; when both blocks share a bg the glyph keeps the
|
|
646
|
+
// previous block's fg so they read as a single block
|
|
647
|
+
var bridgeFg = prev.bg === it.bg ? prev.fg : prev.bg;
|
|
648
|
+
parts.push(paint(GLYPH_JOIN, bridgeFg, false, it.bg));
|
|
649
|
+
plainParts.push(GLYPH_JOIN);
|
|
650
|
+
} else if (isPower) {
|
|
651
|
+
parts.push(' '); // a block without bg gets a plain gap, never a '\u2502'
|
|
652
|
+
plainParts.push(' ');
|
|
653
|
+
} else {
|
|
654
|
+
parts.push(CONFIG.separator);
|
|
655
|
+
plainParts.push(CONFIG.separator);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
// an empty cap config ("" / disabled) skips that cap entirely
|
|
659
|
+
if (isPower && k === firstBg && GLYPH_START) {
|
|
660
|
+
parts.push(paint(GLYPH_START, it.bg, false, null));
|
|
661
|
+
plainParts.push(GLYPH_START);
|
|
662
|
+
}
|
|
663
|
+
if (isPower) {
|
|
664
|
+
parts.push(paint(' ' + it.text + ' ', it.fg, it.bold, it.bg));
|
|
665
|
+
plainParts.push(' ' + it.text + ' ');
|
|
666
|
+
} else {
|
|
667
|
+
parts.push(paint(it.text, it.fg, it.bold, null));
|
|
668
|
+
plainParts.push(it.text);
|
|
669
|
+
}
|
|
670
|
+
if (isPower && k === lastBg && GLYPH_END) {
|
|
671
|
+
parts.push(paint(GLYPH_END, it.bg, false, null));
|
|
672
|
+
plainParts.push(GLYPH_END);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return { line: parts.join(''), plain: plainParts.join('') };
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Pad the line so it sits left / centre / right of the terminal width. The
|
|
679
|
+
// script runs as a subprocess, so it falls back to a sane default width. A
|
|
680
|
+
// small safety margin keeps the statusline clear of the terminal's right
|
|
681
|
+
// edge (ccstatusline reserves 6 columns for the same reason).
|
|
682
|
+
function alignLine(rendered) {
|
|
683
|
+
if (CONFIG.align === 'left') return rendered.line;
|
|
684
|
+
var cols =
|
|
685
|
+
(input.terminal && (input.terminal.columns || input.terminal.width)) ||
|
|
686
|
+
(process.stdout && process.stdout.columns) ||
|
|
687
|
+
80;
|
|
688
|
+
var usable = Math.max(20, cols - 6);
|
|
689
|
+
var pad = 0;
|
|
690
|
+
if (CONFIG.align === 'right') pad = Math.max(0, usable - rendered.plain.length - 2);
|
|
691
|
+
else pad = Math.max(0, Math.floor((usable - rendered.plain.length) / 2));
|
|
692
|
+
if (pad <= 0) return rendered.line;
|
|
693
|
+
return new Array(pad + 1).join(' ') + rendered.line;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// The logo is always rendered \u2014 it identifies the statusline as weave's and
|
|
697
|
+
// carries the version that generated this script. Only its text/colour vary.
|
|
698
|
+
function logoItem() {
|
|
699
|
+
return {
|
|
700
|
+
text: CONFIG.logoText + ' v' + WEAVE_VERSION,
|
|
701
|
+
fg: CONFIG.logoColor,
|
|
702
|
+
bold: true,
|
|
703
|
+
bg: null,
|
|
704
|
+
merge: false
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// Ordered rows; each row is the left-to-right segment order. The logo prefixes
|
|
709
|
+
// row 0 only; rows are joined with a dim divider.
|
|
710
|
+
var rows = CONFIG.lines && CONFIG.lines.length ? CONFIG.lines : [${JSON.stringify(p)}];
|
|
711
|
+
var renderedLines = [];
|
|
712
|
+
for (var li = 0; li < rows.length; li++) {
|
|
713
|
+
var items = renderParts(rows[li]);
|
|
714
|
+
if (li === 0) items.unshift(logoItem());
|
|
715
|
+
// lead each content line with a reset so Claude Code's dim setting for the
|
|
716
|
+
// statusline area does not wash out our colours (ccstatusline does the same)
|
|
717
|
+
renderedLines.push('\\x1b[0m' + alignLine(renderLine(items)));
|
|
718
|
+
// the row divider is configurable and empty by default (the reference
|
|
719
|
+
// layout stacks its rows with no rule between them)
|
|
720
|
+
if (li < rows.length - 1 && CONFIG.divider) renderedLines.push(dim(CONFIG.divider));
|
|
721
|
+
}
|
|
722
|
+
process.stdout.write(renderedLines.join('\\n'));
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// stdin: \u5BBF\u4E3B\u5199\u5B8C\u5373\u5173\u95ED\u3002\u539F\u5148\u7528\u540C\u6B65 readFileSync(0) \u2014\u2014 \u5BBF\u4E3B\u4E0D\u5173\u95ED stdin \u65F6\u5B83\u4F1A\u628A
|
|
726
|
+
// \u4E8B\u4EF6\u5FAA\u73AF\u5835\u6B7B\uFF08\u8FDE\u5B88\u536B timer \u90FD\u6551\u4E0D\u4E86\uFF09\uFF0C\u8FDB\u7A0B\u6C38\u4E45\u6EDE\u7559\uFF08\u5B9E\u6D4B 3 \u5C0F\u65F6\u6512\u4E86 77 \u4E2A\u5B64\u513F\uFF09\u3002
|
|
727
|
+
// \u6539\u5F02\u6B65\u8BFB + \u515C\u5E95 timer\uFF0C\u4F7F\u4E0B\u9762\u7684\u5B88\u536B\u771F\u6B63\u751F\u6548\u3002
|
|
728
|
+
var raw = '';
|
|
729
|
+
var flushed = false;
|
|
730
|
+
function flush() {
|
|
731
|
+
if (flushed) return;
|
|
732
|
+
flushed = true;
|
|
733
|
+
try { main(raw); } catch (e) {}
|
|
734
|
+
// \u7F13\u5B58\u56DE\u5199\u5728\u6E32\u67D3\u4E4B\u540E\uFF1A\u4E3B\u6D41\u7A0B\u4E2D\u9014\u629B\u9519\u4E5F\u4FDD\u4F4F\u8FD9\u4E00\u8F6E\u626B\u5230\u7684\u589E\u91CF
|
|
735
|
+
try { flushCache(); } catch (e) {}
|
|
736
|
+
try { flushGitCache(); } catch (e) {}
|
|
737
|
+
process.exit(0);
|
|
738
|
+
}
|
|
739
|
+
process.stdin.setEncoding('utf8');
|
|
740
|
+
process.stdin.on('data', function (c) { raw += c; });
|
|
741
|
+
process.stdin.on('end', flush);
|
|
742
|
+
setTimeout(flush, 700).unref();
|
|
743
|
+
// \u786C\u5B88\u536B\uFF1A\u4EFB\u4F55\u8DEF\u5F84\u90FD\u4E0D\u8BB8\u8D56\u7740\u4E0D\u8D70\uFF08\u6B63\u5E38\u4E00\u6B21\u6E32\u67D3\u662F\u6BEB\u79D2\u7EA7\uFF09\u3002
|
|
744
|
+
setTimeout(function () { process.exit(0); }, 5000).unref();
|
|
745
|
+
`}
|