@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.
Files changed (166) hide show
  1. package/node_modules/@weavecode/cli/dist/cli.js +4 -4
  2. package/node_modules/@weavecode/cli/dist/commands/chpass.js +1 -1
  3. package/node_modules/@weavecode/cli/dist/commands/daemon-auth.js +1 -1
  4. package/node_modules/@weavecode/cli/dist/commands/daemon.d.ts +7 -0
  5. package/node_modules/@weavecode/cli/dist/commands/daemon.js +2 -2
  6. package/node_modules/@weavecode/cli/dist/commands/dashboard.js +2 -2
  7. package/node_modules/@weavecode/cli/dist/commands/doctor.js +1 -1
  8. package/node_modules/@weavecode/cli/dist/commands/init.js +1 -1
  9. package/node_modules/@weavecode/cli/dist/commands/scan.js +1 -1
  10. package/node_modules/@weavecode/cli/dist/commands/status.js +2 -2
  11. package/node_modules/@weavecode/cli/dist/commands/uninstall.js +1 -1
  12. package/node_modules/@weavecode/cli/dist/i18n/dict/en.js +1 -9
  13. package/node_modules/@weavecode/cli/dist/i18n/dict/zh.d.ts +0 -1
  14. package/node_modules/@weavecode/cli/dist/i18n/dict/zh.js +2 -10
  15. package/node_modules/@weavecode/cli/dist/i18n/index.js +1 -1
  16. package/node_modules/@weavecode/cli/dist/i18n/t.js +1 -1
  17. package/node_modules/@weavecode/cli/dist/init/claudemd-gen.d.ts +2 -3
  18. package/node_modules/@weavecode/cli/dist/init/claudemd-gen.js +4 -4
  19. package/node_modules/@weavecode/cli/dist/init/executor.js +3 -3
  20. package/node_modules/@weavecode/cli/dist/init/git-commit.js +2 -2
  21. package/node_modules/@weavecode/cli/dist/init/harnesses/claude.js +7 -7
  22. package/node_modules/@weavecode/cli/dist/init/harnesses/codex.js +1 -1
  23. package/node_modules/@weavecode/cli/dist/init/harnesses/zcode.d.ts +29 -0
  24. package/node_modules/@weavecode/cli/dist/init/harnesses/zcode.js +1 -0
  25. package/node_modules/@weavecode/cli/dist/init/instruction-link.js +1 -1
  26. package/node_modules/@weavecode/cli/dist/init/plugin-templates.js +1 -1
  27. package/node_modules/@weavecode/cli/dist/init/prompt.js +4 -4
  28. package/node_modules/@weavecode/cli/dist/init/uninstall.js +6 -6
  29. package/node_modules/@weavecode/cli/dist/init/wiring.js +1 -1
  30. package/node_modules/@weavecode/cli/dist/parser.js +1 -1
  31. package/node_modules/@weavecode/cli/dist/statusline/ansi.js +1 -1
  32. package/node_modules/@weavecode/cli/dist/statusline/mock-input.js +1 -1
  33. package/node_modules/@weavecode/cli/dist/statusline/preview.js +2 -2
  34. package/node_modules/@weavecode/cli/package.json +5 -5
  35. package/node_modules/@weavecode/core/dist/harness-checks.js +1 -1
  36. package/node_modules/@weavecode/core/dist/index.d.ts +1 -1
  37. package/node_modules/@weavecode/core/dist/index.js +1 -1
  38. package/node_modules/@weavecode/core/dist/platform.js +1 -1
  39. package/node_modules/@weavecode/core/dist/types.d.ts +0 -8
  40. package/node_modules/@weavecode/core/dist/types.js +1 -1
  41. package/node_modules/@weavecode/core/dist/utils/fs.js +1 -1
  42. package/node_modules/@weavecode/core/dist/utils/logger.js +1 -1
  43. package/node_modules/@weavecode/core/package.json +1 -1
  44. package/node_modules/@weavecode/harness/dist/claude/assets.d.ts +10 -0
  45. package/node_modules/@weavecode/harness/dist/claude/assets.js +4 -1
  46. package/node_modules/@weavecode/harness/dist/claude/capture.d.ts +4 -2
  47. package/node_modules/@weavecode/harness/dist/claude/capture.js +15 -9
  48. package/node_modules/@weavecode/harness/dist/claude/detect.js +1 -1
  49. package/node_modules/@weavecode/harness/dist/claude/event-source.js +1 -1
  50. package/node_modules/@weavecode/harness/dist/claude/index.d.ts +6 -1
  51. package/node_modules/@weavecode/harness/dist/claude/index.js +1 -1
  52. package/node_modules/@weavecode/harness/dist/claude/mcp-gen.js +1 -1
  53. package/node_modules/@weavecode/harness/dist/claude/messaging.js +2 -2
  54. package/node_modules/@weavecode/harness/dist/claude/model-aliases.d.ts +14 -0
  55. package/node_modules/@weavecode/harness/dist/claude/model-aliases.js +1 -1
  56. package/node_modules/@weavecode/harness/dist/claude/peers.js +1 -1
  57. package/node_modules/@weavecode/harness/dist/claude/plan.d.ts +2 -0
  58. package/node_modules/@weavecode/harness/dist/claude/plan.js +1 -0
  59. package/node_modules/@weavecode/harness/dist/claude/plugins.js +4 -4
  60. package/node_modules/@weavecode/harness/dist/claude/prompt-inbox.js +1 -1
  61. package/node_modules/@weavecode/harness/dist/claude/prompt.js +3 -3
  62. package/node_modules/@weavecode/harness/dist/claude/questions.js +2 -2
  63. package/node_modules/@weavecode/harness/dist/claude/settings-gen.js +1 -1
  64. package/node_modules/@weavecode/harness/dist/claude/side-question.d.ts +12 -0
  65. package/node_modules/@weavecode/harness/dist/claude/side-question.js +1 -0
  66. package/node_modules/@weavecode/harness/dist/claude/statusline/config.d.ts +200 -0
  67. package/node_modules/@weavecode/harness/dist/claude/statusline/config.js +1 -0
  68. package/node_modules/@weavecode/harness/dist/claude/statusline/generator.d.ts +12 -0
  69. package/node_modules/@weavecode/harness/dist/claude/statusline/generator.js +745 -0
  70. package/node_modules/@weavecode/harness/dist/claude/statusline/git-cache.d.ts +10 -0
  71. package/node_modules/@weavecode/harness/dist/claude/statusline/git-cache.js +1 -0
  72. package/node_modules/@weavecode/harness/dist/claude/statusline/surface.d.ts +20 -0
  73. package/node_modules/@weavecode/harness/dist/claude/statusline/surface.js +2 -0
  74. package/node_modules/@weavecode/harness/dist/claude/transcript.js +3 -3
  75. package/node_modules/@weavecode/harness/dist/codex/assets.js +1 -1
  76. package/node_modules/@weavecode/harness/dist/codex/detect.js +1 -1
  77. package/node_modules/@weavecode/harness/dist/codex/index.d.ts +1 -0
  78. package/node_modules/@weavecode/harness/dist/codex/index.js +1 -1
  79. package/node_modules/@weavecode/harness/dist/codex/mcp.d.ts +30 -0
  80. package/node_modules/@weavecode/harness/dist/codex/mcp.js +1 -0
  81. package/node_modules/@weavecode/harness/dist/codex/messaging.js +1 -1
  82. package/node_modules/@weavecode/harness/dist/codex/plugin-state.js +1 -1
  83. package/node_modules/@weavecode/harness/dist/codex/toml.js +9 -9
  84. package/node_modules/@weavecode/harness/dist/driver.d.ts +110 -2
  85. package/node_modules/@weavecode/harness/dist/driver.js +1 -1
  86. package/node_modules/@weavecode/harness/dist/init/generated.js +2 -2
  87. package/node_modules/@weavecode/harness/dist/init/pipeline.js +1 -1
  88. package/node_modules/@weavecode/harness/dist/registry-wiring.js +1 -1
  89. package/node_modules/@weavecode/harness/dist/shared/install-link.js +1 -1
  90. package/node_modules/@weavecode/harness/dist/zcode/assets.d.ts +8 -0
  91. package/node_modules/@weavecode/harness/dist/zcode/assets.js +1 -0
  92. package/node_modules/@weavecode/harness/dist/zcode/capture.d.ts +36 -0
  93. package/node_modules/@weavecode/harness/dist/zcode/capture.js +3 -0
  94. package/node_modules/@weavecode/harness/dist/zcode/detect.d.ts +2 -0
  95. package/node_modules/@weavecode/harness/dist/zcode/detect.js +1 -0
  96. package/node_modules/@weavecode/harness/dist/zcode/event-source.d.ts +8 -0
  97. package/node_modules/@weavecode/harness/dist/zcode/event-source.js +1 -0
  98. package/node_modules/@weavecode/harness/dist/zcode/index.d.ts +13 -0
  99. package/node_modules/@weavecode/harness/dist/zcode/index.js +1 -0
  100. package/node_modules/@weavecode/harness/dist/zcode/mcp.d.ts +6 -0
  101. package/node_modules/@weavecode/harness/dist/zcode/mcp.js +2 -0
  102. package/node_modules/@weavecode/harness/dist/zcode/messaging.d.ts +13 -0
  103. package/node_modules/@weavecode/harness/dist/zcode/messaging.js +1 -0
  104. package/node_modules/@weavecode/harness/dist/zcode/resume.d.ts +7 -0
  105. package/node_modules/@weavecode/harness/dist/zcode/resume.js +1 -0
  106. package/node_modules/@weavecode/harness/package.json +2 -2
  107. package/node_modules/@weavecode/server/dist/daemon/dev-entry.js +1 -1
  108. package/node_modules/@weavecode/server/dist/daemon/hook-pipeline.d.ts +8 -1
  109. package/node_modules/@weavecode/server/dist/daemon/hook-pipeline.js +1 -1
  110. package/node_modules/@weavecode/server/dist/daemon/hooks-routes.js +1 -1
  111. package/node_modules/@weavecode/server/dist/daemon/server.d.ts +7 -0
  112. package/node_modules/@weavecode/server/dist/daemon/server.js +8 -6
  113. package/node_modules/@weavecode/server/dist/daemon/session-probe.d.ts +13 -0
  114. package/node_modules/@weavecode/server/dist/daemon/session-probe.js +1 -1
  115. package/node_modules/@weavecode/server/dist/daemon/snapshot-refresher.d.ts +7 -1
  116. package/node_modules/@weavecode/server/dist/daemon/snapshot-refresher.js +1 -1
  117. package/node_modules/@weavecode/server/dist/daemon/sse-hub.d.ts +40 -0
  118. package/node_modules/@weavecode/server/dist/daemon/sse-hub.js +1 -0
  119. package/node_modules/@weavecode/server/dist/daemon/start.js +1 -1
  120. package/node_modules/@weavecode/server/dist/db/db.js +20 -2
  121. package/node_modules/@weavecode/server/dist/hooks/bus.d.ts +5 -0
  122. package/node_modules/@weavecode/server/dist/hooks/inbox-worker.d.ts +8 -1
  123. package/node_modules/@weavecode/server/dist/hooks/inbox-worker.js +3 -3
  124. package/node_modules/@weavecode/server/dist/hooks/message-deliverer.d.ts +29 -0
  125. package/node_modules/@weavecode/server/dist/hooks/message-deliverer.js +2 -2
  126. package/node_modules/@weavecode/server/dist/hooks/mirror.d.ts +9 -4
  127. package/node_modules/@weavecode/server/dist/hooks/mirror.js +4 -4
  128. package/node_modules/@weavecode/server/dist/hooks/session-purge.d.ts +3 -0
  129. package/node_modules/@weavecode/server/dist/hooks/session-purge.js +1 -1
  130. package/node_modules/@weavecode/server/dist/hooks/side-questions.d.ts +41 -0
  131. package/node_modules/@weavecode/server/dist/hooks/side-questions.js +1 -0
  132. package/node_modules/@weavecode/server/dist/hooks/toolkit.js +5 -5
  133. package/node_modules/@weavecode/server/dist/install/apply-update.js +1 -1
  134. package/node_modules/@weavecode/server/dist/install/capabilities.d.ts +10 -0
  135. package/node_modules/@weavecode/server/dist/install/capabilities.js +1 -1
  136. package/node_modules/@weavecode/server/dist/install/codex-toml.d.ts +8 -26
  137. package/node_modules/@weavecode/server/dist/install/codex-toml.js +1 -1
  138. package/node_modules/@weavecode/server/dist/install/global-config.d.ts +2 -4
  139. package/node_modules/@weavecode/server/dist/install/global-config.js +1 -2
  140. package/node_modules/@weavecode/server/dist/install/installer.d.ts +3 -5
  141. package/node_modules/@weavecode/server/dist/install/installer.js +1 -3
  142. package/node_modules/@weavecode/server/dist/install/mcp-rows.d.ts +5 -5
  143. package/node_modules/@weavecode/server/dist/install/mcp-rows.js +1 -1
  144. package/node_modules/@weavecode/server/dist/registry.js +2 -2
  145. package/node_modules/@weavecode/server/dist/repositories/hook-events.d.ts +7 -0
  146. package/node_modules/@weavecode/server/dist/repositories/hook-events.js +2 -2
  147. package/node_modules/@weavecode/server/dist/repositories/sessions.d.ts +15 -4
  148. package/node_modules/@weavecode/server/dist/repositories/sessions.js +2 -2
  149. package/node_modules/@weavecode/server/dist/repositories/side-questions.d.ts +50 -0
  150. package/node_modules/@weavecode/server/dist/repositories/side-questions.js +6 -0
  151. package/node_modules/@weavecode/server/dist/statusline/config.d.ts +4 -198
  152. package/node_modules/@weavecode/server/dist/statusline/config.js +1 -1
  153. package/node_modules/@weavecode/server/dist/statusline/generator.d.ts +5 -0
  154. package/node_modules/@weavecode/server/dist/statusline/generator.js +1 -717
  155. package/node_modules/@weavecode/server/dist/statusline/manager.d.ts +5 -5
  156. package/node_modules/@weavecode/server/dist/statusline/manager.js +3 -4
  157. package/node_modules/@weavecode/server/dist/surfaces.d.ts +11 -1
  158. package/node_modules/@weavecode/server/dist/surfaces.js +1 -1
  159. package/node_modules/@weavecode/server/package.json +4 -4
  160. package/node_modules/@weavecode/server/web/dist/assets/index-CHWbLAje.js +594 -0
  161. package/node_modules/@weavecode/server/web/dist/assets/index-D0s5v8QS.css +11 -0
  162. package/node_modules/@weavecode/server/web/dist/index.html +2 -2
  163. package/node_modules/@weavecode/templates/package.json +2 -2
  164. package/package.json +6 -6
  165. package/node_modules/@weavecode/server/web/dist/assets/index-D5WFh77i.css +0 -11
  166. 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
+ `}