pi-vcc 0.4.0
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/LICENSE +21 -0
- package/README.md +120 -0
- package/demo.gif +0 -0
- package/flow/plans/20260515-1300/plan.md +206 -0
- package/index.ts +14 -0
- package/package.json +36 -0
- package/pi-vcc-config.schema.json +131 -0
- package/scripts/audit-sessions.ts +88 -0
- package/scripts/benchmark-real-sessions.ts +25 -0
- package/scripts/compare-before-after.ts +36 -0
- package/scripts/dump-branch-output.ts +20 -0
- package/src/commands/pi-vcc.ts +33 -0
- package/src/commands/vcc-recall.ts +65 -0
- package/src/core/brief.ts +381 -0
- package/src/core/build-sections.ts +87 -0
- package/src/core/content.ts +60 -0
- package/src/core/filter-noise.ts +42 -0
- package/src/core/format-recall.ts +27 -0
- package/src/core/format.ts +56 -0
- package/src/core/lineage.ts +26 -0
- package/src/core/load-messages.ts +63 -0
- package/src/core/normalize.ts +66 -0
- package/src/core/recall-scope.ts +14 -0
- package/src/core/render-entries.ts +68 -0
- package/src/core/report.ts +237 -0
- package/src/core/sanitize.ts +5 -0
- package/src/core/search-entries.ts +230 -0
- package/src/core/settings.ts +215 -0
- package/src/core/skill-collapse.ts +35 -0
- package/src/core/summarize.ts +159 -0
- package/src/core/tool-args.ts +14 -0
- package/src/details.ts +7 -0
- package/src/extract/commits.ts +69 -0
- package/src/extract/files.ts +80 -0
- package/src/extract/goals.ts +79 -0
- package/src/extract/preferences.ts +55 -0
- package/src/extract/references.ts +214 -0
- package/src/extract/signals.ts +145 -0
- package/src/hooks/before-compact.ts +405 -0
- package/src/sections.ts +14 -0
- package/src/tools/recall.ts +109 -0
- package/src/types.ts +14 -0
- package/tests/before-compact-hook.test.ts +181 -0
- package/tests/before-compact.test.ts +140 -0
- package/tests/brief.test.ts +206 -0
- package/tests/build-sections.test.ts +90 -0
- package/tests/compile.test.ts +110 -0
- package/tests/config-integration.test.ts +107 -0
- package/tests/content.test.ts +31 -0
- package/tests/edge-cases.test.ts +368 -0
- package/tests/extract-goals.test.ts +86 -0
- package/tests/extract-preferences.test.ts +30 -0
- package/tests/extract-references.test.ts +475 -0
- package/tests/extract-signals.test.ts +561 -0
- package/tests/filter-noise.test.ts +61 -0
- package/tests/fixtures.ts +61 -0
- package/tests/format-recall.test.ts +30 -0
- package/tests/format.test.ts +91 -0
- package/tests/lineage.test.ts +33 -0
- package/tests/load-messages.test.ts +51 -0
- package/tests/normalize.test.ts +97 -0
- package/tests/real-sessions.test.ts +38 -0
- package/tests/recall-expand.test.ts +15 -0
- package/tests/recall-scope.test.ts +32 -0
- package/tests/recall-tool-scope.test.ts +67 -0
- package/tests/render-entries.test.ts +62 -0
- package/tests/report.test.ts +44 -0
- package/tests/sanitize.test.ts +24 -0
- package/tests/search-entries.test.ts +144 -0
- package/tests/settings-scaffold.test.ts +120 -0
- package/tests/settings.test.ts +32 -0
- package/tests/support/load-session.ts +23 -0
- package/tests/support/real-sessions.ts +51 -0
- package/tsconfig.json +14 -0
- package/vitest.config.ts +7 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 buihongduc132
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# pi-vcc
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@sting8k/pi-vcc)
|
|
4
|
+
[](https://spdx.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Algorithmic conversation compactor for [Pi](https://github.com/badlogic/pi-mono). No LLM calls — produces a brief transcript via extraction and formatting.
|
|
7
|
+
|
|
8
|
+
Inspired by [VCC](https://github.com/lllyasviel/VCC) **(View-oriented Conversation Compiler)**.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Zero LLM calls** — deterministic, same input always produces same output
|
|
13
|
+
- **35–99% token reduction** on real sessions (higher on longer sessions)
|
|
14
|
+
- **30–470ms latency** — no API calls, pure algorithmic extraction
|
|
15
|
+
- **4 semantic sections** — Session Goal, Outstanding Context, References, Key Signals
|
|
16
|
+
- **Brief transcript** with tool-call rollup and noise filtering
|
|
17
|
+
- **History search** via `vcc_recall` tool (lineage-scoped, expand, pagination)
|
|
18
|
+
- **Section merging** across repeated compactions with deduplication
|
|
19
|
+
- **Configurable extraction** — additive patterns for URLs, GitHub refs, versions, constraints, decisions, statuses
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### For humans (npm)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @sting8k/pi-vcc
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### For AI agents (pi settings.json)
|
|
30
|
+
|
|
31
|
+
Add to your pi `settings.json` packages array:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"packages": ["@sting8k/pi-vcc"]
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Git-sourced
|
|
40
|
+
|
|
41
|
+
In `settings.json`, reference the repo directly:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"packages": ["github:buihongduc132/pi-vcc"]
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or clone into `profile/git/github.com/buihongduc132/`:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git clone https://github.com/buihongduc132/pi-vcc.git profile/git/github.com/buihongduc132/pi-vcc
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
Once installed, pi-vcc registers two hooks and one tool automatically:
|
|
58
|
+
|
|
59
|
+
### Commands
|
|
60
|
+
|
|
61
|
+
| Command | Description |
|
|
62
|
+
|---------|-------------|
|
|
63
|
+
| `/pi-vcc` | Trigger pi-vcc compaction explicitly |
|
|
64
|
+
| `/compact` | Handled by pi-vcc when `overrideDefaultCompaction: true` |
|
|
65
|
+
|
|
66
|
+
### Tool: `vcc_recall`
|
|
67
|
+
|
|
68
|
+
Search session history after compaction:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
vcc_recall({ query: "auth bug", scope: "all", page: 1 })
|
|
72
|
+
vcc_recall({ expand: [0, 3], scope: "all" })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- **Default scope**: active lineage only; use `scope: "all"` for off-lineage branches
|
|
76
|
+
- Supports regex queries, pagination, and expand indices
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
Config file: `~/.pi/agent/pi-vcc-config.json` (auto-scaffolded with defaults).
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"overrideDefaultCompaction": false,
|
|
85
|
+
"debug": false,
|
|
86
|
+
"extraction": {
|
|
87
|
+
"references": {
|
|
88
|
+
"enabled": true,
|
|
89
|
+
"extraUrlPatterns": [],
|
|
90
|
+
"extraGithubRefPatterns": [],
|
|
91
|
+
"extraVersionPatterns": [],
|
|
92
|
+
"extraBranchPatterns": []
|
|
93
|
+
},
|
|
94
|
+
"keySignals": {
|
|
95
|
+
"enabled": true,
|
|
96
|
+
"extraConstraintPatterns": [],
|
|
97
|
+
"extraDecisionPatterns": [],
|
|
98
|
+
"extraStatusPatterns": []
|
|
99
|
+
},
|
|
100
|
+
"goals": {
|
|
101
|
+
"enabled": true,
|
|
102
|
+
"extraTaskVerbs": [],
|
|
103
|
+
"extraScopeChangeWords": []
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
| Field | Default | Description |
|
|
110
|
+
|-------|---------|-------------|
|
|
111
|
+
| `overrideDefaultCompaction` | `false` | When `true`, pi-vcc handles all compaction triggers |
|
|
112
|
+
| `debug` | `false` | Write debug snapshot to `/tmp/pi-vcc-debug.json` |
|
|
113
|
+
| `extraction.*.enabled` | `true` | Enable/disable individual extraction categories |
|
|
114
|
+
| `extraction.*.extra*` | `[]` | Additive patterns (built-ins never removed) |
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT © [buihongduc132](https://github.com/buihongduc132)
|
|
119
|
+
|
|
120
|
+
Repository: [buihongduc132/pi-vcc](https://github.com/buihongduc132/pi-vcc)
|
package/demo.gif
ADDED
|
Binary file
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Plan: Enhance pi-vcc Extractors + Configurable Rules
|
|
2
|
+
|
|
3
|
+
## User Context (verbatim)
|
|
4
|
+
|
|
5
|
+
> "Do it be able to capture: url like ? ALSO what are others important mark / anchor that it should capture but currently it is not? Also what are NLP keywords that it can capture for better information as well?"
|
|
6
|
+
> "make it to be able to configured as a config file as well (JUST adding, not removing the rules)"
|
|
7
|
+
> "TDD approach; must have verifier loop; LOTS of test cases"
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
pi-vcc compaction loses critical information:
|
|
12
|
+
1. **URLs** — explicitly filtered OUT by `NON_GOAL_RE` in `goals.ts`
|
|
13
|
+
2. **GitHub refs** — `#42`, `PR #7`, `@user`, `owner/repo` not captured
|
|
14
|
+
3. **Version/context anchors** — semver, branch names, commit refs, error codes
|
|
15
|
+
4. **NLP signals** — constraints, decisions, status changes not extracted
|
|
16
|
+
5. **No configurability** — all regexes are hardcoded, users can't tune them
|
|
17
|
+
|
|
18
|
+
## Declarative Target State
|
|
19
|
+
|
|
20
|
+
### 1. New Section: `[References]` in output
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
[References]
|
|
24
|
+
- URL: https://docs.example.com/api/v2
|
|
25
|
+
- URL: http://100.114.135.99:4747
|
|
26
|
+
- GitHub: #42, PR #7, buihongduc132/pi-plugins
|
|
27
|
+
- Version: v2.3.1, @1.0.0
|
|
28
|
+
- Branch: feat/new-auth
|
|
29
|
+
- CommitRef: abc1234
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2. New Section: `[Key Signals]` in output
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
[Key Signals]
|
|
36
|
+
- Constraint: must not push to main directly
|
|
37
|
+
- Decision: use Redis for caching instead of in-process LRU
|
|
38
|
+
- Status: DONE — auth module migrated
|
|
39
|
+
- Blocker: CI fails on Node 18
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3. Configurable extraction rules via `pi-vcc-config.json`
|
|
43
|
+
|
|
44
|
+
```jsonc
|
|
45
|
+
{
|
|
46
|
+
// Existing settings preserved
|
|
47
|
+
"overrideDefaultCompaction": false,
|
|
48
|
+
"debug": false,
|
|
49
|
+
|
|
50
|
+
// NEW: extraction config (additive only — defaults match current behavior)
|
|
51
|
+
"extraction": {
|
|
52
|
+
"references": {
|
|
53
|
+
"enabled": true,
|
|
54
|
+
"urlPatterns": ["https?://\\S+"], // additive to built-in
|
|
55
|
+
"githubRefPatterns": ["#\\d+", "PR\\s*#\\d+"], // additive
|
|
56
|
+
"versionPatterns": ["v?\\d+\\.\\d+\\.\\d+"],
|
|
57
|
+
"branchPatterns": ["\\b(?:feat|fix|main|develop)/[\\w-]+"]
|
|
58
|
+
},
|
|
59
|
+
"keySignals": {
|
|
60
|
+
"enabled": true,
|
|
61
|
+
"constraintPatterns": ["\\b(must not|don'?t|cannot|forbidden|never)\\b"],
|
|
62
|
+
"decisionPatterns": ["\\b(decided|let'?s use|going with|chose)\\b"],
|
|
63
|
+
"statusPatterns": ["\\b(DONE|TODO|WIP|blocked|resolved)\\b"]
|
|
64
|
+
},
|
|
65
|
+
"goals": {
|
|
66
|
+
// Can ADD patterns to existing extractors, never remove
|
|
67
|
+
"extraTaskVerbs": [],
|
|
68
|
+
"extraScopeChangeWords": []
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 4. No breaking changes
|
|
75
|
+
|
|
76
|
+
- All existing extractors unchanged
|
|
77
|
+
- Existing tests must continue to pass
|
|
78
|
+
- New sections appear AFTER existing sections in output
|
|
79
|
+
- Config defaults = current behavior + new extractors enabled
|
|
80
|
+
|
|
81
|
+
## Implementation Tasks (TDD)
|
|
82
|
+
|
|
83
|
+
### Phase 1: New Extractor — References
|
|
84
|
+
|
|
85
|
+
| # | Task | Files | Test First |
|
|
86
|
+
|---|------|-------|------------|
|
|
87
|
+
| 1.1 | Create `src/extract/references.ts` — extract URLs, GitHub refs, versions, branches, commit refs | `extract/references.ts` | `tests/extract-references.test.ts` |
|
|
88
|
+
| 1.2 | Wire into `buildSections` — add `references` field to `SectionData` | `sections.ts`, `build-sections.ts` | `tests/build-sections.test.ts` |
|
|
89
|
+
| 1.3 | Wire into `formatSummary` — render `[References]` section | `format.ts` | `tests/format.test.ts` |
|
|
90
|
+
| 1.4 | Wire into `mergePrevious` — merge references across compactions | `summarize.ts` | `tests/compile.test.ts` |
|
|
91
|
+
|
|
92
|
+
### Phase 2: New Extractor — Key Signals
|
|
93
|
+
|
|
94
|
+
| # | Task | Files | Test First |
|
|
95
|
+
|---|------|-------|------------|
|
|
96
|
+
| 2.1 | Create `src/extract/signals.ts` — extract constraints, decisions, status markers | `extract/signals.ts` | `tests/extract-signals.test.ts` |
|
|
97
|
+
| 2.2 | Wire into `buildSections` + `formatSummary` + `mergePrevious` | `sections.ts`, `build-sections.ts`, `format.ts`, `summarize.ts` | extend existing tests |
|
|
98
|
+
|
|
99
|
+
### Phase 3: Configurable Rules
|
|
100
|
+
|
|
101
|
+
| # | Task | Files | Test First |
|
|
102
|
+
|---|------|-------|------------|
|
|
103
|
+
| 3.1 | Extend `PiVccSettings` with `extraction` config schema | `settings.ts` | `tests/settings.test.ts` |
|
|
104
|
+
| 3.2 | Pass config to extractors — patterns become configurable (defaults = current) | all extractors | extend all extractor tests |
|
|
105
|
+
| 3.3 | Fix `goals.ts` `NON_GOAL_RE` — URLs no longer filtered from goals (moved to dedicated extractor) | `extract/goals.ts` | `tests/extract-goals.test.ts` |
|
|
106
|
+
|
|
107
|
+
### Phase 4: Comprehensive Test Coverage
|
|
108
|
+
|
|
109
|
+
| # | Task |
|
|
110
|
+
|---|------|
|
|
111
|
+
| 4.1 | Edge cases: multi-line URLs, malformed refs, duplicate dedup |
|
|
112
|
+
| 4.2 | Config override: custom patterns replace defaults |
|
|
113
|
+
| 4.3 | Merge across compactions: references persist correctly |
|
|
114
|
+
| 4.4 | Backwards compat: output format identical when new sections empty |
|
|
115
|
+
|
|
116
|
+
## Test Cases (Detailed)
|
|
117
|
+
|
|
118
|
+
### extract-references.test.ts
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
URLs:
|
|
122
|
+
- http://example.com
|
|
123
|
+
- https://docs.site.com/api/v2#section
|
|
124
|
+
- http://100.114.135.99:4747
|
|
125
|
+
- file:///path/to/file
|
|
126
|
+
- URL inside markdown [text](url)
|
|
127
|
+
- Multiple URLs in one message
|
|
128
|
+
- URL at line boundary
|
|
129
|
+
- Dedup same URL across blocks
|
|
130
|
+
- Skip URLs that are only tool_result noise (large JSON dumps)
|
|
131
|
+
|
|
132
|
+
GitHub refs:
|
|
133
|
+
- #42, #1234
|
|
134
|
+
- PR #7, pr#12
|
|
135
|
+
- Issue #5
|
|
136
|
+
- @username mentions
|
|
137
|
+
- owner/repo paths (e.g., buihongduc132/pi-plugins)
|
|
138
|
+
- Full GitHub URLs (https://github.com/owner/repo/issues/42)
|
|
139
|
+
|
|
140
|
+
Versions:
|
|
141
|
+
- v1.2.3, v0.3.12
|
|
142
|
+
- @1.0.0, @^2.0.0
|
|
143
|
+
- semver ranges: >=1.0.0 <2.0.0
|
|
144
|
+
|
|
145
|
+
Branches:
|
|
146
|
+
- feat/new-auth, fix/login-bug
|
|
147
|
+
- main, develop, master (only if context suggests branch ref)
|
|
148
|
+
|
|
149
|
+
Commit refs:
|
|
150
|
+
- abc1234, abc1234567 (7-12 hex chars, standalone)
|
|
151
|
+
- NOT inside git commit output (already handled by commits extractor)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### extract-signals.test.ts
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
Constraints:
|
|
158
|
+
- "must not push to main directly"
|
|
159
|
+
- "don't use any external deps"
|
|
160
|
+
- "cannot modify the public API"
|
|
161
|
+
- "forbidden to write to /etc"
|
|
162
|
+
- CONSTRAINT_RE from summary: /\b(don'?t|must not|cannot|forbidden|disallowed|off[- ]limits|out of scope|excluded|do not)\b/i
|
|
163
|
+
|
|
164
|
+
Decisions:
|
|
165
|
+
- "decided to use Redis"
|
|
166
|
+
- "let's go with approach B"
|
|
167
|
+
- "going with the microservice pattern"
|
|
168
|
+
- "chose SQLite for simplicity"
|
|
169
|
+
|
|
170
|
+
Status markers:
|
|
171
|
+
- "DONE — auth migrated"
|
|
172
|
+
- "WIP: still debugging"
|
|
173
|
+
- "TODO: add tests"
|
|
174
|
+
- "blocked on upstream fix"
|
|
175
|
+
- "resolved: was a typo"
|
|
176
|
+
|
|
177
|
+
Negatives (should NOT match):
|
|
178
|
+
- Questions ("should we use X?")
|
|
179
|
+
- Hypotheticals ("what if we can't?")
|
|
180
|
+
- Code comments in tool results
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Config tests
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
- Default config = all extractors enabled with built-in patterns
|
|
187
|
+
- Custom patterns additive (append to defaults)
|
|
188
|
+
- Disable specific extractor via enabled: false
|
|
189
|
+
- Invalid config gracefully ignored (fallback to defaults)
|
|
190
|
+
- Config hot-reload (re-read on each compaction)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Risk Assessment
|
|
194
|
+
|
|
195
|
+
| Risk | Level | Mitigation |
|
|
196
|
+
|------|-------|------------|
|
|
197
|
+
| Breaking existing output format | LOW | New sections only; existing untouched |
|
|
198
|
+
| Performance (regex on every block) | LOW | Patterns pre-compiled; only user/assistant blocks scanned |
|
|
199
|
+
| False positives (refs in code dumps) | MEDIUM | Filter tool_result blocks; only scan user + assistant |
|
|
200
|
+
| Config migration | LOW | scaffoldSettings fills missing keys |
|
|
201
|
+
|
|
202
|
+
## PR Target
|
|
203
|
+
|
|
204
|
+
`buihongduc132/pi-vcc` (fork) → `sting8k/pi-vcc` (upstream)
|
|
205
|
+
|
|
206
|
+
Branch: `feat/enhance-extractors-configurable`
|
package/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { scaffoldSettings } from "./src/core/settings";
|
|
3
|
+
import { registerBeforeCompactHook } from "./src/hooks/before-compact";
|
|
4
|
+
import { registerPiVccCommand } from "./src/commands/pi-vcc";
|
|
5
|
+
import { registerVccRecallCommand } from "./src/commands/vcc-recall";
|
|
6
|
+
import { registerRecallTool } from "./src/tools/recall";
|
|
7
|
+
|
|
8
|
+
export default (pi: ExtensionAPI) => {
|
|
9
|
+
scaffoldSettings();
|
|
10
|
+
registerBeforeCompactHook(pi);
|
|
11
|
+
registerPiVccCommand(pi);
|
|
12
|
+
registerVccRecallCommand(pi);
|
|
13
|
+
registerRecallTool(pi);
|
|
14
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-vcc",
|
|
3
|
+
"version": "0.4.0",
|
|
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
|
+
"pi",
|
|
10
|
+
"pi-coding-agent",
|
|
11
|
+
"vcc",
|
|
12
|
+
"version-control",
|
|
13
|
+
"compaction"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/buihongduc132/pi-vcc.git"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"prepublishOnly": "npm run typecheck && npm test"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@mariozechner/pi-coding-agent": "*",
|
|
26
|
+
"@sinclair/typebox": "*"
|
|
27
|
+
},
|
|
28
|
+
"pi": {
|
|
29
|
+
"extensions": [
|
|
30
|
+
"./index.ts"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"typescript": "^6.0.3"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/buihongduc132/pi-vcc/pi-vcc-config.schema.json",
|
|
4
|
+
"title": "pi-vcc Configuration",
|
|
5
|
+
"description": "Configuration for pi-vcc algorithmic conversation compactor",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"overrideDefaultCompaction": {
|
|
9
|
+
"type": "boolean",
|
|
10
|
+
"default": false,
|
|
11
|
+
"description": "When true, pi-vcc handles ALL compactions (/compact, auto-threshold, /pi-vcc). When false, only /pi-vcc triggers pi-vcc; everything else falls back to pi core's LLM-based compaction."
|
|
12
|
+
},
|
|
13
|
+
"debug": {
|
|
14
|
+
"type": "boolean",
|
|
15
|
+
"default": false,
|
|
16
|
+
"description": "Write debug snapshot to /tmp/pi-vcc-debug.json on each compaction."
|
|
17
|
+
},
|
|
18
|
+
"extraction": {
|
|
19
|
+
"$ref": "#/$defs/extraction"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"$defs": {
|
|
23
|
+
"extraction": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"description": "Fine-grained extraction configuration. All extra* patterns are ADDITIVE — built-in patterns are never removed.",
|
|
26
|
+
"properties": {
|
|
27
|
+
"references": {
|
|
28
|
+
"$ref": "#/$defs/references"
|
|
29
|
+
},
|
|
30
|
+
"keySignals": {
|
|
31
|
+
"$ref": "#/$defs/keySignals"
|
|
32
|
+
},
|
|
33
|
+
"goals": {
|
|
34
|
+
"$ref": "#/$defs/goals"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"additionalProperties": false
|
|
38
|
+
},
|
|
39
|
+
"references": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"description": "References extractor: URLs, GitHub refs, versions, branches, commit refs. Output appears as [References] section.",
|
|
42
|
+
"properties": {
|
|
43
|
+
"enabled": {
|
|
44
|
+
"type": "boolean",
|
|
45
|
+
"default": true,
|
|
46
|
+
"description": "When false, skip this extraction entirely."
|
|
47
|
+
},
|
|
48
|
+
"extraUrlPatterns": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": { "type": "string", "format": "regex" },
|
|
51
|
+
"default": [],
|
|
52
|
+
"description": "Additional URL regex patterns (appended to built-in https?://\\S+). Each pattern is compiled with global flag. Capture group 1 or full match is used."
|
|
53
|
+
},
|
|
54
|
+
"extraGithubRefPatterns": {
|
|
55
|
+
"type": "array",
|
|
56
|
+
"items": { "type": "string", "format": "regex" },
|
|
57
|
+
"default": [],
|
|
58
|
+
"description": "Additional GitHub ref patterns (appended to built-in #\\d+, PR\\s*#\\d+, owner/repo). Full match is used."
|
|
59
|
+
},
|
|
60
|
+
"extraVersionPatterns": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"items": { "type": "string", "format": "regex" },
|
|
63
|
+
"default": [],
|
|
64
|
+
"description": "Additional version patterns (appended to built-in v?\\d+\\.\\d+\\.\\d+). Capture group 1 or full match is used."
|
|
65
|
+
},
|
|
66
|
+
"extraBranchPatterns": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": { "type": "string", "format": "regex" },
|
|
69
|
+
"default": [],
|
|
70
|
+
"description": "Additional branch patterns (appended to built-in feat|fix|hotfix|release|.../xxx). Full match is used."
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"additionalProperties": false
|
|
74
|
+
},
|
|
75
|
+
"keySignals": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"description": "Key Signals extractor: constraints, decisions, status markers. Output appears as [Key Signals] section.",
|
|
78
|
+
"properties": {
|
|
79
|
+
"enabled": {
|
|
80
|
+
"type": "boolean",
|
|
81
|
+
"default": true,
|
|
82
|
+
"description": "When false, skip this extraction entirely."
|
|
83
|
+
},
|
|
84
|
+
"extraConstraintPatterns": {
|
|
85
|
+
"type": "array",
|
|
86
|
+
"items": { "type": "string", "format": "regex" },
|
|
87
|
+
"default": [],
|
|
88
|
+
"description": "Additional constraint patterns (appended to built-in don't|must not|cannot|forbidden|disallowed|off-limits|out of scope|excluded|do not). Applied to user messages only."
|
|
89
|
+
},
|
|
90
|
+
"extraDecisionPatterns": {
|
|
91
|
+
"type": "array",
|
|
92
|
+
"items": { "type": "string", "format": "regex" },
|
|
93
|
+
"default": [],
|
|
94
|
+
"description": "Additional decision patterns (appended to built-in decided|let's use|going with|chose|we'll use). Applied to user messages only."
|
|
95
|
+
},
|
|
96
|
+
"extraStatusPatterns": {
|
|
97
|
+
"type": "array",
|
|
98
|
+
"items": { "type": "string" },
|
|
99
|
+
"default": [],
|
|
100
|
+
"description": "Additional status keywords (appended to built-in DONE|TODO|WIP|blocked|resolved). Keywords are matched at sentence start or after punctuation. Applied to user + assistant messages."
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"additionalProperties": false
|
|
104
|
+
},
|
|
105
|
+
"goals": {
|
|
106
|
+
"type": "object",
|
|
107
|
+
"description": "Goals extractor tweaks. Controls session goal and scope-change detection.",
|
|
108
|
+
"properties": {
|
|
109
|
+
"enabled": {
|
|
110
|
+
"type": "boolean",
|
|
111
|
+
"default": true,
|
|
112
|
+
"description": "When false, skip goals extraction entirely (only affects [Session Goal] section)."
|
|
113
|
+
},
|
|
114
|
+
"extraTaskVerbs": {
|
|
115
|
+
"type": "array",
|
|
116
|
+
"items": { "type": "string" },
|
|
117
|
+
"default": [],
|
|
118
|
+
"description": "Additional task verbs (appended to built-in fix|implement|add|create|build|refactor|...). Used to detect new task statements in user messages."
|
|
119
|
+
},
|
|
120
|
+
"extraScopeChangeWords": {
|
|
121
|
+
"type": "array",
|
|
122
|
+
"items": { "type": "string" },
|
|
123
|
+
"default": [],
|
|
124
|
+
"description": "Additional scope-change keywords (appended to built-in instead|actually|change of plan|...). Used to detect when user pivots to a new goal."
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"additionalProperties": false
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"additionalProperties": false
|
|
131
|
+
}
|
|
@@ -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
|
+
}
|