@whispering233/ai-editor-tools 0.0.1

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.
Files changed (109) hide show
  1. package/dist/analysis/conflict.d.ts +20 -0
  2. package/dist/analysis/conflict.d.ts.map +1 -0
  3. package/dist/analysis/conflict.js +116 -0
  4. package/dist/analysis/conflict.js.map +1 -0
  5. package/dist/analysis/consistency.d.ts +28 -0
  6. package/dist/analysis/consistency.d.ts.map +1 -0
  7. package/dist/analysis/consistency.js +108 -0
  8. package/dist/analysis/consistency.js.map +1 -0
  9. package/dist/analysis/hook.d.ts +157 -0
  10. package/dist/analysis/hook.d.ts.map +1 -0
  11. package/dist/analysis/hook.js +381 -0
  12. package/dist/analysis/hook.js.map +1 -0
  13. package/dist/analysis/orphan.d.ts +60 -0
  14. package/dist/analysis/orphan.d.ts.map +1 -0
  15. package/dist/analysis/orphan.js +153 -0
  16. package/dist/analysis/orphan.js.map +1 -0
  17. package/dist/analysis/path.d.ts +23 -0
  18. package/dist/analysis/path.d.ts.map +1 -0
  19. package/dist/analysis/path.js +114 -0
  20. package/dist/analysis/path.js.map +1 -0
  21. package/dist/analysis/suggest.d.ts +19 -0
  22. package/dist/analysis/suggest.d.ts.map +1 -0
  23. package/dist/analysis/suggest.js +92 -0
  24. package/dist/analysis/suggest.js.map +1 -0
  25. package/dist/analysis/utils.d.ts +54 -0
  26. package/dist/analysis/utils.d.ts.map +1 -0
  27. package/dist/analysis/utils.js +123 -0
  28. package/dist/analysis/utils.js.map +1 -0
  29. package/dist/context.d.ts +10 -0
  30. package/dist/context.d.ts.map +1 -0
  31. package/dist/context.js +11 -0
  32. package/dist/context.js.map +1 -0
  33. package/dist/executor/delta.d.ts +4 -0
  34. package/dist/executor/delta.d.ts.map +1 -0
  35. package/dist/executor/delta.js +22 -0
  36. package/dist/executor/delta.js.map +1 -0
  37. package/dist/executor/entity.d.ts +8 -0
  38. package/dist/executor/entity.d.ts.map +1 -0
  39. package/dist/executor/entity.js +47 -0
  40. package/dist/executor/entity.js.map +1 -0
  41. package/dist/executor/hook.d.ts +8 -0
  42. package/dist/executor/hook.d.ts.map +1 -0
  43. package/dist/executor/hook.js +183 -0
  44. package/dist/executor/hook.js.map +1 -0
  45. package/dist/executor/index.d.ts +22 -0
  46. package/dist/executor/index.d.ts.map +1 -0
  47. package/dist/executor/index.js +89 -0
  48. package/dist/executor/index.js.map +1 -0
  49. package/dist/executor/outline.d.ts +8 -0
  50. package/dist/executor/outline.d.ts.map +1 -0
  51. package/dist/executor/outline.js +39 -0
  52. package/dist/executor/outline.js.map +1 -0
  53. package/dist/executor/relation.d.ts +6 -0
  54. package/dist/executor/relation.d.ts.map +1 -0
  55. package/dist/executor/relation.js +33 -0
  56. package/dist/executor/relation.js.map +1 -0
  57. package/dist/executor/types.d.ts +25 -0
  58. package/dist/executor/types.d.ts.map +1 -0
  59. package/dist/executor/types.js +54 -0
  60. package/dist/executor/types.js.map +1 -0
  61. package/dist/index.d.ts +23 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +359 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/proposal/delta.d.ts +8 -0
  66. package/dist/proposal/delta.d.ts.map +1 -0
  67. package/dist/proposal/delta.js +29 -0
  68. package/dist/proposal/delta.js.map +1 -0
  69. package/dist/proposal/entity.d.ts +16 -0
  70. package/dist/proposal/entity.d.ts.map +1 -0
  71. package/dist/proposal/entity.js +54 -0
  72. package/dist/proposal/entity.js.map +1 -0
  73. package/dist/proposal/hook.d.ts +24 -0
  74. package/dist/proposal/hook.d.ts.map +1 -0
  75. package/dist/proposal/hook.js +81 -0
  76. package/dist/proposal/hook.js.map +1 -0
  77. package/dist/proposal/outline.d.ts +24 -0
  78. package/dist/proposal/outline.d.ts.map +1 -0
  79. package/dist/proposal/outline.js +78 -0
  80. package/dist/proposal/outline.js.map +1 -0
  81. package/dist/proposal/relation.d.ts +12 -0
  82. package/dist/proposal/relation.d.ts.map +1 -0
  83. package/dist/proposal/relation.js +50 -0
  84. package/dist/proposal/relation.js.map +1 -0
  85. package/dist/proposal/types.d.ts +72 -0
  86. package/dist/proposal/types.d.ts.map +1 -0
  87. package/dist/proposal/types.js +105 -0
  88. package/dist/proposal/types.js.map +1 -0
  89. package/dist/query/delta.d.ts +20 -0
  90. package/dist/query/delta.d.ts.map +1 -0
  91. package/dist/query/delta.js +41 -0
  92. package/dist/query/delta.js.map +1 -0
  93. package/dist/query/entity.d.ts +29 -0
  94. package/dist/query/entity.d.ts.map +1 -0
  95. package/dist/query/entity.js +53 -0
  96. package/dist/query/entity.js.map +1 -0
  97. package/dist/query/outline.d.ts +20 -0
  98. package/dist/query/outline.d.ts.map +1 -0
  99. package/dist/query/outline.js +68 -0
  100. package/dist/query/outline.js.map +1 -0
  101. package/dist/query/relation.d.ts +12 -0
  102. package/dist/query/relation.d.ts.map +1 -0
  103. package/dist/query/relation.js +30 -0
  104. package/dist/query/relation.js.map +1 -0
  105. package/dist/registry.d.ts +41 -0
  106. package/dist/registry.d.ts.map +1 -0
  107. package/dist/registry.js +46 -0
  108. package/dist/registry.js.map +1 -0
  109. package/package.json +35 -0
