omp-vcc 0.1.7 → 0.1.8

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
@@ -26,6 +26,7 @@ New here → [`docs/setup.md`](docs/setup.md).
26
26
  | `/vcc-recall [query] [scope:all] [page:N] [mode:touched]` | Search compacted history |
27
27
  | `/pi-vcc-recall` | Legacy alias for `/vcc-recall` |
28
28
  | `/vcc-stats [history\|all]` | Last savings + history table (single, no alias) |
29
+ | `/vcc-config` | Effective config with per-key source (`file` / `host overlay` / `default`) plus resolved file path. Args ignored. |
29
30
 
30
31
  Tools: `vcc_recall`, `vcc_stats` (approval `read`). Extension-only — no `commands/*.md` file slash commands (avoids duplicate `/omp-vcc`).
31
32
 
@@ -53,7 +54,7 @@ File `~/.omp/omp-vcc/config.json` — XDG: `$OMP_VCC_CONFIG_PATH` > `$PI_VCC_CON
53
54
  | `debug` | `false` | Write `/tmp/omp-vcc-debug.json` per compaction. |
54
55
  | `chainShakeHint` | `false` | Eager post-VCC `shake` chain. Host rescue already auto-shakes on dead-end; this forces it. |
55
56
 
56
- Toggle live: `omp config set plugins."@zhulinchng/omp-vcc".debug true` or `/settings` → `@zhulinchng/omp-vcc`. File is source of truth; `ctx.settings` overlays at runtime.
57
+ Toggle live: `omp config set plugins."@zhulinchng/omp-vcc".debug true` or `/settings` → `@zhulinchng/omp-vcc`. File is source of truth; `ctx.settings` overlays at runtime. `/vcc-config` shows the merged result — use it to confirm a toggle took effect.
57
58
 
58
59
  ## How it works
59
60
 
@@ -82,9 +83,9 @@ Additive VCC+shake is automatic. Eager chain: `chainShakeHint:true`. Explicit mo
82
83
 
83
84
  ```sh
84
85
  bunx tsc --noEmit
85
- bun test # 515 tests, 48 files, 1443 expects, 0 fail
86
+ bun test # 619 tests, 55 files, 1876 expects, 0 fail
86
87
  bun test tests/e2e --timeout 120000 # 111 E2E
87
- bun run smoke # 9 checks: 3 hooks + 4 cmds + 2 tools + pipeline
88
+ bun run smoke # 13 checks: 3 hooks + 6 cmds + 2 tools + dedup (+ pipeline)
88
89
  omp plugin link . && omp plugin doctor
89
90
  ```
90
91
 
@@ -97,6 +98,7 @@ bunx tsc --noEmit && bun test && bun run smoke
97
98
  omp -e @zhulinchng/omp-vcc
98
99
  /omp-vcc keep:1 # expect [Session Goal] + toast omp-vcc: kept 1/2 turns
99
100
  /vcc-stats # table + history
101
+ /vcc-config # effective config card with per-key source
100
102
  cat /tmp/omp-vcc-debug.json # when debug:true
101
103
  ```
102
104
 
@@ -19,6 +19,7 @@ import {
19
19
  scheduleCompactionStatsNotify,
20
20
  registerVccStatsTool as registerVccStatsToolHook,
21
21
  registerVccStatsCommand as registerVccStatsCommandHook,
22
+ registerVccConfigCommand as registerVccConfigCommandHook,
22
23
  } from "./vcc-core/hook";
23
24
  import { searchEntriesDetailed, getTouchedFiles } from "./vcc-core/core/search-entries";
24
25
  import { formatRecallOutput, formatTouchedOutput } from "./vcc-core/core/format-recall";
@@ -342,9 +343,10 @@ export default function (pi: ExtensionAPI): void {
342
343
  });
343
344
  // ── /vcc-stats commands — show savings table (PR3) ──
344
345
  registerVccStatsCommandHook(pi);
346
+ registerVccConfigCommandHook(pi);
345
347
 
346
348
  }
347
349
  // ── Re-exports for pi-vcc test compatibility (not dead: tests import via hook directly,
348
350
  // but external consumers and the `vcc-recall` shim may import via main) ──
349
- export { registerBeforeCompactHook, PI_VCC_COMPACT_INSTRUCTION, OMP_VCC_COMPACT_INSTRUCTION, getLastCompactionStats, getCompactionHistory, formatCompactionStats, formatStatsTable, formatLastStatsDetail, scheduleCompactionStatsNotify, AUTO_CONTINUE_CUSTOM_TYPE, LEGACY_AUTO_CONTINUE_CUSTOM_TYPE, invalidExpandIndices, registerRecallTool, registerVccRecallCommand, registerPiVccCommand, registerVccStatsTool, registerVccStatsCommand, clearCompactionHistoryForTests } from "./vcc-core/hook";
351
+ export { registerBeforeCompactHook, PI_VCC_COMPACT_INSTRUCTION, OMP_VCC_COMPACT_INSTRUCTION, getLastCompactionStats, getCompactionHistory, formatCompactionStats, formatStatsTable, formatLastStatsDetail, scheduleCompactionStatsNotify, AUTO_CONTINUE_CUSTOM_TYPE, LEGACY_AUTO_CONTINUE_CUSTOM_TYPE, invalidExpandIndices, registerRecallTool, registerVccRecallCommand, registerPiVccCommand, registerVccStatsTool, registerVccStatsCommand, registerVccConfigCommand, clearCompactionHistoryForTests } from "./vcc-core/hook";
350
352
  export { buildPiVccCustomInstructions, parseKeepAndPrompt } from "./vcc-core/core/compact-args";
