proteum 2.5.6 → 2.5.7

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.
@@ -81,7 +81,6 @@ Managed compact root routers must use trigger -> canonical instruction file refe
81
81
  - Before finishing, re-check touched files against root-level `CODING_STYLE.md` and any narrower area `AGENTS.md` that applied to the edit. Re-check against root-level `optimizations.md` only for touched client-side files. Re-check against root-level `diagnostics.md` only if the task involved an issue, diagnosis, runtime reproduction, or verification failure.
82
82
  - Before finishing a production code change, re-check root-level `DOCUMENTATION.md` update rules. If behavior changed, a bug was fixed, a decision changed, or an important route, auth/OAuth, or integration issue was addressed, update the relevant docs before committing or explicitly explain why no docs update was needed.
83
83
  - Run targeted tests and checks that match the changed surface before finishing each feature or change. When the repository defines `proteum.verify.config.ts`, use `npx proteum verify changed` as the first post-change verification pass and expand only when the selected plan is insufficient. Continue running tests after changes, but do not run coverage by default. Downstream app commit workflows run only `proteum refresh`, then targeted lint, typecheck, and test commands in parallel; framework-repo commit workflows skip this downstream app verification. Reserve the full `npm run check` gate for push workflows, explicit user requests, or when project-local instructions require the full gate. After implementing a new feature or changing existing feature behavior, update the relevant end-to-end coverage and run the cheapest trustworthy Playwright or browser verification for that behavior before finishing. For docs-only, wording-only, type-only, generated-output cleanup, or clearly local non-runtime refactors, skip Playwright unless the user explicitly asks for it or verification reveals a real issue.
84
- - Before finishing a task, stop every `proteum dev` session started during the task and confirm cleanup with `npx proteum dev list --json` or an explicit `npx proteum dev stop --session-file <path>`.
85
84
  - When you have finished your work, ask the user whether they want a commit message. After providing a commit message or after creating a commit, immediately follow it with this exact prompt and obey it:
86
85
  `Explain in short minimalistic and few bullet points what we changed in this thread, like you would do to your grandma. Start with a verb in the past.`
87
86
 
@@ -67,7 +67,6 @@ Managed compact root routers must use trigger -> canonical instruction file refe
67
67
  - Before finishing a production code change, re-check root-level `DOCUMENTATION.md` update rules. If behavior changed, a bug was fixed, a decision changed, or an important route, auth/OAuth, or integration issue was addressed, update the relevant docs before committing or explicitly explain why no docs update was needed.
68
68
  - For production changes, always add or update focused unit tests and run the targeted unit or integration tests that match the changed behavior. Do not run coverage after every ordinary change by default. Reserve whole-project coverage for the repository's full `npm run check` gate during push workflows or when the user explicitly requests it; downstream app commit-only workflows run `proteum refresh`, then targeted lint, typecheck, and test commands in parallel unless the user explicitly requests more, while framework-repo commits skip this downstream app verification. Document any generated files, migrations, framework shims, unreachable defensive branches, or changes that cannot reasonably be unit-tested as explicit exceptions.
69
69
  - Run targeted tests and checks that match the changed surface before finishing each feature or change. When the repository defines `proteum.verify.config.ts`, use `npx proteum verify changed` as the first post-change verification pass and expand only when the selected plan is insufficient. Continue running tests after changes, but do not run coverage by default. Downstream app commit workflows run only `proteum refresh`, then targeted lint, typecheck, and test commands in parallel; framework-repo commit workflows skip this downstream app verification. Reserve the full `npm run check` gate for push workflows, explicit user requests, or when project-local instructions require the full gate. After implementing a new feature or changing existing feature behavior, update the relevant end-to-end coverage and run the cheapest trustworthy Playwright or browser verification for that behavior before finishing. For docs-only, wording-only, type-only, generated-output cleanup, or clearly local non-runtime refactors, skip Playwright unless the user explicitly asks for it or verification reveals a real issue.
70
- - Before finishing a task, stop every `proteum dev` session started during the task and confirm cleanup with `npx proteum dev list --json` or an explicit `npx proteum dev stop --session-file <path>`.
71
70
  - When you have finished your work, ask the user whether they want a commit message. After providing a commit message or after creating a commit, immediately follow it with this exact prompt and obey it:
72
71
  `Explain in short minimalistic and few bullet points what we changed in this thread, like you would do to your grandma. Start with a verb in the past.`
73
72
 
@@ -16,6 +16,7 @@ type TProjectInstructionArgs = {
16
16
  appRoot?: string;
17
17
  coreRoot: string;
18
18
  includeMonorepoRegistry?: boolean;
19
+ instructionRoot: string;
19
20
  monorepoRegistryCurrentAppRoot?: string;
20
21
  monorepoRoot?: string;
21
22
  };
