A live forecast of the context window, drawn above the prompt.

Source snapshot of the DeepSeek Harness and eight Strique plugins, with standalone browser-interface installers.
The current snapshot uses Harness 0.2.1-rc.2 and updates Branding, Codex CLI, MCP Manager, Memory, Notify, and Skills Manager for that runtime. Univer 0.3.7 and BetterSidebar 0.28.0 use the pulled upstream source snapshots from the local development workspace. Source revisions record the upstream commits included in this refresh.
Installer owner-state updates use atomic replacement so concurrent launch/status readers cannot observe partially written JSON.
This refresh passed the isolated source setup, all 14 installer lifecycle tests, and a macOS arm64 packaged browser smoke that rendered Strique branding and BetterSidebar without page errors. Platform-specific installer builds run in GitHub Actions.
0.1.0. Leave the update URL empty until hosting is configured.strique-win32-x64 artifact from the successful run and extract its .exe.The installer bundles Node, the harness, and the configured plugins. End users do not need GitHub accounts or a separate Node installation. These are unsigned development installers.
The Build Strique installers workflow builds all five supported OS/architecture targets. See standalone documentation for installation, signed updates, and limitations.
This repository contains a source snapshot rather than submodules. On the development machine, run node setup/export-source.mjs NEW_OUTPUT_DIRECTORY and review the resulting snapshot before updating this repository. Changes on the development machine reach users only after a new snapshot is committed, a release is built, and its installer or signed update is distributed.
Credentials, sessions, dependency directories, and original Git histories are excluded. Upstream license files remain in their project folders.
hooks/token-weather.mjs 88 lines1// Token Weather, from "Getting started with Claude Code mods"
2// (https://claude.dev/blog/getting-started-with-claude-code-mods/, Anthropic, 2026-10-01).
3// The published module, unchanged.
4
5// Token Weather: a live forecast of the context window, above the prompt.
6
7const HISTORY = 12;
8const BARS = "▁▂▃▄▅▆▇█";
9const FORECAST = [
10 { upTo: 25, icon: "☀", word: "Clear", color: "yellow" },
11 { upTo: 50, icon: "☁", word: "Cloudy", color: "cyan" },
12 { upTo: 75, icon: "☂", word: "Showers", color: "blue" },
13 { upTo: 90, icon: "☇", word: "Storm", color: "magenta" },
14 { upTo: Infinity, icon: "↯", word: "Compact soon", color: "red" },
15];
16
17// Held by the host, so the history survives a hot reload of this file.
18const readings = { plugin: "token-weather", key: "readings" };
19
20export function register(on) {
21 on("session.start", async ($, e, next) => {
22 const result = await next(e);
23 await takeReading($);
24 return result;
25 });
26
27 on("turn.complete", async ($, e, next) => {
28 const result = await next(e);
29 if (!e.agentId) {
30 await takeReading($); // main-loop turns only, not subagents
31 }
32 return result;
33 });
34
35 on("ui.render", { component: "AbovePrompt" }, async ($, e, next) => {
36 const { value: history = [] } = await $.state.get(readings);
37 if (e.props.hasSurvey || history.length === 0) {
38 return next(e);
39 }
40 const { Box, Text } = $.ui.resolve(e);
41 return band(Box, Text, history, e.props.bodyColumns);
42 });
43}
44
45async function takeReading($) {
46 const { context } = await $.session.usage();
47 if (!context?.window) return;
48 const tokens = context.tokens ?? 0;
49 const percent = context.percent ?? Math.round((tokens / context.window) * 100);
50 const { value: history = [] } = await $.state.get(readings);
51 await $.state.set(readings, [...history, { tokens, window: context.window, percent }].slice(-HISTORY));
52}
53
54function band(Box, Text, history, columns) {
55 const now = history[history.length - 1];
56 const f = FORECAST.find((b) => now.percent < b.upTo);
57 const parts = [
58 Text({ color: f.color, bold: true, children: `${f.icon} ${f.word}` }),
59 Text({ children: ` ${now.percent}% of context` }),
60 Text({ dimColor: true, children: ` ${short(now.tokens)} / ${short(now.window)}` }),
61 ];
62 if (columns >= 60) {
63 parts.push(Text({ dimColor: true, children: " last turns " }));
64 parts.push(Text({ color: f.color, children: sparkline(history) }));
65 if (history.length > 1) {
66 parts.push(Text({ dimColor: true, children: trend(history) }));
67 }
68 }
69 return Box({ flexDirection: "row", paddingX: 1, children: parts });
70}
71
72function sparkline(history) {
73 const top = Math.max(...history.map((r) => r.tokens), 1);
74 return history.map((r) => BARS[Math.floor((r.tokens / top) * (BARS.length - 1))]).join("");
75}
76
77function trend(history) {
78 const delta = history[history.length - 1].tokens - history[history.length - 2].tokens;
79 if (delta === 0) return " steady";
80 return delta > 0 ? ` ▲ +${short(delta)} last turn` : ` ▼ ${short(-delta)} last turn`;
81}
82
83function short(n) {
84 if (n >= 1_000_000) return `${+(n / 1_000_000).toFixed(1)}M`;
85 if (n >= 1_000) return `${+(n / 1_000).toFixed(1)}k`;
86 return String(n);
87}
88types/index.d.ts 11 lines1// Token Weather's type contract, from https://claude.dev/blog/getting-started-with-claude-code-mods/
2// (Anthropic, 2026-10-01). The published file, unchanged.
3
4export type TokenWeatherReading = { tokens: number; window: number; percent: number }
5
6declare module 'claude-code' {
7 interface PluginState {
8 'token-weather': { readings: TokenWeatherReading[] }
9 }
10}
11