simt-dev 0.3.1 → 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.
Files changed (52) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +14 -4
  3. package/package.json +2 -2
  4. package/rules/core.md +1 -1
  5. package/src/agenthooks/policy.mjs +10 -2
  6. package/src/agenthooks/prompt.mjs +81 -0
  7. package/src/agenthooks/render.mjs +138 -17
  8. package/src/argv.mjs +16 -1
  9. package/src/bench/grade.mjs +3 -0
  10. package/src/bench/run.mjs +85 -39
  11. package/src/bench/variants.mjs +456 -0
  12. package/src/brief/assemble.mjs +346 -0
  13. package/src/brief/kind.mjs +134 -0
  14. package/src/brief/packet.mjs +197 -0
  15. package/src/commands/agenthook.mjs +84 -5
  16. package/src/commands/bench.mjs +40 -15
  17. package/src/commands/brief.mjs +78 -0
  18. package/src/commands/compare.mjs +10 -2
  19. package/src/commands/decide.mjs +116 -0
  20. package/src/commands/help.mjs +34 -3
  21. package/src/commands/index.mjs +17 -1
  22. package/src/commands/lessons.mjs +127 -0
  23. package/src/commands/local.mjs +97 -0
  24. package/src/commands/map.mjs +42 -7
  25. package/src/commands/repoindex.mjs +4 -1
  26. package/src/commands/rules.mjs +17 -2
  27. package/src/commands/skills.mjs +3 -17
  28. package/src/commands/skillscompile.mjs +255 -0
  29. package/src/commands/sync.mjs +15 -0
  30. package/src/commands/tests.mjs +2 -49
  31. package/src/commands/why.mjs +18 -23
  32. package/src/compare/learn.mjs +680 -0
  33. package/src/decisions/index.mjs +116 -65
  34. package/src/federation/client.mjs +188 -0
  35. package/src/federation/config.mjs +134 -0
  36. package/src/federation/query.mjs +48 -0
  37. package/src/graph/compile.mjs +475 -0
  38. package/src/index/gate.mjs +1 -13
  39. package/src/index/govern.mjs +23 -11
  40. package/src/index/map.mjs +251 -17
  41. package/src/index/persist.mjs +74 -7
  42. package/src/index/store.mjs +222 -8
  43. package/src/kernel/variants.mjs +353 -0
  44. package/src/ledger/index.mjs +36 -1
  45. package/src/local/client.mjs +482 -0
  46. package/src/local/config.mjs +234 -0
  47. package/src/mcp/tools.mjs +138 -1
  48. package/src/memory/decide.mjs +462 -0
  49. package/src/memory/lessons.mjs +858 -0
  50. package/src/projection/lock.mjs +39 -7
  51. package/src/projection/rules.mjs +32 -8
  52. package/src/toolwords.mjs +6 -0
@@ -13,17 +13,11 @@ import { promisify } from 'node:util';
13
13
  import { EXIT, emit, emitJson, fail, isJson } from '../output/index.mjs';
14
14
  import { CLI } from '../name.mjs';
15
15
  import { isTestPath } from '../bench/mine.mjs';
16
- import { readEdges, readFiles, reindex } from '../index/store.mjs';
16
+ import { blobTexts, langOf, readEdges, readFiles, reindex } from '../index/store.mjs';
17
17
  import { HISTORY, importsOf, parseCoverage, testEdges, testsFor } from '../index/tests.mjs';
18
18
 
19
19
  const exec = promisify(execFile);
20
20
 
