@hamp10/agentforge 0.2.37 → 0.2.38

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hamp10/agentforge",
3
- "version": "0.2.37",
3
+ "version": "0.2.38",
4
4
  "description": "AgentForge worker — connect your machine to agentforge.ai",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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,33 @@ 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]);
252
285
  const projectsRoot = mkdtempSync(path.join(tmpdir(), 'agentforge-project-list-'));
253
286
  let agentWorkspace = null;
254
287
  try {
@@ -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', 'two',
15
- 'both', 'change', 'edit', 'modify', 'touch', 'make', 'improve', 'fix',
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 x = rawLine[0] || ' ';
992
- const y = rawLine[1] || ' ';
993
- if (x !== 'D' && y !== 'D') continue;
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
  }