@@ -166,3 +166,80 @@ export function scaffoldSettings(): void {
166
166
  // best-effort; never crash extension load
167
167
  }
168
168
  }
169
+ /** Live-resolved config path. Unlike the `SETTINGS_PATH` const (frozen at import
170
+ * time), this reflects the current `OMP_VCC_CONFIG_PATH` / `PI_VCC_CONFIG_PATH` env. */
171
+ export function getSettingsPath(): string {
172
+ return settingsPath();
173
+ }
174
+
175
+ export type VccSettingSource = "file" | "overlay" | "default";
176
+
177
+ export interface VccConfigView {
178
+ /** Live primary path — where a config file WOULD live. */
179
+ path: string;
180
+ /** Actual file parsed (primary, XDG/legacy fallback, or null when none). */
181
+ readPath: string | null;
182
+ /** True when a candidate file exists (even if unparseable). */
183
+ filePresent: boolean;
184
+ /** True when a candidate file parsed as a JSON object. */
185
+ fileValid: boolean;
186
+ /** Effective values — same merge as `loadSettings` (defaults ← file ← ctx overlay). */
187
+ values: PiVccSettings;
188
+ /** Per-key provenance. Presence check is `key in parsed`, so a file key that
189
+ * happens to equal the default still counts as `file`. */
190
+ sources: Record<keyof PiVccSettings, VccSettingSource>;
191
+ }
192
+
193
+ /**
194
+ * `loadSettings` plus provenance for `/vcc-config`. Read-only: never creates or
195
+ * repairs files (`scaffoldSettings` owns that). Merge order mirrors `loadSettings`
196
+ * exactly — defaults, then the first readable candidate (primary, else the same
197
+ * `fallbackReadPath()` order), then the identical ctx overlay chain.
198
+ */
199
+ export function loadSettingsWithSources(ctx?: unknown): VccConfigView {
200
+ const path = settingsPath();
201
+ const primaryParsed = readJson(path);
202
+ let parsed: Record<string, unknown> | null = primaryParsed;
203
+ let readPath: string | null = primaryParsed ? path : null;
204
+ if (!parsed) {
205
+ const fb = fallbackReadPath();
206
+ if (fb && fb !== path) {
207
+ const fbParsed = readJson(fb);
208
+ if (fbParsed) {
209
+ parsed = fbParsed;
210
+ readPath = fb;
211
+ }
212
+ }
213
+ }
214
+ const candidateExists = fallbackReadPath() !== null;
215
+ const valid = !!parsed && typeof parsed === "object";
216
+ const values: PiVccSettings =
217
+ valid && parsed
218
+ ? { ...DEFAULT_SETTINGS, ...(parsed as Partial<PiVccSettings>) }
219
+ : { ...DEFAULT_SETTINGS };
220
+ const sources = {} as Record<keyof PiVccSettings, VccSettingSource>;
221
+ for (const k of Object.keys(DEFAULT_SETTINGS) as (keyof PiVccSettings)[]) {
222
+ sources[k] = valid && parsed && k in parsed ? "file" : "default";
223
+ }
224
+ if (ctx) {
225
+ const tryGet = (key: string): unknown => {
226
+ try {
227
+ const c = ctx as any;
228
+ if (!c) return undefined;
229
+ if (c.settings?.get) return c.settings.get(key);
230
+ if (c.config?.get) return c.config.get(key);
231
+ if (c.settings && typeof c.settings === "object" && key in c.settings) return c.settings[key];
232
+ if (c.config && typeof c.config === "object" && key in c.config) return c.config[key];
233
+ } catch {}
234
+ return undefined;
235
+ };
236
+ for (const k of Object.keys(DEFAULT_SETTINGS) as (keyof PiVccSettings)[]) {
237
+ const v = tryGet(`plugins.@zhulinchng/omp-vcc.${k}`) ?? tryGet(`plugins.omp-vcc.${k}`) ?? tryGet(`omp-vcc.${k}`) ?? tryGet(k);
238
+ if (v !== undefined) {
239
+ (values as any)[k] = v;
240
+ sources[k] = "overlay";
241
+ }
242
+ }
243
+ }
244
+ return { path, readPath, filePresent: candidateExists, fileValid: valid, values, sources };
245
+ }
@@ -4,7 +4,7 @@ import { createRequire } from "node:module";
4
4
  import { writeFileSync } from "fs";
