ultimate-pi 0.3.1 → 0.4.1

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 (184) hide show
  1. package/.agents/skills/harness-decisions/SKILL.md +37 -0
  2. package/.agents/skills/harness-governor/SKILL.md +1 -1
  3. package/.agents/skills/harness-orchestration/SKILL.md +54 -0
  4. package/.agents/skills/harness-plan/SKILL.md +4 -3
  5. package/.agents/skills/harness-sentrux-setup/SKILL.md +57 -0
  6. package/.agents/skills/scrapling-web/SKILL.md +93 -0
  7. package/.pi/PACKAGING.md +1 -0
  8. package/.pi/SYSTEM.md +13 -15
  9. package/.pi/agents/harness/adversary.md +3 -0
  10. package/.pi/agents/harness/evaluator.md +3 -0
  11. package/.pi/agents/harness/executor.md +4 -1
  12. package/.pi/agents/harness/meta-optimizer.md +2 -1
  13. package/.pi/agents/harness/planner.md +22 -1
  14. package/.pi/agents/harness/sentrux-bootstrap.md +42 -0
  15. package/.pi/agents/harness/tie-breaker.md +2 -0
  16. package/.pi/extensions/harness-ask-user.ts +74 -0
  17. package/.pi/extensions/harness-subagents.ts +9 -0
  18. package/.pi/extensions/lib/ask-user/dialog.ts +260 -0
  19. package/.pi/extensions/lib/ask-user/fallback.ts +78 -0
  20. package/.pi/extensions/lib/ask-user/render.ts +66 -0
  21. package/.pi/extensions/lib/ask-user/schema.ts +69 -0
  22. package/.pi/extensions/lib/ask-user/types.ts +41 -0
  23. package/.pi/extensions/lib/ask-user/validate-core.mjs +79 -0
  24. package/.pi/extensions/lib/ask-user/validate.ts +92 -0
  25. package/.pi/extensions/lib/harness-subagents/agent-loader.ts +126 -0
  26. package/.pi/extensions/lib/harness-subagents/agent-manifest.ts +119 -0
  27. package/.pi/extensions/lib/harness-subagents/agent-parser.ts +87 -0
  28. package/.pi/extensions/lib/harness-subagents/blackboard-tool.ts +118 -0
  29. package/.pi/extensions/lib/harness-subagents/blackboard.ts +175 -0
  30. package/.pi/extensions/lib/harness-subagents/spawn-policy.ts +27 -0
  31. package/.pi/extensions/lib/harness-subagents/types-blackboard.ts +27 -0
  32. package/.pi/extensions/lib/harness-subagents/vendored/agent-manager.ts +553 -0
  33. package/.pi/extensions/lib/harness-subagents/vendored/agent-runner.ts +637 -0
  34. package/.pi/extensions/lib/harness-subagents/vendored/agent-types.ts +175 -0
  35. package/.pi/extensions/lib/harness-subagents/vendored/context.ts +59 -0
  36. package/.pi/extensions/lib/harness-subagents/vendored/cross-extension-rpc.ts +134 -0
  37. package/.pi/extensions/lib/harness-subagents/vendored/custom-agents.ts +5 -0
  38. package/.pi/extensions/lib/harness-subagents/vendored/default-agents.ts +123 -0
  39. package/.pi/extensions/lib/harness-subagents/vendored/env.ts +43 -0
  40. package/.pi/extensions/lib/harness-subagents/vendored/group-join.ts +144 -0
  41. package/.pi/extensions/lib/harness-subagents/vendored/index.ts +2447 -0
  42. package/.pi/extensions/lib/harness-subagents/vendored/invocation-config.ts +52 -0
  43. package/.pi/extensions/lib/harness-subagents/vendored/memory.ts +182 -0
  44. package/.pi/extensions/lib/harness-subagents/vendored/model-resolver.ts +92 -0
  45. package/.pi/extensions/lib/harness-subagents/vendored/output-file.ts +115 -0
  46. package/.pi/extensions/lib/harness-subagents/vendored/prompts.ts +103 -0
  47. package/.pi/extensions/lib/harness-subagents/vendored/schedule-store.ts +177 -0
  48. package/.pi/extensions/lib/harness-subagents/vendored/schedule.ts +416 -0
  49. package/.pi/extensions/lib/harness-subagents/vendored/settings.ts +210 -0
  50. package/.pi/extensions/lib/harness-subagents/vendored/skill-loader.ts +108 -0
  51. package/.pi/extensions/lib/harness-subagents/vendored/types.ts +187 -0
  52. package/.pi/extensions/lib/harness-subagents/vendored/ui/agent-widget.ts +637 -0
  53. package/.pi/extensions/lib/harness-subagents/vendored/ui/conversation-viewer.ts +324 -0
  54. package/.pi/extensions/lib/harness-subagents/vendored/ui/schedule-menu.ts +110 -0
  55. package/.pi/extensions/lib/harness-subagents/vendored/usage.ts +71 -0
  56. package/.pi/extensions/lib/harness-subagents/vendored/worktree.ts +195 -0
  57. package/.pi/extensions/lib/harness-vcc-settings.ts +50 -0
  58. package/.pi/extensions/ultimate-pi-vcc.ts +17 -0
  59. package/.pi/harness/README.md +2 -1
  60. package/.pi/harness/agents.manifest.json +80 -0
  61. package/.pi/harness/docs/adrs/0009-sentrux-rules-lifecycle.md +9 -5
  62. package/.pi/harness/docs/adrs/0030-inhouse-vcc-compaction.md +40 -0
  63. package/.pi/harness/docs/adrs/README.md +1 -0
  64. package/.pi/harness/env.harness.template +28 -0
  65. package/.pi/harness/sentrux/architecture.manifest.json +6 -1
  66. package/.pi/prompts/harness-auto.md +2 -2
  67. package/.pi/prompts/harness-plan.md +2 -2
  68. package/.pi/prompts/harness-router-tune.md +2 -2
  69. package/.pi/prompts/harness-run.md +1 -0
  70. package/.pi/prompts/harness-setup.md +179 -340
  71. package/.pi/scripts/README.md +6 -1
  72. package/.pi/scripts/harness-agents-manifest.mjs +123 -0
  73. package/.pi/scripts/harness-cli-verify.sh +60 -11
  74. package/.pi/scripts/harness-generate-model-router.mjs +242 -0
  75. package/.pi/scripts/harness-graphify-bootstrap.sh +1 -6
  76. package/.pi/scripts/harness-resolve-up-pkg.mjs +71 -0
  77. package/.pi/scripts/harness-seed-project-contracts.mjs +33 -1
  78. package/.pi/scripts/harness-sentrux-bootstrap.mjs +146 -0
  79. package/.pi/scripts/harness-sync-env.mjs +148 -0
  80. package/.pi/scripts/harness-verify.mjs +19 -0
  81. package/.pi/scripts/harness-web-search.md +33 -0
  82. package/.pi/scripts/harness-web.py +177 -0
  83. package/.pi/scripts/harness_web/__init__.py +1 -0
  84. package/.pi/scripts/harness_web/config.py +80 -0
  85. package/.pi/scripts/harness_web/output.py +55 -0
  86. package/.pi/scripts/harness_web/scrape.py +120 -0
  87. package/.pi/scripts/harness_web/search_ddg.py +106 -0
  88. package/.pi/scripts/release.sh +338 -0
  89. package/.pi/scripts/sentrux-rules-sync.mjs +29 -7
  90. package/.pi/scripts/vendor-pi-vcc-settings.stub.ts +8 -0
  91. package/.pi/scripts/vendor-sync-pi-vcc.sh +40 -0
  92. package/.pi/settings.example.json +1 -7
  93. package/.sentrux/rules.toml +1 -1
  94. package/AGENTS.md +1 -1
  95. package/CHANGELOG.md +14 -0
  96. package/THIRD_PARTY_NOTICES.md +8 -0
  97. package/package.json +16 -12
  98. package/vendor/pi-vcc/README.md +215 -0
  99. package/vendor/pi-vcc/UPSTREAM_PIN.md +12 -0
  100. package/vendor/pi-vcc/demo.gif +0 -0
  101. package/vendor/pi-vcc/index.ts +12 -0
  102. package/vendor/pi-vcc/package.json +26 -0
  103. package/vendor/pi-vcc/scripts/audit-sessions.ts +88 -0
  104. package/vendor/pi-vcc/scripts/benchmark-real-sessions.ts +25 -0
  105. package/vendor/pi-vcc/scripts/compare-before-after.ts +36 -0
  106. package/vendor/pi-vcc/scripts/dump-branch-output.ts +20 -0
  107. package/vendor/pi-vcc/src/commands/pi-vcc.ts +36 -0
  108. package/vendor/pi-vcc/src/commands/vcc-recall.ts +65 -0
  109. package/vendor/pi-vcc/src/core/brief.ts +381 -0
  110. package/vendor/pi-vcc/src/core/build-sections.ts +79 -0
  111. package/vendor/pi-vcc/src/core/content.ts +60 -0
  112. package/vendor/pi-vcc/src/core/filter-noise.ts +42 -0
  113. package/vendor/pi-vcc/src/core/format-recall.ts +27 -0
  114. package/vendor/pi-vcc/src/core/format.ts +49 -0
  115. package/vendor/pi-vcc/src/core/lineage.ts +26 -0
  116. package/vendor/pi-vcc/src/core/load-messages.ts +41 -0
  117. package/vendor/pi-vcc/src/core/normalize.ts +66 -0
  118. package/vendor/pi-vcc/src/core/recall-scope.ts +14 -0
  119. package/vendor/pi-vcc/src/core/render-entries.ts +55 -0
  120. package/vendor/pi-vcc/src/core/report.ts +237 -0
  121. package/vendor/pi-vcc/src/core/sanitize.ts +5 -0
  122. package/vendor/pi-vcc/src/core/search-entries.ts +221 -0
  123. package/vendor/pi-vcc/src/core/settings.ts +8 -0
  124. package/vendor/pi-vcc/src/core/skill-collapse.ts +35 -0
  125. package/vendor/pi-vcc/src/core/summarize.ts +157 -0
  126. package/vendor/pi-vcc/src/core/tool-args.ts +14 -0
  127. package/vendor/pi-vcc/src/details.ts +7 -0
  128. package/vendor/pi-vcc/src/extract/commits.ts +69 -0
  129. package/vendor/pi-vcc/src/extract/files.ts +80 -0
  130. package/vendor/pi-vcc/src/extract/goals.ts +79 -0
  131. package/vendor/pi-vcc/src/extract/preferences.ts +55 -0
  132. package/vendor/pi-vcc/src/hooks/before-compact.ts +314 -0
  133. package/vendor/pi-vcc/src/sections.ts +12 -0
  134. package/vendor/pi-vcc/src/tools/recall.ts +109 -0
  135. package/vendor/pi-vcc/src/types.ts +14 -0
  136. package/vendor/pi-vcc/tests/before-compact-hook.test.ts +204 -0
  137. package/vendor/pi-vcc/tests/before-compact.test.ts +145 -0
  138. package/vendor/pi-vcc/tests/brief.test.ts +206 -0
  139. package/vendor/pi-vcc/tests/build-sections.test.ts +59 -0
  140. package/vendor/pi-vcc/tests/compile.test.ts +80 -0
  141. package/vendor/pi-vcc/tests/content.test.ts +31 -0
  142. package/vendor/pi-vcc/tests/extract-goals.test.ts +86 -0
  143. package/vendor/pi-vcc/tests/extract-preferences.test.ts +30 -0
  144. package/vendor/pi-vcc/tests/filter-noise.test.ts +61 -0
  145. package/vendor/pi-vcc/tests/fixtures.ts +61 -0
  146. package/vendor/pi-vcc/tests/format-recall.test.ts +30 -0
  147. package/vendor/pi-vcc/tests/format.test.ts +62 -0
  148. package/vendor/pi-vcc/tests/lineage.test.ts +33 -0
  149. package/vendor/pi-vcc/tests/load-messages.test.ts +51 -0
  150. package/vendor/pi-vcc/tests/normalize.test.ts +97 -0
  151. package/vendor/pi-vcc/tests/real-sessions.test.ts +38 -0
  152. package/vendor/pi-vcc/tests/recall-expand.test.ts +15 -0
  153. package/vendor/pi-vcc/tests/recall-scope.test.ts +32 -0
  154. package/vendor/pi-vcc/tests/recall-tool-scope.test.ts +67 -0
  155. package/vendor/pi-vcc/tests/render-entries.test.ts +62 -0
  156. package/vendor/pi-vcc/tests/report.test.ts +44 -0
  157. package/vendor/pi-vcc/tests/sanitize.test.ts +24 -0
  158. package/vendor/pi-vcc/tests/search-entries.test.ts +144 -0
  159. package/vendor/pi-vcc/tests/support/load-session.ts +23 -0
  160. package/vendor/pi-vcc/tests/support/real-sessions.ts +51 -0
  161. package/.agents/skills/firecrawl/SKILL.md +0 -150
  162. package/.agents/skills/firecrawl/rules/install.md +0 -82
  163. package/.agents/skills/firecrawl/rules/security.md +0 -26
  164. package/.agents/skills/firecrawl-agent/SKILL.md +0 -57
  165. package/.agents/skills/firecrawl-build-interact/SKILL.md +0 -67
  166. package/.agents/skills/firecrawl-build-onboarding/SKILL.md +0 -102
  167. package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +0 -39
  168. package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +0 -20
  169. package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +0 -17
  170. package/.agents/skills/firecrawl-build-scrape/SKILL.md +0 -68
  171. package/.agents/skills/firecrawl-build-search/SKILL.md +0 -68
  172. package/.agents/skills/firecrawl-crawl/SKILL.md +0 -58
  173. package/.agents/skills/firecrawl-download/SKILL.md +0 -69
  174. package/.agents/skills/firecrawl-interact/SKILL.md +0 -83
  175. package/.agents/skills/firecrawl-map/SKILL.md +0 -50
  176. package/.agents/skills/firecrawl-parse/SKILL.md +0 -61
  177. package/.agents/skills/firecrawl-scrape/SKILL.md +0 -68
  178. package/.agents/skills/firecrawl-search/SKILL.md +0 -59
  179. package/.pi/pi-vcc-config.json +0 -4
  180. package/firecrawl/.env.template +0 -62
  181. package/firecrawl/README.md +0 -49
  182. package/firecrawl/docker-compose.yaml +0 -201
  183. package/firecrawl/searxng/searxng.env +0 -3
  184. package/firecrawl/searxng/settings.yml +0 -85