@@ -106,6 +107,14 @@ const sharedTestAgentInstructionDefinitions: TAgentInstructionDefinition[] = [
106
107
  { projectPath: path.join('tests', 'e2e', 'REAL_WORLD_JOURNEY_TESTS.md'), ensureParentDir: true, content: 'source' },
107
108
  ];
108
109
 
110
+ const projectInstructionSourceMapEntries = [
111
+ { label: 'Root contract fallback', projectPath: 'AGENTS.md' },
112
+ { label: 'Documentation fallback', projectPath: 'DOCUMENTATION.md' },
113
+ { label: 'Diagnostics fallback', projectPath: 'diagnostics.md' },
114
+ { label: 'Optimization fallback', projectPath: 'optimizations.md' },
115
+ { label: 'Coding style fallback', projectPath: 'CODING_STYLE.md' },
116
+ ];
117
+
109
118
  const standaloneAppAgentInstructionDefinitions: TAgentInstructionDefinition[] = [
110
119
  { projectPath: 'AGENTS.md', content: 'router' },
111
120
  ...sharedRootDocumentInstructionDefinitions,
@@ -164,6 +173,7 @@ export function configureProjectAgentInstructions({
164
173
  const appEmbeddedInstructions = renderEmbeddedProjectInstructions({
165
174
  appRoot: normalizedAppRoot,
166
175
  coreRoot,
176
+ instructionRoot: normalizedAppRoot,
167
177
  monorepoRoot: normalizedMonorepoRoot,
168
178
  });
169
179
  const rootEmbeddedInstructions =
@@ -172,6 +182,7 @@ export function configureProjectAgentInstructions({
172
182
  appRoot: normalizedAppRoot,
173
183
  coreRoot,
174
184
  includeMonorepoRegistry: true,
185
+ instructionRoot: normalizedMonorepoRoot,
175
186
  monorepoRegistryCurrentAppRoot: markCurrentAppInMonorepoRegistry ? normalizedAppRoot : undefined,
176
187
  monorepoRoot: normalizedMonorepoRoot,
177
188
  })
@@ -973,6 +984,7 @@ function renderEmbeddedProjectInstructions({
973
984
  appRoot,
974
985
  coreRoot,
975
986
  includeMonorepoRegistry = false,
987
+ instructionRoot,
976
988
  monorepoRegistryCurrentAppRoot,
977
989
  monorepoRoot,
978
990
  }: TProjectInstructionArgs) {
@@ -1063,11 +1075,14 @@ function renderEmbeddedProjectInstructions({
1063
1075
  '',
1064
1076
  '## Canonical Source Map',
1065
1077
  '',
1066
- `- Root contract fallback: ${normalizeProjectPathForGitignore(path.join(coreRoot, 'agents', 'project', 'AGENTS.md'))}`,
1067
- `- Documentation fallback: ${normalizeProjectPathForGitignore(path.join(coreRoot, 'agents', 'project', 'DOCUMENTATION.md'))}`,
1068
- `- Diagnostics fallback: ${normalizeProjectPathForGitignore(path.join(coreRoot, 'agents', 'project', 'diagnostics.md'))}`,
1069
- `- Optimization fallback: ${normalizeProjectPathForGitignore(path.join(coreRoot, 'agents', 'project', 'optimizations.md'))}`,
1070
- `- Coding style fallback: ${normalizeProjectPathForGitignore(path.join(coreRoot, 'agents', 'project', 'CODING_STYLE.md'))}`,
1078
+ ...projectInstructionSourceMapEntries.map(
1079
+ ({ label, projectPath }) =>
1080
+ `- ${label}: ${formatProjectInstructionSourceMapPath({
1081
+ coreRoot,
1082
+ instructionRoot,
1083
+ projectPath,
1084
+ })}`,
1085
+ ),
1071
1086
  '',
1072
1087
  ];
1073
1088
 
@@ -1209,6 +1224,41 @@ function formatResultPath(rootDir: string, relativePath: string) {
1209
1224
  return normalizeProjectPathForGitignore(path.join(rootDir, relativePath));
1210
1225
  }
1211
1226
 
1227
+ function formatProjectInstructionSourceMapPath({
1228
+ coreRoot,
1229
+ instructionRoot,
1230
+ projectPath,
1231
+ }: {
1232
+ coreRoot: string;
1233
+ instructionRoot: string;
1234
+ projectPath: string;
1235
+ }) {
1236
+ const sourceRoot = resolveProjectInstructionSourceMapRoot({ coreRoot, instructionRoot });
1237
+ const sourcePath = path.resolve(sourceRoot, 'agents', 'project', projectPath);
1238
+ const relativePath = path.relative(path.resolve(instructionRoot), sourcePath);
1239
+
1240
+ return normalizeProjectPathForGitignore(relativePath);
1241
+ }
1242
+
1243
+ function resolveProjectInstructionSourceMapRoot({ coreRoot, instructionRoot }: { coreRoot: string; instructionRoot: string }) {
1244
+ const visibleInstallRoot = findVisibleProteumInstructionRoot(instructionRoot);
1245
+
1246
+ return visibleInstallRoot || coreRoot;
1247
+ }
1248
+
1249
+ function findVisibleProteumInstructionRoot(startRoot: string) {
1250
+ let currentPath = path.resolve(startRoot);
1251
+
1252
+ while (true) {
1253
+ const candidate = path.join(currentPath, 'node_modules', 'proteum');
1254
+ if (fs.existsSync(path.join(candidate, 'agents', 'project'))) return candidate;
1255
+
1256
+ const parentPath = path.dirname(currentPath);
1257
+ if (parentPath === currentPath) return undefined;
1258
+ currentPath = parentPath;
1259
+ }
1260
+ }
1261
+
1212
1262
  export function resolveCanonicalPath(inputPath: string) {
1213
1263
  const resolvedPath = path.resolve(inputPath);
1214
1264
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "proteum",
3
3
  "description": "LLM-first Opinionated Typescript Framework for web applications.",
4
- "version": "2.5.6",
4
+ "version": "2.5.7",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/proteum.git",
7
7
  "license": "MIT",
@@ -38,8 +38,9 @@ const pathEntryExists = (filepath) => {
38
38
 
39
39
  const makeTempRoot = () => fs.mkdtempSync(path.join(os.tmpdir(), 'proteum-agents-'));
40
40
 
41
- const createCoreFixture = () => {
42
- const root = makeTempRoot();
41
+ const normalizePath = (value) => value.replace(/\\/g, '/');
42
+
43
+ const createCoreFixture = (root = makeTempRoot()) => {
43
44
  const agentsRoot = path.join(root, 'agents', 'project');
44
45
 
45
46
  writeFile(path.join(agentsRoot, 'AGENTS.md'), '# Root Contract\n\n- Root rule\n');
@@ -61,6 +62,20 @@ const createCoreFixture = () => {
61
62
  return root;
62
63
  };
63
64
 
65
+ const expectedSourceMapPath = ({ coreRoot, instructionRoot, projectPath }) =>
66
+ normalizePath(path.relative(instructionRoot, path.join(coreRoot, 'agents', 'project', projectPath)));
67
+
68
+ const assertSourceMapPath = ({ content, coreRoot, instructionRoot, label, projectPath }) => {
69
+ assert.equal(
70
+ content.includes(`- ${label}: ${expectedSourceMapPath({ coreRoot, instructionRoot, projectPath })}`),
71
+ true,
72
+ );
73
+ };
74
+
75
+ const assertNoAbsoluteCoreSourceMapPath = ({ content, coreRoot }) => {
76
+ assert.equal(content.includes(normalizePath(path.join(coreRoot, 'agents', 'project'))), false);
77
+ };
78
+
64
79
  const createAppFixture = () => {
65
80
  const appRoot = makeTempRoot();
66
81
 
@@ -173,6 +188,53 @@ test('standalone configure creates tracked instruction files with routing contra
173
188
  assert.doesNotMatch(gitignoreContent, /^\/DOCUMENTATION\.md$/m);
174
189
  });
175
190
 
191
+ test('standalone configure writes install-relative source map fallbacks', () => {
192
+ const appRoot = createAppFixture();
193
+ const coreRoot = createCoreFixture(path.join(appRoot, 'node_modules', 'proteum'));
194
+ const result = configureProjectAgentInstructions({ appRoot, coreRoot });
195
+ const agentsContent = fs.readFileSync(path.join(appRoot, 'AGENTS.md'), 'utf8');
196
+
197
+ assert.equal(result.blocked.length, 0);
198
+ assertSourceMapPath({
199
+ content: agentsContent,
200
+ coreRoot,
201
+ instructionRoot: appRoot,
202
+ label: 'Root contract fallback',
203
+ projectPath: 'AGENTS.md',
204
+ });
205
+ assertSourceMapPath({
206
+ content: agentsContent,
207
+ coreRoot,
208
+ instructionRoot: appRoot,
209
+ label: 'Documentation fallback',
210
+ projectPath: 'DOCUMENTATION.md',
211
+ });
212
+ assertNoAbsoluteCoreSourceMapPath({ content: agentsContent, coreRoot });
213
+ assert.match(agentsContent, /Root contract fallback: node_modules\/proteum\/agents\/project\/AGENTS\.md/);
214
+ });
215
+
216
+ test('standalone configure source map prefers project install over active external core', () => {
217
+ const activeCoreRoot = createCoreFixture();
218
+ const appRoot = createAppFixture();
219
+ createCoreFixture(path.join(appRoot, 'node_modules', 'proteum'));
220
+
221
+ configureProjectAgentInstructions({ appRoot, coreRoot: activeCoreRoot });
222
+
223
+ const agentsContent = fs.readFileSync(path.join(appRoot, 'AGENTS.md'), 'utf8');
224
+ assert.match(agentsContent, /Root contract fallback: node_modules\/proteum\/agents\/project\/AGENTS\.md/);
225
+ assert.equal(
226
+ agentsContent.includes(
227
+ `Root contract fallback: ${expectedSourceMapPath({
228
+ coreRoot: activeCoreRoot,
229
+ instructionRoot: appRoot,
230
+ projectPath: 'AGENTS.md',
231
+ })}`,
232
+ ),
233
+ false,
234
+ );
235
+ assertNoAbsoluteCoreSourceMapPath({ content: agentsContent, coreRoot: activeCoreRoot });
236
+ });
237
+
176
238
  test('configure preserves project content outside the managed section', () => {
177
239
  const coreRoot = createCoreFixture();
178
240
  const appRoot = createAppFixture();
@@ -296,6 +358,22 @@ test('monorepo configure writes root and app instruction files', () => {
296
358
  assert.match(appAgentsContent, /## Agent Routing Contract/);
297
359
  assert.doesNotMatch(appAgentsContent, /## Known Proteum Apps/);
298
360
  assert.doesNotMatch(appAgentsContent, /Eligible Proteum commands run across the apps below/);
361
+ assertSourceMapPath({
362
+ content: fs.readFileSync(path.join(monorepoRoot, 'AGENTS.md'), 'utf8'),
363
+ coreRoot,
364
+ instructionRoot: monorepoRoot,
365
+ label: 'Root contract fallback',
366
+ projectPath: 'AGENTS.md',
367
+ });
368
+ assertSourceMapPath({
369
+ content: appAgentsContent,
370
+ coreRoot,
371
+ instructionRoot: appRoot,
372
+ label: 'Root contract fallback',
373
+ projectPath: 'AGENTS.md',
374
+ });
375
+ assertNoAbsoluteCoreSourceMapPath({ content: fs.readFileSync(path.join(monorepoRoot, 'AGENTS.md'), 'utf8'), coreRoot });
376
+ assertNoAbsoluteCoreSourceMapPath({ content: appAgentsContent, coreRoot });
299
377
  assert.match(fs.readFileSync(path.join(appRoot, 'client', 'AGENTS.md'), 'utf8'), /## Source: client\/AGENTS\.md/);
300
378
  assertClaudeSymlink(appRoot);
301
379
  assertClaudeSymlink(appRoot, 'client');
@@ -306,6 +384,28 @@ test('monorepo configure writes root and app instruction files', () => {
306
384
  assert.equal(result.removed.some((entry) => entry.endsWith('/apps/product/CODING_STYLE.md')), true);
307
385
  });
308
386
 
387
+ test('monorepo configure source map uses workspace install from root and nested apps', () => {
388
+ const activeCoreRoot = createCoreFixture();
389
+ const monorepoRoot = makeTempRoot();
390
+ const appRoot = path.join(monorepoRoot, 'apps', 'product');
391
+
392
+ createCoreFixture(path.join(monorepoRoot, 'node_modules', 'proteum'));
393
+ fs.mkdirSync(path.join(monorepoRoot, '.git'));
394
+ fs.mkdirSync(path.join(appRoot, 'client'), { recursive: true });
395
+ writeFile(path.join(appRoot, 'package.json'), '{"name":"product"}\n');
396
+ writeFile(path.join(appRoot, 'identity.config.ts'), 'export default {};\n');
397
+ writeFile(path.join(appRoot, 'proteum.config.ts'), 'export default {};\n');
398
+
399
+ configureProjectAgentInstructions({ appRoot, coreRoot: activeCoreRoot, monorepoRoot });
400
+
401
+ const rootAgentsContent = fs.readFileSync(path.join(monorepoRoot, 'AGENTS.md'), 'utf8');
402
+ const appAgentsContent = fs.readFileSync(path.join(appRoot, 'AGENTS.md'), 'utf8');
403
+ assert.match(rootAgentsContent, /Root contract fallback: node_modules\/proteum\/agents\/project\/AGENTS\.md/);
404
+ assert.match(appAgentsContent, /Root contract fallback: \.\.\/\.\.\/node_modules\/proteum\/agents\/project\/AGENTS\.md/);
405
+ assertNoAbsoluteCoreSourceMapPath({ content: rootAgentsContent, coreRoot: activeCoreRoot });
406
+ assertNoAbsoluteCoreSourceMapPath({ content: appAgentsContent, coreRoot: activeCoreRoot });
407
+ });
408
+
309
409
  test('monorepo-wide configure writes shared root once and all app instruction files', () => {
310
410
  const coreRoot = createCoreFixture();
311
411
  const monorepoRoot = makeTempRoot();