21
- const LANG = {
22
- js: 'javascript', mjs: 'javascript', cjs: 'javascript', jsx: 'javascript',
23
- ts: 'typescript', tsx: 'typescript', kt: 'kotlin', kts: 'kotlin', java: 'java',
24
- py: 'python', go: 'go', rs: 'rust', swift: 'swift', dart: 'dart', rb: 'ruby', cs: 'csharp',
25
- };
26
-
27
21
  const COVERAGE_PATHS = [
28
22
  'coverage/lcov.info',
29
23
  'lcov.info',
@@ -112,7 +106,7 @@ async function testBlobs(root, files) {
112
106
  for (const file of files) {
113
107
  if (isTestPath(file.path) && file.blob) blobs.push(file.blob);
114
108
  }
115
- return cat(root, blobs);
109
+ return blobTexts(root, blobs).catch(() => new Map());
116
110
  }
117
111
 
118
112
  async function coverageTexts(root) {
@@ -149,44 +143,3 @@ async function history(root) {
149
143
  }
150
144
  return commits;
151
145
  }
152
-
153
- async function cat(root, blobs) {
154
- const unique = [...new Set(blobs)];
155
- const texts = new Map();
156
- if (unique.length === 0) return texts;
157
- const body = await batch(root, unique);
158
- let offset = 0;
159
- while (offset < body.length) {
160
- const nl = body.indexOf(0x0a, offset);
161
- if (nl < 0) break;
162
- const header = body.toString('utf8', offset, nl);
163
- const parts = header.split(' ');
164
- const size = Number(parts[2]);
165
- if (parts[1] !== 'blob' || !Number.isFinite(size)) {
166
- offset = nl + 1;
167
- continue;
168
- }
169
- const start = nl + 1;
170
- const bytes = body.subarray(start, start + size);
171
- offset = start + size + 1;
172
- texts.set(parts[0], bytes.includes(0) ? null : bytes.toString('utf8'));
173
- }
174
- return texts;
175
- }
176
-
177
- function batch(root, blobs) {
178
- return new Promise((resolve) => {
179
- const child = execFile('git', ['cat-file', '--batch'], { cwd: root, maxBuffer: 32 << 20, encoding: 'buffer' }, (error, stdout) => {
180
- resolve(error ? Buffer.alloc(0) : stdout);
181
- });
182
- child.stdin.write(`${blobs.join('\n')}\n`);
183
- child.stdin.end();
184
- });
185
- }
186
-
187
- function langOf(path) {
188
- const base = path.slice(path.lastIndexOf('/') + 1);
189
- const dot = base.lastIndexOf('.');
190
- if (dot <= 0) return '';
191
- return LANG[base.slice(dot + 1)] ?? '';
192
- }
@@ -15,13 +15,14 @@ import { CLI, MANIFEST_FILE } from '../name.mjs';
15
15
  import { FORMATTABLE, GATES } from '../hooks/gates.mjs';
16
16
  import {
17
17
  decisionHits,
18
- lessonPaths,
19
18
  scopeHits,
20
19
  scopePaths,
21
20
  sortHits,
22
21
  sourceRoots,
23
22
  unresolvedAffects,
24
23
  } from '../index/govern.mjs';
24
+ import { splitFrontmatter } from '../memory/decide.mjs';
25
+ import { listPromotedLessons } from '../memory/lessons.mjs';
25
26
 
26
27
  const exec = promisify(execFile);
27
28
 
@@ -81,16 +82,17 @@ export async function decisionRecords(root) {
81
82
  const dir = join(root, 'docs', 'decisions');
82
83
  let names = [];
83
84
  try {
84
- names = (await readdir(dir)).filter((name) => /^ADR-\d{3}-.*\.md$/.test(name)).sort();
85
+ names = (await readdir(dir)).filter((name) => /^ADR-\d{3,}-.*\.md$/i.test(name)).sort();
85
86
  } catch {
86
87
  return [];
87
88
  }
88
89
  const records = [];
89
90
  for (const name of names) {
90
91
  const text = await readFile(join(dir, name), 'utf8');
91
- const id = /^ADR-\d{3}/.exec(name)[0];
92
- const heading = /^#\s+(.*)$/m.exec(text)?.[1] ?? id;
93
- const title = heading.replace(/^ADR-\d{3}\s+[—–-]\s+/, '');
92
+ const { body } = splitFrontmatter(text);
93
+ const id = /^ADR-\d{3,}/i.exec(name)[0].toUpperCase();
94
+ const heading = /^#\s+(.*)$/m.exec(body)?.[1] ?? id;
95
+ const title = heading.replace(/^ADR-\d{3,}\s*[—–-]\s*/i, '').trim();
94
96
  records.push({ id, title, text });
95
97
  }
96
98
  return records;
@@ -146,24 +148,17 @@ async function skillScopes(root) {
146
148
  .map((skill) => ({ id: String(skill.name ?? ''), detail: String(skill.summary ?? ''), paths: skill.paths }));
147
149
  }
148
150
 
149
- async function lessonScopes(root) {
150
- const dir = join(root, 'docs', 'lessons');
151
- let names = [];
152
- try {
153
- names = (await readdir(dir)).filter((name) => name.endsWith('.md')).sort();
154
- } catch {
155
- return [];
156
- }
157
- const lessons = [];
158
- for (const name of names) {
159
- const text = await readFile(join(dir, name), 'utf8');
160
- const paths = lessonPaths(text);
161
- if (paths.length === 0) continue;
162
- const id = name.replace(/\.md$/, '');
163
- const detail = /^#\s+(.*)$/m.exec(text)?.[1] ?? id;
164
- lessons.push({ id, detail, paths });
165
- }
166
- return lessons;
151
+ export async function lessonScopes(root) {
152
+ return (await listPromotedLessons(root))
153
+ .filter((lesson) => lesson.paths.length > 0)
154
+ .map((lesson) => ({
155
+ id: lesson.id,
156
+ detail: lesson.title,
157
+ paragraph: lesson.paragraph,
158
+ paths: lesson.paths,
159
+ signature: lesson.signature,
160
+ keywords: lesson.keywords,
161
+ }));
167
162
  }
168
163
 
169
164
  async function gateCommands(root) {