@hamp10/agentforge 0.2.37 → 0.2.39
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/package.json +1 -1
- package/scripts/check-task-semantics.js +47 -0
- package/src/taskSemantics.js +3 -2
- package/src/worker.js +32 -6
package/package.json
CHANGED
|
@@ -35,6 +35,12 @@ const cases = [
|
|
|
35
35
|
absent: ['example-com', 'make', 'fix'],
|
|
36
36
|
pageOnly: true,
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
text: 'Work on the Example.com listing pages for AlphaBoard.ai, BetaMatch.ai, and GammaForge.ai. Make all three pages visually polished and consistent with the rest of the site.',
|
|
40
|
+
slugs: ['alphaboard-ai', 'betamatch-ai', 'gammaforge-ai'],
|
|
41
|
+
absent: ['example-com', 'three', 'all-three', 'visual', 'polished'],
|
|
42
|
+
pageOnly: true,
|
|
43
|
+
},
|
|
38
44
|
{
|
|
39
45
|
text: 'Work on the Example.com listing pages for AlphaBoard and BetaMatch. Delete and rebuild those two listing page implementations from a clean start, preserving the same URLs and site conventions. Fix the readability and design issues. Only change those two listing pages.',
|
|
40
46
|
slugs: ['alphaboard', 'betamatch'],
|
|
@@ -249,6 +255,47 @@ try {
|
|
|
249
255
|
'clean-start rebuild tasks should still require the scoped target page to be recreated'
|
|
250
256
|
);
|
|
251
257
|
git(fixture.repo, ['restore', '--', 'public_html/domains/alpha.html']);
|
|
258
|
+
const nestedAlphaRel = `${path.basename(fixture.repo)}/public_html/domains/alpha.html`;
|
|
259
|
+
rmSync(path.join(fixture.repo, nestedAlphaRel), { force: true });
|
|
260
|
+
assert.equal(
|
|
261
|
+
worker._buildDeletedScopedPageNudge(
|
|
262
|
+
[{ root: fixture.repo, head: fixture.head }],
|
|
263
|
+
'Delete and rebuild the Alpha listing page from a clean start, preserving the same URL. Only change that listing page.'
|
|
264
|
+
),
|
|
265
|
+
'',
|
|
266
|
+
'tracked same-name nested project copies should not count as missing scoped page sources'
|
|
267
|
+
);
|
|
268
|
+
assert.equal(
|
|
269
|
+
worker._scopeAllowsChangedPath(
|
|
270
|
+
{ root: fixture.repo, head: fixture.head },
|
|
271
|
+
nestedAlphaRel,
|
|
272
|
+
['alpha'],
|
|
273
|
+
true,
|
|
274
|
+
'Delete and rebuild the Alpha listing page from a clean start. Only change that listing page.'
|
|
275
|
+
),
|
|
276
|
+
false,
|
|
277
|
+
'tracked same-name nested project copies should be treated as out-of-scope changes'
|
|
278
|
+
);
|
|
279
|
+
assert.equal(
|
|
280
|
+
worker._allCurrentPageSourceFiles(fixture.repo).includes(nestedAlphaRel),
|
|
281
|
+
false,
|
|
282
|
+
'tracked same-name nested project copies should not pollute current page source discovery'
|
|
283
|
+
);
|
|
284
|
+
git(fixture.repo, ['restore', '--', nestedAlphaRel]);
|
|
285
|
+
const untrackedGammaRel = 'public_html/domains/gamma.html';
|
|
286
|
+
writeFileSync(
|
|
287
|
+
path.join(fixture.repo, untrackedGammaRel),
|
|
288
|
+
'<!doctype html><html><body><main><section><h1>Gamma</h1><p>New scoped page.</p></section></main></body></html>'
|
|
289
|
+
);
|
|
290
|
+
assert.equal(
|
|
291
|
+
worker._buildIncompleteScopedUiTargetsNudge(
|
|
292
|
+
[{ root: fixture.repo, head: fixture.head, initialDirtyPaths: [] }],
|
|
293
|
+
'Work on the Example.com listing pages for Alpha.ai, Beta.ai, and Gamma.ai. Make all three pages visually polished.'
|
|
294
|
+
),
|
|
295
|
+
'',
|
|
296
|
+
'new untracked scoped page files should count as touched UI targets'
|
|
297
|
+
);
|
|
298
|
+
rmSync(path.join(fixture.repo, untrackedGammaRel), { force: true });
|
|
252
299
|
const projectsRoot = mkdtempSync(path.join(tmpdir(), 'agentforge-project-list-'));
|
|
253
300
|
let agentWorkspace = null;
|
|
254
301
|
try {
|
package/src/taskSemantics.js
CHANGED
|
@@ -11,8 +11,9 @@ const GENERIC_SCOPE_WORDS = new Set([
|
|
|
11
11
|
'website', 'app', 'readability', 'readable', 'legibility', 'contrast',
|
|
12
12
|
'quality', 'visual', 'content', 'layout', 'styling', 'polish',
|
|
13
13
|
'current', 'target', 'targets', 'requested', 'same', 'rest', 'live',
|
|
14
|
-
'local', 'itself', 'only', 'those', 'these', 'this', 'that',
|
|
15
|
-
'
|
|
14
|
+
'local', 'itself', 'only', 'all', 'those', 'these', 'this', 'that',
|
|
15
|
+
'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine',
|
|
16
|
+
'ten', 'both', 'change', 'edit', 'modify', 'touch', 'make', 'improve', 'fix',
|
|
16
17
|
'update', 'redesign', 'owned', 'source', 'style', 'styles', 'global',
|
|
17
18
|
'shared', 'reference', 'references', 'broaden', 'work', 'wrong', 'bad',
|
|
18
19
|
'broken',
|
package/src/worker.js
CHANGED
|
@@ -650,6 +650,22 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
650
650
|
return /\.(?:html?|xhtml|astro|mdx?)$/i.test(String(relativePath || ''));
|
|
651
651
|
}
|
|
652
652
|
|
|
653
|
+
_nestedProjectCopyCanonicalRel(repo, rel) {
|
|
654
|
+
const normalized = String(rel || '').replace(/^[/\\]+/, '');
|
|
655
|
+
if (!repo || !normalized) return '';
|
|
656
|
+
const parts = normalized.split(/[\\/]+/).filter(Boolean);
|
|
657
|
+
if (parts.length < 2 || parts[0].toLowerCase() !== path.basename(repo).toLowerCase()) return '';
|
|
658
|
+
return parts.slice(1).join('/');
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
_isNestedProjectCopyRel(repo, rel, ref = 'HEAD') {
|
|
662
|
+
const canonicalRel = this._nestedProjectCopyCanonicalRel(repo, rel);
|
|
663
|
+
if (!canonicalRel) return false;
|
|
664
|
+
const canonicalAbs = path.join(repo, ...canonicalRel.split('/'));
|
|
665
|
+
if (existsSync(canonicalAbs) || existsSync(path.dirname(canonicalAbs))) return true;
|
|
666
|
+
return this._gitPathExistsAtRef(repo, ref || 'HEAD', canonicalRel);
|
|
667
|
+
}
|
|
668
|
+
|
|
653
669
|
_isBroadReferenceSourcePath(relativePath) {
|
|
654
670
|
const normalized = String(relativePath || '').replace(/\\/g, '/').toLowerCase();
|
|
655
671
|
const parts = normalized.split('/').filter(Boolean);
|
|
@@ -727,6 +743,7 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
727
743
|
const names = new Set();
|
|
728
744
|
const add = (output) => {
|
|
729
745
|
for (const rel of String(output || '').split('\n').map(line => line.trim()).filter(Boolean)) {
|
|
746
|
+
if (this._isNestedProjectCopyRel(repo, rel)) continue;
|
|
730
747
|
if (this._isPageSourcePath(rel)) names.add(rel);
|
|
731
748
|
}
|
|
732
749
|
};
|
|
@@ -785,6 +802,7 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
785
802
|
|
|
786
803
|
_scopeAllowsChangedPath(baseline, rel, allowedSlugs, pageOnly, userMessage = '') {
|
|
787
804
|
const lower = String(rel || '').toLowerCase();
|
|
805
|
+
if (this._isNestedProjectCopyRel(baseline?.root, rel, baseline?.head || 'HEAD')) return false;
|
|
788
806
|
const slugAllowed = this._scopeSlugsMatchingText(lower, allowedSlugs).length > 0;
|
|
789
807
|
if (slugAllowed) {
|
|
790
808
|
if (this._isNewHtmlPageOutsideExistingCollection(baseline, rel, allowedSlugs, pageOnly)) return false;
|
|
@@ -988,13 +1006,12 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
988
1006
|
const deleted = [];
|
|
989
1007
|
const status = this._gitStatusPorcelain(baseline.root, 10000);
|
|
990
1008
|
for (const rawLine of String(status || '').split('\n').filter(Boolean)) {
|
|
991
|
-
const
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
const rel = rawLine.length >= 3 && rawLine[2] === ' ' ? rawLine.slice(3) : rawLine.trim().replace(/^..\s+/, '');
|
|
995
|
-
const pathName = rel.includes(' -> ') ? rel.split(' -> ').pop() : rel;
|
|
1009
|
+
const statusCode = rawLine.slice(0, 2);
|
|
1010
|
+
if (!statusCode.includes('D')) continue;
|
|
1011
|
+
const pathName = this._parseGitStatusPaths(rawLine)[0] || '';
|
|
996
1012
|
const lower = String(pathName || '').toLowerCase();
|
|
997
1013
|
if (!this._isPageSourcePath(lower)) continue;
|
|
1014
|
+
if (this._isNestedProjectCopyRel(baseline.root, pathName, baseline.head || 'HEAD')) continue;
|
|
998
1015
|
if (this._scopeSlugsMatchingText(lower, allowedSlugs).length === 0) continue;
|
|
999
1016
|
deleted.push(pathName);
|
|
1000
1017
|
}
|
|
@@ -1523,9 +1540,18 @@ export class AgentForgeWorker extends EventEmitter {
|
|
|
1523
1540
|
this._gitOutput(baseline.root, ['diff', '--numstat'], 10000),
|
|
1524
1541
|
'working tree'
|
|
1525
1542
|
);
|
|
1543
|
+
const initialDirty = new Set(baseline.initialDirtyPaths || []);
|
|
1544
|
+
const untracked = String(this._gitOutput(baseline.root, ['ls-files', '--others', '--exclude-standard'], 10000) || '')
|
|
1545
|
+
.split('\n')
|
|
1546
|
+
.map(line => line.trim())
|
|
1547
|
+
.filter(Boolean)
|
|
1548
|
+
.filter(rel => uiFileRe.test(rel))
|
|
1549
|
+
.filter(rel => !initialDirty.has(rel))
|
|
1550
|
+
.filter(rel => !this._isNestedProjectCopyRel(baseline.root, rel, baseline.head || 'HEAD'));
|
|
1526
1551
|
const changedFiles = [...committed, ...working]
|
|
1527
1552
|
.filter(stat => uiFileRe.test(stat.path))
|
|
1528
|
-
.map(stat => stat.path)
|
|
1553
|
+
.map(stat => stat.path)
|
|
1554
|
+
.concat(untracked);
|
|
1529
1555
|
if (changedFiles.length === 0) continue;
|
|
1530
1556
|
|
|
1531
1557
|
const touchedSlugs = new Set();
|