@open-mercato/cli 0.6.7-develop.6725.1.07a748cfc2 → 0.6.7-develop.6744.1.0483b18565
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/dist/agentic/guides/module-facts.json +2247 -172
- package/dist/agentic/guides/modules/ai_assistant.md +47 -3
- package/dist/agentic/guides/modules/api_docs.md +23 -2
- package/dist/agentic/guides/modules/api_keys.md +25 -3
- package/dist/agentic/guides/modules/attachments.md +25 -3
- package/dist/agentic/guides/modules/audit_logs.md +24 -3
- package/dist/agentic/guides/modules/auth.md +47 -3
- package/dist/agentic/guides/modules/business_rules.md +31 -3
- package/dist/agentic/guides/modules/catalog.md +69 -4
- package/dist/agentic/guides/modules/channel_gmail.md +19 -2
- package/dist/agentic/guides/modules/channel_imap.md +19 -2
- package/dist/agentic/guides/modules/checkout.md +34 -2
- package/dist/agentic/guides/modules/communication_channels.md +23 -2
- package/dist/agentic/guides/modules/configs.md +28 -3
- package/dist/agentic/guides/modules/content.md +22 -2
- package/dist/agentic/guides/modules/currencies.md +32 -3
- package/dist/agentic/guides/modules/customer_accounts.md +27 -2
- package/dist/agentic/guides/modules/customers.md +70 -4
- package/dist/agentic/guides/modules/dashboards.md +25 -3
- package/dist/agentic/guides/modules/data_sync.md +22 -2
- package/dist/agentic/guides/modules/dictionaries.md +21 -2
- package/dist/agentic/guides/modules/directory.md +27 -2
- package/dist/agentic/guides/modules/entities.md +36 -3
- package/dist/agentic/guides/modules/events.md +19 -2
- package/dist/agentic/guides/modules/feature_toggles.md +32 -3
- package/dist/agentic/guides/modules/gateway_stripe.md +23 -3
- package/dist/agentic/guides/modules/generators.md +19 -2
- package/dist/agentic/guides/modules/inbox_ops.md +29 -2
- package/dist/agentic/guides/modules/integrations.md +23 -2
- package/dist/agentic/guides/modules/messages.md +25 -2
- package/dist/agentic/guides/modules/notifications.md +24 -3
- package/dist/agentic/guides/modules/onboarding.md +22 -2
- package/dist/agentic/guides/modules/payment_gateways.md +21 -2
- package/dist/agentic/guides/modules/perspectives.md +19 -2
- package/dist/agentic/guides/modules/planner.md +27 -3
- package/dist/agentic/guides/modules/portal.md +27 -2
- package/dist/agentic/guides/modules/progress.md +19 -2
- package/dist/agentic/guides/modules/query_index.md +27 -3
- package/dist/agentic/guides/modules/record_locks.md +21 -2
- package/dist/agentic/guides/modules/resources.md +32 -3
- package/dist/agentic/guides/modules/sales.md +44 -3
- package/dist/agentic/guides/modules/scheduler.md +30 -3
- package/dist/agentic/guides/modules/search.md +38 -3
- package/dist/agentic/guides/modules/security.md +32 -2
- package/dist/agentic/guides/modules/shipping_carriers.md +21 -2
- package/dist/agentic/guides/modules/sso.md +23 -2
- package/dist/agentic/guides/modules/staff.md +48 -3
- package/dist/agentic/guides/modules/storage_s3.md +23 -3
- package/dist/agentic/guides/modules/sync_akeneo.md +23 -3
- package/dist/agentic/guides/modules/sync_excel.md +19 -2
- package/dist/agentic/guides/modules/system_status_overlays.md +19 -2
- package/dist/agentic/guides/modules/translations.md +21 -2
- package/dist/agentic/guides/modules/webhooks.md +29 -4
- package/dist/agentic/guides/modules/wms.md +35 -3
- package/dist/agentic/guides/modules/workflows.md +42 -3
- package/dist/agentic/guides/upstream/manifest.json +1 -1
- package/dist/agentic/shared/ai/harness/README.md +4 -4
- package/dist/agentic/shared/ai/harness/RELEASE.md +2 -2
- package/dist/agentic/shared/ai/harness/cases.json +334 -467
- package/dist/agentic/shared/ai/harness/cases.schema.json +1 -0
- package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +4 -4
- package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +53 -0
- package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +62 -0
- package/dist/agentic/shared/ai/harness/release-matrix.json +24 -0
- package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +192 -0
- package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +1 -1
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +4 -4
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +1 -1
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +34 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +4 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +11 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +31 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +39 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +43 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +11 -0
- package/dist/agentic/shared/ai/skills/tiers.json +1 -0
- package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +19 -6
- package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +163 -31
- package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +26 -13
- package/dist/lib/generators/module-facts.js +197 -9
- package/dist/lib/generators/module-facts.js.map +2 -2
- package/package.json +5 -5
- package/src/lib/generators/__tests__/module-facts.source-links.test.ts +119 -0
- package/src/lib/generators/module-facts.ts +271 -7
|
@@ -63,9 +63,9 @@ tree. Externally supplied targets must use the same
|
|
|
63
63
|
node_modules link to that protected tree; nested dependency copies are rejected.
|
|
64
64
|
The complete release requires trusted Bubblewrap plus working user/network namespaces on Linux;
|
|
65
65
|
native macOS, Windows, untrusted runtimes, and unavailable isolation fail closed. The command preflights complete matrix,
|
|
66
|
-
fixture,
|
|
66
|
+
fixture, judge, and target coverage before
|
|
67
67
|
running deterministic validation, selected primary routing, optional portability routing, writable trusted-oracle
|
|
68
|
-
gates, explicit om-code-review, and the release-matrix validation commands. It stores
|
|
68
|
+
gates, explicit om-judge-agent-session with om-code-review, and the release-matrix validation commands. It stores
|
|
69
69
|
only a sanitized aggregate report under .ai/harness/results/.`
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -541,8 +541,8 @@ export function buildReleasePlan({ cases, registry, releaseMatrix, fixtures, see
|
|
|
541
541
|
if (!entry || typeof entry !== 'object' || Array.isArray(entry) || Object.keys(entry).length !== 1 || typeof entry.caseId !== 'string') violations.push(`writable release assignment must be runner-neutral: ${String(entry?.caseId)}`)
|
|
542
542
|
}
|
|
543
543
|
|
|
544
|
-
const configuredReviewIds = Array.isArray(releaseMatrix?.
|
|
545
|
-
? releaseMatrix.
|
|
544
|
+
const configuredReviewIds = Array.isArray(releaseMatrix?.generativeJudge?.caseIds)
|
|
545
|
+
? releaseMatrix.generativeJudge.caseIds
|
|
546
546
|
: []
|
|
547
547
|
const missingReviewMatrixCaseIds = difference(reviewEligibleCaseIds, configuredReviewIds)
|
|
548
548
|
const unexpectedReviewMatrixCaseIds = difference(configuredReviewIds, reviewEligibleCaseIds)
|
|
@@ -553,10 +553,17 @@ export function buildReleasePlan({ cases, registry, releaseMatrix, fixtures, see
|
|
|
553
553
|
if (JSON.stringify(configuredReviewIds) !== JSON.stringify(reviewEligibleCaseIds)) {
|
|
554
554
|
violations.push('generated-code review matrix must list every writable case exactly once in catalog order')
|
|
555
555
|
}
|
|
556
|
-
if (release.
|
|
557
|
-
if (
|
|
558
|
-
if (releaseMatrix?.
|
|
559
|
-
|
|
556
|
+
if (release.requireGenerativeJudge !== true) violations.push('release suite must require the generative judge')
|
|
557
|
+
if (release.requireGeneratedCodeReview !== true) violations.push('release suite must retain generated-code review compatibility')
|
|
558
|
+
if (releaseMatrix?.generativeJudge?.skill !== 'om-judge-agent-session') violations.push('generative judge must explicitly use om-judge-agent-session')
|
|
559
|
+
if (releaseMatrix?.generativeJudge?.reviewSkill !== 'om-code-review') violations.push('generative judge must explicitly compose om-code-review')
|
|
560
|
+
if (releaseMatrix?.generativeJudge?.required !== true) violations.push('generative judge must be mandatory for every writable case')
|
|
561
|
+
if (releaseMatrix?.generatedCodeReview?.skill !== 'om-code-review'
|
|
562
|
+
|| JSON.stringify(releaseMatrix.generatedCodeReview?.caseIds) !== JSON.stringify(configuredReviewIds)
|
|
563
|
+
|| JSON.stringify(releaseMatrix.generatedCodeReview?.runners) !== JSON.stringify(releaseMatrix.generativeJudge?.runners)) {
|
|
564
|
+
violations.push('generated-code review compatibility matrix must match the generative judge')
|
|
565
|
+
}
|
|
566
|
+
const reviewRunners = releaseMatrix?.generativeJudge?.runners ?? {}
|
|
560
567
|
for (const runner of RELEASE_SUPPORTED_RUNNERS) {
|
|
561
568
|
if (!validModelSelector(reviewRunners?.[runner]?.modelSelector)) violations.push(`generated-code review lacks a valid bounded ${runner} model selector`)
|
|
562
569
|
}
|
|
@@ -564,6 +571,12 @@ export function buildReleasePlan({ cases, registry, releaseMatrix, fixtures, see
|
|
|
564
571
|
if (!RUNNERS.has(runner)) violations.push(`generated-code review declares an invalid runner: ${runner}`)
|
|
565
572
|
}
|
|
566
573
|
const reviewSkillFiles = [
|
|
574
|
+
'.ai/skills/om-judge-agent-session/SKILL.md',
|
|
575
|
+
'.ai/skills/om-judge-agent-session/references/agentic-setup.md',
|
|
576
|
+
'.ai/skills/om-judge-agent-session/references/input-normalization.md',
|
|
577
|
+
'.ai/skills/om-judge-agent-session/references/judge-workflow.md',
|
|
578
|
+
'.ai/skills/om-judge-agent-session/references/report-template.md',
|
|
579
|
+
'.ai/skills/om-judge-agent-session/references/rules.md',
|
|
567
580
|
'.agents/skills/om-code-review/SKILL.md',
|
|
568
581
|
'.agents/skills/om-code-review/references/agentic-setup.md',
|
|
569
582
|
'.agents/skills/om-code-review/references/output-format.md',
|
|
@@ -572,7 +585,7 @@ export function buildReleasePlan({ cases, registry, releaseMatrix, fixtures, see
|
|
|
572
585
|
'.agents/skills/.om-external-ownership.json',
|
|
573
586
|
]
|
|
574
587
|
const reviewSkillReady = !root || reviewSkillFiles.every((relative) => fs.existsSync(path.join(root, relative)))
|
|
575
|
-
if (!reviewSkillReady) violations.push('
|
|
588
|
+
if (!reviewSkillReady) violations.push('generative judge or code-review skill is not installed with ownership evidence')
|
|
576
589
|
|
|
577
590
|
const registryCaseCountMatches = registry?.catalog?.expectedCaseCount === cases.length
|
|
578
591
|
if (!registryCaseCountMatches) violations.push(`validator registry expects ${String(registry?.catalog?.expectedCaseCount)} cases but catalog contains ${cases.length}`)
|
|
@@ -720,8 +733,8 @@ export function buildReleasePlan({ cases, registry, releaseMatrix, fixtures, see
|
|
|
720
733
|
})
|
|
721
734
|
}
|
|
722
735
|
if (configuredReviewIds.includes(caseId)) {
|
|
723
|
-
const modelSelector = releaseMatrix.
|
|
724
|
-
if (!validModelSelector(modelSelector)) violations.push(`
|
|
736
|
+
const modelSelector = releaseMatrix.generativeJudge?.runners?.[primaryRunner]?.modelSelector
|
|
737
|
+
if (!validModelSelector(modelSelector)) violations.push(`generative judge lacks a valid bounded ${primaryRunner} model selector for ${caseId}`)
|
|
725
738
|
steps.push({ id: `review:${caseId}`, kind: 'review', caseId, runner: primaryRunner, modelSelector })
|
|
726
739
|
}
|
|
727
740
|
}
|
|
@@ -1696,8 +1709,8 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
1696
1709
|
const reviewExecution = execute(process.execPath, [
|
|
1697
1710
|
evaluator, '--root', root, '--runner', reviewStep.runner,
|
|
1698
1711
|
'--model', reviewStep.modelSelector, '--timeout', String(caseTimeout),
|
|
1699
|
-
'--
|
|
1700
|
-
'--
|
|
1712
|
+
'--judge-writable-result', path.join(root, sourceArtifact.path), '--writable-root', target,
|
|
1713
|
+
'--judge-validation-result', targetValidationArtifact.absolute,
|
|
1701
1714
|
], root, caseTimeout + 60_000)
|
|
1702
1715
|
const reviewArtifacts = readNewResults(root, beforeReview)
|
|
1703
1716
|
resultArtifacts.push(...reviewArtifacts)
|
|
@@ -330,9 +330,9 @@ function parseApiRouteEntry(entryLiteral) {
|
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
|
-
return { path: routePath, methods, auth };
|
|
333
|
+
return { path: routePath, methods, auth, sourcePath: null };
|
|
334
334
|
}
|
|
335
|
-
function extractApiRoutes(moduleId, registrySource, registryDescription, warnings) {
|
|
335
|
+
function extractApiRoutes(moduleId, registrySource, registryDescription, routeSourcePaths, warnings) {
|
|
336
336
|
if (registrySource == null) {
|
|
337
337
|
warnings.push(`[module-facts] module registry unavailable (${registryDescription}); API route auth omitted for ${moduleId}`);
|
|
338
338
|
return [];
|
|
@@ -356,7 +356,7 @@ function extractApiRoutes(moduleId, registrySource, registryDescription, warning
|
|
|
356
356
|
const route = parseApiRouteEntry(element);
|
|
357
357
|
if (route && !seenPaths.has(route.path)) {
|
|
358
358
|
seenPaths.add(route.path);
|
|
359
|
-
routes.push(route);
|
|
359
|
+
routes.push({ ...route, sourcePath: routeSourcePaths.get(route.path) ?? null });
|
|
360
360
|
}
|
|
361
361
|
}
|
|
362
362
|
}
|
|
@@ -514,6 +514,153 @@ function listSourceFilesRecursive(directory) {
|
|
|
514
514
|
}
|
|
515
515
|
return files;
|
|
516
516
|
}
|
|
517
|
+
function toPortableSourceRoot(moduleId, sourcePackage) {
|
|
518
|
+
return path.posix.join("node_modules", sourcePackage ?? "@open-mercato/core", "src", "modules", moduleId);
|
|
519
|
+
}
|
|
520
|
+
function toPortableSourcePath(moduleRoot, sourceRoot, filePath) {
|
|
521
|
+
const relativePath = path.relative(moduleRoot, filePath).split(path.sep).join("/");
|
|
522
|
+
return path.posix.join(sourceRoot, relativePath);
|
|
523
|
+
}
|
|
524
|
+
function sourceHasDefaultExport(sourceFile) {
|
|
525
|
+
return sourceFile.statements.some((statement) => {
|
|
526
|
+
if (ts.isExportAssignment(statement) && !statement.isExportEquals) return true;
|
|
527
|
+
if (!ts.isFunctionDeclaration(statement) && !ts.isClassDeclaration(statement)) return false;
|
|
528
|
+
return statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword) ?? false;
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
function readRouteMetadataPath(sourceFile) {
|
|
532
|
+
const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
|
|
533
|
+
return metadata ? readStringPropertyInitializer(metadata, "path") ?? null : null;
|
|
534
|
+
}
|
|
535
|
+
function extractApiRouteSourcePaths(moduleId, moduleRoot, sourceRoot) {
|
|
536
|
+
const apiRoot = path.join(moduleRoot, "api");
|
|
537
|
+
const sources = /* @__PURE__ */ new Map();
|
|
538
|
+
if (!fs.existsSync(apiRoot)) return sources;
|
|
539
|
+
const methodDirectories = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
|
|
540
|
+
for (const filePath of listSourceFilesRecursive(apiRoot)) {
|
|
541
|
+
const relativePath = path.relative(apiRoot, filePath).split(path.sep).join("/");
|
|
542
|
+
const segments = relativePath.split("/");
|
|
543
|
+
const fileName = segments.pop();
|
|
544
|
+
const fileStem = fileName.replace(/\.tsx?$/, "");
|
|
545
|
+
let routeSegments;
|
|
546
|
+
if (fileStem === "route") {
|
|
547
|
+
routeSegments = segments;
|
|
548
|
+
} else if (segments[0] && methodDirectories.has(segments[0].toLowerCase())) {
|
|
549
|
+
routeSegments = [...segments.slice(1), fileStem];
|
|
550
|
+
} else {
|
|
551
|
+
routeSegments = [...segments, fileStem];
|
|
552
|
+
}
|
|
553
|
+
const defaultPath = `/${[moduleId, ...routeSegments].filter(Boolean).join("/")}`;
|
|
554
|
+
const sourceFile = readSourceFile(filePath);
|
|
555
|
+
const routePath = sourceFile ? readRouteMetadataPath(sourceFile) ?? defaultPath : defaultPath;
|
|
556
|
+
sources.set(routePath, toPortableSourcePath(moduleRoot, sourceRoot, filePath));
|
|
557
|
+
}
|
|
558
|
+
return sources;
|
|
559
|
+
}
|
|
560
|
+
function extractModulePages(moduleId, moduleRoot, sourceRoot, surface) {
|
|
561
|
+
const pageRoot = path.join(moduleRoot, surface);
|
|
562
|
+
if (!fs.existsSync(pageRoot)) return [];
|
|
563
|
+
const pages = [];
|
|
564
|
+
const seen = /* @__PURE__ */ new Set();
|
|
565
|
+
for (const filePath of listSourceFilesRecursive(pageRoot)) {
|
|
566
|
+
if (!filePath.endsWith(".tsx")) continue;
|
|
567
|
+
const relativePath = path.relative(pageRoot, filePath).split(path.sep).join("/");
|
|
568
|
+
const segments = relativePath.split("/");
|
|
569
|
+
const fileName = segments.pop();
|
|
570
|
+
const fileStem = fileName.replace(/\.tsx$/, "");
|
|
571
|
+
const isModernPage = fileStem === "page";
|
|
572
|
+
if (!isModernPage && (fileStem.endsWith(".meta") || /^[A-Z]/.test(fileStem))) continue;
|
|
573
|
+
const sourceFile = readSourceFile(filePath);
|
|
574
|
+
if (!sourceFile || !sourceHasDefaultExport(sourceFile)) continue;
|
|
575
|
+
const routeSegments = isModernPage ? segments : [...segments, fileStem];
|
|
576
|
+
const routePath = surface === "frontend" ? `/${routeSegments.filter(Boolean).join("/")}` : isModernPage ? `/backend/${routeSegments.join("/") || moduleId}` : `/backend/${routeSegments[0] === moduleId ? routeSegments.filter(Boolean).join("/") : [moduleId, ...routeSegments].filter(Boolean).join("/")}`;
|
|
577
|
+
if (seen.has(routePath)) continue;
|
|
578
|
+
seen.add(routePath);
|
|
579
|
+
pages.push({
|
|
580
|
+
path: routePath,
|
|
581
|
+
sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, filePath)
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
return pages.sort((left, right) => left.path.localeCompare(right.path));
|
|
585
|
+
}
|
|
586
|
+
function findAncestorVariableDeclaration(node) {
|
|
587
|
+
let current = node.parent;
|
|
588
|
+
while (current) {
|
|
589
|
+
if (ts.isVariableDeclaration(current)) return current;
|
|
590
|
+
current = current.parent;
|
|
591
|
+
}
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
function findAncestorCallExpression(node) {
|
|
595
|
+
let current = node.parent;
|
|
596
|
+
while (current) {
|
|
597
|
+
if (ts.isCallExpression(current)) return current;
|
|
598
|
+
if (ts.isVariableDeclaration(current) || ts.isStatement(current)) return null;
|
|
599
|
+
current = current.parent;
|
|
600
|
+
}
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
function extractNamedAiFacts(filePaths, moduleRoot, sourceRoot, propertyName) {
|
|
604
|
+
const facts = [];
|
|
605
|
+
const seen = /* @__PURE__ */ new Set();
|
|
606
|
+
for (const filePath of filePaths) {
|
|
607
|
+
const sourceFile = readSourceFile(filePath);
|
|
608
|
+
if (!sourceFile) continue;
|
|
609
|
+
const initializers = buildVariableInitializerMap(sourceFile);
|
|
610
|
+
const readDefinitionValue = (objectLiteral) => {
|
|
611
|
+
const initializer = getObjectPropertyInitializer(objectLiteral, propertyName);
|
|
612
|
+
if (!initializer) return void 0;
|
|
613
|
+
const resolved = unwrapExpression(initializer);
|
|
614
|
+
if (ts.isStringLiteralLike(resolved)) return resolved.text;
|
|
615
|
+
if (ts.isIdentifier(resolved)) {
|
|
616
|
+
const declarationInitializer = initializers.get(resolved.text);
|
|
617
|
+
if (declarationInitializer) {
|
|
618
|
+
const declarationValue = unwrapExpression(declarationInitializer);
|
|
619
|
+
if (ts.isStringLiteralLike(declarationValue)) return declarationValue.text;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return void 0;
|
|
623
|
+
};
|
|
624
|
+
const visit = (node) => {
|
|
625
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
626
|
+
const value = readDefinitionValue(node);
|
|
627
|
+
if (value && !seen.has(value)) {
|
|
628
|
+
const declaration = findAncestorVariableDeclaration(node);
|
|
629
|
+
const declarationName = declaration && ts.isIdentifier(declaration.name) ? declaration.name.text : "";
|
|
630
|
+
const declarationType = declaration?.type?.getText(sourceFile) ?? "";
|
|
631
|
+
const call = findAncestorCallExpression(node);
|
|
632
|
+
const callee = call?.expression.getText(sourceFile) ?? "";
|
|
633
|
+
const isDefinition = propertyName === "name" ? /AiTool/.test(declarationType) || /tool$/i.test(declarationName) || /AiTool/.test(callee) : /AiAgentDefinition/.test(declarationType) || /agent$/i.test(declarationName) || /defineAiAgent/.test(callee);
|
|
634
|
+
if (isDefinition) {
|
|
635
|
+
seen.add(value);
|
|
636
|
+
facts.push({
|
|
637
|
+
value,
|
|
638
|
+
sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, filePath)
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
node.forEachChild(visit);
|
|
644
|
+
};
|
|
645
|
+
sourceFile.forEachChild(visit);
|
|
646
|
+
}
|
|
647
|
+
return facts.sort((left, right) => left.value.localeCompare(right.value));
|
|
648
|
+
}
|
|
649
|
+
function extractAiTools(moduleRoot, sourceRoot) {
|
|
650
|
+
const files = /* @__PURE__ */ new Set();
|
|
651
|
+
const rootFile = resolveConventionFile(moduleRoot, "ai-tools");
|
|
652
|
+
if (rootFile) files.add(rootFile);
|
|
653
|
+
const toolsDirectory = path.join(moduleRoot, "ai-tools");
|
|
654
|
+
if (fs.existsSync(toolsDirectory)) {
|
|
655
|
+
for (const filePath of listSourceFilesRecursive(toolsDirectory)) files.add(filePath);
|
|
656
|
+
}
|
|
657
|
+
return extractNamedAiFacts([...files], moduleRoot, sourceRoot, "name").map((fact) => ({ name: fact.value, sourcePath: fact.sourcePath }));
|
|
658
|
+
}
|
|
659
|
+
function extractAiAgents(moduleRoot, sourceRoot) {
|
|
660
|
+
const agentsFile = resolveConventionFile(moduleRoot, "ai-agents");
|
|
661
|
+
if (!agentsFile) return [];
|
|
662
|
+
return extractNamedAiFacts([agentsFile], moduleRoot, sourceRoot, "id").map((fact) => ({ id: fact.value, sourcePath: fact.sourcePath }));
|
|
663
|
+
}
|
|
517
664
|
function extractTableIds(moduleRoot) {
|
|
518
665
|
if (!fs.existsSync(moduleRoot)) return [];
|
|
519
666
|
const tableIds = [];
|
|
@@ -571,6 +718,7 @@ function extractModuleFacts(options) {
|
|
|
571
718
|
if (!moduleRoot) {
|
|
572
719
|
throw new Error(`[internal] extractModuleFacts requires moduleRoot or coreSrcRoot for module "${moduleId}"`);
|
|
573
720
|
}
|
|
721
|
+
const sourceRoot = toPortableSourceRoot(moduleId, sourcePackage);
|
|
574
722
|
const entitiesFilePath = resolveConventionFile(path.join(moduleRoot, "data"), "entities") ?? resolveConventionFile(path.join(moduleRoot, "db"), "entities") ?? resolveConventionFile(path.join(moduleRoot, "data"), "schema");
|
|
575
723
|
const ceFilePath = resolveConventionFile(moduleRoot, "ce");
|
|
576
724
|
const eventsFilePath = resolveConventionFile(moduleRoot, "events");
|
|
@@ -587,11 +735,20 @@ function extractModuleFacts(options) {
|
|
|
587
735
|
const events = extractEvents(eventsFilePath);
|
|
588
736
|
const aclFeatures = extractAclFeatures(aclFilePath);
|
|
589
737
|
const { source: registrySource, description: registryDescription } = resolveRegistrySource(options);
|
|
590
|
-
const
|
|
738
|
+
const apiRouteSourcePaths = extractApiRouteSourcePaths(moduleId, moduleRoot, sourceRoot);
|
|
739
|
+
const apiRoutes = extractApiRoutes(moduleId, registrySource, registryDescription, apiRouteSourcePaths, warnings);
|
|
591
740
|
const diTokens = extractDiTokens(diFilePath);
|
|
592
741
|
const searchEntities = extractSearchEntities(searchFilePath, warnings);
|
|
593
742
|
const notifications = extractNotifications(notificationsFilePath, warnings);
|
|
594
743
|
const cli = extractCli(cliFilePath, warnings);
|
|
744
|
+
const cliCommands = cliFilePath ? cli.map((command) => ({
|
|
745
|
+
command,
|
|
746
|
+
sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, cliFilePath)
|
|
747
|
+
})) : [];
|
|
748
|
+
const backendPages = extractModulePages(moduleId, moduleRoot, sourceRoot, "backend");
|
|
749
|
+
const frontendPages = extractModulePages(moduleId, moduleRoot, sourceRoot, "frontend");
|
|
750
|
+
const aiTools = extractAiTools(moduleRoot, sourceRoot);
|
|
751
|
+
const aiAgents = extractAiAgents(moduleRoot, sourceRoot);
|
|
595
752
|
const hostTokens = {
|
|
596
753
|
entityIds: extractHostEntityIds(entities),
|
|
597
754
|
tableIds: extractTableIds(moduleRoot)
|
|
@@ -603,6 +760,7 @@ function extractModuleFacts(options) {
|
|
|
603
760
|
coreVersion,
|
|
604
761
|
sourcePackage,
|
|
605
762
|
sourceVersion,
|
|
763
|
+
sourceRoot,
|
|
606
764
|
entities,
|
|
607
765
|
events,
|
|
608
766
|
aclFeatures,
|
|
@@ -612,6 +770,11 @@ function extractModuleFacts(options) {
|
|
|
612
770
|
hostTokens,
|
|
613
771
|
notifications,
|
|
614
772
|
cli,
|
|
773
|
+
backendPages,
|
|
774
|
+
frontendPages,
|
|
775
|
+
cliCommands,
|
|
776
|
+
aiTools,
|
|
777
|
+
aiAgents,
|
|
615
778
|
warnings
|
|
616
779
|
};
|
|
617
780
|
}
|
|
@@ -652,6 +815,9 @@ ${EMPTY_SECTION_MARKER}`;
|
|
|
652
815
|
|
|
653
816
|
${values.join(" \xB7 ")}`;
|
|
654
817
|
}
|
|
818
|
+
function renderSourceLink(sourcePath) {
|
|
819
|
+
return `[${sourcePath}](../../../${sourcePath})`;
|
|
820
|
+
}
|
|
655
821
|
function describeAuthRule(rule) {
|
|
656
822
|
if (!rule) return "public";
|
|
657
823
|
if (rule.requireFeatures && rule.requireFeatures.length > 0) return rule.requireFeatures.join(", ");
|
|
@@ -674,13 +840,20 @@ function renderApiRoutesSection(routes) {
|
|
|
674
840
|
if (routes.length === 0) return `## API routes
|
|
675
841
|
|
|
676
842
|
${EMPTY_SECTION_MARKER}`;
|
|
677
|
-
const header = "| Path | Methods | Auth (per-method requireFeatures) |";
|
|
678
|
-
const divider = "
|
|
843
|
+
const header = "| Path | Methods | Auth (per-method requireFeatures) | Source |";
|
|
844
|
+
const divider = "|---|---|---|---|";
|
|
679
845
|
const rows = routes.map(
|
|
680
|
-
(route) => `| ${route.path} | ${route.methods.join(" ")} | ${renderApiRouteAuthCell(route)} |`
|
|
846
|
+
(route) => `| ${route.path} | ${route.methods.join(" ")} | ${renderApiRouteAuthCell(route)} | ${route.sourcePath ? renderSourceLink(route.sourcePath) : "\u2014"} |`
|
|
681
847
|
);
|
|
682
848
|
return ["## API routes", "", header, divider, ...rows].join("\n");
|
|
683
849
|
}
|
|
850
|
+
function renderLinkedFactsSection(heading, facts) {
|
|
851
|
+
if (facts.length === 0) return `${heading}
|
|
852
|
+
|
|
853
|
+
${EMPTY_SECTION_MARKER}`;
|
|
854
|
+
const rows = facts.map((fact) => `| ${fact.label} | ${renderSourceLink(fact.sourcePath)} |`);
|
|
855
|
+
return [heading, "", "| ID / path | Source |", "|---|---|", ...rows].join("\n");
|
|
856
|
+
}
|
|
684
857
|
function renderHostTokensSection(hostTokens) {
|
|
685
858
|
const entityIdsLine = hostTokens.entityIds.length > 0 ? hostTokens.entityIds.join(" \xB7 ") : EMPTY_SECTION_MARKER;
|
|
686
859
|
const tableIdsLine = hostTokens.tableIds.length > 0 ? hostTokens.tableIds.join(" \xB7 ") : EMPTY_SECTION_MARKER;
|
|
@@ -690,6 +863,7 @@ function renderModuleFactsMarkdown(facts) {
|
|
|
690
863
|
const sections = [
|
|
691
864
|
`# ${facts.module} \u2014 module facts (generated, do not edit)`,
|
|
692
865
|
renderVersionStamp(facts.coreVersion, facts.sourcePackage, facts.sourceVersion),
|
|
866
|
+
`Source root: ${renderSourceLink(facts.sourceRoot)}`,
|
|
693
867
|
"",
|
|
694
868
|
renderEntitiesSection(facts.entities),
|
|
695
869
|
"",
|
|
@@ -699,6 +873,10 @@ function renderModuleFactsMarkdown(facts) {
|
|
|
699
873
|
"",
|
|
700
874
|
renderApiRoutesSection(facts.apiRoutes),
|
|
701
875
|
"",
|
|
876
|
+
renderLinkedFactsSection("## Backend pages", facts.backendPages.map((page) => ({ label: page.path, sourcePath: page.sourcePath }))),
|
|
877
|
+
"",
|
|
878
|
+
renderLinkedFactsSection("## Frontend pages", facts.frontendPages.map((page) => ({ label: page.path, sourcePath: page.sourcePath }))),
|
|
879
|
+
"",
|
|
702
880
|
renderInlineListSection("## DI service tokens", facts.diTokens),
|
|
703
881
|
"",
|
|
704
882
|
renderInlineListSection("## Search entities", facts.searchEntities),
|
|
@@ -707,7 +885,11 @@ function renderModuleFactsMarkdown(facts) {
|
|
|
707
885
|
"",
|
|
708
886
|
renderInlineListSection("## Notifications", facts.notifications),
|
|
709
887
|
"",
|
|
710
|
-
|
|
888
|
+
renderLinkedFactsSection("## CLI commands", facts.cliCommands.map((command) => ({ label: command.command, sourcePath: command.sourcePath }))),
|
|
889
|
+
"",
|
|
890
|
+
renderLinkedFactsSection("## AI tools / MCP capabilities", facts.aiTools.map((tool) => ({ label: tool.name, sourcePath: tool.sourcePath }))),
|
|
891
|
+
"",
|
|
892
|
+
renderLinkedFactsSection("## AI agents", facts.aiAgents.map((agent) => ({ label: agent.id, sourcePath: agent.sourcePath }))),
|
|
711
893
|
""
|
|
712
894
|
];
|
|
713
895
|
return sections.join("\n");
|
|
@@ -719,6 +901,7 @@ function toModuleFactsJsonEntry(facts) {
|
|
|
719
901
|
coreVersion: facts.coreVersion,
|
|
720
902
|
sourcePackage: facts.sourcePackage,
|
|
721
903
|
sourceVersion: facts.sourceVersion,
|
|
904
|
+
sourceRoot: facts.sourceRoot,
|
|
722
905
|
entities: facts.entities,
|
|
723
906
|
events: facts.events.map((event) => ({ id: event.id, category: event.category, entity: event.entity })),
|
|
724
907
|
aclFeatures: facts.aclFeatures,
|
|
@@ -727,7 +910,12 @@ function toModuleFactsJsonEntry(facts) {
|
|
|
727
910
|
searchEntities: facts.searchEntities,
|
|
728
911
|
hostTokens: facts.hostTokens,
|
|
729
912
|
notifications: facts.notifications,
|
|
730
|
-
cli: facts.cli
|
|
913
|
+
cli: facts.cli,
|
|
914
|
+
backendPages: facts.backendPages,
|
|
915
|
+
frontendPages: facts.frontendPages,
|
|
916
|
+
cliCommands: facts.cliCommands,
|
|
917
|
+
aiTools: facts.aiTools,
|
|
918
|
+
aiAgents: facts.aiAgents
|
|
731
919
|
};
|
|
732
920
|
}
|
|
733
921
|
function buildModuleFactsJsonObject(factsByModule) {
|