@blockspool/cli 0.4.2 → 0.5.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.
Files changed (37) hide show
  1. package/dist/bin/blockspool.js +1 -1
  2. package/dist/lib/codebase-index.d.ts +46 -0
  3. package/dist/lib/codebase-index.d.ts.map +1 -0
  4. package/dist/lib/codebase-index.js +427 -0
  5. package/dist/lib/codebase-index.js.map +1 -0
  6. package/dist/lib/learnings.d.ts +68 -0
  7. package/dist/lib/learnings.d.ts.map +1 -0
  8. package/dist/lib/learnings.js +248 -0
  9. package/dist/lib/learnings.js.map +1 -0
  10. package/dist/lib/project-metadata.d.ts +34 -0
  11. package/dist/lib/project-metadata.d.ts.map +1 -0
  12. package/dist/lib/project-metadata.js +504 -0
  13. package/dist/lib/project-metadata.js.map +1 -0
  14. package/dist/lib/proposal-review.d.ts +26 -0
  15. package/dist/lib/proposal-review.d.ts.map +1 -0
  16. package/dist/lib/proposal-review.js +89 -0
  17. package/dist/lib/proposal-review.js.map +1 -0
  18. package/dist/lib/solo-auto.d.ts.map +1 -1
  19. package/dist/lib/solo-auto.js +183 -2
  20. package/dist/lib/solo-auto.js.map +1 -1
  21. package/dist/lib/solo-ci.d.ts +1 -1
  22. package/dist/lib/solo-ci.d.ts.map +1 -1
  23. package/dist/lib/solo-ci.js +6 -0
  24. package/dist/lib/solo-ci.js.map +1 -1
  25. package/dist/lib/solo-config.d.ts +8 -0
  26. package/dist/lib/solo-config.d.ts.map +1 -1
  27. package/dist/lib/solo-config.js +4 -0
  28. package/dist/lib/solo-config.js.map +1 -1
  29. package/dist/lib/solo-ticket.d.ts +6 -2
  30. package/dist/lib/solo-ticket.d.ts.map +1 -1
  31. package/dist/lib/solo-ticket.js +29 -3
  32. package/dist/lib/solo-ticket.js.map +1 -1
  33. package/dist/lib/spindle.d.ts +21 -1
  34. package/dist/lib/spindle.d.ts.map +1 -1
  35. package/dist/lib/spindle.js +152 -3
  36. package/dist/lib/spindle.js.map +1 -1
  37. package/package.json +1 -1
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Cross-run learning mechanism.
3
+ *
4
+ * Persists learnings to `.blockspool/learnings.json` and provides
5
+ * decay, consolidation, relevance scoring, and prompt formatting.
6
+ *
7
+ * Copied from packages/mcp/src/learnings.ts with titleSimilarity
8
+ * inlined to avoid cross-package dependency.
9
+ */
10
+ import * as fs from 'node:fs';
11
+ import * as path from 'node:path';
12
+ import * as crypto from 'node:crypto';
13
+ // ---------------------------------------------------------------------------
14
+ // Inlined bigrams / titleSimilarity (from proposals.ts)
15
+ // ---------------------------------------------------------------------------
16
+ function bigrams(s) {
17
+ const result = new Set();
18
+ const cleaned = s.replace(/[^a-z0-9 ]/g, '').trim();
19
+ for (let i = 0; i < cleaned.length - 1; i++) {
20
+ result.add(cleaned.slice(i, i + 2));
21
+ }
22
+ return result;
23
+ }
24
+ function titleSimilarity(a, b) {
25
+ const bigramsA = bigrams(a.toLowerCase());
26
+ const bigramsB = bigrams(b.toLowerCase());
27
+ if (bigramsA.size === 0 && bigramsB.size === 0)
28
+ return 1;
29
+ if (bigramsA.size === 0 || bigramsB.size === 0)
30
+ return 0;
31
+ let intersection = 0;
32
+ for (const bg of bigramsA) {
33
+ if (bigramsB.has(bg))
34
+ intersection++;
35
+ }
36
+ const union = bigramsA.size + bigramsB.size - intersection;
37
+ return union === 0 ? 0 : intersection / union;
38
+ }
39
+ // ---------------------------------------------------------------------------
40
+ // Constants
41
+ // ---------------------------------------------------------------------------
42
+ const LEARNINGS_FILE = 'learnings.json';
43
+ const DEFAULT_DECAY_RATE = 3;
44
+ const DEFAULT_WEIGHT = 50;
45
+ const MAX_WEIGHT = 100;
46
+ const CONSOLIDATION_THRESHOLD = 50;
47
+ const SIMILARITY_MERGE_THRESHOLD = 0.7;
48
+ const CONFIRMATION_WINDOW_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
49
+ const DEFAULT_BUDGET = 2000;
50
+ // ---------------------------------------------------------------------------
51
+ // File I/O
52
+ // ---------------------------------------------------------------------------
53
+ function learningsPath(projectRoot) {
54
+ return path.join(projectRoot, '.blockspool', LEARNINGS_FILE);
55
+ }
56
+ function readLearnings(projectRoot) {
57
+ const fp = learningsPath(projectRoot);
58
+ if (!fs.existsSync(fp))
59
+ return [];
60
+ try {
61
+ const raw = fs.readFileSync(fp, 'utf8');
62
+ const parsed = JSON.parse(raw);
63
+ return Array.isArray(parsed) ? parsed : [];
64
+ }
65
+ catch {
66
+ return [];
67
+ }
68
+ }
69
+ function writeLearnings(projectRoot, learnings) {
70
+ const fp = learningsPath(projectRoot);
71
+ const dir = path.dirname(fp);
72
+ if (!fs.existsSync(dir)) {
73
+ fs.mkdirSync(dir, { recursive: true });
74
+ }
75
+ fs.writeFileSync(fp, JSON.stringify(learnings, null, 2) + '\n', 'utf8');
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // Core API
79
+ // ---------------------------------------------------------------------------
80
+ /**
81
+ * Load learnings, apply decay, prune dead entries, write back.
82
+ * Called once per session start.
83
+ */
84
+ export function loadLearnings(projectRoot, decayRate = DEFAULT_DECAY_RATE) {
85
+ const learnings = readLearnings(projectRoot);
86
+ const now = Date.now();
87
+ const surviving = [];
88
+ for (const l of learnings) {
89
+ let decay = decayRate;
90
+ // Access bonus: halve decay if accessed
91
+ if (l.access_count > 0) {
92
+ decay /= 2;
93
+ }
94
+ // Confirmation bonus: halve again if confirmed within 7 days
95
+ const confirmedAt = new Date(l.last_confirmed_at).getTime();
96
+ if (now - confirmedAt < CONFIRMATION_WINDOW_MS) {
97
+ decay /= 2;
98
+ }
99
+ l.weight = Math.min(MAX_WEIGHT, l.weight - decay);
100
+ if (l.weight > 0) {
101
+ surviving.push(l);
102
+ }
103
+ }
104
+ writeLearnings(projectRoot, surviving);
105
+ return surviving;
106
+ }
107
+ /**
108
+ * Add a new learning with sensible defaults.
109
+ */
110
+ export function addLearning(projectRoot, input) {
111
+ const learnings = readLearnings(projectRoot);
112
+ const now = new Date().toISOString();
113
+ const learning = {
114
+ id: crypto.randomBytes(4).toString('hex'),
115
+ text: input.text.slice(0, 200),
116
+ category: input.category,
117
+ source: input.source,
118
+ tags: input.tags ?? [],
119
+ weight: DEFAULT_WEIGHT,
120
+ created_at: now,
121
+ last_confirmed_at: now,
122
+ access_count: 0,
123
+ };
124
+ learnings.push(learning);
125
+ writeLearnings(projectRoot, learnings);
126
+ return learning;
127
+ }
128
+ /**
129
+ * Confirm a learning: bump weight +10 and update last_confirmed_at.
130
+ */
131
+ export function confirmLearning(projectRoot, id) {
132
+ const learnings = readLearnings(projectRoot);
133
+ const l = learnings.find(x => x.id === id);
134
+ if (!l)
135
+ return;
136
+ l.weight = Math.min(MAX_WEIGHT, l.weight + 10);
137
+ l.last_confirmed_at = new Date().toISOString();
138
+ writeLearnings(projectRoot, learnings);
139
+ }
140
+ /**
141
+ * Record access: increment access_count for each id.
142
+ */
143
+ export function recordAccess(projectRoot, ids) {
144
+ if (ids.length === 0)
145
+ return;
146
+ const learnings = readLearnings(projectRoot);
147
+ const idSet = new Set(ids);
148
+ for (const l of learnings) {
149
+ if (idSet.has(l.id)) {
150
+ l.access_count++;
151
+ }
152
+ }
153
+ writeLearnings(projectRoot, learnings);
154
+ }
155
+ /**
156
+ * Consolidate near-duplicate learnings (>70% text similarity).
157
+ * Keeps the higher weight entry, sums access counts.
158
+ */
159
+ export function consolidateLearnings(projectRoot) {
160
+ const learnings = readLearnings(projectRoot);
161
+ if (learnings.length <= CONSOLIDATION_THRESHOLD)
162
+ return;
163
+ const merged = new Set();
164
+ for (let i = 0; i < learnings.length; i++) {
165
+ if (merged.has(i))
166
+ continue;
167
+ for (let j = i + 1; j < learnings.length; j++) {
168
+ if (merged.has(j))
169
+ continue;
170
+ if (titleSimilarity(learnings[i].text, learnings[j].text) >= SIMILARITY_MERGE_THRESHOLD) {
171
+ // Merge j into i (keep higher weight)
172
+ if (learnings[j].weight > learnings[i].weight) {
173
+ learnings[i].weight = learnings[j].weight;
174
+ learnings[i].text = learnings[j].text;
175
+ }
176
+ learnings[i].access_count += learnings[j].access_count;
177
+ // Merge tags
178
+ const tagSet = new Set([...learnings[i].tags, ...learnings[j].tags]);
179
+ learnings[i].tags = [...tagSet];
180
+ merged.add(j);
181
+ }
182
+ }
183
+ }
184
+ const result = learnings.filter((_, idx) => !merged.has(idx));
185
+ writeLearnings(projectRoot, result);
186
+ }
187
+ /**
188
+ * Format learnings for prompt injection.
189
+ * Sorts by weight descending, respects char budget.
190
+ */
191
+ export function formatLearningsForPrompt(learnings, budget = DEFAULT_BUDGET) {
192
+ if (learnings.length === 0)
193
+ return '';
194
+ const sorted = [...learnings].sort((a, b) => b.weight - a.weight);
195
+ const lines = [];
196
+ let charCount = 0;
197
+ const header = '<project-learnings>\n## Learnings from Previous Runs\n';
198
+ const footer = '\n</project-learnings>';
199
+ charCount += header.length + footer.length;
200
+ for (const l of sorted) {
201
+ const tag = l.category.toUpperCase();
202
+ const line = `- [${tag}] ${l.text} (w:${Math.round(l.weight)})`;
203
+ if (charCount + line.length + 1 > budget)
204
+ break;
205
+ lines.push(line);
206
+ charCount += line.length + 1;
207
+ }
208
+ if (lines.length === 0)
209
+ return '';
210
+ return header + lines.join('\n') + footer;
211
+ }
212
+ /**
213
+ * Select learnings relevant to the current context.
214
+ * Scores by tag overlap × weight.
215
+ */
216
+ export function selectRelevant(learnings, context) {
217
+ const contextTags = extractTags(context.paths ?? [], context.commands ?? []);
218
+ if (contextTags.length === 0)
219
+ return learnings;
220
+ const contextTagSet = new Set(contextTags);
221
+ const scored = learnings.map(l => {
222
+ let tagScore = 0;
223
+ for (const t of l.tags) {
224
+ if (contextTagSet.has(t))
225
+ tagScore += 20;
226
+ }
227
+ return { learning: l, score: tagScore + l.weight };
228
+ });
229
+ scored.sort((a, b) => b.score - a.score);
230
+ return scored.map(s => s.learning);
231
+ }
232
+ /**
233
+ * Extract tags from paths and commands for tag matching.
234
+ */
235
+ export function extractTags(paths, commands) {
236
+ const tags = [];
237
+ for (const p of paths) {
238
+ // Normalize: strip trailing globs
239
+ const clean = p.replace(/\/?\*\*?$/, '');
240
+ if (clean)
241
+ tags.push(`path:${clean}`);
242
+ }
243
+ for (const c of commands) {
244
+ tags.push(`cmd:${c}`);
245
+ }
246
+ return tags;
247
+ }
248
+ //# sourceMappingURL=learnings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learnings.js","sourceRoot":"","sources":["../../src/lib/learnings.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,8EAA8E;AAC9E,wDAAwD;AACxD,8EAA8E;AAE9E,SAAS,OAAO,CAAC,CAAS;IACxB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,CAAS,EAAE,CAAS;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAE1C,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACzD,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAEzD,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC1B,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,YAAY,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3D,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,KAAK,CAAC;AAChD,CAAC;AA4BD,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,MAAM,cAAc,GAAG,gBAAgB,CAAC;AACxC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAC7B,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,MAAM,uBAAuB,GAAG,EAAE,CAAC;AACnC,MAAM,0BAA0B,GAAG,GAAG,CAAC;AACvC,MAAM,sBAAsB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,SAAS;AACjE,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E,SAAS,aAAa,CAAC,WAAmB;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,aAAa,CAAC,WAAmB;IACxC,MAAM,EAAE,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,WAAmB,EAAE,SAAqB;IAChE,MAAM,EAAE,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAC1E,CAAC;AAED,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,WAAmB,EAAE,YAAoB,kBAAkB;IACvF,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,IAAI,KAAK,GAAG,SAAS,CAAC;QAEtB,wCAAwC;QACxC,IAAI,CAAC,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;QAED,6DAA6D;QAC7D,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,OAAO,EAAE,CAAC;QAC5D,IAAI,GAAG,GAAG,WAAW,GAAG,sBAAsB,EAAE,CAAC;YAC/C,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;QAED,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;QAElD,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjB,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,cAAc,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACvC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CACzB,WAAmB,EACnB,KAKC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAa;QACzB,EAAE,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;QACzC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;QACtB,MAAM,EAAE,cAAc;QACtB,UAAU,EAAE,GAAG;QACf,iBAAiB,EAAE,GAAG;QACtB,YAAY,EAAE,CAAC;KAChB,CAAC;IACF,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzB,cAAc,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACvC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB,EAAE,EAAU;IAC7D,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3C,IAAI,CAAC,CAAC;QAAE,OAAO;IACf,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAC/C,CAAC,CAAC,iBAAiB,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/C,cAAc,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,WAAmB,EAAE,GAAa;IAC7D,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC7B,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;YACpB,CAAC,CAAC,YAAY,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;IACD,cAAc,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACtD,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC7C,IAAI,SAAS,CAAC,MAAM,IAAI,uBAAuB;QAAE,OAAO;IAExD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,SAAS;QAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YAC5B,IAAI,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,0BAA0B,EAAE,CAAC;gBACxF,sCAAsC;gBACtC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;oBAC9C,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;oBAC1C,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBACxC,CAAC;gBACD,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;gBACvD,aAAa;gBACb,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;gBACrE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;gBAChC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9D,cAAc,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,SAAqB,EAAE,SAAiB,cAAc;IAC7F,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtC,MAAM,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAClE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,MAAM,MAAM,GAAG,wDAAwD,CAAC;IACxE,MAAM,MAAM,GAAG,wBAAwB,CAAC;IACxC,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAE3C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;QAChE,IAAI,SAAS,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM;YAAE,MAAM;QAChD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,OAAO,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,SAAqB,EAAE,OAAkD;IACtG,MAAM,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC7E,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAE/C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IAE3C,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;QAC/B,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;YACvB,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,QAAQ,IAAI,EAAE,CAAC;QAC3C,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,KAAe,EAAE,QAAkB;IAC7D,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,kCAAkC;QAClC,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACzC,IAAI,KAAK;YAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Project Metadata — framework-agnostic detection of tooling, test runners,
3
+ * package managers, and languages.
4
+ *
5
+ * Scans config files at the project root to build a structured summary
6
+ * that gets injected into scout prompts so the LLM knows which CLI
7
+ * flags and conventions to use.
8
+ */
9
+ export interface ProjectMetadata {
10
+ /** Primary language(s) detected */
11
+ languages: string[];
12
+ /** Package manager (npm, yarn, pnpm, bun, pip, poetry, cargo, go, mix, bundle, maven, gradle) */
13
+ package_manager: string | null;
14
+ /** Test runner and CLI syntax */
15
+ test_runner: TestRunnerInfo | null;
16
+ /** App framework (next, remix, express, django, flask, rails, phoenix, spring, etc.) */
17
+ framework: string | null;
18
+ /** Linter (eslint, biome, ruff, clippy, golangci-lint, rubocop, etc.) */
19
+ linter: string | null;
20
+ /** Type checker if separate from compiler (tsc, mypy, pyright, etc.) */
21
+ type_checker: string | null;
22
+ /** Monorepo tool (turborepo, nx, lerna, cargo workspaces, etc.) */
23
+ monorepo_tool: string | null;
24
+ /** Raw detection signals for debugging */
25
+ signals: string[];
26
+ }
27
+ export interface TestRunnerInfo {
28
+ name: string;
29
+ run_command: string;
30
+ filter_syntax: string;
31
+ }
32
+ export declare function detectProjectMetadata(projectRoot: string): ProjectMetadata;
33
+ export declare function formatMetadataForPrompt(meta: ProjectMetadata): string;
34
+ //# sourceMappingURL=project-metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-metadata.d.ts","sourceRoot":"","sources":["../../src/lib/project-metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,iGAAiG;IACjG,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iCAAiC;IACjC,WAAW,EAAE,cAAc,GAAG,IAAI,CAAC;IACnC,wFAAwF;IACxF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,yEAAyE;IACzE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,wEAAwE;IACxE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mEAAmE;IACnE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0CAA0C;IAC1C,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAMD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAuX1E;AAqBD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CA8BrE"}