dsh-context 0.10.0 → 0.10.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ![Social preview](https://raw.githubusercontent.com/bowenliang123/dsh-context/main/docs/social-preview.png)
1
+ <p align="center"><img src="https://raw.githubusercontent.com/bowenliang123/dsh-context/main/docs/social-preview.png" width="840" alt="Social preview"></p>
2
2
 
3
3
  # dsh-context
4
4
 
package/lib/client.js CHANGED
@@ -245,15 +245,14 @@ function anchoredParts(parts, target) {
245
245
  }
246
246
 
247
247
  // src/client/headline.ts
248
- function headlineOf(data) {
248
+ function headlineOf(data, pressure = null) {
249
249
  const current = data.current;
250
- const occ = data.occupancy;
251
- const projected = occ !== void 0 && typeof occ.projectedTokens === "number" ? occ.projectedTokens : void 0;
250
+ const projected = pressure !== null && typeof pressure.projectedTokens === "number" ? pressure.projectedTokens : void 0;
252
251
  const requests = data.requests || [];
253
252
  const lastReq = requests.length > 0 ? requests[requests.length - 1] : null;
254
253
  const derived = lastReq !== null && typeof lastReq.prompt === "number" ? lastReq.prompt + (current.total - lastReq.total) : void 0;
255
254
  const occupancyTokens = projected ?? derived ?? null;
256
- const window2 = occ !== void 0 && typeof occ.contextWindow === "number" ? occ.contextWindow : data.contextWindow;
255
+ const window2 = pressure !== null && typeof pressure.contextWindow === "number" ? pressure.contextWindow : data.contextWindow;
257
256
  const tokens = occupancyTokens ?? current.total;
258
257
  const pct = window2 !== void 0 && window2 > 0 ? Math.min(100, Math.round(tokens / window2 * 100)) : null;
259
258
  const parts = anchoredParts(partsOf(current), occupancyTokens !== null && tokens > 0 ? tokens : null);
@@ -265,6 +264,10 @@ function timelineOf(value) {
265
264
  if (value === null || value === void 0 || typeof value !== "object") return null;
266
265
  return value;
267
266
  }
267
+ function contextPressureOf(value) {
268
+ if (value === null || value === void 0 || typeof value !== "object") return null;
269
+ return value;
270
+ }
268
271
 
269
272
  // src/client/react.ts
270
273
  var React = require("react");
@@ -581,6 +584,7 @@ function makeContextModal(ctx, kit) {
581
584
  const sessionId = typeof props.sessionId === "string" ? props.sessionId : "";
582
585
  const open = typeof props.useContextModal === "function" ? props.useContextModal((s) => s) : false;
583
586
  const data = typeof props.useProjection === "function" ? timelineOf(props.useProjection("contextTimeline")) : null;
587
+ const pressure = typeof props.useProjection === "function" ? contextPressureOf(props.useProjection("contextPressure")) : null;
584
588
  const [selectedSeq, setSelectedSeq] = React.useState(null);
585
589
  const [hoveredSeq, setHoveredSeq] = React.useState(null);
586
590
  const [hoverCat, setHoverCat] = React.useState(null);
@@ -624,7 +628,7 @@ function makeContextModal(ctx, kit) {
624
628
  const i = turns.indexOf(req);
625
629
  return i >= 0 ? markers[i] : void 0;
626
630
  };
627
- const head = data !== null ? headlineOf(data) : null;
631
+ const head = data !== null ? headlineOf(data, pressure) : null;
628
632
  return /* @__PURE__ */ React.createElement("div", { className: "lc-modal-backdrop", onClick: close }, /* @__PURE__ */ React.createElement("div", { className: "lc-modal-card", onClick: (ev) => {
629
633
  ev.stopPropagation();
630
634
  } }, /* @__PURE__ */ React.createElement("div", { className: "lc-modal-head" }, /* @__PURE__ */ React.createElement("span", { className: "lc-modal-title" }, t("tab")), data !== null && (data.model || data.provider) ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, (data.model ? data.model : "") + (data.provider ? " \xB7 " + data.provider : "")) : null, /* @__PURE__ */ React.createElement("button", { className: "lc-modal-close", "aria-label": t("cmd.close"), onClick: close }, "\xD7")), data === null || head === null ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "lc-overview-num" }, /* @__PURE__ */ React.createElement("b", null, fmt3(head.tokens)), /* @__PURE__ */ React.createElement("span", null, head.window ? " / " + fmt3(head.window) + " " + tr("overview.ofWindow", { p: head.pct ?? 0 }) : " " + t("overview.estimate")), !head.estimated ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("overview.splitEst")) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: head.parts, height: 16, max: head.window, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement(Legend, { parts: head.parts, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement("div", { className: "lc-card-title lc-modal-trend" }, t("trend.title")), turns.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("trend.empty")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
@@ -910,6 +914,7 @@ function makeContextView(ctx, kit) {
910
914
  return function ContextView(props) {
911
915
  const sessionId = props.sessionId;
912
916
  const data = typeof props.useProjection === "function" ? timelineOf(props.useProjection("contextTimeline")) : null;
917
+ const pressure = typeof props.useProjection === "function" ? contextPressureOf(props.useProjection("contextPressure")) : null;
913
918
  const [selectedSeq, setSelectedSeq] = React.useState(null);
914
919
  const [hoveredSeq, setHoveredSeq] = React.useState(null);
915
920
  const [hoverTurn, setHoverTurn] = React.useState(null);
@@ -969,7 +974,7 @@ function makeContextView(ctx, kit) {
969
974
  break;
970
975
  }
971
976
  }
972
- const head = headlineOf(data);
977
+ const head = headlineOf(data, pressure);
973
978
  return /* @__PURE__ */ React.createElement("div", { className: "lc-root", ref: rootRef }, /* @__PURE__ */ React.createElement(StatsBoard, { requests, events }), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("overview.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, (data.model ? data.model : "") + (data.provider ? " \xB7 " + data.provider : ""))), /* @__PURE__ */ React.createElement("div", { className: "lc-overview-num" }, /* @__PURE__ */ React.createElement("b", null, fmt3(head.tokens)), /* @__PURE__ */ React.createElement("span", null, head.window ? " / " + fmt3(head.window) + " " + tr("overview.ofWindow", { p: head.pct ?? 0 }) : " " + t("overview.estimate")), !head.estimated ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("overview.splitEst")) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: head.parts, height: 16, max: head.window, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement(Legend, { parts: head.parts, hoverKey: hoverCat, onHoverKey: setHoverCat }), data.toolList && data.toolList.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-tools" }, t("tools.top"), data.toolList.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => {
974
979
  return /* @__PURE__ */ React.createElement("span", { key: tool.name, className: "lc-tool-chip" }, tool.name + " " + fmt3(tool.tokens));
975
980
  }), data.toolList.length > 5 ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, " " + tr("tools.more", { n: data.toolList.length })) : null) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("trend.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("trend.hint")), /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, /* @__PURE__ */ React.createElement(
package/lib/index.js CHANGED
@@ -1,5 +1,32 @@
1
- // src/host/timeline.ts
1
+ // src/host/config.ts
2
2
  import { z } from "zod";
3
+ var DEFAULT_BOUNDS = {
4
+ maxRequestSteps: 1500,
5
+ maxKeptTurns: 300,
6
+ maxEvents: 400,
7
+ maxNodes: 200
8
+ };
9
+ var Config = z.preprocess(
10
+ (v) => v ?? {},
11
+ z.object({
12
+ maxRequestSteps: z.number().int().min(1).default(DEFAULT_BOUNDS.maxRequestSteps),
13
+ maxKeptTurns: z.number().int().min(1).default(DEFAULT_BOUNDS.maxKeptTurns),
14
+ maxEvents: z.number().int().min(1).default(DEFAULT_BOUNDS.maxEvents),
15
+ maxNodes: z.number().int().min(1).default(DEFAULT_BOUNDS.maxNodes)
16
+ }).strict()
17
+ );
18
+ function resolveBounds(config) {
19
+ const parsed = Config.parse(config ?? {});
20
+ return {
21
+ maxRequestSteps: parsed.maxRequestSteps,
22
+ maxKeptTurns: parsed.maxKeptTurns,
23
+ maxEvents: parsed.maxEvents,
24
+ maxNodes: parsed.maxNodes
25
+ };
26
+ }
27
+
28
+ // src/host/timeline.ts
29
+ import { z as z2 } from "zod";
3
30
 
4
31
  // src/host/pricing.ts
5
32
  var CHARS_PER_TOKEN = 4;
@@ -65,10 +92,6 @@ function isInjection(source) {
65
92
  }
66
93
 
67
94
  // src/host/fold.ts
68
- var MAX_REQUEST_STEPS = 1500;
69
- var MAX_KEPT_TURNS = 300;
70
- var MAX_EVENTS = 400;
71
- var MAX_NODES = 200;
72
95
  function trimToLastTurns(requests, maxTurns) {
73
96
  let runs = 0;
74
97
  let start = requests.length;
@@ -95,14 +118,14 @@ function countTurnRuns(requests) {
95
118
  }
96
119
  return runs;
97
120
  }
98
- function trimState(st) {
99
- if (countTurnRuns(st.requests) > MAX_KEPT_TURNS) {
100
- st.requests = trimToLastTurns(st.requests, MAX_KEPT_TURNS);
121
+ function trimState(st, bounds) {
122
+ if (countTurnRuns(st.requests) > bounds.maxKeptTurns) {
123
+ st.requests = trimToLastTurns(st.requests, bounds.maxKeptTurns);
101
124
  }
102
- if (st.requests.length > MAX_REQUEST_STEPS) {
103
- st.requests = st.requests.slice(-MAX_REQUEST_STEPS);
125
+ if (st.requests.length > bounds.maxRequestSteps) {
126
+ st.requests = st.requests.slice(-bounds.maxRequestSteps);
104
127
  }
105
- if (st.events.length > MAX_EVENTS) st.events = st.events.slice(-MAX_EVENTS);
128
+ if (st.events.length > bounds.maxEvents) st.events = st.events.slice(-bounds.maxEvents);
106
129
  }
107
130
  function createTimelineState() {
108
131
  return {
@@ -115,9 +138,6 @@ function createTimelineState() {
115
138
  provider: void 0,
116
139
  lastModel: void 0,
117
140
  contextWindow: void 0,
118
- pressureTokens: void 0,
119
- sampledSurfaceTokens: void 0,
120
- occupancyWindow: void 0,
121
141
  requests: [],
122
142
  events: [],
123
143
  callNames: {}
@@ -207,17 +227,7 @@ function applySurface(st, ev, type, data, message) {
207
227
  st.sums[cat] += node.tokens;
208
228
  return node;
209
229
  }
210
- function pressureOf(usage) {
211
- if (usage === void 0 || typeof usage.inputTokens !== "number") return void 0;
212
- return usage.inputTokens + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0);
213
- }
214
- function sampleUsage(st, usage) {
215
- const pressureTokens = pressureOf(usage);
216
- if (pressureTokens === void 0) return;
217
- st.pressureTokens = pressureTokens;
218
- st.sampledSurfaceTokens = st.sums.user + st.sums.inject + st.sums.assistant + st.sums.tool;
219
- }
220
- function applyTimeline(state, event) {
230
+ function applyTimeline(state, event, bounds) {
221
231
  let st;
222
232
  const ensure = () => st ??= {
223
233
  ...state,
@@ -253,7 +263,6 @@ function applyTimeline(state, event) {
253
263
  if (data && typeof data.contextWindow === "number") s.contextWindow = data.contextWindow;
254
264
  if (data && typeof data.model === "string") s.model = data.model;
255
265
  if (data && typeof data.provider === "string") s.provider = data.provider;
256
- s.occupancyWindow = data && typeof data.contextWindow === "number" ? data.contextWindow : void 0;
257
266
  break;
258
267
  }
259
268
  case "tool/call": {
@@ -292,18 +301,9 @@ function applyTimeline(state, event) {
292
301
  applySurface(s, event, event.type, data, toolMsg);
293
302
  break;
294
303
  }
295
- case "assistant/chunk": {
296
- const chunk = data?.chunk;
297
- if (chunk !== void 0 && chunk.type === "usage") {
298
- const s = ensure();
299
- sampleUsage(s, chunk.usage);
300
- }
301
- break;
302
- }
303
304
  case "assistant/message": {
304
305
  const usage = data?.usage;
305
306
  const s = ensure();
306
- sampleUsage(s, usage);
307
307
  const total = s.systemTokens + s.toolsTokens + s.sums.user + s.sums.inject + s.sums.assistant + s.sums.tool;
308
308
  const record = {
309
309
  turn: data && typeof data.turn === "number" ? data.turn : void 0,
@@ -346,14 +346,13 @@ function applyTimeline(state, event) {
346
346
  return state;
347
347
  }
348
348
  if (st !== void 0) {
349
- trimState(st);
349
+ trimState(st, bounds);
350
350
  return st;
351
351
  }
352
352
  return state;
353
353
  }
354
- function buildTimelineView(state) {
354
+ function buildTimelineView(state, bounds) {
355
355
  const surfaceTotal = state.sums.user + state.sums.inject + state.sums.assistant + state.sums.tool;
356
- const projectedTokens = state.pressureTokens !== void 0 && state.sampledSurfaceTokens !== void 0 ? Math.max(0, state.pressureTokens + surfaceTotal - state.sampledSurfaceTokens) : void 0;
357
356
  const result = {
358
357
  ok: true,
359
358
  model: state.model,
@@ -368,21 +367,14 @@ function buildTimelineView(state) {
368
367
  tool: state.sums.tool,
369
368
  total: surfaceTotal + state.systemTokens + state.toolsTokens
370
369
  },
371
- occupancy: {
372
- ...state.pressureTokens === void 0 ? {} : { pressureTokens: state.pressureTokens },
373
- surfaceTokens: surfaceTotal,
374
- ...state.sampledSurfaceTokens === void 0 ? {} : { sampledSurfaceTokens: state.sampledSurfaceTokens },
375
- ...projectedTokens === void 0 ? {} : { projectedTokens },
376
- ...state.occupancyWindow === void 0 ? {} : { contextWindow: state.occupancyWindow }
377
- },
378
370
  toolList: state.toolList,
379
371
  requests: state.requests.map((r) => ({ ...r })),
380
372
  events: state.events.map((e) => ({ ...e })),
381
373
  nodes: [],
382
374
  droppedNodes: 0
383
375
  };
384
- result.droppedNodes = Math.max(0, state.surface.length - MAX_NODES);
385
- result.nodes = state.surface.slice(-MAX_NODES);
376
+ result.droppedNodes = Math.max(0, state.surface.length - bounds.maxNodes);
377
+ result.nodes = state.surface.slice(-bounds.maxNodes);
386
378
  const requests = result.requests;
387
379
  const events = result.events;
388
380
  let ri = 0;
@@ -403,97 +395,95 @@ function buildTimelineView(state) {
403
395
  }
404
396
 
405
397
  // src/host/timeline.ts
406
- var surfaceNodeSchema = z.object({
407
- seq: z.number().int().nonnegative(),
408
- time: z.number().optional(),
409
- cat: z.enum(["user", "inject", "assistant", "tool"]),
410
- tokens: z.number().int().nonnegative(),
411
- form: z.string().optional(),
412
- text: z.string().optional(),
413
- tool: z.string().optional(),
414
- err: z.boolean().optional(),
415
- skill: z.string().optional(),
416
- calls: z.array(z.string()).optional()
417
- }).strict();
418
- var requestRecordSchema = z.object({
419
- turn: z.number().optional(),
420
- step: z.number().optional(),
421
- time: z.number(),
422
- seq: z.number(),
423
- system: z.number().int().nonnegative(),
424
- tools: z.number().int().nonnegative(),
425
- user: z.number().int().nonnegative(),
426
- inject: z.number().int().nonnegative(),
427
- assistant: z.number().int().nonnegative(),
428
- tool: z.number().int().nonnegative(),
429
- total: z.number().int().nonnegative(),
430
- prompt: z.number().int().nonnegative().optional(),
431
- output: z.number().int().nonnegative().optional(),
432
- stepCount: z.number().int().positive().optional()
398
+ var surfaceNodeSchema = z2.object({
399
+ seq: z2.number().int().nonnegative(),
400
+ time: z2.number().optional(),
401
+ cat: z2.enum(["user", "inject", "assistant", "tool"]),
402
+ tokens: z2.number().int().nonnegative(),
403
+ form: z2.string().optional(),
404
+ text: z2.string().optional(),
405
+ tool: z2.string().optional(),
406
+ err: z2.boolean().optional(),
407
+ skill: z2.string().optional(),
408
+ calls: z2.array(z2.string()).optional()
433
409
  }).strict();
434
- var contextEventSchema = z.object({
435
- seq: z.number(),
436
- time: z.number(),
437
- kind: z.enum(["compaction", "prune", "inject", "model"]),
438
- form: z.string().optional(),
439
- tokens: z.number().optional(),
440
- count: z.number().optional(),
441
- sub: z.string().optional(),
442
- name: z.string().optional(),
443
- from: z.string().optional(),
444
- to: z.string().optional(),
445
- fromTurn: z.number().optional(),
446
- fromStep: z.number().optional(),
447
- turn: z.number().optional(),
448
- step: z.number().optional()
410
+ var requestRecordSchema = z2.object({
411
+ turn: z2.number().optional(),
412
+ step: z2.number().optional(),
413
+ time: z2.number(),
414
+ seq: z2.number(),
415
+ system: z2.number().int().nonnegative(),
416
+ tools: z2.number().int().nonnegative(),
417
+ user: z2.number().int().nonnegative(),
418
+ inject: z2.number().int().nonnegative(),
419
+ assistant: z2.number().int().nonnegative(),
420
+ tool: z2.number().int().nonnegative(),
421
+ total: z2.number().int().nonnegative(),
422
+ prompt: z2.number().int().nonnegative().optional(),
423
+ output: z2.number().int().nonnegative().optional(),
424
+ stepCount: z2.number().int().positive().optional()
449
425
  }).strict();
450
- var currentSchema = z.object({
451
- system: z.number().int().nonnegative(),
452
- tools: z.number().int().nonnegative(),
453
- user: z.number().int().nonnegative(),
454
- inject: z.number().int().nonnegative(),
455
- assistant: z.number().int().nonnegative(),
456
- tool: z.number().int().nonnegative(),
457
- total: z.number().int().nonnegative()
426
+ var contextEventSchema = z2.object({
427
+ seq: z2.number(),
428
+ time: z2.number(),
429
+ kind: z2.enum(["compaction", "prune", "inject", "model"]),
430
+ form: z2.string().optional(),
431
+ tokens: z2.number().optional(),
432
+ count: z2.number().optional(),
433
+ sub: z2.string().optional(),
434
+ name: z2.string().optional(),
435
+ from: z2.string().optional(),
436
+ to: z2.string().optional(),
437
+ fromTurn: z2.number().optional(),
438
+ fromStep: z2.number().optional(),
439
+ turn: z2.number().optional(),
440
+ step: z2.number().optional()
458
441
  }).strict();
459
- var occupancySchema = z.object({
460
- pressureTokens: z.number().int().nonnegative().optional(),
461
- surfaceTokens: z.number().int().nonnegative(),
462
- sampledSurfaceTokens: z.number().int().nonnegative().optional(),
463
- projectedTokens: z.number().int().nonnegative().optional(),
464
- contextWindow: z.number().int().positive().optional()
442
+ var currentSchema = z2.object({
443
+ system: z2.number().int().nonnegative(),
444
+ tools: z2.number().int().nonnegative(),
445
+ user: z2.number().int().nonnegative(),
446
+ inject: z2.number().int().nonnegative(),
447
+ assistant: z2.number().int().nonnegative(),
448
+ tool: z2.number().int().nonnegative(),
449
+ total: z2.number().int().nonnegative()
465
450
  }).strict();
466
- var contextTimelineSchema = z.object({
467
- ok: z.literal(true),
468
- model: z.string().optional(),
469
- provider: z.string().optional(),
470
- contextWindow: z.number().optional(),
451
+ var contextTimelineSchema = z2.object({
452
+ ok: z2.literal(true),
453
+ model: z2.string().optional(),
454
+ provider: z2.string().optional(),
455
+ contextWindow: z2.number().optional(),
471
456
  current: currentSchema,
472
- occupancy: occupancySchema.optional(),
473
- toolList: z.array(z.object({ name: z.string(), tokens: z.number().int().nonnegative() }).strict()),
474
- requests: z.array(requestRecordSchema),
475
- events: z.array(contextEventSchema),
476
- nodes: z.array(surfaceNodeSchema),
477
- droppedNodes: z.number().int().nonnegative()
457
+ toolList: z2.array(z2.object({ name: z2.string(), tokens: z2.number().int().nonnegative() }).strict()),
458
+ requests: z2.array(requestRecordSchema),
459
+ events: z2.array(contextEventSchema),
460
+ nodes: z2.array(surfaceNodeSchema),
461
+ droppedNodes: z2.number().int().nonnegative()
478
462
  }).strict();
479
- var contextTimelineDefinition = {
480
- key: "contextTimeline",
481
- schema: contextTimelineSchema,
482
- init: () => createTimelineState(),
483
- apply: (state, event) => applyTimeline(state, event),
484
- view: (state) => buildTimelineView(state),
485
- stateVersion: 1
486
- };
463
+ function createContextTimelineDefinition(config) {
464
+ const bounds = resolveBounds(config);
465
+ return {
466
+ key: "contextTimeline",
467
+ schema: contextTimelineSchema,
468
+ init: () => createTimelineState(),
469
+ apply: (state, event) => applyTimeline(state, event, bounds),
470
+ view: (state) => buildTimelineView(state, bounds),
471
+ // 2 since 0.11: the occupancy mirror (pressureTokens/sampledSurfaceTokens/
472
+ // occupancyWindow) left the persisted state — the client now reads the
473
+ // official token-meter `contextPressure` projection instead. Old cached
474
+ // rows are discarded and refolded.
475
+ stateVersion: 2
476
+ };
477
+ }
487
478
 
488
479
  // src/host/index.ts
489
480
  var name = "dsh-context";
490
481
  var inject = ["sessionProjections"];
491
- function apply(ctx) {
492
- ctx.inject(["sessionProjections"], (projectionCtx) => {
493
- projectionCtx.sessionProjections.register(contextTimelineDefinition);
494
- });
482
+ function apply(ctx, config) {
483
+ ctx.sessionProjections.register(createContextTimelineDefinition(config));
495
484
  }
496
485
  export {
486
+ Config,
497
487
  apply,
498
488
  inject,
499
489
  name
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "description": "A DeepSeek Harness plugin for context dashboard and context command, for understanding how the context is made of, and how it evolves.",
5
5
  "author": "bowenliang123",
6
6
  "repository": {