@@ -3,11 +3,5 @@
3
3
  "enabled": true,
4
4
  "maxRetries": 3
5
5
  },
6
- "packages": [
7
- "npm:ultimate-pi",
8
- "npm:@posthog/pi",
9
- "npm:context-mode",
10
- "npm:@tintinweb/pi-subagents",
11
- "npm:@sting8k/pi-vcc"
12
- ]
6
+ "packages": ["npm:ultimate-pi", "npm:@posthog/pi", "npm:context-mode"]
13
7
  }
@@ -35,7 +35,7 @@ order = 2
35
35
 
36
36
  [[layers]]
37
37
  name = "agents"
38
- paths = [".pi/agents/*", ".pi/prompts/*", ".agents/skills/*"]
38
+ paths = [".pi/agents/*", ".pi/harness/agents.manifest.json", ".pi/prompts/*", ".agents/skills/*"]
39
39
  order = 3
40
40
  # Agent definitions, prompts, and skills
41
41
 
package/AGENTS.md CHANGED
@@ -30,7 +30,7 @@ Created: 2026-05-14
30
30
  - Harness context: **context-mode only** — never lean-ctx on harness paths (see harness-context skill)
31
31
  - `graphify update .` after significant code changes
32
32
  - ast-grep (`sg`) is the default code search tool — use `sg -p 'pattern'` for structural search, never grep for code
