@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
|
@@ -21,6 +21,27 @@ export interface ModuleApiRouteFact {
|
|
|
21
21
|
path: string
|
|
22
22
|
methods: string[]
|
|
23
23
|
auth: Record<string, ApiRouteAuthRule>
|
|
24
|
+
sourcePath: string | null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ModulePageFact {
|
|
28
|
+
path: string
|
|
29
|
+
sourcePath: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ModuleCliCommandFact {
|
|
33
|
+
command: string
|
|
34
|
+
sourcePath: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ModuleAiToolFact {
|
|
38
|
+
name: string
|
|
39
|
+
sourcePath: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ModuleAiAgentFact {
|
|
43
|
+
id: string
|
|
44
|
+
sourcePath: string
|
|
24
45
|
}
|
|
25
46
|
|
|
26
47
|
export interface ModuleEventFact {
|
|
@@ -42,6 +63,7 @@ export interface ModuleFacts {
|
|
|
42
63
|
coreVersion: string | null
|
|
43
64
|
sourcePackage: string | null
|
|
44
65
|
sourceVersion: string | null
|
|
66
|
+
sourceRoot: string
|
|
45
67
|
entities: ModuleEntityFact[]
|
|
46
68
|
events: ModuleEventFact[]
|
|
47
69
|
aclFeatures: string[]
|
|
@@ -51,6 +73,11 @@ export interface ModuleFacts {
|
|
|
51
73
|
hostTokens: ModuleHostTokens
|
|
52
74
|
notifications: string[]
|
|
53
75
|
cli: string[]
|
|
76
|
+
backendPages: ModulePageFact[]
|
|
77
|
+
frontendPages: ModulePageFact[]
|
|
78
|
+
cliCommands: ModuleCliCommandFact[]
|
|
79
|
+
aiTools: ModuleAiToolFact[]
|
|
80
|
+
aiAgents: ModuleAiAgentFact[]
|
|
54
81
|
warnings: string[]
|
|
55
82
|
}
|
|
56
83
|
|
|
@@ -495,13 +522,14 @@ function parseApiRouteEntry(entryLiteral: ts.ObjectLiteralExpression): ModuleApi
|
|
|
495
522
|
}
|
|
496
523
|
}
|
|
497
524
|
|
|
498
|
-
return { path: routePath, methods, auth }
|
|
525
|
+
return { path: routePath, methods, auth, sourcePath: null }
|
|
499
526
|
}
|
|
500
527
|
|
|
501
528
|
function extractApiRoutes(
|
|
502
529
|
moduleId: string,
|
|
503
530
|
registrySource: string | null,
|
|
504
531
|
registryDescription: string,
|
|
532
|
+
routeSourcePaths: ReadonlyMap<string, string>,
|
|
505
533
|
warnings: string[],
|
|
506
534
|
): ModuleApiRouteFact[] {
|
|
507
535
|
if (registrySource == null) {
|
|
@@ -529,7 +557,7 @@ function extractApiRoutes(
|
|
|
529
557
|
const route = parseApiRouteEntry(element)
|
|
530
558
|
if (route && !seenPaths.has(route.path)) {
|
|
531
559
|
seenPaths.add(route.path)
|
|
532
|
-
routes.push(route)
|
|
560
|
+
routes.push({ ...route, sourcePath: routeSourcePaths.get(route.path) ?? null })
|
|
533
561
|
}
|
|
534
562
|
}
|
|
535
563
|
}
|
|
@@ -717,6 +745,190 @@ function listSourceFilesRecursive(directory: string): string[] {
|
|
|
717
745
|
return files
|
|
718
746
|
}
|
|
719
747
|
|
|
748
|
+
function toPortableSourceRoot(moduleId: string, sourcePackage: string | null): string {
|
|
749
|
+
return path.posix.join('node_modules', sourcePackage ?? '@open-mercato/core', 'src', 'modules', moduleId)
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function toPortableSourcePath(moduleRoot: string, sourceRoot: string, filePath: string): string {
|
|
753
|
+
const relativePath = path.relative(moduleRoot, filePath).split(path.sep).join('/')
|
|
754
|
+
return path.posix.join(sourceRoot, relativePath)
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function sourceHasDefaultExport(sourceFile: ts.SourceFile): boolean {
|
|
758
|
+
return sourceFile.statements.some((statement) => {
|
|
759
|
+
if (ts.isExportAssignment(statement) && !statement.isExportEquals) return true
|
|
760
|
+
if (!ts.isFunctionDeclaration(statement) && !ts.isClassDeclaration(statement)) return false
|
|
761
|
+
return statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword) ?? false
|
|
762
|
+
})
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function readRouteMetadataPath(sourceFile: ts.SourceFile): string | null {
|
|
766
|
+
const metadata = findObjectLiteralDeclaration(sourceFile, 'metadata')
|
|
767
|
+
return metadata ? readStringPropertyInitializer(metadata, 'path') ?? null : null
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
function extractApiRouteSourcePaths(
|
|
771
|
+
moduleId: string,
|
|
772
|
+
moduleRoot: string,
|
|
773
|
+
sourceRoot: string,
|
|
774
|
+
): Map<string, string> {
|
|
775
|
+
const apiRoot = path.join(moduleRoot, 'api')
|
|
776
|
+
const sources = new Map<string, string>()
|
|
777
|
+
if (!fs.existsSync(apiRoot)) return sources
|
|
778
|
+
const methodDirectories = new Set(['get', 'post', 'put', 'patch', 'delete'])
|
|
779
|
+
|
|
780
|
+
for (const filePath of listSourceFilesRecursive(apiRoot)) {
|
|
781
|
+
const relativePath = path.relative(apiRoot, filePath).split(path.sep).join('/')
|
|
782
|
+
const segments = relativePath.split('/')
|
|
783
|
+
const fileName = segments.pop() as string
|
|
784
|
+
const fileStem = fileName.replace(/\.tsx?$/, '')
|
|
785
|
+
let routeSegments: string[]
|
|
786
|
+
if (fileStem === 'route') {
|
|
787
|
+
routeSegments = segments
|
|
788
|
+
} else if (segments[0] && methodDirectories.has(segments[0].toLowerCase())) {
|
|
789
|
+
routeSegments = [...segments.slice(1), fileStem]
|
|
790
|
+
} else {
|
|
791
|
+
routeSegments = [...segments, fileStem]
|
|
792
|
+
}
|
|
793
|
+
const defaultPath = `/${[moduleId, ...routeSegments].filter(Boolean).join('/')}`
|
|
794
|
+
const sourceFile = readSourceFile(filePath)
|
|
795
|
+
const routePath = sourceFile ? readRouteMetadataPath(sourceFile) ?? defaultPath : defaultPath
|
|
796
|
+
sources.set(routePath, toPortableSourcePath(moduleRoot, sourceRoot, filePath))
|
|
797
|
+
}
|
|
798
|
+
return sources
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function extractModulePages(
|
|
802
|
+
moduleId: string,
|
|
803
|
+
moduleRoot: string,
|
|
804
|
+
sourceRoot: string,
|
|
805
|
+
surface: 'backend' | 'frontend',
|
|
806
|
+
): ModulePageFact[] {
|
|
807
|
+
const pageRoot = path.join(moduleRoot, surface)
|
|
808
|
+
if (!fs.existsSync(pageRoot)) return []
|
|
809
|
+
const pages: ModulePageFact[] = []
|
|
810
|
+
const seen = new Set<string>()
|
|
811
|
+
|
|
812
|
+
for (const filePath of listSourceFilesRecursive(pageRoot)) {
|
|
813
|
+
if (!filePath.endsWith('.tsx')) continue
|
|
814
|
+
const relativePath = path.relative(pageRoot, filePath).split(path.sep).join('/')
|
|
815
|
+
const segments = relativePath.split('/')
|
|
816
|
+
const fileName = segments.pop() as string
|
|
817
|
+
const fileStem = fileName.replace(/\.tsx$/, '')
|
|
818
|
+
const isModernPage = fileStem === 'page'
|
|
819
|
+
if (!isModernPage && (fileStem.endsWith('.meta') || /^[A-Z]/.test(fileStem))) continue
|
|
820
|
+
const sourceFile = readSourceFile(filePath)
|
|
821
|
+
if (!sourceFile || !sourceHasDefaultExport(sourceFile)) continue
|
|
822
|
+
const routeSegments = isModernPage ? segments : [...segments, fileStem]
|
|
823
|
+
const routePath = surface === 'frontend'
|
|
824
|
+
? `/${routeSegments.filter(Boolean).join('/')}`
|
|
825
|
+
: isModernPage
|
|
826
|
+
? `/backend/${routeSegments.join('/') || moduleId}`
|
|
827
|
+
: `/backend/${routeSegments[0] === moduleId
|
|
828
|
+
? routeSegments.filter(Boolean).join('/')
|
|
829
|
+
: [moduleId, ...routeSegments].filter(Boolean).join('/')}`
|
|
830
|
+
if (seen.has(routePath)) continue
|
|
831
|
+
seen.add(routePath)
|
|
832
|
+
pages.push({
|
|
833
|
+
path: routePath,
|
|
834
|
+
sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, filePath),
|
|
835
|
+
})
|
|
836
|
+
}
|
|
837
|
+
return pages.sort((left, right) => left.path.localeCompare(right.path))
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function findAncestorVariableDeclaration(node: ts.Node): ts.VariableDeclaration | null {
|
|
841
|
+
let current: ts.Node | undefined = node.parent
|
|
842
|
+
while (current) {
|
|
843
|
+
if (ts.isVariableDeclaration(current)) return current
|
|
844
|
+
current = current.parent
|
|
845
|
+
}
|
|
846
|
+
return null
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function findAncestorCallExpression(node: ts.Node): ts.CallExpression | null {
|
|
850
|
+
let current: ts.Node | undefined = node.parent
|
|
851
|
+
while (current) {
|
|
852
|
+
if (ts.isCallExpression(current)) return current
|
|
853
|
+
if (ts.isVariableDeclaration(current) || ts.isStatement(current)) return null
|
|
854
|
+
current = current.parent
|
|
855
|
+
}
|
|
856
|
+
return null
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
function extractNamedAiFacts(
|
|
860
|
+
filePaths: readonly string[],
|
|
861
|
+
moduleRoot: string,
|
|
862
|
+
sourceRoot: string,
|
|
863
|
+
propertyName: 'name' | 'id',
|
|
864
|
+
): Array<{ value: string; sourcePath: string }> {
|
|
865
|
+
const facts: Array<{ value: string; sourcePath: string }> = []
|
|
866
|
+
const seen = new Set<string>()
|
|
867
|
+
for (const filePath of filePaths) {
|
|
868
|
+
const sourceFile = readSourceFile(filePath)
|
|
869
|
+
if (!sourceFile) continue
|
|
870
|
+
const initializers = buildVariableInitializerMap(sourceFile)
|
|
871
|
+
const readDefinitionValue = (objectLiteral: ts.ObjectLiteralExpression): string | undefined => {
|
|
872
|
+
const initializer = getObjectPropertyInitializer(objectLiteral, propertyName)
|
|
873
|
+
if (!initializer) return undefined
|
|
874
|
+
const resolved = unwrapExpression(initializer)
|
|
875
|
+
if (ts.isStringLiteralLike(resolved)) return resolved.text
|
|
876
|
+
if (ts.isIdentifier(resolved)) {
|
|
877
|
+
const declarationInitializer = initializers.get(resolved.text)
|
|
878
|
+
if (declarationInitializer) {
|
|
879
|
+
const declarationValue = unwrapExpression(declarationInitializer)
|
|
880
|
+
if (ts.isStringLiteralLike(declarationValue)) return declarationValue.text
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return undefined
|
|
884
|
+
}
|
|
885
|
+
const visit = (node: ts.Node): void => {
|
|
886
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
887
|
+
const value = readDefinitionValue(node)
|
|
888
|
+
if (value && !seen.has(value)) {
|
|
889
|
+
const declaration = findAncestorVariableDeclaration(node)
|
|
890
|
+
const declarationName = declaration && ts.isIdentifier(declaration.name) ? declaration.name.text : ''
|
|
891
|
+
const declarationType = declaration?.type?.getText(sourceFile) ?? ''
|
|
892
|
+
const call = findAncestorCallExpression(node)
|
|
893
|
+
const callee = call?.expression.getText(sourceFile) ?? ''
|
|
894
|
+
const isDefinition = propertyName === 'name'
|
|
895
|
+
? /AiTool/.test(declarationType) || /tool$/i.test(declarationName) || /AiTool/.test(callee)
|
|
896
|
+
: /AiAgentDefinition/.test(declarationType) || /agent$/i.test(declarationName) || /defineAiAgent/.test(callee)
|
|
897
|
+
if (isDefinition) {
|
|
898
|
+
seen.add(value)
|
|
899
|
+
facts.push({
|
|
900
|
+
value,
|
|
901
|
+
sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, filePath),
|
|
902
|
+
})
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
node.forEachChild(visit)
|
|
907
|
+
}
|
|
908
|
+
sourceFile.forEachChild(visit)
|
|
909
|
+
}
|
|
910
|
+
return facts.sort((left, right) => left.value.localeCompare(right.value))
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function extractAiTools(moduleRoot: string, sourceRoot: string): ModuleAiToolFact[] {
|
|
914
|
+
const files = new Set<string>()
|
|
915
|
+
const rootFile = resolveConventionFile(moduleRoot, 'ai-tools')
|
|
916
|
+
if (rootFile) files.add(rootFile)
|
|
917
|
+
const toolsDirectory = path.join(moduleRoot, 'ai-tools')
|
|
918
|
+
if (fs.existsSync(toolsDirectory)) {
|
|
919
|
+
for (const filePath of listSourceFilesRecursive(toolsDirectory)) files.add(filePath)
|
|
920
|
+
}
|
|
921
|
+
return extractNamedAiFacts([...files], moduleRoot, sourceRoot, 'name')
|
|
922
|
+
.map((fact) => ({ name: fact.value, sourcePath: fact.sourcePath }))
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
function extractAiAgents(moduleRoot: string, sourceRoot: string): ModuleAiAgentFact[] {
|
|
926
|
+
const agentsFile = resolveConventionFile(moduleRoot, 'ai-agents')
|
|
927
|
+
if (!agentsFile) return []
|
|
928
|
+
return extractNamedAiFacts([agentsFile], moduleRoot, sourceRoot, 'id')
|
|
929
|
+
.map((fact) => ({ id: fact.value, sourcePath: fact.sourcePath }))
|
|
930
|
+
}
|
|
931
|
+
|
|
720
932
|
function extractTableIds(moduleRoot: string): string[] {
|
|
721
933
|
if (!fs.existsSync(moduleRoot)) return []
|
|
722
934
|
|
|
@@ -780,6 +992,7 @@ export function extractModuleFacts(options: ExtractModuleFactsOptions): ModuleFa
|
|
|
780
992
|
if (!moduleRoot) {
|
|
781
993
|
throw new Error(`[internal] extractModuleFacts requires moduleRoot or coreSrcRoot for module "${moduleId}"`)
|
|
782
994
|
}
|
|
995
|
+
const sourceRoot = toPortableSourceRoot(moduleId, sourcePackage)
|
|
783
996
|
|
|
784
997
|
const entitiesFilePath =
|
|
785
998
|
resolveConventionFile(path.join(moduleRoot, 'data'), 'entities') ??
|
|
@@ -802,11 +1015,22 @@ export function extractModuleFacts(options: ExtractModuleFactsOptions): ModuleFa
|
|
|
802
1015
|
const aclFeatures = extractAclFeatures(aclFilePath)
|
|
803
1016
|
|
|
804
1017
|
const { source: registrySource, description: registryDescription } = resolveRegistrySource(options)
|
|
805
|
-
const
|
|
1018
|
+
const apiRouteSourcePaths = extractApiRouteSourcePaths(moduleId, moduleRoot, sourceRoot)
|
|
1019
|
+
const apiRoutes = extractApiRoutes(moduleId, registrySource, registryDescription, apiRouteSourcePaths, warnings)
|
|
806
1020
|
const diTokens = extractDiTokens(diFilePath)
|
|
807
1021
|
const searchEntities = extractSearchEntities(searchFilePath, warnings)
|
|
808
1022
|
const notifications = extractNotifications(notificationsFilePath, warnings)
|
|
809
1023
|
const cli = extractCli(cliFilePath, warnings)
|
|
1024
|
+
const cliCommands = cliFilePath
|
|
1025
|
+
? cli.map((command) => ({
|
|
1026
|
+
command,
|
|
1027
|
+
sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, cliFilePath),
|
|
1028
|
+
}))
|
|
1029
|
+
: []
|
|
1030
|
+
const backendPages = extractModulePages(moduleId, moduleRoot, sourceRoot, 'backend')
|
|
1031
|
+
const frontendPages = extractModulePages(moduleId, moduleRoot, sourceRoot, 'frontend')
|
|
1032
|
+
const aiTools = extractAiTools(moduleRoot, sourceRoot)
|
|
1033
|
+
const aiAgents = extractAiAgents(moduleRoot, sourceRoot)
|
|
810
1034
|
const hostTokens: ModuleHostTokens = {
|
|
811
1035
|
entityIds: extractHostEntityIds(entities),
|
|
812
1036
|
tableIds: extractTableIds(moduleRoot),
|
|
@@ -819,6 +1043,7 @@ export function extractModuleFacts(options: ExtractModuleFactsOptions): ModuleFa
|
|
|
819
1043
|
coreVersion,
|
|
820
1044
|
sourcePackage,
|
|
821
1045
|
sourceVersion,
|
|
1046
|
+
sourceRoot,
|
|
822
1047
|
entities,
|
|
823
1048
|
events,
|
|
824
1049
|
aclFeatures,
|
|
@@ -828,6 +1053,11 @@ export function extractModuleFacts(options: ExtractModuleFactsOptions): ModuleFa
|
|
|
828
1053
|
hostTokens,
|
|
829
1054
|
notifications,
|
|
830
1055
|
cli,
|
|
1056
|
+
backendPages,
|
|
1057
|
+
frontendPages,
|
|
1058
|
+
cliCommands,
|
|
1059
|
+
aiTools,
|
|
1060
|
+
aiAgents,
|
|
831
1061
|
warnings,
|
|
832
1062
|
}
|
|
833
1063
|
}
|
|
@@ -844,6 +1074,7 @@ export interface ModuleFactsJsonEntry {
|
|
|
844
1074
|
coreVersion: string | null
|
|
845
1075
|
sourcePackage: string | null
|
|
846
1076
|
sourceVersion: string | null
|
|
1077
|
+
sourceRoot: string
|
|
847
1078
|
entities: ModuleEntityFact[]
|
|
848
1079
|
events: ModuleFactsJsonEvent[]
|
|
849
1080
|
aclFeatures: string[]
|
|
@@ -853,6 +1084,11 @@ export interface ModuleFactsJsonEntry {
|
|
|
853
1084
|
hostTokens: ModuleHostTokens
|
|
854
1085
|
notifications: string[]
|
|
855
1086
|
cli: string[]
|
|
1087
|
+
backendPages: ModulePageFact[]
|
|
1088
|
+
frontendPages: ModulePageFact[]
|
|
1089
|
+
cliCommands: ModuleCliCommandFact[]
|
|
1090
|
+
aiTools: ModuleAiToolFact[]
|
|
1091
|
+
aiAgents: ModuleAiAgentFact[]
|
|
856
1092
|
}
|
|
857
1093
|
|
|
858
1094
|
const EMPTY_SECTION_MARKER = '_none_'
|
|
@@ -894,6 +1130,10 @@ function renderInlineListSection(heading: string, values: string[]): string {
|
|
|
894
1130
|
return `${heading}\n\n${values.join(' · ')}`
|
|
895
1131
|
}
|
|
896
1132
|
|
|
1133
|
+
function renderSourceLink(sourcePath: string): string {
|
|
1134
|
+
return `[${sourcePath}](../../../${sourcePath})`
|
|
1135
|
+
}
|
|
1136
|
+
|
|
897
1137
|
function describeAuthRule(rule: ApiRouteAuthRule | undefined): string {
|
|
898
1138
|
if (!rule) return 'public'
|
|
899
1139
|
if (rule.requireFeatures && rule.requireFeatures.length > 0) return rule.requireFeatures.join(', ')
|
|
@@ -916,14 +1156,23 @@ function renderApiRouteAuthCell(route: ModuleApiRouteFact): string {
|
|
|
916
1156
|
|
|
917
1157
|
function renderApiRoutesSection(routes: ModuleApiRouteFact[]): string {
|
|
918
1158
|
if (routes.length === 0) return `## API routes\n\n${EMPTY_SECTION_MARKER}`
|
|
919
|
-
const header = '| Path | Methods | Auth (per-method requireFeatures) |'
|
|
920
|
-
const divider = '
|
|
1159
|
+
const header = '| Path | Methods | Auth (per-method requireFeatures) | Source |'
|
|
1160
|
+
const divider = '|---|---|---|---|'
|
|
921
1161
|
const rows = routes.map(
|
|
922
|
-
(route) => `| ${route.path} | ${route.methods.join(' ')} | ${renderApiRouteAuthCell(route)} |`,
|
|
1162
|
+
(route) => `| ${route.path} | ${route.methods.join(' ')} | ${renderApiRouteAuthCell(route)} | ${route.sourcePath ? renderSourceLink(route.sourcePath) : '—'} |`,
|
|
923
1163
|
)
|
|
924
1164
|
return ['## API routes', '', header, divider, ...rows].join('\n')
|
|
925
1165
|
}
|
|
926
1166
|
|
|
1167
|
+
function renderLinkedFactsSection(
|
|
1168
|
+
heading: string,
|
|
1169
|
+
facts: ReadonlyArray<{ label: string; sourcePath: string }>,
|
|
1170
|
+
): string {
|
|
1171
|
+
if (facts.length === 0) return `${heading}\n\n${EMPTY_SECTION_MARKER}`
|
|
1172
|
+
const rows = facts.map((fact) => `| ${fact.label} | ${renderSourceLink(fact.sourcePath)} |`)
|
|
1173
|
+
return [heading, '', '| ID / path | Source |', '|---|---|', ...rows].join('\n')
|
|
1174
|
+
}
|
|
1175
|
+
|
|
927
1176
|
function renderHostTokensSection(hostTokens: ModuleHostTokens): string {
|
|
928
1177
|
const entityIdsLine = hostTokens.entityIds.length > 0 ? hostTokens.entityIds.join(' · ') : EMPTY_SECTION_MARKER
|
|
929
1178
|
const tableIdsLine = hostTokens.tableIds.length > 0 ? hostTokens.tableIds.join(' · ') : EMPTY_SECTION_MARKER
|
|
@@ -934,6 +1183,7 @@ export function renderModuleFactsMarkdown(facts: ModuleFacts): string {
|
|
|
934
1183
|
const sections = [
|
|
935
1184
|
`# ${facts.module} — module facts (generated, do not edit)`,
|
|
936
1185
|
renderVersionStamp(facts.coreVersion, facts.sourcePackage, facts.sourceVersion),
|
|
1186
|
+
`Source root: ${renderSourceLink(facts.sourceRoot)}`,
|
|
937
1187
|
'',
|
|
938
1188
|
renderEntitiesSection(facts.entities),
|
|
939
1189
|
'',
|
|
@@ -943,6 +1193,10 @@ export function renderModuleFactsMarkdown(facts: ModuleFacts): string {
|
|
|
943
1193
|
'',
|
|
944
1194
|
renderApiRoutesSection(facts.apiRoutes),
|
|
945
1195
|
'',
|
|
1196
|
+
renderLinkedFactsSection('## Backend pages', facts.backendPages.map((page) => ({ label: page.path, sourcePath: page.sourcePath }))),
|
|
1197
|
+
'',
|
|
1198
|
+
renderLinkedFactsSection('## Frontend pages', facts.frontendPages.map((page) => ({ label: page.path, sourcePath: page.sourcePath }))),
|
|
1199
|
+
'',
|
|
946
1200
|
renderInlineListSection('## DI service tokens', facts.diTokens),
|
|
947
1201
|
'',
|
|
948
1202
|
renderInlineListSection('## Search entities', facts.searchEntities),
|
|
@@ -951,7 +1205,11 @@ export function renderModuleFactsMarkdown(facts: ModuleFacts): string {
|
|
|
951
1205
|
'',
|
|
952
1206
|
renderInlineListSection('## Notifications', facts.notifications),
|
|
953
1207
|
'',
|
|
954
|
-
|
|
1208
|
+
renderLinkedFactsSection('## CLI commands', facts.cliCommands.map((command) => ({ label: command.command, sourcePath: command.sourcePath }))),
|
|
1209
|
+
'',
|
|
1210
|
+
renderLinkedFactsSection('## AI tools / MCP capabilities', facts.aiTools.map((tool) => ({ label: tool.name, sourcePath: tool.sourcePath }))),
|
|
1211
|
+
'',
|
|
1212
|
+
renderLinkedFactsSection('## AI agents', facts.aiAgents.map((agent) => ({ label: agent.id, sourcePath: agent.sourcePath }))),
|
|
955
1213
|
'',
|
|
956
1214
|
]
|
|
957
1215
|
return sections.join('\n')
|
|
@@ -964,6 +1222,7 @@ export function toModuleFactsJsonEntry(facts: ModuleFacts): ModuleFactsJsonEntry
|
|
|
964
1222
|
coreVersion: facts.coreVersion,
|
|
965
1223
|
sourcePackage: facts.sourcePackage,
|
|
966
1224
|
sourceVersion: facts.sourceVersion,
|
|
1225
|
+
sourceRoot: facts.sourceRoot,
|
|
967
1226
|
entities: facts.entities,
|
|
968
1227
|
events: facts.events.map((event) => ({ id: event.id, category: event.category, entity: event.entity })),
|
|
969
1228
|
aclFeatures: facts.aclFeatures,
|
|
@@ -973,6 +1232,11 @@ export function toModuleFactsJsonEntry(facts: ModuleFacts): ModuleFactsJsonEntry
|
|
|
973
1232
|
hostTokens: facts.hostTokens,
|
|
974
1233
|
notifications: facts.notifications,
|
|
975
1234
|
cli: facts.cli,
|
|
1235
|
+
backendPages: facts.backendPages,
|
|
1236
|
+
frontendPages: facts.frontendPages,
|
|
1237
|
+
cliCommands: facts.cliCommands,
|
|
1238
|
+
aiTools: facts.aiTools,
|
|
1239
|
+
aiAgents: facts.aiAgents,
|
|
976
1240
|
}
|
|
977
1241
|
}
|
|
978
1242
|
|