@@ -0,0 +1,23 @@
1
+ import type { ToolContext } from "../context.js";
2
+ import type { TracePlotPathsArgs } from "@whispering233/ai-editor-shared";
3
+ /** 路径节点(tools.md trace_plot_paths paths[].nodes 项;type/id/name 与 relation 路径同构) */
4
+ export interface PlotPathNode {
5
+ type: string;
6
+ id: string;
7
+ name: string;
8
+ }
9
+ /** 一条剧情路径(tools.md trace_plot_paths paths[] 项) */
10
+ export interface PlotPath {
11
+ nodes: PlotPathNode[];
12
+ description: string;
13
+ risk_factors: string[];
14
+ }
15
+ /**
16
+ * 剧情路径推演(tools.md trace_plot_paths(from_node_id, to_node_id))。
17
+ * 输出 paths(树路径优先,随后连线路径);两节点不存在/已软删 → null(查询无结果)。
18
+ * signal:连线路径遍历为长任务候选,循环中检查(决策 16 ③)。
19
+ */
20
+ export declare function runTracePlotPaths(ctx: ToolContext, args: TracePlotPathsArgs, signal?: AbortSignal): {
21
+ paths: PlotPath[];
22
+ } | null;
23
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/analysis/path.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,mFAAmF;AACnF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kDAAkD;AAClD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAsED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG;IAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;CAAE,GAAG,IAAI,CAwChI"}
@@ -0,0 +1,114 @@
1
+ // 分析类工具:trace_plot_paths(剧情路径推演,S6.4)
2
+ // 契约来源:doc/api/tools.md「路径分析」→ { paths: [{ nodes: [], description, risk_factors: [] }] }
3
+ // 语义:从 from_node_id 到 to_node_id 推演可能的剧情路径——两类来源:
4
+ // 1. **树路径**:两节点在大纲树同一分支(祖先后裔关系)→ 沿树的直接推进链(决策 19 严格三层下唯一)
5
+ // 2. **连线路径**:沿 plot_edge 剧情连线(决策 10 画布连线)从 from 出发的 k 跳路径(depth=3)
6
+ // 风险因素(risk_factors,从节点属性推导):路径过长(≥5 节点)、途经 scene 缺 goal、
7
+ // 途经 chapter 缺 reversal、路径经过软删节点(手改树的不一致形态)。
8
+ // 数据访问:db 查询层(listRelations plot_edge)+ outline.json 读取(树路径),无原生 SQL。
9
+ import { findOutlineNode, getOutlinePathIds, listRelations, readOutlineFile } from "@whispering233/ai-editor-db";
10
+ import { throwIfAborted } from "./utils.js";
11
+ /** 节点信息(type/id/name=title;root 防御:name 用 "root" 占位) */
12
+ function nodeInfo(node, id) {
13
+ if (node === undefined)
14
+ return { type: "outline_node", id, name: id }; // 脏引用防御(树外 id)
15
+ return { type: node.type, id: node.id, name: node.title };
16
+ }
17
+ /**
18
+ * 树路径推导:若 from 是 to 的祖先 → 顺向链(from → to);若 to 是 from 的祖先 → 回溯链(from → to)。
19
+ * 两节点在不同分支(无祖先后裔关系)→ null(无树路径,靠连线路径)。
20
+ * root 非可寻址大纲节点 → null。
21
+ */
22
+ function treeSubPath(tree, fromId, toId) {
23
+ if (fromId === "root" || toId === "root")
24
+ return null;
25
+ let fromPath;
26
+ let toPath;
27
+ try {
28
+ fromPath = getOutlinePathIds(tree, fromId);
29
+ toPath = getOutlinePathIds(tree, toId);
30
+ }
31
+ catch {
32
+ return null; // 节点不存在(getOutlinePathIds 抛错语义)
33
+ }
34
+ const fromIdx = toPath.indexOf(fromId); // from 在 to 的路径上(from 是 to 祖先)
35
+ if (fromIdx !== -1) {
36
+ return { nodes: toPath.slice(fromIdx).map((id) => nodeInfo(findOutlineNode(tree, id), id)), backward: false };
37
+ }
38
+ const toIdx = fromPath.indexOf(toId); // to 在 from 的路径上(to 是 from 祖先)
39
+ if (toIdx !== -1) {
40
+ return { nodes: fromPath.slice(toIdx).reverse().map((id) => nodeInfo(findOutlineNode(tree, id), id)), backward: true };
41
+ }
42
+ return null; // 不同分支
43
+ }
44
+ /**
45
+ * 风险因素收集(树路径与连线路径共用):从节点属性推导推进风险。
46
+ * - 路径过长(≥4 节点)→ 中段易拖沓(连线路径 3 跳 = depth=3 推演上限,已达全量深度;
47
+ * 树路径最多 3 层不触发)
48
+ * - scene 缺 data.goal(麦基字段集,决策 23)→ 场景缺乏目标
49
+ * - chapter 缺 data.reversal → 章节缺乏反转
50
+ * - 途经软删节点(手改 outline.json 的不一致形态)→ 数据不一致
51
+ */
52
+ function collectRiskFactors(tree, nodes) {
53
+ const risks = [];
54
+ if (nodes.length >= 4) {
55
+ risks.push(`路径过长(${nodes.length} 个节点),中段易拖沓,建议拆分为多幕推进`);
56
+ }
57
+ for (const n of nodes) {
58
+ const node = findOutlineNode(tree, n.id);
59
+ if (node === undefined) {
60
+ risks.push(`路径经过不存在的节点 ${n.id}(数据不一致)`);
61
+ continue;
62
+ }
63
+ if (node.type === "scene" && node.data?.goal === undefined) {
64
+ risks.push(`场景「${node.title}」未定义目标(goal),推进缺乏动机`);
65
+ }
66
+ if (node.type === "chapter" && node.data?.reversal === undefined) {
67
+ risks.push(`章「${node.title}」未定义反转(reversal),高潮乏力`);
68
+ }
69
+ if (node.deleted === true) {
70
+ risks.push(`路径经过已软删节点「${node.title}」(数据不一致,建议先还原)`);
71
+ }
72
+ }
73
+ return risks;
74
+ }
75
+ /**
76
+ * 剧情路径推演(tools.md trace_plot_paths(from_node_id, to_node_id))。
77
+ * 输出 paths(树路径优先,随后连线路径);两节点不存在/已软删 → null(查询无结果)。
78
+ * signal:连线路径遍历为长任务候选,循环中检查(决策 16 ③)。
79
+ */
80
+ export function runTracePlotPaths(ctx, args, signal) {
81
+ const tree = readOutlineFile(ctx.outlineDir);
82
+ const from = findOutlineNode(tree, args.from_node_id);
83
+ const to = findOutlineNode(tree, args.to_node_id);
84
+ if (from === undefined || from.deleted === true || to === undefined || to.deleted === true)
85
+ return null;
86
+ throwIfAborted(signal);
87
+ const paths = [];
88
+ // 1. 树路径(唯一性:严格三层下同分支路径唯一,决策 19)
89
+ const treePath = treeSubPath(tree, args.from_node_id, args.to_node_id);
90
+ if (treePath !== null) {
91
+ paths.push({
92
+ nodes: treePath.nodes,
93
+ description: treePath.backward
94
+ ? `沿大纲树回溯(${args.from_node_id} → ${args.to_node_id}):共 ${treePath.nodes.length} 个节点`
95
+ : `沿大纲树直接推进(${args.from_node_id} → ${args.to_node_id}):共 ${treePath.nodes.length} 个节点`,
96
+ risk_factors: collectRiskFactors(tree, treePath.nodes),
97
+ });
98
+ }
99
+ // 2. plot_edge 连线路径(决策 10 画布连线;有向 BFS 沿 source→target,depth=3 上限)
100
+ const edgeResult = listRelations(ctx.db, { relationType: "plot_edge", sourceId: args.from_node_id }, 3, ctx.outlineDir);
101
+ for (const p of edgeResult.paths ?? []) {
102
+ throwIfAborted(signal);
103
+ const last = p.nodes[p.nodes.length - 1];
104
+ if (last === undefined || last.id !== args.to_node_id)
105
+ continue; // 只取终点 = to 的路径
106
+ paths.push({
107
+ nodes: p.nodes.map((n) => ({ type: n.type, id: n.id, name: n.name })),
108
+ description: `沿剧情连线(plot_edge)推进:共 ${p.nodes.length - 1} 跳`,
109
+ risk_factors: collectRiskFactors(tree, p.nodes),
110
+ });
111
+ }
112
+ return { paths };
113
+ }
114
+ //# sourceMappingURL=path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/analysis/path.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ToolContext } from "../context.js";
2
+ import type { SuggestConnectionsArgs } from "@whispering233/ai-editor-shared";
3
+ /** 潜在关联建议(tools.md suggest_connections 返回项) */
4
+ export interface ConnectionSuggestion {
5
+ target_id: string;
6
+ /** 建议的关系类型(预定义枚举;用户确认后可建立) */
7
+ relation_type: string;
8
+ reason: string;
9
+ }
10
+ /**
11
+ * 潜在关系发现(tools.md suggest_connections(entity_id))。
12
+ * 实体不存在/已软删 → null(查询无结果);同类型无其他实体 → 空建议。
13
+ * 信号优先级:共享场景(S1)> 共同邻居(S2),每候选最多一条建议(取最强信号);
14
+ * 软删对象不可见(查询层默认过滤,决策 12);已有直接关系的候选跳过。
15
+ */
16
+ export declare function runSuggestConnections(ctx: ToolContext, args: SuggestConnectionsArgs, signal?: AbortSignal): {
17
+ suggestions: ConnectionSuggestion[];
18
+ } | null;
19
+ //# sourceMappingURL=suggest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggest.d.ts","sourceRoot":"","sources":["../../src/analysis/suggest.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAE9E,+CAA+C;AAC/C,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAUD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG;IAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;CAAE,GAAG,IAAI,CAyE1J"}
@@ -0,0 +1,92 @@
1
+ // 分析类工具:suggest_connections(潜在关系发现,S6.4)
2
+ // 契约来源:doc/api/tools.md「关系发现」→ { suggestions: [{ target_id, relation_type, reason }] }
3
+ // 语义:为指定实体发现与同类型其他实体的潜在关联(启发式信号):
4
+ // - S1 共享场景(强信号):两角色共同 appears_in 于同一大纲节点(同场戏出现过)
5
+ // - S2 共同邻居(次信号):两实体在实体关系图中共享直接关联实体(「朋友的朋友」)
6
+ // 已存在直接关系的候选跳过;建议按信号强度降序取 top 10;relation_type 建议
7
+ // ally(同场戏相识)/ ally(经中间人相识)。
8
+ // 数据访问:db 查询层(getEntity/listEntities/listRelations)+ 纯函数图分析,无原生 SQL。
9
+ // signal:全量候选 × 信号计算为长任务候选,循环中检查(决策 16 ③)。
10
+ import { getEntity, listEntities, listRelations } from "@whispering233/ai-editor-db";
11
+ import { buildEntityGraph, intersectSets, isEntityType, throwIfAborted } from "./utils.js";
12
+ /** 建议条数上限(防 token 爆炸,决策 15) */
13
+ const SUGGESTION_LIMIT = 10;
14
+ /** 场景标题映射(outline_node 端点的 targetName 由 listRelations 联表填充) */
15
+ function sceneTitleOf(relations, sceneId) {
16
+ return relations.find((r) => r.targetId === sceneId)?.targetName ?? sceneId;
17
+ }
18
+ /**
19
+ * 潜在关系发现(tools.md suggest_connections(entity_id))。
20
+ * 实体不存在/已软删 → null(查询无结果);同类型无其他实体 → 空建议。
21
+ * 信号优先级:共享场景(S1)> 共同邻居(S2),每候选最多一条建议(取最强信号);
22
+ * 软删对象不可见(查询层默认过滤,决策 12);已有直接关系的候选跳过。
23
+ */
24
+ export function runSuggestConnections(ctx, args, signal) {
25
+ const entity = getEntity(ctx.db, args.entity_id);
26
+ if (entity === null)
27
+ return null;
28
+ throwIfAborted(signal);
29
+ // 1. 同类型候选(非软删,排除自身);全量实体名映射(S2 共同邻居可能跨类型——
30
+ // 邻居名从全量映射取,避免退化为 id)
31
+ const candidates = listEntities(ctx.db, { type: entity.type, limit: 200 }).items.filter((c) => c.id !== entity.id);
32
+ const allEntities = listEntities(ctx.db, { limit: 200 }).items;
33
+ const entityName = new Map(allEntities.map((e) => [e.id, e.name]));
34
+ if (candidates.length === 0)
35
+ return { suggestions: [] };
36
+ // 2. 全量可见关系:实体图(共同邻居)+ appears_in 分组(共享场景)+ 直接关联集合
37
+ const relations = listRelations(ctx.db, {}, 1, ctx.outlineDir).relations;
38
+ throwIfAborted(signal);
39
+ const graph = buildEntityGraph(relations);
40
+ const appearsIn = new Map(); // 实体 id → 出现节点集合
41
+ const directlyLinked = new Set(); // 与目标实体已有实体-实体直接关系的候选
42
+ for (const r of relations) {
43
+ if (r.relationType === "appears_in" && isEntityType(r.sourceType) && r.targetType === "outline_node") {
44
+ const list = appearsIn.get(r.sourceId) ?? [];
45
+ list.push(r.targetId);
46
+ appearsIn.set(r.sourceId, list);
47
+ }
48
+ if (isEntityType(r.sourceType) && isEntityType(r.targetType)) {
49
+ if (r.sourceId === entity.id)
50
+ directlyLinked.add(r.targetId);
51
+ if (r.targetId === entity.id)
52
+ directlyLinked.add(r.sourceId);
53
+ }
54
+ }
55
+ const scored = [];
56
+ const myScenes = new Set(appearsIn.get(entity.id) ?? []);
57
+ const myNeighbors = new Set(graph.get(entity.id) ?? []);
58
+ for (const c of candidates) {
59
+ throwIfAborted(signal);
60
+ if (directlyLinked.has(c.id))
61
+ continue; // 已有直接关系,无需建议
62
+ // S1:共享场景(同场戏出现过)
63
+ const sharedScenes = intersectSets(myScenes, new Set(appearsIn.get(c.id) ?? []));
64
+ if (sharedScenes.length > 0) {
65
+ const sceneName = sceneTitleOf(relations, sharedScenes[0]);
66
+ scored.push({
67
+ targetId: c.id,
68
+ score: 100 + sharedScenes.length, // 场景信号优先于邻居信号
69
+ relationType: "ally",
70
+ reason: `与「${c.name}」共同出现于 ${sharedScenes.length} 个场景(如「${sceneName}」),可建立相识/盟友关系`,
71
+ });
72
+ continue;
73
+ }
74
+ // S2:共同邻居(「朋友的朋友」)
75
+ const sharedNeighbors = intersectSets(myNeighbors, new Set(graph.get(c.id) ?? []));
76
+ if (sharedNeighbors.length > 0) {
77
+ const neighborName = entityName.get(sharedNeighbors[0]) ?? sharedNeighbors[0]; // 跨类型邻居名(全量映射)
78
+ scored.push({
79
+ targetId: c.id,
80
+ score: sharedNeighbors.length,
81
+ relationType: "ally",
82
+ reason: `与「${c.name}」有 ${sharedNeighbors.length} 个共同关联实体(如「${neighborName}」),可经中间人建立关系`,
83
+ });
84
+ }
85
+ }
86
+ // 4. 信号强度降序 → top 10(并列按 target_id 稳定排序)
87
+ scored.sort((a, b) => (b.score === a.score ? a.targetId.localeCompare(b.targetId) : b.score - a.score));
88
+ return {
89
+ suggestions: scored.slice(0, SUGGESTION_LIMIT).map((s) => ({ target_id: s.targetId, relation_type: s.relationType, reason: s.reason })),
90
+ };
91
+ }
92
+ //# sourceMappingURL=suggest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggest.js","sourceRoot":"","sources":["../../src/analysis/suggest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import type { OutlineFileTree, RelationRecord } from "@whispering233/ai-editor-shared";
2
+ import type { ToolContext } from "../context.js";
3
+ /**
4
+ * 工具取消专用错误(决策 16 ③):signal 中止时抛出。
5
+ * name = "AbortError"(与 DOMException 同名约定)——S7.4 executor 据此区分
6
+ * 「SSE 断开取消」(**不喂回 LLM、不计失败轮**)与「工具执行失败」(结构化喂回 LLM 自纠)。
7
+ */
8
+ export declare class AbortedError extends Error {
9
+ constructor(message?: string);
10
+ }
11
+ /**
12
+ * 中止检查(决策 16 ③「长工具执行中检查 signal」):
13
+ * 分析工具是长任务候选(全量关系/Delta 遍历),在循环中周期性调用——
14
+ * signal 已中止即抛 AbortedError(executor 捕获后按取消处理,不产生部分结果)。
15
+ */
16
+ export declare function throwIfAborted(signal?: AbortSignal): void;
17
+ /** 实体端点判定(relation 端点类型:character/setting/location/hook 为实体,outline_node 为大纲节点) */
18
+ export declare function isEntityType(type: string): boolean;
19
+ /**
20
+ * 无向实体邻接图:从可见关系构建「实体 id → 邻居实体 id 集合」。
21
+ * 只保留两端均为实体的边(appears_in 等指向大纲节点的关系不参与实体连通性);
22
+ * 自环(source === target)忽略。供 detect_conflicts(对称缺失检测)与
23
+ * suggest_connections(共同邻居信号)复用。
24
+ */
25
+ export declare function buildEntityGraph(relations: readonly RelationRecord[]): Map<string, Set<string>>;
26
+ /** 两集合交集元素(保持第一个集合的迭代序;供共享场景/共同邻居信号使用) */
27
+ export declare function intersectSets<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): T[];
28
+ /**
29
+ * 章节序索引:一次读树 + 推导章节序,供工具内多次「节点 → 章序号」查询。
30
+ * 口径(决策 21):全局**章**序号(跨卷连续累计),root → 卷 → 章先序遍历,
31
+ * scene 归入所属 chapter 不单独编号;节点 move 后下次构建自动更新(不落库、查询时现推)。
32
+ * **软删可见性(决策 12)**:软删节点视为不可见——chapterOf 返回 null(供伏笔指标/
33
+ * 活跃度等判定;级联软删保证其子树一并软删,无需逐祖先检查)。
34
+ */
35
+ export interface ChapterIndex {
36
+ /** 节点 id → 全局章序号(scene 归入所属章、chapter 取自身;volume/root/不存在/**软删** → null) */
37
+ chapterOf(nodeId: string): number | null;
38
+ /**
39
+ * 当前章节(决策 21/hooks.md「当前章节」= project.json 的 current_position 所属章——
40
+ * 写作进度而非规划终点,与伏笔/孤儿工具口径一致):
41
+ * 1. current_position 已设置且可推导章号 → 该章序号
42
+ * 2. 未设置/节点不存在/无章号 → 退化树末章(合理默认)
43
+ */
44
+ currentChapter: number | null;
45
+ }
46
+ /**
47
+ * 构建章节序索引(决策 21)。节点所属章查找:chapter 取自身序号;scene 沿父链
48
+ * 向上找最近 chapter(严格三层下即其父)。读一次 outline.json 支撑全量查询,
49
+ * 避免 N 次文件读取(getChapterNumber 逐次读文件)。
50
+ */
51
+ export declare function buildChapterIndex(ctx: ToolContext): ChapterIndex;
52
+ /** 大纲节点名称(title;节点不存在 → id 占位——脏引用防御) */
53
+ export declare function outlineNodeName(tree: OutlineFileTree, nodeId: string): string;
54
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/analysis/utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAmB,eAAe,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACxG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,SAAqB;CAIzC;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAIzD;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAiB/F;AAED,0CAA0C;AAC1C,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAM1E;AAID;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACzC;;;;;OAKG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,YAAY,CA6ChE;AAED,yCAAyC;AACzC,wBAAgB,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAG7E"}
@@ -0,0 +1,123 @@
1
+ // 分析类工具共享辅助(S6.4/S6.5):中止检查 + 实体关系图 + 章节序索引
2
+ // 纯函数模块,单一职责:被 analysis/ 各工具与测试复用,不持有状态。
3
+ import { deriveChapterOrder, findOutlineNode, readOutlineFile, readProjectFile } from "@whispering233/ai-editor-db";
4
+ /**
5
+ * 工具取消专用错误(决策 16 ③):signal 中止时抛出。
6
+ * name = "AbortError"(与 DOMException 同名约定)——S7.4 executor 据此区分
7
+ * 「SSE 断开取消」(**不喂回 LLM、不计失败轮**)与「工具执行失败」(结构化喂回 LLM 自纠)。
8
+ */
9
+ export class AbortedError extends Error {
10
+ constructor(message = "工具执行已中止(aborted)") {
11
+ super(message);
12
+ this.name = "AbortError";
13
+ }
14
+ }
15
+ /**
16
+ * 中止检查(决策 16 ③「长工具执行中检查 signal」):
17
+ * 分析工具是长任务候选(全量关系/Delta 遍历),在循环中周期性调用——
18
+ * signal 已中止即抛 AbortedError(executor 捕获后按取消处理,不产生部分结果)。
19
+ */
20
+ export function throwIfAborted(signal) {
21
+ if (signal?.aborted === true) {
22
+ throw new AbortedError();
23
+ }
24
+ }
25
+ /** 实体端点判定(relation 端点类型:character/setting/location/hook 为实体,outline_node 为大纲节点) */
26
+ export function isEntityType(type) {
27
+ return type === "character" || type === "setting" || type === "location" || type === "hook";
28
+ }
29
+ /**
30
+ * 无向实体邻接图:从可见关系构建「实体 id → 邻居实体 id 集合」。
31
+ * 只保留两端均为实体的边(appears_in 等指向大纲节点的关系不参与实体连通性);
32
+ * 自环(source === target)忽略。供 detect_conflicts(对称缺失检测)与
33
+ * suggest_connections(共同邻居信号)复用。
34
+ */
35
+ export function buildEntityGraph(relations) {
36
+ const graph = new Map();
37
+ const addEdge = (a, b) => {
38
+ if (a === b)
39
+ return;
40
+ let neighbors = graph.get(a);
41
+ if (neighbors === undefined) {
42
+ neighbors = new Set();
43
+ graph.set(a, neighbors);
44
+ }
45
+ neighbors.add(b);
46
+ };
47
+ for (const r of relations) {
48
+ if (!isEntityType(r.sourceType) || !isEntityType(r.targetType))
49
+ continue;
50
+ addEdge(r.sourceId, r.targetId);
51
+ addEdge(r.targetId, r.sourceId); // 无向:双向登记
52
+ }
53
+ return graph;
54
+ }
55
+ /** 两集合交集元素(保持第一个集合的迭代序;供共享场景/共同邻居信号使用) */
56
+ export function intersectSets(a, b) {
57
+ const out = [];
58
+ for (const v of a) {
59
+ if (b.has(v))
60
+ out.push(v);
61
+ }
62
+ return out;
63
+ }
64
+ /**
65
+ * 构建章节序索引(决策 21)。节点所属章查找:chapter 取自身序号;scene 沿父链
66
+ * 向上找最近 chapter(严格三层下即其父)。读一次 outline.json 支撑全量查询,
67
+ * 避免 N 次文件读取(getChapterNumber 逐次读文件)。
68
+ */
69
+ export function buildChapterIndex(ctx) {
70
+ const tree = readOutlineFile(ctx.outlineDir);
71
+ const order = deriveChapterOrder(ctx.outlineDir);
72
+ const chapterNumbers = new Map(order.map((c) => [c.chapterId, c.chapterNumber]));
73
+ // 软删节点集合(决策 12:不可见 → 无章号)
74
+ const deletedNodeIds = new Set();
75
+ // 节点 id → 父节点 id(scene → chapter 归属链)
76
+ const parentOf = new Map();
77
+ const visit = (nodes, parentId) => {
78
+ for (const node of nodes) {
79
+ parentOf.set(node.id, parentId);
80
+ if (node.deleted === true)
81
+ deletedNodeIds.add(node.id);
82
+ const children = node.children;
83
+ if (children !== undefined)
84
+ visit(children, node.id);
85
+ }
86
+ };
87
+ visit(tree.children, "root");
88
+ const chapterOf = (nodeId) => {
89
+ if (deletedNodeIds.has(nodeId))
90
+ return null; // 软删节点不可见(决策 12)
91
+ // 自身是章
92
+ const self = chapterNumbers.get(nodeId);
93
+ if (self !== undefined)
94
+ return self;
95
+ // 沿父链向上找章(scene → chapter;volume/root 无章号)
96
+ let cur = nodeId;
97
+ for (let depth = 0; depth < 4 && cur !== undefined; depth++) {
98
+ cur = parentOf.get(cur);
99
+ if (cur === undefined)
100
+ return null;
101
+ const n = chapterNumbers.get(cur);
102
+ if (n !== undefined)
103
+ return n;
104
+ }
105
+ return null;
106
+ };
107
+ // 当前章节:current_position 优先(决策 21),退化树末章
108
+ let currentChapter = null;
109
+ const config = readProjectFile(ctx.outlineDir);
110
+ if (config !== null && config.current_position !== null && config.current_position !== "") {
111
+ currentChapter = chapterOf(config.current_position);
112
+ }
113
+ if (currentChapter === null) {
114
+ currentChapter = order.length > 0 ? order[order.length - 1].chapterNumber : null;
115
+ }
116
+ return { chapterOf, currentChapter };
117
+ }
118
+ /** 大纲节点名称(title;节点不存在 → id 占位——脏引用防御) */
119
+ export function outlineNodeName(tree, nodeId) {
120
+ const node = findOutlineNode(tree, nodeId);
121
+ return node === undefined ? nodeId : node.title;
122
+ }
123
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/analysis/utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { Db } from "@whispering233/ai-editor-db";
2
+ export interface ToolContext {
3
+ /** 当前项目的 data.db 连接(@whispering233/ai-editor-db openDatabase 返回值) */
4
+ db: Db;
5
+ /** 项目根目录(outline.json 所在目录,决策 8;关系/Delta 端点软删校验读树用) */
6
+ outlineDir: string;
7
+ /** 当前项目 id(proj- 前缀;提案归属绑定,S6.6 使用) */
8
+ projectId: string;
9
+ }
10
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,EAAE,EAAE,EAAE,CAAC;IACP,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,11 @@
1
+ // 工具执行上下文(S6.3 ToolContext 设计)
2
+ // 由上层(agent/server,S7)注入:db 访问 + outline.json 读取 + projectId。
3
+ // 工具实现只依赖本接口工作,不持有任何全局状态——同一实例可服务多次工具调用;
4
+ // projectId 本卡查询工具暂未使用(S6.6 提案工具需要提案归属项目),先定义供后续扩展。
5
+ //
6
+ // 注入方职责(S7.4 executor 组装):
7
+ // - db:当前项目的 data.db 连接(better-sqlite3 同步连接,查询层 API 直接使用)
8
+ // - outlineDir:项目根目录(outline.json 所在目录,决策 8)
9
+ // - projectId:当前打开项目的 id(决策 22 会话归属项目;提案绑定 project_id)
10
+ export {};
11
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,8DAA8D;AAC9D,yCAAyC;AACzC,oDAAoD;AACpD,EAAE;AACF,2BAA2B;AAC3B,0DAA0D;AAC1D,6CAA6C;AAC7C,uDAAuD"}
@@ -0,0 +1,4 @@
1
+ import { type ExecutorFn } from "./types.js";
2
+ /** add_delta(tools.md:add_delta(node_id, target, changes) → id) */
3
+ export declare const executeAddDelta: ExecutorFn;
4
+ //# sourceMappingURL=delta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delta.d.ts","sourceRoot":"","sources":["../../src/executor/delta.ts"],"names":[],"mappings":"AAUA,OAAO,EAA+B,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAE1E,mEAAmE;AACnE,eAAO,MAAM,eAAe,EAAE,UAU7B,CAAC"}
@@ -0,0 +1,22 @@
1
+ // 执行类工具:Delta(S6.7,tools.md「执行类」1 个)
2
+ // add_delta
3
+ //
4
+ // - node_id / target_type / target_id / changes 已由 S6.6 提案层派生规范化到 args
5
+ // - description(delta_records NOT NULL):取 **proposal.summary** 作人类可读描述
6
+ // (S6.6 delta.ts 注释:由执行器取 summary——args 不带 description,提案摘要即变更描述)
7
+ // - order 由 db 层全局单调生成(insertDelta 事务内 MAX+1),此处无 order 入参
8
+ import { insertDelta } from "@whispering233/ai-editor-db";
9
+ import { requireArray, requireString } from "./types.js";
10
+ /** add_delta(tools.md:add_delta(node_id, target, changes) → id) */
11
+ export const executeAddDelta = (ctx, proposal) => {
12
+ const args = proposal.args;
13
+ const row = insertDelta(ctx.db, {
14
+ nodeId: requireString(args, "node_id"),
15
+ targetType: requireString(args, "target_type"),
16
+ targetId: requireString(args, "target_id"),
17
+ changes: requireArray(args, "changes"), // 提案层 schema 已校验 deltaChangeSchema
18
+ description: proposal.summary,
19
+ });
20
+ return { id: row.id };
21
+ };
22
+ //# sourceMappingURL=delta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delta.js","sourceRoot":"","sources":["../../src/executor/delta.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,YAAY;AACZ,EAAE;AACF,uEAAuE;AACvE,uEAAuE;AACvE,oEAAoE;AACpE,2DAA2D;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAmB,MAAM,YAAY,CAAC;AAE1E,mEAAmE;AACnE,MAAM,CAAC,MAAM,eAAe,GAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;IAC3D,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC3B,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE;QAC9B,MAAM,EAAE,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC;QACtC,UAAU,EAAE,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC;QAC9C,QAAQ,EAAE,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC;QAC1C,OAAO,EAAE,YAAY,CAAC,IAAI,EAAE,SAAS,CAAkB,EAAE,mCAAmC;QAC5F,WAAW,EAAE,QAAQ,CAAC,OAAO;KAC9B,CAAC,CAAC;IACH,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC;AACxB,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type ExecutorFn } from "./types.js";
2
+ /** create_entity(tools.md:create_entity(type, name, data) → id) */
3
+ export declare const executeCreateEntity: ExecutorFn;
4
+ /** update_entity(tools.md:update_entity(id, patches) → updated;patches 浅合并进 data) */
5
+ export declare const executeUpdateEntity: ExecutorFn;
6
+ /** delete_entity(tools.md:delete_entity(id) → void;软删 + 级联,可回收站还原,决策 12) */
7
+ export declare const executeDeleteEntity: ExecutorFn;
8
+ //# sourceMappingURL=entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/executor/entity.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAgD,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAE3F,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,EAAE,UAQjC,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,EAAE,UASjC,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,EAAE,UAOjC,CAAC"}
@@ -0,0 +1,47 @@
1
+ // 执行类工具:实体(S6.7,tools.md「执行类」3 个)
2
+ // create_entity / update_entity / delete_entity
3
+ //
4
+ // 与 S6.6 提案工具的核心差异:**直接调 db 写层落库**(唯一写路径,S7.5 确认后由 executor
5
+ // 门面调用);抛错即失败(事务/单语句失败向上传播,S7.5 转错误响应)。不注册 registry
6
+ // (registry 是 LLM 可见工具表;执行类不暴露给 LLM,tools.md「核心设计原则」)。
7
+ //
8
+ // 语义对齐(决策 12/14):
9
+ // - create_entity:type/name 必填 + data 可选,id 由 db 层生成(char-/set-/loc-/hook- 前缀)
10
+ // - update_entity:patches 为 data **浅合并**字段(未传字段保留,endpoints.md 第 267 行);
11
+ // 软删实体不可更新(getEntity 过滤 → null → 抛错)
12
+ // - delete_entity:软删 + 级联关系与 Delta(决策 12,本体保留可回收站还原)
13
+ // 参数形态:与 S6.6 proposal.args 对齐(propose_create_entity → { type, name, data? } 等),
14
+ // 由 executeProposal 按 proposal.type 映射后直接消费。
15
+ import { createEntity, nowIso, softDeleteEntity, updateEntity } from "@whispering233/ai-editor-db";
16
+ import { optionalRecord, requireRecord, requireString } from "./types.js";
17
+ /** create_entity(tools.md:create_entity(type, name, data) → id) */
18
+ export const executeCreateEntity = (ctx, proposal) => {
19
+ const args = proposal.args;
20
+ const row = createEntity(ctx.db, {
21
+ type: requireString(args, "type"), // 提案层 zod 校验(ENTITY_TYPES),此处防御
22
+ name: requireString(args, "name"),
23
+ data: optionalRecord(args, "data"),
24
+ });
25
+ return { id: row.id };
26
+ };
27
+ /** update_entity(tools.md:update_entity(id, patches) → updated;patches 浅合并进 data) */
28
+ export const executeUpdateEntity = (ctx, proposal) => {
29
+ const args = proposal.args;
30
+ const entityId = requireString(args, "entity_id");
31
+ const patches = requireRecord(args, "patches");
32
+ const row = updateEntity(ctx.db, entityId, { data: patches }); // data 浅合并(决策 23 同语义)
33
+ if (row === null) {
34
+ throw new Error(`实体不存在或已软删: ${entityId}`);
35
+ }
36
+ return { id: row.id, updated: true };
37
+ };
38
+ /** delete_entity(tools.md:delete_entity(id) → void;软删 + 级联,可回收站还原,决策 12) */
39
+ export const executeDeleteEntity = (ctx, proposal) => {
40
+ const entityId = requireString(proposal.args, "entity_id");
41
+ const result = softDeleteEntity(ctx.db, entityId, nowIso());
42
+ if (result === null) {
43
+ throw new Error(`实体不存在或已软删: ${entityId}`);
44
+ }
45
+ return { id: entityId, deleted: true, cascaded: result };
46
+ };
47
+ //# sourceMappingURL=entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity.js","sourceRoot":"","sources":["../../src/executor/entity.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,gDAAgD;AAChD,EAAE;AACF,4DAA4D;AAC5D,oDAAoD;AACpD,uDAAuD;AACvD,EAAE;AACF,kBAAkB;AAClB,+EAA+E;AAC/E,yEAAyE;AACzE,uCAAuC;AACvC,qDAAqD;AACrD,iFAAiF;AACjF,6CAA6C;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEnG,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAmB,MAAM,YAAY,CAAC;AAE3F,mEAAmE;AACnE,MAAM,CAAC,MAAM,mBAAmB,GAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;IAC/D,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC3B,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE;QAC/B,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,MAAM,CAAe,EAAE,gCAAgC;QACjF,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC;QACjC,IAAI,EAAE,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC;KACnC,CAAC,CAAC;IACH,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC;AACxB,CAAC,CAAC;AAEF,qFAAqF;AACrF,MAAM,CAAC,MAAM,mBAAmB,GAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;IAC/D,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC3B,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,sBAAsB;IACrF,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,cAAc,QAAQ,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACvC,CAAC,CAAC;AAEF,4EAA4E;AAC5E,MAAM,CAAC,MAAM,mBAAmB,GAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;IAC/D,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5D,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,cAAc,QAAQ,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AAC3D,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type ExecutorFn } from "./types.js";
2
+ /** advance_hook(tools.md:advance_hook(hook_id, node_id, description) → id) */
3
+ export declare const executeAdvanceHook: ExecutorFn;
4
+ /** resolve_hook(tools.md:resolve_hook(hook_id, node_id, description) → id) */
5
+ export declare const executeResolveHook: ExecutorFn;
6
+ /** abandon_hook(tools.md:abandon_hook(hook_id, description) → id;仅 delta 记 status=abandoned) */
7
+ export declare const executeAbandonHook: ExecutorFn;
8
+ //# sourceMappingURL=hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/executor/hook.ts"],"names":[],"mappings":"AAqCA,OAAO,EAAiB,KAAK,UAAU,EAAuB,MAAM,YAAY,CAAC;AAuJjF,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,EAAE,UACsC,CAAC;AAExE,8EAA8E;AAC9E,eAAO,MAAM,kBAAkB,EAAE,UACmC,CAAC;AAErE,gGAAgG;AAChG,eAAO,MAAM,kBAAkB,EAAE,UAC8B,CAAC"}