33
- - Self-hosted Firecrawl at http://localhost:3002 (FIRECRAWL_API_URL)
33
+ - Web fetch/search via `python3 "$UP_PKG/.pi/scripts/harness-web.py"` (Scrapling; see scrapling-web skill)
34
34
 
35
35
  ## graphify
36
36
 
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [Unreleased]
6
+
7
+ ## [v0.4.1] — 2026-05-17
8
+
9
+ ### ✨ Features
10
+
11
+ - **In-house VCC compaction:** vendored [pi-vcc](https://github.com/sting8k/pi-vcc) (inspired by [lllyasviel/VCC](https://github.com/lllyasviel/VCC)); removed `@sting8k/pi-vcc` npm dependency. Configuration is **env-only** (`HARNESS_VCC_COMPACTION`, `HARNESS_VCC_DEBUG`) — no `PI_VCC_CONFIG_PATH` or JSON config files. VCC overrides `/compact` and auto-compaction by default; set `HARNESS_VCC_COMPACTION=false` for Pi LLM compaction. Refresh upstream: `npm run vendor:sync-vcc`.
12
+ - **Harness subagents:** vendored harness-subagents extension with Sentrux bootstrap agent and related harness wiring.
13
+ - **Harness web:** replace Firecrawl with Scrapling-based `harness-web` CLI for search and fetch.
14
+
15
+ ### 🔧 Chores
16
+
17
+ - Format `.pi/settings.json` / `.pi/settings.example.json` and refresh `graphify-out` after VCC merge.
18
+
5
19
  ## [v0.3.1] — 2026-05-15
6
20
 
7
21
  ### 🐛 Fixes
@@ -6,3 +6,11 @@
6
6
  - **License:** MIT ([vendor/pi-model-router/LICENSE](vendor/pi-model-router/LICENSE))
7
7
  - **Pinned revision:** See [vendor/pi-model-router/UPSTREAM_PIN.md](vendor/pi-model-router/UPSTREAM_PIN.md)
8
8
  - ultimate-pi loads it from [`vendor/pi-model-router`](vendor/pi-model-router); import specifiers were adapted for `@mariozechner/pi-coding-agent` and related Pi packages.
9
+
10
+ ## pi-vcc (vendored)
11
+
12
+ - **Project:** https://github.com/sting8k/pi-vcc
13
+ - **Conceptual basis:** https://github.com/lllyasviel/VCC (View-oriented Conversation Compiler)
14
+ - **License:** MIT (see upstream repository)
15
+ - **Pinned revision:** See [vendor/pi-vcc/UPSTREAM_PIN.md](vendor/pi-vcc/UPSTREAM_PIN.md)
16
+ - ultimate-pi loads it from [`vendor/pi-vcc`](vendor/pi-vcc) via [`.pi/extensions/ultimate-pi-vcc.ts`](.pi/extensions/ultimate-pi-vcc.ts). Harness configuration is env-only: `HARNESS_VCC_COMPACTION`, `HARNESS_VCC_DEBUG` ([`.pi/extensions/lib/harness-vcc-settings.ts`](.pi/extensions/lib/harness-vcc-settings.ts)). Maintainer refresh: `npm run vendor:sync-vcc`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-pi",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "Ultimate AI coding harness for pi.dev — extensible skills, Obsidian wiki knowledge layer, compressed context, deterministic output",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -12,7 +12,8 @@
12
12
  "knowledge-base",
13
13
  "context-compression",
14
14
  "agent-skills",
15
- "firecrawl",
15
+ "scrapling",
16
+ "harness-web",
16
17
  "context-mode",
17
18
  "vcc"
18
19
  ],
