@madkid/relay-ctx 0.1.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/dist/commands/checkpoint.d.ts +18 -0
- package/dist/commands/checkpoint.d.ts.map +1 -0
- package/dist/commands/checkpoint.js +130 -0
- package/dist/commands/checkpoint.js.map +1 -0
- package/dist/commands/hooks.d.ts +36 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +226 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +228 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/inject.d.ts +20 -0
- package/dist/commands/inject.d.ts.map +1 -0
- package/dist/commands/inject.js +209 -0
- package/dist/commands/inject.js.map +1 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +174 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/sync.d.ts +17 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +239 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/core/compressor.d.ts +16 -0
- package/dist/core/compressor.d.ts.map +1 -0
- package/dist/core/compressor.js +354 -0
- package/dist/core/compressor.js.map +1 -0
- package/dist/core/scanner.d.ts +40 -0
- package/dist/core/scanner.d.ts.map +1 -0
- package/dist/core/scanner.js +366 -0
- package/dist/core/scanner.js.map +1 -0
- package/dist/core/storage.d.ts +39 -0
- package/dist/core/storage.d.ts.map +1 -0
- package/dist/core/storage.js +122 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/test-relay.d.ts +2 -0
- package/dist/test-relay.d.ts.map +1 -0
- package/dist/test-relay.js +433 -0
- package/dist/test-relay.js.map +1 -0
- package/package.json +32 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* relay sync — Scan project and update context.
|
|
3
|
+
*
|
|
4
|
+
* Options:
|
|
5
|
+
* --silent Suppress all output (used by git hooks)
|
|
6
|
+
*
|
|
7
|
+
* Flow:
|
|
8
|
+
* 1. Scan the project directory
|
|
9
|
+
* 2. Read existing PROJECT.md
|
|
10
|
+
* 3. Update auto-generated sections, preserve human-written ones
|
|
11
|
+
* 4. Write updated PROJECT.md
|
|
12
|
+
* 5. Show diff summary (unless --silent)
|
|
13
|
+
*/
|
|
14
|
+
export declare function syncCommand(options?: {
|
|
15
|
+
silent?: boolean;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAsIA;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgH/E"}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.syncCommand = syncCommand;
|
|
7
|
+
const scanner_1 = require("../core/scanner");
|
|
8
|
+
const storage_1 = require("../core/storage");
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
/**
|
|
12
|
+
* Format GitContext data into a markdown string.
|
|
13
|
+
*/
|
|
14
|
+
function formatGitSection(git) {
|
|
15
|
+
if (!git.isGitRepo) {
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
const lines = [];
|
|
19
|
+
lines.push(`- **Branch:** ${git.currentBranch || 'unknown'}`);
|
|
20
|
+
lines.push(`- **Last Commit:** ${git.lastCommitHash ? `\`${git.lastCommitHash.slice(0, 7)}\`` : 'none'}`);
|
|
21
|
+
lines.push('\n### Uncommitted changes');
|
|
22
|
+
if (git.uncommittedChanges.length === 0) {
|
|
23
|
+
lines.push('Uncommitted changes: (clean)');
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
git.uncommittedChanges.forEach((change) => {
|
|
27
|
+
lines.push(`- \`${change.file}\` (${change.status}) | +${change.additions} -${change.deletions}`);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (git.stagedFiles.length > 0) {
|
|
31
|
+
lines.push('\n### Staged files');
|
|
32
|
+
git.stagedFiles.forEach((file) => {
|
|
33
|
+
lines.push(`- \`${file}\``);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (git.recentCommits.length > 0) {
|
|
37
|
+
lines.push('\n### Recent commits');
|
|
38
|
+
git.recentCommits.forEach((commit) => {
|
|
39
|
+
lines.push(`- \`${commit.hash}\` (${commit.timeAgo}) - ${commit.message}`);
|
|
40
|
+
if (commit.filesChanged.length > 0) {
|
|
41
|
+
lines.push(` _Changed:_ ${commit.filesChanged.map((f) => `\`${f}\``).join(', ')}`);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return '\n' + lines.join('\n');
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Sections that are auto-updated by sync.
|
|
49
|
+
* These are regenerated from the scan data.
|
|
50
|
+
*/
|
|
51
|
+
const AUTO_SECTIONS = new Set([
|
|
52
|
+
'File structure',
|
|
53
|
+
'Tech stack',
|
|
54
|
+
]);
|
|
55
|
+
/**
|
|
56
|
+
* Sections that are human-written and never overwritten by sync.
|
|
57
|
+
*/
|
|
58
|
+
const PROTECTED_SECTIONS = new Set([
|
|
59
|
+
"What we're building",
|
|
60
|
+
'Conventions',
|
|
61
|
+
"What's next",
|
|
62
|
+
"What we're NOT building",
|
|
63
|
+
]);
|
|
64
|
+
/**
|
|
65
|
+
* Parse PROJECT.md into sections by ## headings.
|
|
66
|
+
*/
|
|
67
|
+
function parseSections(md) {
|
|
68
|
+
const sections = new Map();
|
|
69
|
+
const lines = md.split('\n');
|
|
70
|
+
let currentHeading = '__preamble__';
|
|
71
|
+
let currentLines = [];
|
|
72
|
+
for (const line of lines) {
|
|
73
|
+
const match = line.match(/^##\s+(.+)/);
|
|
74
|
+
if (match) {
|
|
75
|
+
sections.set(currentHeading, currentLines.join('\n').trimEnd());
|
|
76
|
+
currentHeading = match[1].trim();
|
|
77
|
+
currentLines = [];
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
currentLines.push(line);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
sections.set(currentHeading, currentLines.join('\n').trimEnd());
|
|
84
|
+
return sections;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Render a ProjectScan as a file structure section.
|
|
88
|
+
*/
|
|
89
|
+
function renderFileStructure(scan) {
|
|
90
|
+
let content = '\n```\n' + scan.structure + '\n```\n';
|
|
91
|
+
// File counts
|
|
92
|
+
const counts = Object.entries(scan.fileCount)
|
|
93
|
+
.sort((a, b) => b[1] - a[1])
|
|
94
|
+
.slice(0, 10)
|
|
95
|
+
.map(([ext, count]) => ` ${ext}: ${count}`)
|
|
96
|
+
.join('\n');
|
|
97
|
+
content += `\n**File counts:**\n${counts}`;
|
|
98
|
+
return content;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Render tech stack section.
|
|
102
|
+
*/
|
|
103
|
+
function renderTechStack(scan) {
|
|
104
|
+
return '\n' + scan.techStack.map((t) => `- ${t}`).join('\n');
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Render "What's in progress" section from recently modified files.
|
|
108
|
+
*/
|
|
109
|
+
function renderInProgress(scan, existing) {
|
|
110
|
+
const recentFiles = scan.recentlyModified
|
|
111
|
+
.map((f) => `- \`${f}\``)
|
|
112
|
+
.join('\n');
|
|
113
|
+
// Strip out any previously generated "Recently modified:" section
|
|
114
|
+
const cleanedExisting = existing.replace(/\*\*Recently modified:\*\*[\s\S]*/i, '').trim();
|
|
115
|
+
// Keep any existing human-written content and append recent changes
|
|
116
|
+
const lines = [];
|
|
117
|
+
if (cleanedExisting) {
|
|
118
|
+
lines.push(cleanedExisting);
|
|
119
|
+
}
|
|
120
|
+
lines.push(`\n**Recently modified:**\n${recentFiles}`);
|
|
121
|
+
return '\n' + lines.join('\n');
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* relay sync — Scan project and update context.
|
|
125
|
+
*
|
|
126
|
+
* Options:
|
|
127
|
+
* --silent Suppress all output (used by git hooks)
|
|
128
|
+
*
|
|
129
|
+
* Flow:
|
|
130
|
+
* 1. Scan the project directory
|
|
131
|
+
* 2. Read existing PROJECT.md
|
|
132
|
+
* 3. Update auto-generated sections, preserve human-written ones
|
|
133
|
+
* 4. Write updated PROJECT.md
|
|
134
|
+
* 5. Show diff summary (unless --silent)
|
|
135
|
+
*/
|
|
136
|
+
async function syncCommand(options) {
|
|
137
|
+
const silent = options?.silent ?? false;
|
|
138
|
+
const projectName = (0, storage_1.getProjectName)();
|
|
139
|
+
const projectPath = (0, storage_1.getProjectPath)();
|
|
140
|
+
if (!fs_1.default.existsSync(projectPath)) {
|
|
141
|
+
if (silent)
|
|
142
|
+
return;
|
|
143
|
+
console.log('❌ Relay not initialized for this project. Run `relay init` first.');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
if (!silent) {
|
|
147
|
+
console.log(`\n🔄 Syncing "${projectName}"...\n`);
|
|
148
|
+
}
|
|
149
|
+
// 1. Scan
|
|
150
|
+
const scan = await (0, scanner_1.scanProject)(process.cwd());
|
|
151
|
+
// 2. Read existing PROJECT.md
|
|
152
|
+
let existingMd = (0, storage_1.readProjectMd)();
|
|
153
|
+
// Strip any historical/duplicated sync footers
|
|
154
|
+
existingMd = existingMd.replace(/\n*---\n*_Last synced:[0-9: \-_]+_\s*/gi, '').trimEnd();
|
|
155
|
+
const sections = parseSections(existingMd);
|
|
156
|
+
// 3. Update sections
|
|
157
|
+
const updated = [];
|
|
158
|
+
// Track what changed
|
|
159
|
+
const changes = [];
|
|
160
|
+
// Preamble (title etc.)
|
|
161
|
+
if (sections.has('__preamble__')) {
|
|
162
|
+
updated.push(sections.get('__preamble__'));
|
|
163
|
+
}
|
|
164
|
+
// Update timestamp in preamble
|
|
165
|
+
const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
166
|
+
// Rebuild the document section by section
|
|
167
|
+
const sectionOrder = [
|
|
168
|
+
"What we're building",
|
|
169
|
+
'Tech stack',
|
|
170
|
+
'Git context',
|
|
171
|
+
"What's in progress",
|
|
172
|
+
"What's next",
|
|
173
|
+
'Conventions',
|
|
174
|
+
'File structure',
|
|
175
|
+
"What's working",
|
|
176
|
+
"What we're NOT building",
|
|
177
|
+
'Checkpoints',
|
|
178
|
+
];
|
|
179
|
+
for (const heading of sectionOrder) {
|
|
180
|
+
const existing = sections.get(heading) || '';
|
|
181
|
+
if (heading === 'File structure') {
|
|
182
|
+
updated.push(`## File structure\n${renderFileStructure(scan)}`);
|
|
183
|
+
changes.push('file structure');
|
|
184
|
+
}
|
|
185
|
+
else if (heading === 'Tech stack') {
|
|
186
|
+
updated.push(`## Tech stack\n${renderTechStack(scan)}`);
|
|
187
|
+
changes.push('tech stack');
|
|
188
|
+
}
|
|
189
|
+
else if (heading === "What's in progress") {
|
|
190
|
+
updated.push(`## What's in progress\n${renderInProgress(scan, existing)}`);
|
|
191
|
+
changes.push('recently modified');
|
|
192
|
+
}
|
|
193
|
+
else if (heading === 'Git context') {
|
|
194
|
+
const gitContent = formatGitSection(scan.gitContext);
|
|
195
|
+
if (gitContent) {
|
|
196
|
+
updated.push(`## Git context\n${gitContent}`);
|
|
197
|
+
changes.push('git context');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else if (existing || sections.has(heading)) {
|
|
201
|
+
// Keep human-written sections as-is
|
|
202
|
+
updated.push(`## ${heading}\n${existing}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Add any extra sections not in our known order
|
|
206
|
+
for (const [heading, content] of sections) {
|
|
207
|
+
if (heading === '__preamble__')
|
|
208
|
+
continue;
|
|
209
|
+
if (sectionOrder.includes(heading))
|
|
210
|
+
continue;
|
|
211
|
+
updated.push(`## ${heading}\n${content}`);
|
|
212
|
+
}
|
|
213
|
+
// Append sync timestamp
|
|
214
|
+
const footer = `\n\n---\n_Last synced: ${timestamp}_`;
|
|
215
|
+
// 4. Write
|
|
216
|
+
(0, storage_1.writeProjectMd)(updated.join('\n\n') + footer + '\n');
|
|
217
|
+
// Update meta.json
|
|
218
|
+
const metaPath = path_1.default.join(projectPath, 'meta.json');
|
|
219
|
+
let meta = {};
|
|
220
|
+
if (fs_1.default.existsSync(metaPath)) {
|
|
221
|
+
try {
|
|
222
|
+
meta = JSON.parse(fs_1.default.readFileSync(metaPath, 'utf-8'));
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// ignore
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
meta.name = projectName;
|
|
229
|
+
meta.cwd = process.cwd();
|
|
230
|
+
meta.lastSync = new Date().toISOString();
|
|
231
|
+
fs_1.default.writeFileSync(metaPath, JSON.stringify(meta, null, 2), 'utf-8');
|
|
232
|
+
// 5. Report
|
|
233
|
+
if (!silent) {
|
|
234
|
+
console.log(`✅ Sync complete for "${projectName}"`);
|
|
235
|
+
console.log(` Updated: ${changes.join(', ')}`);
|
|
236
|
+
console.log(` 📁 ${projectPath}\n`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type InjectIntent = 'continue' | 'newTask' | 'debug';
|
|
2
|
+
/**
|
|
3
|
+
* Compress PROJECT.md to under 1800 tokens using Claude Haiku.
|
|
4
|
+
*/
|
|
5
|
+
export declare function compressContext(projectMd: string, apiKey: string, intent?: InjectIntent): Promise<string>;
|
|
6
|
+
/**
|
|
7
|
+
* Fallback naive truncation respecting section priority.
|
|
8
|
+
* Used when no API key is available.
|
|
9
|
+
*/
|
|
10
|
+
export declare function truncateContext(projectMd: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Preprocesses PROJECT.md content based on intent.
|
|
13
|
+
* Enforces intent-specific section inclusion, ordering, formatting, and the 1800-token limit.
|
|
14
|
+
*/
|
|
15
|
+
export declare function preprocessContext(projectMd: string, intent: InjectIntent, errorMessage?: string): string;
|
|
16
|
+
//# sourceMappingURL=compressor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compressor.d.ts","sourceRoot":"","sources":["../../src/core/compressor.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5D;;GAEG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC,CA8BjB;AAwCD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAwEzD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAsOR"}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.compressContext = compressContext;
|
|
7
|
+
exports.truncateContext = truncateContext;
|
|
8
|
+
exports.preprocessContext = preprocessContext;
|
|
9
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
10
|
+
/**
|
|
11
|
+
* Section priority order for context compression.
|
|
12
|
+
* Lower index = higher priority = kept first.
|
|
13
|
+
*/
|
|
14
|
+
const SECTION_PRIORITY = [
|
|
15
|
+
"What we're building", // 1 — always kept, max 3 sentences
|
|
16
|
+
'Tech stack', // 2 — always kept
|
|
17
|
+
'Git context', // 3 — always kept
|
|
18
|
+
"What's in progress", // 4 — always kept
|
|
19
|
+
"What's next", // 5 — always kept
|
|
20
|
+
'Conventions', // 6 — truncated if needed
|
|
21
|
+
'File structure', // 7 — collapsed to top level only
|
|
22
|
+
"What's working", // 8 — dropped if token pressure
|
|
23
|
+
'Checkpoints', // 9 — always dropped
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* Compress PROJECT.md to under 1800 tokens using Claude Haiku.
|
|
27
|
+
*/
|
|
28
|
+
async function compressContext(projectMd, apiKey, intent) {
|
|
29
|
+
const client = new sdk_1.default({ apiKey });
|
|
30
|
+
let systemPrompt = `You are a context compressor. Given a PROJECT.md file, compress it to under 1800 tokens while preserving the most important information.`;
|
|
31
|
+
if (intent) {
|
|
32
|
+
systemPrompt += `\n\nOptimize the compression for the following developer intent: "${intent}". Keep the focus on the sections priority listed in the user's intent.`;
|
|
33
|
+
}
|
|
34
|
+
systemPrompt += `\n\nOutput ONLY the compressed context, no explanations or meta-commentary.`;
|
|
35
|
+
const response = await client.messages.create({
|
|
36
|
+
model: 'claude-3-haiku-20240307',
|
|
37
|
+
max_tokens: 2048,
|
|
38
|
+
system: systemPrompt,
|
|
39
|
+
messages: [
|
|
40
|
+
{
|
|
41
|
+
role: 'user',
|
|
42
|
+
content: `Compress this PROJECT.md:\n\n${projectMd}`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
const block = response.content[0];
|
|
47
|
+
if (block.type === 'text') {
|
|
48
|
+
return block.text;
|
|
49
|
+
}
|
|
50
|
+
// Fallback if response isn't text
|
|
51
|
+
return truncateContext(projectMd);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse PROJECT.md into sections by ## headings.
|
|
55
|
+
*/
|
|
56
|
+
function parseSections(projectMd) {
|
|
57
|
+
const lines = projectMd.split('\n');
|
|
58
|
+
const sections = [];
|
|
59
|
+
let currentHeading = '';
|
|
60
|
+
let currentLines = [];
|
|
61
|
+
for (const line of lines) {
|
|
62
|
+
const headingMatch = line.match(/^##\s+(.+)/);
|
|
63
|
+
if (headingMatch) {
|
|
64
|
+
if (currentHeading || currentLines.length > 0) {
|
|
65
|
+
sections.push({
|
|
66
|
+
heading: currentHeading,
|
|
67
|
+
content: currentLines.join('\n').trim(),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
currentHeading = headingMatch[1].trim();
|
|
71
|
+
currentLines = [];
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
currentLines.push(line);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Push final section
|
|
78
|
+
if (currentHeading || currentLines.length > 0) {
|
|
79
|
+
sections.push({
|
|
80
|
+
heading: currentHeading,
|
|
81
|
+
content: currentLines.join('\n').trim(),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return sections;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Fallback naive truncation respecting section priority.
|
|
88
|
+
* Used when no API key is available.
|
|
89
|
+
*/
|
|
90
|
+
function truncateContext(projectMd) {
|
|
91
|
+
const sections = parseSections(projectMd);
|
|
92
|
+
const TOKEN_LIMIT = 2000;
|
|
93
|
+
// Rough approximation: 1 token ≈ 4 chars
|
|
94
|
+
const CHAR_LIMIT = TOKEN_LIMIT * 4;
|
|
95
|
+
const result = [];
|
|
96
|
+
let charCount = 0;
|
|
97
|
+
// First pass: add sections in priority order
|
|
98
|
+
for (const priorityName of SECTION_PRIORITY) {
|
|
99
|
+
const section = sections.find((s) => s.heading.toLowerCase() === priorityName.toLowerCase());
|
|
100
|
+
if (!section || !section.content)
|
|
101
|
+
continue;
|
|
102
|
+
// Always drop checkpoints
|
|
103
|
+
if (priorityName === 'Checkpoints')
|
|
104
|
+
continue;
|
|
105
|
+
let sectionText = `## ${section.heading}\n\n${section.content}`;
|
|
106
|
+
// Drop "What's working" if we're over 75% of limit
|
|
107
|
+
if (priorityName === "What's working" &&
|
|
108
|
+
charCount > CHAR_LIMIT * 0.75) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// Collapse file structure to top level
|
|
112
|
+
if (priorityName === 'File structure') {
|
|
113
|
+
const lines = section.content.split('\n');
|
|
114
|
+
const topLevel = lines.filter((l) => !l.startsWith(' ') && !l.startsWith('\t\t'));
|
|
115
|
+
sectionText = `## ${section.heading}\n\n${topLevel.join('\n')}`;
|
|
116
|
+
}
|
|
117
|
+
// Truncate conventions if over budget
|
|
118
|
+
if (priorityName === 'Conventions' && charCount + sectionText.length > CHAR_LIMIT) {
|
|
119
|
+
const remaining = Math.max(0, CHAR_LIMIT - charCount - 50);
|
|
120
|
+
sectionText = sectionText.slice(0, remaining) + '\n...(truncated)';
|
|
121
|
+
}
|
|
122
|
+
if (charCount + sectionText.length > CHAR_LIMIT)
|
|
123
|
+
continue;
|
|
124
|
+
result.push(sectionText);
|
|
125
|
+
charCount += sectionText.length;
|
|
126
|
+
}
|
|
127
|
+
// Second pass: add custom user-defined sections that are not in SECTION_PRIORITY
|
|
128
|
+
for (const section of sections) {
|
|
129
|
+
if (section.heading === '')
|
|
130
|
+
continue; // preamble handled separately
|
|
131
|
+
const isPredefined = SECTION_PRIORITY.some((p) => p.toLowerCase() === section.heading.toLowerCase());
|
|
132
|
+
if (isPredefined)
|
|
133
|
+
continue;
|
|
134
|
+
const sectionText = `## ${section.heading}\n\n${section.content}`;
|
|
135
|
+
if (charCount + sectionText.length < CHAR_LIMIT) {
|
|
136
|
+
result.push(sectionText);
|
|
137
|
+
charCount += sectionText.length;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Add any preamble (content before first ## heading)
|
|
141
|
+
const preamble = sections.find((s) => s.heading === '');
|
|
142
|
+
if (preamble && preamble.content && charCount + preamble.content.length < CHAR_LIMIT) {
|
|
143
|
+
result.unshift(preamble.content);
|
|
144
|
+
}
|
|
145
|
+
return result.join('\n\n');
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Preprocesses PROJECT.md content based on intent.
|
|
149
|
+
* Enforces intent-specific section inclusion, ordering, formatting, and the 1800-token limit.
|
|
150
|
+
*/
|
|
151
|
+
function preprocessContext(projectMd, intent, errorMessage) {
|
|
152
|
+
const sections = parseSections(projectMd);
|
|
153
|
+
const result = [];
|
|
154
|
+
const findSection = (name) => sections.find((s) => s.heading.toLowerCase() === name.toLowerCase());
|
|
155
|
+
const getSentences = (text, max) => {
|
|
156
|
+
if (!text.trim())
|
|
157
|
+
return '';
|
|
158
|
+
const sentences = text.split(/(?<=[.!?])\s+/);
|
|
159
|
+
return sentences.slice(0, max).join(' ').trim();
|
|
160
|
+
};
|
|
161
|
+
const trimList = (text, max) => {
|
|
162
|
+
const lines = text.split('\n');
|
|
163
|
+
const listLines = [];
|
|
164
|
+
let count = 0;
|
|
165
|
+
for (const line of lines) {
|
|
166
|
+
if (line.trim().startsWith('-') || line.trim().startsWith('*')) {
|
|
167
|
+
if (count < max) {
|
|
168
|
+
listLines.push(line);
|
|
169
|
+
count++;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
if (count === 0) {
|
|
174
|
+
listLines.push(line);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return listLines.join('\n').trim();
|
|
179
|
+
};
|
|
180
|
+
const collapseStructure = (text) => {
|
|
181
|
+
const lines = text.split('\n');
|
|
182
|
+
const topLevel = lines.filter((l) => !l.startsWith(' ') && !l.startsWith('\t\t'));
|
|
183
|
+
return topLevel.join('\n').trim();
|
|
184
|
+
};
|
|
185
|
+
const formatGitContext = (text, rule) => {
|
|
186
|
+
const lines = text.split('\n');
|
|
187
|
+
const outputLines = [];
|
|
188
|
+
let inRecentCommits = false;
|
|
189
|
+
let commitCount = 0;
|
|
190
|
+
const maxCommits = rule === 'continue' ? 3 : 5;
|
|
191
|
+
for (let i = 0; i < lines.length; i++) {
|
|
192
|
+
const line = lines[i];
|
|
193
|
+
if (line.includes('### Uncommitted changes')) {
|
|
194
|
+
if (rule === 'newTask') {
|
|
195
|
+
while (i + 1 < lines.length && !lines[i + 1].startsWith('###') && !lines[i + 1].startsWith('##')) {
|
|
196
|
+
i++;
|
|
197
|
+
}
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
outputLines.push(line);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (line.includes('### Staged files')) {
|
|
204
|
+
if (rule === 'newTask' || rule === 'continue') {
|
|
205
|
+
while (i + 1 < lines.length && !lines[i + 1].startsWith('###') && !lines[i + 1].startsWith('##')) {
|
|
206
|
+
i++;
|
|
207
|
+
}
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
outputLines.push(line);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (line.includes('### Recent commits')) {
|
|
214
|
+
outputLines.push(line);
|
|
215
|
+
inRecentCommits = true;
|
|
216
|
+
commitCount = 0;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (inRecentCommits) {
|
|
220
|
+
if (line.trim().startsWith('- ')) {
|
|
221
|
+
if (commitCount < maxCommits) {
|
|
222
|
+
outputLines.push(line);
|
|
223
|
+
commitCount++;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
inRecentCommits = false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else if (line.trim().startsWith('_Changed:_')) {
|
|
230
|
+
if (rule !== 'newTask') {
|
|
231
|
+
outputLines.push(line);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
outputLines.push(line);
|
|
236
|
+
}
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
outputLines.push(line);
|
|
240
|
+
}
|
|
241
|
+
return outputLines.join('\n').trim();
|
|
242
|
+
};
|
|
243
|
+
if (intent === 'continue') {
|
|
244
|
+
// 1. What We're Building (2 sentences max, truncated)
|
|
245
|
+
const building = findSection("What we're building") || findSection("What We're Building");
|
|
246
|
+
if (building) {
|
|
247
|
+
result.push(`## ${building.heading}\n\n${getSentences(building.content, 2)}`);
|
|
248
|
+
}
|
|
249
|
+
// 2. Current Session State
|
|
250
|
+
const sessionState = findSection("Current Session State") || findSection("Current session state");
|
|
251
|
+
if (sessionState) {
|
|
252
|
+
result.push(`## ${sessionState.heading}\n\n${sessionState.content}`);
|
|
253
|
+
}
|
|
254
|
+
// 3. What's In Progress
|
|
255
|
+
const inProgress = findSection("What's in progress") || findSection("What's In Progress");
|
|
256
|
+
if (inProgress) {
|
|
257
|
+
result.push(`## ${inProgress.heading}\n\n${inProgress.content}`);
|
|
258
|
+
}
|
|
259
|
+
// 4. Recent Activity (Git) — full uncommitted changes + last 3 commits
|
|
260
|
+
const git = findSection("Git context") || findSection("Recent Activity (Git)") || findSection("Recent Activity");
|
|
261
|
+
if (git) {
|
|
262
|
+
result.push(`## ${git.heading}\n\n${formatGitContext(git.content, 'continue')}`);
|
|
263
|
+
}
|
|
264
|
+
// 5. Tech Stack (just the list, no descriptions)
|
|
265
|
+
const tech = findSection("Tech stack") || findSection("Tech Stack");
|
|
266
|
+
if (tech) {
|
|
267
|
+
const listOnly = tech.content
|
|
268
|
+
.split('\n')
|
|
269
|
+
.filter((l) => l.trim().startsWith('-') || l.trim().startsWith('*'))
|
|
270
|
+
.join('\n');
|
|
271
|
+
result.push(`## ${tech.heading}\n\n${listOnly}`);
|
|
272
|
+
}
|
|
273
|
+
// 6. What's Next (first 3 items only)
|
|
274
|
+
const next = findSection("What's next") || findSection("What's Next");
|
|
275
|
+
if (next) {
|
|
276
|
+
result.push(`## ${next.heading}\n\n${trimList(next.content, 3)}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else if (intent === 'newTask') {
|
|
280
|
+
// 1. What We're Building (full)
|
|
281
|
+
const building = findSection("What we're building") || findSection("What We're Building");
|
|
282
|
+
if (building) {
|
|
283
|
+
result.push(`## ${building.heading}\n\n${building.content}`);
|
|
284
|
+
}
|
|
285
|
+
// 2. Tech Stack (full)
|
|
286
|
+
const tech = findSection("Tech stack") || findSection("Tech Stack");
|
|
287
|
+
if (tech) {
|
|
288
|
+
result.push(`## ${tech.heading}\n\n${tech.content}`);
|
|
289
|
+
}
|
|
290
|
+
// 3. Repo Structure (collapsed to top level)
|
|
291
|
+
const structure = findSection("File structure") || findSection("Repo Structure") || findSection("File Structure");
|
|
292
|
+
if (structure) {
|
|
293
|
+
result.push(`## ${structure.heading}\n\n${collapseStructure(structure.content)}`);
|
|
294
|
+
}
|
|
295
|
+
// 4. Conventions (full)
|
|
296
|
+
const conventions = findSection("Conventions");
|
|
297
|
+
if (conventions) {
|
|
298
|
+
result.push(`## ${conventions.heading}\n\n${conventions.content}`);
|
|
299
|
+
}
|
|
300
|
+
// 5. What's Working
|
|
301
|
+
const working = findSection("What's working") || findSection("What's Working");
|
|
302
|
+
if (working) {
|
|
303
|
+
result.push(`## ${working.heading}\n\n${working.content}`);
|
|
304
|
+
}
|
|
305
|
+
// 6. What's Next (full)
|
|
306
|
+
const next = findSection("What's next") || findSection("What's Next");
|
|
307
|
+
if (next) {
|
|
308
|
+
result.push(`## ${next.heading}\n\n${next.content}`);
|
|
309
|
+
}
|
|
310
|
+
// 7. Recent Activity (Git) — last 5 commits only, no diff details
|
|
311
|
+
const git = findSection("Git context") || findSection("Recent Activity (Git)") || findSection("Recent Activity");
|
|
312
|
+
if (git) {
|
|
313
|
+
result.push(`## ${git.heading}\n\n${formatGitContext(git.content, 'newTask')}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else if (intent === 'debug') {
|
|
317
|
+
// 1. What We're Building (1 sentence)
|
|
318
|
+
const building = findSection("What we're building") || findSection("What We're Building");
|
|
319
|
+
if (building) {
|
|
320
|
+
result.push(`## ${building.heading}\n\n${getSentences(building.content, 1)}`);
|
|
321
|
+
}
|
|
322
|
+
// 2. Recent Activity (Git) — full uncommitted changes + last 5 commits + staged files
|
|
323
|
+
const git = findSection("Git context") || findSection("Recent Activity (Git)") || findSection("Recent Activity");
|
|
324
|
+
if (git) {
|
|
325
|
+
result.push(`## ${git.heading}\n\n${formatGitContext(git.content, 'debug')}`);
|
|
326
|
+
}
|
|
327
|
+
// 3. What's In Progress
|
|
328
|
+
const inProgress = findSection("What's in progress") || findSection("What's In Progress");
|
|
329
|
+
if (inProgress) {
|
|
330
|
+
result.push(`## ${inProgress.heading}\n\n${inProgress.content}`);
|
|
331
|
+
}
|
|
332
|
+
// 4. Tech Stack
|
|
333
|
+
const tech = findSection("Tech stack") || findSection("Tech Stack");
|
|
334
|
+
if (tech) {
|
|
335
|
+
result.push(`## ${tech.heading}\n\n${tech.content}`);
|
|
336
|
+
}
|
|
337
|
+
// 5. Current Error (multi-line input collected from user)
|
|
338
|
+
if (errorMessage) {
|
|
339
|
+
result.push(`## Current Error\n\n${errorMessage}`);
|
|
340
|
+
}
|
|
341
|
+
// 6. Repo Structure
|
|
342
|
+
const structure = findSection("File structure") || findSection("Repo Structure") || findSection("File Structure");
|
|
343
|
+
if (structure) {
|
|
344
|
+
result.push(`## ${structure.heading}\n\n${structure.content}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
// Enforce token budget by dropping lowest priority sections
|
|
348
|
+
const CHARACTER_BUDGET = 1800 * 4;
|
|
349
|
+
while (result.length > 0 && result.join('\n\n').length > CHARACTER_BUDGET) {
|
|
350
|
+
result.pop();
|
|
351
|
+
}
|
|
352
|
+
return result.join('\n\n');
|
|
353
|
+
}
|
|
354
|
+
//# sourceMappingURL=compressor.js.map
|