proteum 2.2.6 → 2.2.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.
- package/AGENTS.md +1 -1
- package/README.md +4 -4
- package/agents/project/AGENTS.md +2 -1
- package/agents/project/app-root/AGENTS.md +1 -1
- package/agents/project/diagnostics.md +1 -1
- package/agents/project/root/AGENTS.md +2 -1
- package/cli/commands/configure.ts +14 -35
- package/cli/commands/dev.ts +105 -52
- package/cli/compiler/artifacts/manifest.ts +1 -5
- package/cli/presentation/commands.ts +9 -9
- package/cli/presentation/help.ts +1 -1
- package/cli/scaffold/index.ts +2 -5
- package/cli/scaffold/templates.ts +1 -7
- package/cli/utils/agents.ts +281 -199
- package/package.json +1 -1
- package/tests/agents-utils.test.cjs +207 -0
- package/tests/dev-transpile-watch.test.cjs +513 -0
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.2.
|
|
4
|
+
"version": "2.2.7",
|
|
5
5
|
"author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
|
|
6
6
|
"repository": "git://github.com/gaetanlegac/proteum.git",
|
|
7
7
|
"license": "MIT",
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
const assert = require('node:assert/strict');
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const os = require('node:os');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const test = require('node:test');
|
|
6
|
+
|
|
7
|
+
const coreRoot = path.resolve(__dirname, '..');
|
|
8
|
+
process.env.TS_NODE_PROJECT = path.join(coreRoot, 'cli', 'tsconfig.json');
|
|
9
|
+
process.env.TS_NODE_TRANSPILE_ONLY = '1';
|
|
10
|
+
require('ts-node/register/transpile-only');
|
|
11
|
+
|
|
12
|
+
const { configureProjectAgentInstructions, resolveProjectAgentMonorepoRoot } = require('../cli/utils/agents.ts');
|
|
13
|
+
|
|
14
|
+
const writeFile = (filepath, content) => {
|
|
15
|
+
fs.mkdirSync(path.dirname(filepath), { recursive: true });
|
|
16
|
+
fs.writeFileSync(filepath, content);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const makeTempRoot = () => fs.mkdtempSync(path.join(os.tmpdir(), 'proteum-agents-'));
|
|
20
|
+
|
|
21
|
+
const createCoreFixture = () => {
|
|
22
|
+
const root = makeTempRoot();
|
|
23
|
+
const agentsRoot = path.join(root, 'agents', 'project');
|
|
24
|
+
|
|
25
|
+
writeFile(path.join(agentsRoot, 'AGENTS.md'), '# Root Contract\n\n- Root rule\n');
|
|
26
|
+
writeFile(path.join(agentsRoot, 'CODING_STYLE.md'), '# Coding Style\n\n- Style rule\n');
|
|
27
|
+
writeFile(path.join(agentsRoot, 'client', 'AGENTS.md'), '# Client Rules\n\n- Client rule\n');
|
|
28
|
+
|
|
29
|
+
return root;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const createAppFixture = () => {
|
|
33
|
+
const appRoot = makeTempRoot();
|
|
34
|
+
|
|
35
|
+
for (const dir of ['client/pages', 'server/routes', 'server/services', 'tests/e2e']) {
|
|
36
|
+
fs.mkdirSync(path.join(appRoot, dir), { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
writeFile(
|
|
40
|
+
path.join(appRoot, '.gitignore'),
|
|
41
|
+
[
|
|
42
|
+
'node_modules',
|
|
43
|
+
'# Proteum-managed instruction files',
|
|
44
|
+
'/AGENTS.md',
|
|
45
|
+
'/CODING_STYLE.md',
|
|
46
|
+
'# End Proteum-managed instruction files',
|
|
47
|
+
'/.proteum',
|
|
48
|
+
'',
|
|
49
|
+
].join('\n'),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return appRoot;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
test('standalone configure creates tracked instruction files with embedded corpus', () => {
|
|
56
|
+
const coreRoot = createCoreFixture();
|
|
57
|
+
const appRoot = createAppFixture();
|
|
58
|
+
const result = configureProjectAgentInstructions({ appRoot, coreRoot });
|
|
59
|
+
const agentsContent = fs.readFileSync(path.join(appRoot, 'AGENTS.md'), 'utf8');
|
|
60
|
+
const codingStyleContent = fs.readFileSync(path.join(appRoot, 'CODING_STYLE.md'), 'utf8');
|
|
61
|
+
const gitignoreContent = fs.readFileSync(path.join(appRoot, '.gitignore'), 'utf8');
|
|
62
|
+
|
|
63
|
+
assert.equal(result.blocked.length, 0);
|
|
64
|
+
assert.match(agentsContent, /^# Proteum Instructions/m);
|
|
65
|
+
assert.match(agentsContent, /<!-- proteum-instructions:start -->/);
|
|
66
|
+
assert.match(agentsContent, /## Source: AGENTS\.md/);
|
|
67
|
+
assert.match(agentsContent, /## Root Contract/);
|
|
68
|
+
assert.match(agentsContent, /## Source: CODING_STYLE\.md/);
|
|
69
|
+
assert.match(codingStyleContent, /## Source: client\/AGENTS\.md/);
|
|
70
|
+
assert.doesNotMatch(agentsContent, /Before reading or applying instructions from this file/);
|
|
71
|
+
assert.doesNotMatch(gitignoreContent, /Proteum-managed instruction files/);
|
|
72
|
+
assert.doesNotMatch(gitignoreContent, /^\/AGENTS\.md$/m);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('configure preserves project content outside the managed section', () => {
|
|
76
|
+
const coreRoot = createCoreFixture();
|
|
77
|
+
const appRoot = createAppFixture();
|
|
78
|
+
|
|
79
|
+
writeFile(
|
|
80
|
+
path.join(appRoot, 'AGENTS.md'),
|
|
81
|
+
[
|
|
82
|
+
'# Product Notes',
|
|
83
|
+
'',
|
|
84
|
+
'Keep this product note.',
|
|
85
|
+
'',
|
|
86
|
+
'# Proteum Instructions',
|
|
87
|
+
'<!-- proteum-instructions:start -->',
|
|
88
|
+
'',
|
|
89
|
+
'Old managed content.',
|
|
90
|
+
'',
|
|
91
|
+
'<!-- proteum-instructions:end -->',
|
|
92
|
+
'',
|
|
93
|
+
'# Local Footer',
|
|
94
|
+
'',
|
|
95
|
+
'Keep this footer.',
|
|
96
|
+
'',
|
|
97
|
+
].join('\n'),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
configureProjectAgentInstructions({ appRoot, coreRoot });
|
|
101
|
+
|
|
102
|
+
const content = fs.readFileSync(path.join(appRoot, 'AGENTS.md'), 'utf8');
|
|
103
|
+
assert.match(content, /# Product Notes/);
|
|
104
|
+
assert.match(content, /Keep this product note\./);
|
|
105
|
+
assert.match(content, /## Source: CODING_STYLE\.md/);
|
|
106
|
+
assert.doesNotMatch(content, /Old managed content/);
|
|
107
|
+
assert.match(content, /# Local Footer/);
|
|
108
|
+
assert.match(content, /Keep this footer\./);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('configure preserves project content around legacy managed stubs', () => {
|
|
112
|
+
const coreRoot = createCoreFixture();
|
|
113
|
+
const appRoot = createAppFixture();
|
|
114
|
+
|
|
115
|
+
writeFile(
|
|
116
|
+
path.join(appRoot, 'AGENTS.md'),
|
|
117
|
+
[
|
|
118
|
+
'## Product Bootstrap',
|
|
119
|
+
'',
|
|
120
|
+
'Keep these local bootstrap notes.',
|
|
121
|
+
'',
|
|
122
|
+
'# Proteum Managed Instructions',
|
|
123
|
+
'',
|
|
124
|
+
'This file is managed by `proteum configure agents`.',
|
|
125
|
+
'',
|
|
126
|
+
'Before reading or applying instructions from this file, read and follow the canonical Proteum instruction file at:',
|
|
127
|
+
'',
|
|
128
|
+
'`node_modules/proteum/agents/project/AGENTS.md`',
|
|
129
|
+
'',
|
|
130
|
+
'Resolve that path relative to this file. Treat the canonical file as if its full contents were written here.',
|
|
131
|
+
'',
|
|
132
|
+
'If the canonical file cannot be read, stop and run `npx proteum configure agents` before continuing.',
|
|
133
|
+
'',
|
|
134
|
+
'## Local Footer',
|
|
135
|
+
'',
|
|
136
|
+
'Keep this footer too.',
|
|
137
|
+
'',
|
|
138
|
+
].join('\n'),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
configureProjectAgentInstructions({ appRoot, coreRoot });
|
|
142
|
+
|
|
143
|
+
const content = fs.readFileSync(path.join(appRoot, 'AGENTS.md'), 'utf8');
|
|
144
|
+
assert.match(content, /## Product Bootstrap/);
|
|
145
|
+
assert.match(content, /Keep these local bootstrap notes\./);
|
|
146
|
+
assert.match(content, /# Proteum Instructions/);
|
|
147
|
+
assert.match(content, /## Source: CODING_STYLE\.md/);
|
|
148
|
+
assert.doesNotMatch(content, /# Proteum Managed Instructions/);
|
|
149
|
+
assert.doesNotMatch(content, /Before reading or applying instructions from this file/);
|
|
150
|
+
assert.match(content, /## Local Footer/);
|
|
151
|
+
assert.match(content, /Keep this footer too\./);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('monorepo configure writes root and app instruction files', () => {
|
|
155
|
+
const coreRoot = createCoreFixture();
|
|
156
|
+
const monorepoRoot = makeTempRoot();
|
|
157
|
+
const appRoot = path.join(monorepoRoot, 'apps', 'product');
|
|
158
|
+
|
|
159
|
+
fs.mkdirSync(path.join(monorepoRoot, '.git'));
|
|
160
|
+
fs.mkdirSync(path.join(appRoot, 'client'), { recursive: true });
|
|
161
|
+
|
|
162
|
+
const result = configureProjectAgentInstructions({ appRoot, coreRoot, monorepoRoot });
|
|
163
|
+
|
|
164
|
+
assert.equal(result.mode, 'monorepo');
|
|
165
|
+
assert.equal(resolveProjectAgentMonorepoRoot(appRoot), fs.realpathSync(monorepoRoot));
|
|
166
|
+
assert.match(fs.readFileSync(path.join(monorepoRoot, 'AGENTS.md'), 'utf8'), /## Source: AGENTS\.md/);
|
|
167
|
+
assert.match(fs.readFileSync(path.join(appRoot, 'AGENTS.md'), 'utf8'), /## Source: client\/AGENTS\.md/);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('configure migrates legacy managed symlinks to embedded files', () => {
|
|
171
|
+
const coreRoot = createCoreFixture();
|
|
172
|
+
const appRoot = createAppFixture();
|
|
173
|
+
const installedCoreRoot = createCoreFixture();
|
|
174
|
+
const target = path.join(installedCoreRoot, 'agents', 'project', 'AGENTS.md');
|
|
175
|
+
const linkPath = path.join(appRoot, 'AGENTS.md');
|
|
176
|
+
|
|
177
|
+
fs.symlinkSync(target, linkPath);
|
|
178
|
+
|
|
179
|
+
const result = configureProjectAgentInstructions({ appRoot, coreRoot });
|
|
180
|
+
const stats = fs.lstatSync(linkPath);
|
|
181
|
+
const content = fs.readFileSync(linkPath, 'utf8');
|
|
182
|
+
|
|
183
|
+
assert.equal(result.updated.some((entry) => entry.endsWith('/AGENTS.md')), true);
|
|
184
|
+
assert.equal(stats.isSymbolicLink(), false);
|
|
185
|
+
assert.match(content, /# Proteum Instructions/);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('configure reports blocked paths unless overwrite is allowed', () => {
|
|
189
|
+
const coreRoot = createCoreFixture();
|
|
190
|
+
const appRoot = createAppFixture();
|
|
191
|
+
const blockedPath = path.join(appRoot, 'CODING_STYLE.md');
|
|
192
|
+
|
|
193
|
+
fs.mkdirSync(blockedPath);
|
|
194
|
+
|
|
195
|
+
const preview = configureProjectAgentInstructions({ appRoot, coreRoot, dryRun: true });
|
|
196
|
+
assert.equal(preview.blocked.some((entry) => entry.endsWith('/CODING_STYLE.md')), true);
|
|
197
|
+
|
|
198
|
+
const result = configureProjectAgentInstructions({
|
|
199
|
+
appRoot,
|
|
200
|
+
coreRoot,
|
|
201
|
+
overwriteBlockedPaths: [blockedPath],
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
assert.equal(result.overwritten.some((entry) => entry.endsWith('/CODING_STYLE.md')), true);
|
|
205
|
+
assert.equal(fs.lstatSync(blockedPath).isFile(), true);
|
|
206
|
+
assert.match(fs.readFileSync(blockedPath, 'utf8'), /## Source: AGENTS\.md/);
|
|
207
|
+
});
|