@@ -44,12 +45,14 @@
44
45
  ".pi/scripts",
45
46
  ".pi/lib",
46
47
  ".pi/sounds",
48
+ ".pi/harness/env.harness.template",
47
49
  ".pi/harness/specs",
48
50
  ".pi/harness/docs",
49
51
  ".pi/harness/sentrux",
50
52
  ".pi/harness/evals",
51
53
  ".pi/harness/evolution",
52
54
  ".pi/harness/corpus",
55
+ ".pi/harness/agents.manifest.json",
53
56
  ".pi/harness/README.md",
54
57
  ".pi/npm/package.json",
55
58
  ".pi/npm/.gitignore",
@@ -57,32 +60,33 @@
57
60
  ".pi/settings.example.json",
58
61
  ".pi/auto-commit.json",
59
62
  ".pi/mcp.json",
60
- ".pi/pi-vcc-config.json",
61
63
  ".pi/SYSTEM.md",
62
64
  ".pi/PACKAGING.md",
63
- "firecrawl/docker-compose.yaml",
64
- "firecrawl/.env.template",
65
- "firecrawl/README.md",
66
- "firecrawl/searxng",
67
65
  "AGENTS.md",
68
66
  "biome.json",
69
67
  "CHANGELOG.md",
70
68
  "README.md",
71
69
  "THIRD_PARTY_NOTICES.md",
72
- "vendor/pi-model-router"
70
+ "vendor/pi-model-router",
71
+ "vendor/pi-vcc"
73
72
  ],
74
73
  "peerDependencies": {
75
74
  "@mariozechner/pi-coding-agent": "*"
76
75
  },
77
76
  "scripts": {
78
- "check:ts": "tsc --noEmit --target ES2023 --lib ES2023 --moduleResolution nodenext --module nodenext --skipLibCheck .pi/extensions/dotenv-loader.ts .pi/extensions/lib/posthog-node.d.ts .pi/extensions/lib/harness-posthog.ts .pi/extensions/lib/harness-paths.ts .pi/extensions/pi-model-router-harness.ts .pi/extensions/provider-payload-sanitize.ts .pi/extensions/harness-telemetry.ts .pi/extensions/trace-recorder.ts .pi/extensions/observation-bus.ts .pi/extensions/drift-monitor.ts .pi/extensions/sentrux-rules-sync.ts .pi/extensions/custom-header.ts",
77
+ "check:ts": "tsc --noEmit --target ES2023 --lib ES2023 --moduleResolution nodenext --module nodenext --skipLibCheck .pi/extensions/lib/harness-vcc-settings.ts .pi/extensions/dotenv-loader.ts .pi/extensions/lib/posthog-node.d.ts .pi/extensions/lib/harness-posthog.ts .pi/extensions/lib/harness-paths.ts .pi/extensions/pi-model-router-harness.ts .pi/extensions/provider-payload-sanitize.ts .pi/extensions/harness-telemetry.ts .pi/extensions/harness-ask-user.ts .pi/extensions/lib/ask-user/schema.ts .pi/extensions/lib/ask-user/types.ts .pi/extensions/lib/ask-user/validate.ts .pi/extensions/lib/ask-user/dialog.ts .pi/extensions/lib/ask-user/fallback.ts .pi/extensions/lib/ask-user/render.ts .pi/extensions/trace-recorder.ts .pi/extensions/observation-bus.ts .pi/extensions/drift-monitor.ts .pi/extensions/sentrux-rules-sync.ts .pi/extensions/custom-header.ts .pi/extensions/lib/harness-subagents/agent-loader.ts .pi/extensions/lib/harness-subagents/agent-parser.ts .pi/extensions/lib/harness-subagents/agent-manifest.ts .pi/extensions/lib/harness-subagents/blackboard.ts .pi/extensions/lib/harness-subagents/blackboard-tool.ts .pi/extensions/lib/harness-subagents/spawn-policy.ts .pi/extensions/lib/harness-subagents/types-blackboard.ts",
79
78
  "vendor:sync-router": "bash .pi/scripts/vendor-sync-pi-model-router.sh",
79
+ "vendor:sync-vcc": "bash .pi/scripts/vendor-sync-pi-vcc.sh",
80
+ "release": "bash .pi/scripts/release.sh",
80
81
  "lint": "biome check",
81
82
  "lint:fix": "biome check --fix",
82
83
  "format": "biome format --write",
83
84
  "format:check": "biome format",
84
85
  "prepare": "lefthook install",
85
- "test": "node --test test/harness-verify.test.mjs",
86
+ "test": "node --test test/harness-verify.test.mjs test/harness-ask-user.test.mjs test/harness-subagents-loader.test.mjs test/sentrux-rules-sync.test.mjs && npx -y tsx --test test/harness-vcc-settings.test.ts",
87
+ "test:vcc": "npx -y tsx --test vendor/pi-vcc/tests/*.test.ts",
88
+ "harness:sentrux-bootstrap": "node .pi/scripts/harness-sentrux-bootstrap.mjs",
89
+ "harness:sentrux-sync": "node .pi/scripts/sentrux-rules-sync.mjs --force",
86
90
  "test:integration": "npx -y tsx --test test/cursor-sdk-provider.integration.test.ts"
87
91
  },
88
92
  "devDependencies": {
@@ -97,10 +101,10 @@
97
101
  },