5
5
  import { compileRanked } from "./core/summarize";
6
6
  import { buildPiVccCustomInstructions, parseKeepAndPrompt, PI_VCC_COMPACT_INSTRUCTION } from "./core/compact-args";
7
- import { loadSettings, type PiVccSettings } from "./core/settings";
7
+ import { loadSettings, loadSettingsWithSources, DEFAULT_SETTINGS, type PiVccSettings, type VccConfigView } from "./core/settings";
8
8
  import { calibrateCharsPerToken, estimateMessageContentChars, estimateMessageContentTokens, estimateTokensFromChars, collectUsageStats } from "./core/token-estimate";
9
9
  import type { PiVccCompactionDetails } from "./details";
10
10
  import type { CompactionReason } from "./types";
@@ -1332,4 +1332,31 @@ export const registerVccStatsCommand = (pi: any) => {
1332
1332
  try { ctx?.ui?.notify?.(`vcc_stats: ${history.length} compaction(s)`, "info"); } catch {}
1333
1333
  };
1334
1334
  pi.registerCommand("vcc-stats", { description: "Show omp-vcc compaction savings (last + history)", handler });
1335
+ };
1336
+ // ── /vcc-config command — show effective configuration with per-key source ──
1337
+ export const formatVccConfigCard = (view: VccConfigView): string => {
1338
+ const header = `**omp-vcc config** (\`${view.path}\`)`;
1339
+ const status = !view.filePresent
1340
+ ? "No config file found — showing defaults."
1341
+ : !view.fileValid
1342
+ ? "Config file unparseable — showing defaults."
1343
+ : view.readPath === view.path
1344
+ ? `Source: file ${view.readPath}`
1345
+ : `Source: fallback file ${view.readPath}`;
1346
+ const lines = (Object.keys(DEFAULT_SETTINGS) as (keyof PiVccSettings)[]).map(
1347
+ (k) => `- ${k}: ${view.values[k] ? "on" : "off"} (${view.sources[k] === "overlay" ? "host overlay" : view.sources[k]})`,
1348
+ );
1349
+ return [header, status, ...lines].join("\n");
1350
+ };
1351
+
1352
+ export const registerVccConfigCommand = (pi: any) => {
1353
+ const handler = async (_args: string, ctx: any) => {
1354
+ // args deliberately ignored — always show the effective config
1355
+ const view = loadSettingsWithSources(ctx);
1356
+ const output = formatVccConfigCard(view);
1357
+ const piAny = pi as unknown as { sendMessage?: (msg: unknown, opts?: unknown) => void };
1358
+ try { piAny.sendMessage?.({ customType: "vcc-config", content: output, display: true }, { triggerTurn: false }); } catch {}
1359
+ try { ctx?.ui?.notify?.(`vcc_config: ${Object.keys(view.values).length} keys from ${view.readPath ?? "defaults"}`, "info"); } catch {}
1360
+ };
1361
+ pi.registerCommand("vcc-config", { description: "Show omp-vcc effective configuration with per-key source", handler });
1335
1362
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omp-vcc",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "description": "Algorithmic VCC compaction for omp - fast lossless no-LLM",
6
6
  "author": "Zhu Lin <zhulin@czl.my>",
package/scripts/smoke.ts CHANGED
@@ -75,6 +75,10 @@ try {
75
75
  "vcc-stats command registered",
76
76
  commands.some((c) => c.name === "vcc-stats"),
77
77
  );
78
+ check(
79
+ "vcc-config command registered",
80
+ commands.some((c) => c.name === "vcc-config"),
81
+ );
78
82
  check(
79
83
  "no omp-vcc-stats duplicate",
80
84
  !commands.some((c) => c.name === "omp-vcc-stats"),
@@ -49,6 +49,7 @@ Use `vcc_recall` to search for prior work … Do not redo work already completed
49
49
  | Task | How | Notes |
50
50
  |---|---|---|
51
51
  | **Check savings** | `/vcc-stats` · `vcc_stats({history:true})` | Last + history table (50-capped, no `omp-vcc-stats` alias). `/omp-vcc` toast is single line only; detailed savings via `/vcc-stats`. Use to confirm headroom before long edits. |
52
+ | **View config** | `/vcc-config` | Effective config with per-key source (`file` / `host overlay` / `default`) plus resolved file path. Args ignored. Use to confirm a `/settings` toggle took effect. |
52
53
  | **Recall search** | `/vcc-recall <query> [scope:all] [page:2]` · alias `/pi-vcc-recall` · tool `vcc_recall({query, scope, mode, page, expand})` | 5 hits/page, up to 50 total. See cookbook below. |
53
54
  | **Stats tool** | `vcc_stats({history?: boolean})` | Same as `/vcc-stats`. `history:true` = full 50-row table. |
54
55