autosnippet 3.2.7 → 3.2.8
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/bin/cli.js +7 -0
- package/dashboard/dist/assets/index-D5jiDBQG.css +1 -0
- package/dashboard/dist/assets/{index-DfHY_3ln.js → index-e5OKj-Ni.js} +38 -38
- package/dashboard/dist/index.html +2 -2
- package/lib/cli/AiScanService.js +3 -3
- package/lib/core/AstAnalyzer.js +26 -4
- package/lib/core/analysis/CallEdgeResolver.js +402 -0
- package/lib/core/analysis/CallGraphAnalyzer.js +367 -0
- package/lib/core/analysis/CallSiteExtractor.js +629 -0
- package/lib/core/analysis/DataFlowInferrer.js +57 -0
- package/lib/core/analysis/ImportPathResolver.js +189 -0
- package/lib/core/analysis/ImportRecord.js +105 -0
- package/lib/core/analysis/SymbolTableBuilder.js +211 -0
- package/lib/core/ast/ProjectGraph.js +8 -0
- package/lib/core/ast/lang-dart.js +352 -5
- package/lib/core/ast/lang-go.js +212 -10
- package/lib/core/ast/lang-java.js +205 -1
- package/lib/core/ast/lang-kotlin.js +330 -1
- package/lib/core/ast/lang-python.js +31 -2
- package/lib/core/ast/lang-rust.js +284 -3
- package/lib/core/ast/lang-swift.js +180 -1
- package/lib/core/ast/lang-typescript.js +290 -1
- package/lib/external/mcp/McpServer.js +1 -0
- package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +21 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +5 -4
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +2 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +70 -4
- package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +95 -1
- package/lib/external/mcp/handlers/bootstrap-external.js +9 -2
- package/lib/external/mcp/handlers/bootstrap-internal.js +17 -6
- package/lib/external/mcp/handlers/consolidated.js +9 -0
- package/lib/external/mcp/handlers/guard.js +3 -3
- package/lib/external/mcp/handlers/structure.js +62 -0
- package/lib/external/mcp/handlers/wiki-external.js +66 -3
- package/lib/external/mcp/tools.js +36 -1
- package/lib/http/routes/remote.js +15 -15
- package/lib/injection/ServiceContainer.js +6 -11
- package/lib/platform/ios/index.js +2 -2
- package/lib/platform/ios/spm/PackageSwiftParser.js +14 -3
- package/lib/platform/ios/spm/SpmDiscoverer.js +123 -17
- package/lib/platform/ios/spm/{SpmService.js → SpmHelper.js} +43 -675
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +1 -1
- package/lib/service/chat/ChatAgent.js +1 -1
- package/lib/service/chat/ChatAgentPrompts.js +13 -1
- package/lib/service/chat/ExplorationTracker.js +52 -8
- package/lib/service/chat/HandoffProtocol.js +19 -1
- package/lib/service/chat/WorkingMemory.js +3 -1
- package/lib/service/chat/memory/ActiveContext.js +3 -1
- package/lib/service/chat/memory/SessionStore.js +4 -3
- package/lib/service/chat/tools/ast-graph.js +229 -32
- package/lib/service/chat/tools/index.js +6 -1
- package/lib/service/chat/tools/infrastructure.js +5 -0
- package/lib/service/cursor/CursorDeliveryPipeline.js +167 -1
- package/lib/service/knowledge/CodeEntityGraph.js +327 -2
- package/lib/service/knowledge/KnowledgeService.js +5 -1
- package/lib/service/module/ModuleService.js +9 -0
- package/lib/service/wiki/WikiGenerator.js +1 -1
- package/lib/shared/PathGuard.js +1 -1
- package/package.json +1 -1
- package/dashboard/dist/assets/index-BaGY7kJI.css +0 -1
|
@@ -664,19 +664,281 @@ export class CodeEntityGraph {
|
|
|
664
664
|
lines.push('');
|
|
665
665
|
}
|
|
666
666
|
|
|
667
|
+
// 调用图热路径 (Phase 5)
|
|
668
|
+
try {
|
|
669
|
+
const hotCallees = this.db
|
|
670
|
+
.prepare(
|
|
671
|
+
`SELECT to_id, COUNT(*) as call_count
|
|
672
|
+
FROM knowledge_edges
|
|
673
|
+
WHERE relation = 'calls'
|
|
674
|
+
GROUP BY to_id
|
|
675
|
+
ORDER BY call_count DESC
|
|
676
|
+
LIMIT 15`
|
|
677
|
+
)
|
|
678
|
+
.all();
|
|
679
|
+
|
|
680
|
+
if (hotCallees.length > 0) {
|
|
681
|
+
lines.push('### 调用图热路径 (Call Graph Hot Paths)');
|
|
682
|
+
for (const row of hotCallees) {
|
|
683
|
+
// 查找前几个调用者
|
|
684
|
+
const topCallers = this.db
|
|
685
|
+
.prepare(
|
|
686
|
+
`SELECT from_id FROM knowledge_edges
|
|
687
|
+
WHERE relation = 'calls' AND to_id = ?
|
|
688
|
+
LIMIT 3`
|
|
689
|
+
)
|
|
690
|
+
.all(row.to_id);
|
|
691
|
+
const callerNames = topCallers.map((c) => `\`${c.from_id}\``).join(', ');
|
|
692
|
+
lines.push(
|
|
693
|
+
`- \`${row.to_id}\` ← ${row.call_count} 次调用 (${callerNames}${topCallers.length < row.call_count ? '...' : ''})`
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
lines.push('');
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// 数据流边摘要
|
|
700
|
+
const dataFlowCount = this.db
|
|
701
|
+
.prepare(
|
|
702
|
+
`SELECT COUNT(*) as cnt FROM knowledge_edges WHERE relation = 'data_flow'`
|
|
703
|
+
)
|
|
704
|
+
.get();
|
|
705
|
+
|
|
706
|
+
if (dataFlowCount?.cnt > 0) {
|
|
707
|
+
lines.push(`### 数据流`);
|
|
708
|
+
lines.push(`- 数据流边: ${dataFlowCount.cnt} 条`);
|
|
709
|
+
lines.push('');
|
|
710
|
+
}
|
|
711
|
+
} catch (_e) {
|
|
712
|
+
// 调用图数据可能尚未填充, 静默跳过
|
|
713
|
+
}
|
|
714
|
+
|
|
667
715
|
return lines.join('\n');
|
|
668
716
|
}
|
|
669
717
|
|
|
718
|
+
// ────────────────────────────────────────────
|
|
719
|
+
// Public API — Phase 5: 调用图
|
|
720
|
+
// ────────────────────────────────────────────
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* 从解析后的调用边填充图谱 (Phase 5)
|
|
724
|
+
*
|
|
725
|
+
* @param {Array<{ caller: string, callee: string, callType: string, resolveMethod: string, line: number, file: string, isAwait: boolean }>} callEdges
|
|
726
|
+
* @param {Array<{ from: string, to: string, flowType: string, direction: string }>} dataFlowEdges
|
|
727
|
+
* @returns {GraphPopulateResult}
|
|
728
|
+
*/
|
|
729
|
+
populateCallGraph(callEdges, dataFlowEdges) {
|
|
730
|
+
const t0 = Date.now();
|
|
731
|
+
let edges = 0;
|
|
732
|
+
let entities = 0;
|
|
733
|
+
|
|
734
|
+
const run = this.db.transaction(() => {
|
|
735
|
+
// ── 注册方法实体 (确保 from/to 的 entity 存在) ──
|
|
736
|
+
const registeredMethods = new Set();
|
|
737
|
+
for (const edge of callEdges) {
|
|
738
|
+
for (const fqn of [edge.caller, edge.callee]) {
|
|
739
|
+
if (registeredMethods.has(fqn)) continue;
|
|
740
|
+
registeredMethods.add(fqn);
|
|
741
|
+
|
|
742
|
+
const entityId = this._extractEntityId(fqn);
|
|
743
|
+
const entityName = entityId; // 短名
|
|
744
|
+
const filePath = fqn.includes('::') ? fqn.split('::')[0] : null;
|
|
745
|
+
|
|
746
|
+
this.#upsertEntity({
|
|
747
|
+
entityId,
|
|
748
|
+
entityType: 'method',
|
|
749
|
+
name: entityName,
|
|
750
|
+
filePath,
|
|
751
|
+
metadata: { fqn, source: 'phase5-call-graph' },
|
|
752
|
+
});
|
|
753
|
+
entities++;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// ── 调用边 (聚合同一 caller-callee 对的多次调用,解决 Issue #4) ──
|
|
758
|
+
const aggregated = new Map(); // key = "callerId|calleeId" → aggregated metadata
|
|
759
|
+
for (const edge of callEdges) {
|
|
760
|
+
const callerId = this._extractEntityId(edge.caller);
|
|
761
|
+
const calleeId = this._extractEntityId(edge.callee);
|
|
762
|
+
const key = `${callerId}|${calleeId}`;
|
|
763
|
+
|
|
764
|
+
if (aggregated.has(key)) {
|
|
765
|
+
const agg = aggregated.get(key);
|
|
766
|
+
agg.callCount++;
|
|
767
|
+
agg.callSites.push({ line: edge.line, isAwait: edge.isAwait });
|
|
768
|
+
// 提升权重: direct 优先
|
|
769
|
+
if (edge.resolveMethod === 'direct') agg.resolveMethod = 'direct';
|
|
770
|
+
if (edge.isAwait) agg.hasAwait = true;
|
|
771
|
+
} else {
|
|
772
|
+
aggregated.set(key, {
|
|
773
|
+
callerId,
|
|
774
|
+
calleeId,
|
|
775
|
+
callType: edge.callType,
|
|
776
|
+
resolveMethod: edge.resolveMethod,
|
|
777
|
+
file: edge.file,
|
|
778
|
+
hasAwait: edge.isAwait,
|
|
779
|
+
callCount: 1,
|
|
780
|
+
callSites: [{ line: edge.line, isAwait: edge.isAwait }],
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
for (const agg of aggregated.values()) {
|
|
786
|
+
this.#addEdge(agg.callerId, 'method', agg.calleeId, 'method', 'calls', {
|
|
787
|
+
weight: agg.resolveMethod === 'direct' ? 1.0 : 0.6,
|
|
788
|
+
source: 'phase5-call-graph',
|
|
789
|
+
callType: agg.callType,
|
|
790
|
+
resolveMethod: agg.resolveMethod,
|
|
791
|
+
file: agg.file,
|
|
792
|
+
isAwait: agg.hasAwait,
|
|
793
|
+
callCount: agg.callCount,
|
|
794
|
+
callSites: agg.callSites.slice(0, 10), // 最多保留 10 个调用点
|
|
795
|
+
});
|
|
796
|
+
edges++;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// ── 数据流边 ──
|
|
800
|
+
for (const flow of dataFlowEdges) {
|
|
801
|
+
const fromId = this._extractEntityId(flow.from);
|
|
802
|
+
const toId = this._extractEntityId(flow.to);
|
|
803
|
+
|
|
804
|
+
this.#addEdge(fromId, 'method', toId, 'method', 'data_flow', {
|
|
805
|
+
weight: 0.5,
|
|
806
|
+
source: 'phase5-data-flow',
|
|
807
|
+
flowType: flow.flowType,
|
|
808
|
+
direction: flow.direction,
|
|
809
|
+
});
|
|
810
|
+
edges++;
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
run();
|
|
815
|
+
|
|
816
|
+
const result = { entitiesUpserted: entities, edgesCreated: edges, durationMs: Date.now() - t0 };
|
|
817
|
+
this.log.info(
|
|
818
|
+
`[CodeEntityGraph] Call graph: ${callEdges.length} call edges, ${dataFlowEdges.length} data flow edges, ${entities} method entities (${result.durationMs}ms)`
|
|
819
|
+
);
|
|
820
|
+
return result;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* 获取调用者 — 谁调用了这个方法?
|
|
825
|
+
*
|
|
826
|
+
* @param {string} methodId — "ClassName.methodName" 或 FQN
|
|
827
|
+
* @param {number} [maxDepth=2]
|
|
828
|
+
* @returns {Array<{ caller: string, depth: number, callType: string }>}
|
|
829
|
+
*/
|
|
830
|
+
getCallers(methodId, maxDepth = 2) {
|
|
831
|
+
const results = [];
|
|
832
|
+
const visited = new Set();
|
|
833
|
+
const queue = [{ id: methodId, depth: 0 }];
|
|
834
|
+
|
|
835
|
+
while (queue.length > 0) {
|
|
836
|
+
const { id, depth } = queue.shift();
|
|
837
|
+
if (depth >= maxDepth || visited.has(id)) continue;
|
|
838
|
+
visited.add(id);
|
|
839
|
+
|
|
840
|
+
const callers = this.stmts.getCallers.all(id);
|
|
841
|
+
|
|
842
|
+
for (const row of callers) {
|
|
843
|
+
const meta = JSON.parse(row.metadata_json || '{}');
|
|
844
|
+
results.push({
|
|
845
|
+
caller: row.from_id,
|
|
846
|
+
depth: depth + 1,
|
|
847
|
+
callType: meta.callType || 'unknown',
|
|
848
|
+
});
|
|
849
|
+
if (depth + 1 < maxDepth) {
|
|
850
|
+
queue.push({ id: row.from_id, depth: depth + 1 });
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
return results;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* 获取被调用者 — 这个方法调用了谁?
|
|
860
|
+
*
|
|
861
|
+
* @param {string} methodId — "ClassName.methodName" 或 FQN
|
|
862
|
+
* @param {number} [maxDepth=2]
|
|
863
|
+
* @returns {Array<{ callee: string, depth: number, callType: string }>}
|
|
864
|
+
*/
|
|
865
|
+
getCallees(methodId, maxDepth = 2) {
|
|
866
|
+
const results = [];
|
|
867
|
+
const visited = new Set();
|
|
868
|
+
const queue = [{ id: methodId, depth: 0 }];
|
|
869
|
+
|
|
870
|
+
while (queue.length > 0) {
|
|
871
|
+
const { id, depth } = queue.shift();
|
|
872
|
+
if (depth >= maxDepth || visited.has(id)) continue;
|
|
873
|
+
visited.add(id);
|
|
874
|
+
|
|
875
|
+
const callees = this.stmts.getCallees.all(id);
|
|
876
|
+
|
|
877
|
+
for (const row of callees) {
|
|
878
|
+
const meta = JSON.parse(row.metadata_json || '{}');
|
|
879
|
+
results.push({
|
|
880
|
+
callee: row.to_id,
|
|
881
|
+
depth: depth + 1,
|
|
882
|
+
callType: meta.callType || 'unknown',
|
|
883
|
+
});
|
|
884
|
+
if (depth + 1 < maxDepth) {
|
|
885
|
+
queue.push({ id: row.to_id, depth: depth + 1 });
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
return results;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* 获取方法的 Impact Radius (基于调用图)
|
|
895
|
+
* — 修改此方法可能影响哪些上游方法?
|
|
896
|
+
*
|
|
897
|
+
* @param {string} methodId — "ClassName.methodName"
|
|
898
|
+
* @returns {{ directCallers: number, transitiveCallers: number, affectedFiles: string[] }}
|
|
899
|
+
*/
|
|
900
|
+
getCallImpactRadius(methodId) {
|
|
901
|
+
const callers = this.getCallers(methodId, 3);
|
|
902
|
+
const affectedFiles = new Set();
|
|
903
|
+
|
|
904
|
+
for (const c of callers) {
|
|
905
|
+
const entity = this.getEntity(c.caller, 'method');
|
|
906
|
+
if (entity?.filePath) affectedFiles.add(entity.filePath);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
return {
|
|
910
|
+
directCallers: callers.filter((c) => c.depth === 1).length,
|
|
911
|
+
transitiveCallers: callers.length,
|
|
912
|
+
affectedFiles: [...affectedFiles],
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* 从 FQN 中提取短 Entity ID
|
|
918
|
+
*
|
|
919
|
+
* "src/service/UserService.ts::UserService.getUser" → "UserService.getUser"
|
|
920
|
+
* "src/utils/helpers.ts::formatDate" → "formatDate"
|
|
921
|
+
*
|
|
922
|
+
* @param {string} fqn
|
|
923
|
+
* @returns {string}
|
|
924
|
+
*/
|
|
925
|
+
_extractEntityId(fqn) {
|
|
926
|
+
if (fqn.includes('::')) {
|
|
927
|
+
return fqn.split('::')[1];
|
|
928
|
+
}
|
|
929
|
+
return fqn;
|
|
930
|
+
}
|
|
931
|
+
|
|
670
932
|
/**
|
|
671
933
|
* 清除项目的所有代码实体 (重新 populate 前调用)
|
|
672
934
|
*/
|
|
673
935
|
clearProject() {
|
|
674
936
|
const run = this.db.transaction(() => {
|
|
675
937
|
this.stmts.clearEntities.run(this.projectRoot);
|
|
676
|
-
//
|
|
938
|
+
// 清除 AST 产出的边 + Phase 5 调用图边 (保留 recipe/module 边)
|
|
677
939
|
this.db
|
|
678
940
|
.prepare(
|
|
679
|
-
`DELETE FROM knowledge_edges WHERE metadata_json LIKE '%ast-bootstrap%' OR metadata_json LIKE '%ast-pattern-detection%'`
|
|
941
|
+
`DELETE FROM knowledge_edges WHERE metadata_json LIKE '%ast-bootstrap%' OR metadata_json LIKE '%ast-pattern-detection%' OR metadata_json LIKE '%phase5-%'`
|
|
680
942
|
)
|
|
681
943
|
.run();
|
|
682
944
|
});
|
|
@@ -684,6 +946,55 @@ export class CodeEntityGraph {
|
|
|
684
946
|
this.log.info(`[CodeEntityGraph] Cleared entities for project: ${this.projectRoot}`);
|
|
685
947
|
}
|
|
686
948
|
|
|
949
|
+
/**
|
|
950
|
+
* 增量清除 — 仅删除指定文件的 call graph 边和 method 实体
|
|
951
|
+
*
|
|
952
|
+
* @param {string[]} filePaths — 变更文件的相对路径列表
|
|
953
|
+
* @returns {{ deletedEdges: number, deletedEntities: number }}
|
|
954
|
+
*/
|
|
955
|
+
clearCallGraphForFiles(filePaths) {
|
|
956
|
+
if (!filePaths?.length) return { deletedEdges: 0, deletedEntities: 0 };
|
|
957
|
+
|
|
958
|
+
let deletedEdges = 0;
|
|
959
|
+
let deletedEntities = 0;
|
|
960
|
+
|
|
961
|
+
const run = this.db.transaction(() => {
|
|
962
|
+
// 1. 删除相关 call edges (metadata_json 包含 file 字段)
|
|
963
|
+
const deleteEdgesStmt = this.db.prepare(
|
|
964
|
+
`DELETE FROM knowledge_edges
|
|
965
|
+
WHERE metadata_json LIKE ?
|
|
966
|
+
AND (relation = 'calls' OR relation = 'data_flow')
|
|
967
|
+
AND metadata_json LIKE '%phase5-%'`
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
for (const filePath of filePaths) {
|
|
971
|
+
// 匹配 metadata 中 "file":"xxx" 字段
|
|
972
|
+
const result = deleteEdgesStmt.run(`%"file":"${filePath}"%`);
|
|
973
|
+
deletedEdges += result.changes;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// 2. 删除相关 method 实体
|
|
977
|
+
const deleteEntitiesStmt = this.db.prepare(
|
|
978
|
+
`DELETE FROM code_entities
|
|
979
|
+
WHERE file_path = ? AND entity_type = 'method' AND project_root = ?`
|
|
980
|
+
);
|
|
981
|
+
|
|
982
|
+
for (const filePath of filePaths) {
|
|
983
|
+
const result = deleteEntitiesStmt.run(filePath, this.projectRoot);
|
|
984
|
+
deletedEntities += result.changes;
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
run();
|
|
989
|
+
|
|
990
|
+
this.log.info(
|
|
991
|
+
`[CodeEntityGraph] Incremental clear: ${deletedEdges} edges, ${deletedEntities} entities ` +
|
|
992
|
+
`for ${filePaths.length} files`
|
|
993
|
+
);
|
|
994
|
+
|
|
995
|
+
return { deletedEdges, deletedEntities };
|
|
996
|
+
}
|
|
997
|
+
|
|
687
998
|
// ────────────────────────────────────────────
|
|
688
999
|
// Private — Schema & Statements
|
|
689
1000
|
// ────────────────────────────────────────────
|
|
@@ -738,6 +1049,19 @@ export class CodeEntityGraph {
|
|
|
738
1049
|
INSERT OR REPLACE INTO knowledge_edges (from_id, from_type, to_id, to_type, relation, weight, metadata_json, created_at, updated_at)
|
|
739
1050
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
740
1051
|
`),
|
|
1052
|
+
// Phase 5: 调用图查询 (pre-prepared 避免每次调用都创建)
|
|
1053
|
+
getCallers: this.db.prepare(
|
|
1054
|
+
`SELECT from_id, from_type, metadata_json FROM knowledge_edges
|
|
1055
|
+
WHERE to_id = ? AND relation = 'calls'`
|
|
1056
|
+
),
|
|
1057
|
+
getCallees: this.db.prepare(
|
|
1058
|
+
`SELECT to_id, to_type, metadata_json FROM knowledge_edges
|
|
1059
|
+
WHERE from_id = ? AND relation = 'calls'`
|
|
1060
|
+
),
|
|
1061
|
+
getEdge: this.db.prepare(
|
|
1062
|
+
`SELECT metadata_json FROM knowledge_edges
|
|
1063
|
+
WHERE from_id = ? AND from_type = ? AND to_id = ? AND to_type = ? AND relation = ?`
|
|
1064
|
+
),
|
|
741
1065
|
};
|
|
742
1066
|
}
|
|
743
1067
|
|
|
@@ -788,6 +1112,7 @@ export class CodeEntityGraph {
|
|
|
788
1112
|
* 从 AST 数据推断实体类型
|
|
789
1113
|
*/
|
|
790
1114
|
#inferEntityType(name, astSummary) {
|
|
1115
|
+
if (!name) return 'class'; // guard against undefined
|
|
791
1116
|
if (astSummary.protocols?.some((p) => p.name === name)) {
|
|
792
1117
|
return 'protocol';
|
|
793
1118
|
}
|
|
@@ -75,7 +75,7 @@ export class KnowledgeService {
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
// ── ConfidenceRouter —
|
|
78
|
+
// ── ConfidenceRouter — 标记 auto_approvable ──
|
|
79
79
|
if (this._confidenceRouter) {
|
|
80
80
|
const route = await this._confidenceRouter.route(entry);
|
|
81
81
|
if (route.action === 'auto_approve') {
|
|
@@ -84,6 +84,10 @@ export class KnowledgeService {
|
|
|
84
84
|
// reject / pending 都保持 pending 状态,等待人工审核
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// 注意: Bootstrap 候选保持 pending 状态,由 Dashboard 审核后发布。
|
|
88
|
+
// autoApprovable 标记保留,供前端显示「推荐批准」徽章。
|
|
89
|
+
// CursorDelivery 已支持高置信度 pending 条目的交付。
|
|
90
|
+
|
|
87
91
|
const saved = await this.repository.create(entry);
|
|
88
92
|
|
|
89
93
|
// 同步 relations → knowledge_edges
|
|
@@ -314,7 +314,16 @@ export class ModuleService {
|
|
|
314
314
|
const allNodes = [];
|
|
315
315
|
const allEdges = [];
|
|
316
316
|
|
|
317
|
+
// 如果有专业 Discoverer(非 generic),则跳过 GenericDiscoverer 的依赖图
|
|
318
|
+
// 避免 generic fallback 生成的冗余根节点(如项目名本身)干扰图结构
|
|
319
|
+
const hasSpecializedDiscoverer = this.#activeDiscoverers.some(
|
|
320
|
+
({ discoverer }) => discoverer.id !== 'generic'
|
|
321
|
+
);
|
|
322
|
+
|
|
317
323
|
for (const { discoverer } of this.#activeDiscoverers) {
|
|
324
|
+
if (hasSpecializedDiscoverer && discoverer.id === 'generic') {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
318
327
|
try {
|
|
319
328
|
const graph = await discoverer.getDependencyGraph();
|
|
320
329
|
for (const n of graph.nodes || []) {
|
|
@@ -82,7 +82,7 @@ export class WikiGenerator {
|
|
|
82
82
|
* @param {object} deps
|
|
83
83
|
* @param {string} deps.projectRoot
|
|
84
84
|
* @param {import('../../service/module/ModuleService.js').ModuleService} [deps.moduleService]
|
|
85
|
-
* @param {import('../../platform/ios/spm/
|
|
85
|
+
* @param {import('../../platform/ios/spm/SpmHelper.js').SpmHelper} [deps.spmService] — 向后兼容
|
|
86
86
|
* @param {import('../../service/knowledge/KnowledgeService.js').KnowledgeService} [deps.knowledgeService]
|
|
87
87
|
* @param {import('../../core/ast/ProjectGraph.js').default} [deps.projectGraph]
|
|
88
88
|
* @param {import('../../service/knowledge/CodeEntityGraph.js').CodeEntityGraph} [deps.codeEntityGraph]
|
package/lib/shared/PathGuard.js
CHANGED
|
@@ -141,7 +141,7 @@ class PathGuard {
|
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
143
|
* Layer 1: 断言路径在允许的边界范围内
|
|
144
|
-
* 用于修改已有文件的场景(如 XcodeIntegration 插入 header、
|
|
144
|
+
* 用于修改已有文件的场景(如 XcodeIntegration 插入 header、SpmHelper 修改 Package.swift)
|
|
145
145
|
* @param {string} targetPath - 要写入的绝对路径
|
|
146
146
|
* @throws {PathGuardError}
|
|
147
147
|
*/
|