98
102
  "dependencies": {
99
103
  "@posthog/pi": "latest",
100
- "@sting8k/pi-vcc": "^0.3.12",
101
- "@tintinweb/pi-subagents": "latest",
102
104
  "asciify-image": "^0.1.10",
105
+ "croner": "^9.0.0",
103
106
  "jimp": "^1.6.1",
107
+ "nanoid": "^5.1.5",
104
108
  "posthog-node": "^5.30.6"
105
109
  }
106
110
  }
@@ -0,0 +1,215 @@
1
+ # pi-vcc
2
+
3
+ [![npm](https://img.shields.io/npm/v/@sting8k/pi-vcc)](https://www.npmjs.com/package/@sting8k/pi-vcc)
4
+
5
+ Algorithmic conversation compactor for [Pi](https://github.com/badlogic/pi-mono). No LLM calls — produces a brief transcript via extraction and formatting.
6
+
7
+ Inspired by [VCC](https://github.com/lllyasviel/VCC) **(View-oriented Conversation Compiler)**.
8
+
9
+ ## Demo
10
+
11
+ ![pi-vcc demo](./demo.gif)
12
+
13
+ ## Why pi-vcc
14
+
15
+ | | Pi default | pi-vcc |
16
+ |---|---|---|
17
+ | **Method** | LLM-generated summary | Algorithmic extraction, no LLM |
18
+ | **Determinism** | Non-deterministic, can hallucinate | Same input = same output, always |
19
+ | **Token reduction** | Varies | 35-99% on real sessions (higher on longer sessions) |
20
+ | **Compaction latency** | Waits for LLM call | 30-470ms, no API calls |
21
+ | **History after compaction** | Gone — agent only sees summary | Active lineage searchable via `vcc_recall` (`scope:"all"` available) |
22
+ | **Repeated compactions** | Each rewrite risks losing more | Sections merge and accumulate |
23
+ | **Cost** | Burns tokens on summarization call | Zero — no API calls |
24
+ | **Structure** | Free-form prose | Brief transcript + 4 semantic sections |
25
+
26
+ ### Real session metrics
27
+
28
+ Measured on real session JSONLs under `~/.pi/agent/sessions` (chars = rendered message text).
29
+
30
+ | Session | Messages | Before | After | Reduction | Time |
31
+ |---|---|---|---|---|---|
32
+ | Session A | 2,943 | 997,162 | 7,959 | 99.2% | 64ms |
33
+ | Session B | 1,703 | 428,334 | 7,762 | 98.2% | 29ms |
34
+ | Session C | 1,657 | 424,183 | 9,577 | 97.7% | 54ms |
35
+ | Session D | 1,004 | 2,258,477 | 4,439 | 99.8% | 30ms |
36
+ | Session E | 486 | 295,006 | 11,163 | 96.2% | 30ms |
37
+ | Session F | 46 | 5,234 | 3,364 | 35.7% | 5ms |
38
+ | Session G | 27 | 8,595 | 2,489 | 71.0% | 2ms |
39
+
40
+ ## Features
41
+
42
+ - **No LLM** — purely algorithmic, zero extra API cost
43
+ - **Brief transcript** — chronological conversation flow, each tool call collapsed to a one-liner with `(#N)` refs, text truncated to keep it compact
44
+ - **5 semantic sections** — session goal, files & changes, commits, outstanding context, user preferences
45
+ - **Bounded merge** — rolling sections re-capped after merge instead of growing unbounded
46
+ - **Lossless recall** — `vcc_recall` reads raw session JSONL, so active-lineage history stays searchable across compactions
47
+ - **Scoped recall** — default search is active lineage; use `scope:"all"` / `scope:all` to intentionally search across all lineages
48
+ - **Regex search** — `vcc_recall` supports regex patterns (`hook|inject`, `fail.*build`) and OR-ranked multi-word queries
49
+ - **Result ranking** — search results ranked by term relevance, rare terms weighted higher than common ones
50
+ - **`/pi-vcc-recall`** — slash command to search history directly, results shown as collapsible message and auto-fed to agent as context
51
+ - **Fallback cut** — still works when Pi core returns nothing to summarize
52
+ - **`/pi-vcc`** — manual compaction on demand
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ pi install npm:@sting8k/pi-vcc
58
+ ```
59
+
60
+ Or from GitHub:
61
+
62
+ ```bash
63
+ pi install https://github.com/sting8k/pi-vcc
64
+ ```
65
+
66
+ Or try without installing:
67
+
68
+ ```bash
69
+ pi -e https://github.com/sting8k/pi-vcc
70
+ ```
71
+
72
+ ## Usage
73
+
74
+ Once installed, pi-vcc registers a `session_before_compact` hook.
75
+
76
+ - Run `/pi-vcc` to trigger pi-vcc compaction manually.
77
+ - By default, `/compact` and auto-threshold compactions still go through pi core (LLM-based). Set `overrideDefaultCompaction: true` in the config to let pi-vcc handle all compaction paths.
78
+ - To search older active-lineage history after compaction, use `vcc_recall`.
79
+ - To intentionally search across all lineages, pass `scope:"all"` to `vcc_recall` or run `/pi-vcc-recall <query> scope:all`.
80
+ - To search and feed results to agent yourself, run `/pi-vcc-recall <query> [page:N]`.
81
+ - Tip: type `/recall` and Pi will autocomplete to `/pi-vcc-recall`.
82
+
83
+ ### How compaction works
84
+
85
+ Pi splits the conversation at the **last user message**. Everything after — the **kept tail** — stays intact and untouched. pi-vcc only summarizes the older portion before that cut point.
86
+
87
+ ### Compacted message structure
88
+
89
+ ```
90
+ [Session Goal]
91
+ - Fix the authentication bug in login flow
92
+ - [Scope change]
93
+ - Also update the session token refresh logic
94
+
95
+ [Files And Changes]
96
+ - Modified: src/auth/session.ts
97
+ - Created: tests/auth-refresh.test.ts
98
+
99
+ [Commits]
100
+ - a1b2c3d: fix(auth): refresh token after password reset
101
+
102
+ [Outstanding Context]
103
+ - lint check still failing on line 42
104
+
105
+ [User Preferences]
106
+ - Prefer Vietnamese responses
107
+ - Always run tests before committing
108
+
109
+ [user]
110
+ Fix the auth bug, users can't log in after password reset
111
+
112
+ [assistant]
113
+ Root cause is a missing token refresh after password reset...
114
+ * bash "bun test tests/auth.test.ts" (#12)
115
+ * edit "src/auth/session.ts" (#14)
116
+ * bash "bun test tests/auth.test.ts" (#16)
117
+ ...(28 earlier lines omitted)
118
+ ```
119
+
120
+ Sections appear only when relevant — a session with no git commits won't have `[Commits]`.
121
+
122
+ **Sections:**
123
+
124
+ | Section | Description |
125
+ |---|---|
126
+ | `[Session Goal]` | Initial goal + scope changes (regex-based extraction) |
127
+ | `[Files And Changes]` | Modified/created files from tool calls (capped, paths trimmed to common root) |
128
+ | `[Commits]` | Git commits made during the session (last 8, hash + first line) |
129
+ | `[Outstanding Context]` | Unresolved items — errors, pending questions |
130
+ | `[User Preferences]` | Regex-extracted from user messages (`always`, `never`, `prefer`...) |
131
+ | Brief transcript | Chronological conversation flow — rolling window of ~120 recent lines, tool calls collapsed to one-liners with `(#N)` refs |
132
+
133
+ **Merge policy:**
134
+ - `Session Goal`, `User Preferences`: concise sticky sections
135
+ - `Outstanding Context`: fresh-only (replaced each compaction)
136
+ - `Files And Changes`, `Commits`: unique union across compactions
137
+ - Brief transcript: rolling window, older lines drop off
138
+
139
+ ## Recall (Lossless History)
140
+
141
+ Pi's default compaction discards old messages permanently. After compaction, the agent only sees the summary.
142
+
143
+ `vcc_recall` bypasses this by reading the raw session JSONL file directly. By default it searches only the active conversation lineage, regardless of how many compactions have happened. Use `scope:"all"` only when you intentionally want to include off-lineage branches.
144
+
145
+ ### Search
146
+
147
+ Queries support **regex** and **multi-word OR logic** ranked by relevance:
148
+
149
+ ```
150
+ vcc_recall({ query: "auth token" }) // active-lineage OR search, ranked
151
+ vcc_recall({ query: "auth token", page: 2 }) // paginated (5 results/page)
152
+ vcc_recall({ query: "hook|inject" }) // regex pattern
153
+ vcc_recall({ query: "fail.*build" }) // regex pattern
154
+ vcc_recall({ query: "auth token", scope: "all" }) // search all lineages
155
+ ```
156
+
157
+ Manual slash command:
158
+
159
+ ```
160
+ /pi-vcc-recall auth token scope:all
161
+ ```
162
+
163
+ ### Browse
164
+
165
+ Without a query, returns the last 25 entries as brief summaries:
166
+
167
+ ```
168
+ vcc_recall()
169
+ vcc_recall({ scope: "all" }) // browse recent entries across all lineages
170
+ ```
171
+
172
+ ### Expand
173
+
174
+ Returns full untruncated content for specific indices found via search:
175
+
176
+ ```
177
+ vcc_recall({ expand: [41, 42] }) // active-lineage expand
178
+ vcc_recall({ expand: [41, 42], scope: "all" }) // expand across all lineages
179
+ ```
180
+
181
+ Typical workflow: **search → find relevant entry indices → expand those indices for full content**.
182
+
183
+ > Some tool results are truncated by Pi core at save time. `expand` returns everything in the JSONL but can't recover what Pi already cut.
184
+
185
+ ## Pipeline
186
+
187
+ 1. **Normalize** — raw Pi messages → uniform blocks (user, assistant, tool_call, tool_result, thinking)
188
+ 2. **Filter noise** — strip system messages, empty blocks
189
+ 3. **Build sections** — extract goal, file paths, blockers, preferences
190
+ 4. **Brief transcript** — chronological conversation flow, tool calls collapsed to one-liners, text truncated
191
+ 5. **Format** — render into bracketed sections + transcript
192
+ 6. **Merge** — if previous summary exists: sticky sections merge, volatile sections replace, transcript rolls
193
+
194
+ ## Config
195
+
196
+ Config lives at `~/.pi/agent/pi-vcc-config.json` (auto-scaffolded on first load with safe defaults):
197
+
198
+ ```json
199
+ {
200
+ "overrideDefaultCompaction": false,
201
+ "debug": false
202
+ }
203
+ ```
204
+
205
+ - **`overrideDefaultCompaction`** *(default `false`)*: when `false`, pi-vcc only runs for `/pi-vcc`; `/compact` and auto-threshold compactions fall through to pi core. Set `true` to make pi-vcc handle all compaction paths.
206
+ - **`debug`** *(default `false`)*: when `true`, each compaction writes detailed info to `/tmp/pi-vcc-debug.json` — message counts, cut boundary, summary preview, sections.
207
+
208
+ ## Related Work
209
+
210
+ - [VCC](https://github.com/lllyasviel/VCC) — the original transcript-preserving conversation compiler
211
+ - [Pi](https://github.com/badlogic/pi-mono) — the AI coding agent this extension is built for
212
+
213
+ ## License
214
+
215
+ MIT
@@ -0,0 +1,12 @@
1
+ # Vendored `pi-vcc`
2
+
3
+ - **Repository:** https://github.com/sting8k/pi-vcc
4
+ - **Conceptual basis:** [lllyasviel/VCC](https://github.com/lllyasviel/VCC) (View-oriented Conversation Compiler)
5
+ - **License:** MIT (see upstream repository)
6
+ - **Pinned upstream commit:** `3e0b49e4ef605370e3dd92889e6b70502262cc28`
7
+ - **Local changes:**
8
+ - `src/core/settings.ts` re-exports env-only [`.pi/extensions/lib/harness-vcc-settings.ts`](../../.pi/extensions/lib/harness-vcc-settings.ts) (`HARNESS_VCC_COMPACTION`, `HARNESS_VCC_DEBUG`)
9
+ - No `scaffoldSettings` / no `PI_VCC_CONFIG_PATH`
10
+ - Compaction `details.compactor` is `ultimate-pi-vcc`
11
+
12
+ **Refresh upstream:** run `npm run vendor:sync-vcc` from ultimate-pi root.
Binary file
@@ -0,0 +1,12 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ import { registerBeforeCompactHook } from "./src/hooks/before-compact";
3
+ import { registerPiVccCommand } from "./src/commands/pi-vcc";
4
+ import { registerVccRecallCommand } from "./src/commands/vcc-recall";
5
+ import { registerRecallTool } from "./src/tools/recall";
6
+
7
+ export default (pi: ExtensionAPI) => {
8
+ registerBeforeCompactHook(pi);
9
+ registerPiVccCommand(pi);
10
+ registerVccRecallCommand(pi);
11
+ registerRecallTool(pi);
12
+ };
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@sting8k/pi-vcc",
3
+ "version": "0.3.13",
4
+ "description": "Algorithmic conversation compactor for pi - transcript-preserving structured summaries, no LLM calls",
5
+ "main": "index.ts",
6
+ "keywords": [
7
+ "pi-package",
8
+ "pi-extension",
9
+ "vcc",
10
+ "compact",
11
+ "compaction"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/sting8k/pi-vcc.git"
16
+ },
17
+ "peerDependencies": {
18
+ "@mariozechner/pi-coding-agent": "*",
19
+ "@sinclair/typebox": "*"
20
+ },
21
+ "pi": {
22
+ "extensions": [
23
+ "./index.ts"
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,88 @@
1
+ import { basename, dirname } from "node:path";
2
+ import { compile } from "../src/core/summarize";
3
+ import { normalize } from "../src/core/normalize";
4
+ import { filterNoise } from "../src/core/filter-noise";
5
+ import { renderMessage } from "../src/core/render-entries";
6
+ import { prepareSessionSamples } from "../tests/support/real-sessions";
7
+ import { loadSessionMessages } from "../tests/support/load-session";
8
+
9
+ const SEP = "=".repeat(80);
10
+ const samples = await prepareSessionSamples(10);
11
+
12
+ for (const sample of samples) {
13
+ const loaded = loadSessionMessages(sample.copy);
14
+ const { messages } = loaded;
15
+
16
+ const rawBlocks = normalize(messages);
17
+ const filteredBlocks = filterNoise(rawBlocks);
18
+ const afterText = compile({ messages });
19
+
20
+ const rendered = messages.map((m, i) => renderMessage(m, i));
21
+ const beforeChars = rendered.reduce((s, e) => s + e.summary.length, 0);
22
+
23
+ const project = dirname(sample.source).split("--").filter(Boolean).pop() ?? "unknown";
24
+
25
+ const goalSection = afterText.match(/\[Session Goal\]\n([\s\S]*?)(?=\n\n\[|$)/)?.[1] ?? "(empty)";
26
+ const stateSection = afterText.match(/\[Current State\]\n([\s\S]*?)(?=\n\n\[|$)/)?.[1] ?? "(empty)";
27
+ const doneSection = afterText.match(/\[What Was Done\]\n([\s\S]*?)(?=\n\n\[|$)/)?.[1] ?? "(empty)";
28
+ const problemsSection = afterText.match(/\[Open Problems\]\n([\s\S]*?)(?=\n\n\[|$)/)?.[1] ?? "(empty)";
29
+ const nextSection = afterText.match(/\[Next Best Steps\]\n([\s\S]*?)(?=\n\n\[|$)/)?.[1] ?? "(empty)";
30
+
31
+ const doneLines = doneSection.split("\n").filter(l => l.trim());
32
+ const problemLines = problemsSection.split("\n").filter(l => l.trim());
33
+
34
+ // Detect issues
35
+ const issues: string[] = [];
36
+
37
+ // 1. Goal quality
38
+ const goalLines = goalSection.split("\n").map(l => l.replace(/^- /, "").trim()).filter(Boolean);
39
+ if (goalLines[0] && goalLines[0].length < 5) issues.push(`GOAL_TOO_SHORT: "${goalLines[0]}"`);
40
+ if (goalLines.length === 0) issues.push("GOAL_EMPTY");
41
+
42
+ // 2. Sensitive data in What Was Done
43
+ if (/sshpass|password|secret|token=|api[_-]?key/i.test(doneSection)) {
44
+ issues.push("SENSITIVE_DATA_IN_DONE");
45
+ }
46
+
47
+ // 3. Raw code/minified JS in summary
48
+ if (/\{[a-zA-Z$_]+:[a-zA-Z$_]+,[a-zA-Z$_]+:/.test(afterText) || /var [a-zA-Z]+=/.test(afterText)) {
49
+ issues.push("RAW_CODE_LEAK");
50
+ }
51
+
52
+ // 4. Open problems count
53
+ if (problemLines.length > 10) issues.push(`PROBLEMS_OVERCOUNT: ${problemLines.length}`);
54
+
55
+ // 5. Next steps empty
56
+ if (nextSection === "(empty)") issues.push("NEXT_STEPS_EMPTY");
57
+
58
+ // 6. What Was Done too verbose
59
+ if (doneLines.length > 15) issues.push(`DONE_TOO_VERBOSE: ${doneLines.length} lines`);
60
+
61
+ // 7. Summary too large (>10K chars)
62
+ if (afterText.length > 10000) issues.push(`SUMMARY_TOO_LARGE: ${afterText.length} chars`);
63
+
64
+ console.log(SEP);
65
+ console.log(`PROJECT: ${project}`);
66
+ console.log(`FILE: ${basename(sample.source)}`);
67
+ console.log(`Size: ${(sample.size / 1024).toFixed(0)}KB | Msgs: ${messages.length} | Blocks raw: ${rawBlocks.length} -> filtered: ${filteredBlocks.length}`);
68
+ console.log(`Before: ${beforeChars} chars | After: ${afterText.length} chars | Ratio: ${(beforeChars / afterText.length).toFixed(1)}x`);
69
+ console.log(`Issues: ${issues.length === 0 ? "NONE" : issues.join(", ")}`);
70
+ console.log("");
71
+ console.log("--- GOAL ---");
72
+ console.log(goalSection.slice(0, 300));
73
+ console.log("");
74
+ console.log("--- CURRENT STATE (first 300c) ---");
75
+ console.log(stateSection.slice(0, 300));
76
+ console.log("");
77
+ console.log("--- WHAT WAS DONE (first 5 lines) ---");
78
+ console.log(doneLines.slice(0, 5).join("\n"));
79
+ console.log(`... (${doneLines.length} total lines)`);
80
+ console.log("");
81
+ console.log("--- OPEN PROBLEMS (first 5 lines) ---");
82
+ console.log(problemLines.slice(0, 5).join("\n"));
83
+ console.log(`... (${problemLines.length} total lines)`);
84
+ console.log("");
85
+ console.log("--- NEXT STEPS ---");
86
+ console.log(nextSection.slice(0, 300));
87
+ console.log("");
88
+ }
@@ -0,0 +1,25 @@
1
+ import { performance } from "node:perf_hooks";
2
+ import { basename } from "node:path";
3
+ import { buildCompactReport } from "../src/core/report";
4
+ import { prepareSessionSamples } from "../tests/support/real-sessions";
5
+ import { loadSessionMessages } from "../tests/support/load-session";
6
+
7
+ const samples = await prepareSessionSamples(2);
8
+ for (const sample of samples) {
9
+ const loaded = loadSessionMessages(sample.copy);
10
+ const start = performance.now();
11
+ const report = buildCompactReport({ messages: loaded.messages });
12
+ const elapsedMs = performance.now() - start;
13
+ console.log(JSON.stringify({
14
+ sourceFile: basename(sample.source),
15
+ sourceSizeBytes: sample.size,
16
+ copiedToTemp: true,
17
+ loadedMessages: loaded.messageCount,
18
+ skippedMessages: loaded.skippedCount,
19
+ compileMs: Number(elapsedMs.toFixed(2)),
20
+ before: report.before,
21
+ after: report.after,
22
+ compression: report.compression,
23
+ recall: report.recall,
24
+ }, null, 2));
25
+ }
@@ -0,0 +1,36 @@
1
+ import { basename } from "node:path";
2
+ import { compile } from "../src/core/summarize";
3
+ import { renderMessage } from "../src/core/render-entries";
4
+ import { clip } from "../src/core/content";
5
+ import { prepareSessionSamples } from "../tests/support/real-sessions";
6
+ import { loadSessionMessages } from "../tests/support/load-session";
7
+
8
+ const SEP = "=".repeat(80);
9
+ const samples = await prepareSessionSamples(2);
10
+
11
+ for (const sample of samples) {
12
+ const loaded = loadSessionMessages(sample.copy);
13
+ const { messages } = loaded;
14
+
15
+ const rendered = messages.map((m, i) => renderMessage(m, i));
16
+ const beforeLines = rendered.map(
17
+ (e) => `#${e.index} [${e.role}] ${clip(e.summary, 300)}`,
18
+ );
19
+ const beforeText = beforeLines.join("\n");
20
+ const afterText = compile({ messages });
21
+
22
+ console.log(SEP);
23
+ console.log(`FILE: ${basename(sample.source)}`);
24
+ console.log(`Messages: ${messages.length} | Before chars: ${beforeText.length} | After chars: ${afterText.length}`);
25
+ console.log(`Compression: ${(beforeText.length / afterText.length).toFixed(1)}x`);
26
+ console.log(SEP);
27
+
28
+ console.log("\n--- BEFORE (raw context, first 40 + last 20 entries) ---\n");
29
+ for (const line of beforeLines.slice(0, 40)) console.log(line);
30
+ if (beforeLines.length > 60) console.log(`\n... (${beforeLines.length - 60} entries omitted) ...\n`);
31
+ for (const line of beforeLines.slice(-20)) console.log(line);
32
+
33
+ console.log("\n--- AFTER (pi-vcc compiled summary) ---\n");
34
+ console.log(afterText);
35
+ console.log("\n");
36
+ }
@@ -0,0 +1,20 @@
1
+ import { basename } from "node:path";
2
+ import { compile } from "../src/core/summarize";
3
+ import { prepareSessionSamples } from "../tests/support/real-sessions";
4
+ import { loadSessionMessages } from "../tests/support/load-session";
5
+ import { writeFileSync, mkdirSync } from "node:fs";
6
+
7
+ const outDir = "/tmp/pi-vcc-compare";
8
+ const branch = process.argv[2] || "unknown";
9
+ mkdirSync(`${outDir}/${branch}`, { recursive: true });
10
+
11
+ const samples = await prepareSessionSamples(5);
12
+ for (const sample of samples) {
13
+ const name = basename(sample.source).slice(0, 30);
14
+ const loaded = loadSessionMessages(sample.copy);
15
+ const summary = compile({ messages: loaded.messages });
16
+ const outFile = `${outDir}/${branch}/${name}.txt`;
17
+ writeFileSync(outFile, summary);
18
+ console.log(`${name} (${loaded.messageCount} msgs) => ${summary.length} chars`);
19
+ }
20
+ console.log(`\nSaved to ${outDir}/${branch}/`);
@@ -0,0 +1,36 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ import { getLastCompactionStats, PI_VCC_COMPACT_INSTRUCTION } from "../hooks/before-compact";
3
+
4
+ const formatTokens = (n: number): string => {
5
+ if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;
6
+ return String(n);
7
+ };
8
+
9
+ export const registerPiVccCommand = (pi: ExtensionAPI) => {
10
+ pi.registerCommand("pi-vcc", {
11
+ description: "Compact conversation with pi-vcc structured summary",
12
+ handler: async (_args, ctx) => {
13
+ ctx.compact({
14
+ customInstructions: PI_VCC_COMPACT_INSTRUCTION,
15
+ onComplete: () => {
16
+ const stats = getLastCompactionStats();
17
+ if (stats) {
18
+ ctx.ui.notify(
19
+ `pi-vcc: ${stats.summarized} source entries processed; tail kept ${stats.kept} (~${formatTokens(stats.keptTokensEst)} tok).`,
20
+ "info",
21
+ );
22
+ } else {
23
+ ctx.ui.notify("Compacted with pi-vcc", "info");
24
+ }
25
+ },
26
+ onError: (err) => {
27
+ if (err.message === "Compaction cancelled" || err.message === "Already compacted") {
28
+ ctx.ui.notify("Nothing to compact", "warning");
29
+ } else {
30
+ ctx.ui.notify(`Compaction failed: ${err.message}`, "error");
31
+ }
32
+ },
33
+ });
34
+ },
35
+ });
36
+ };