@josephyan/qingflow-app-builder-mcp 1.1.25 → 1.1.27

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 (100) hide show
  1. package/README.md +6 -6
  2. package/docs/local-agent-install.md +6 -57
  3. package/entry_point.py +1 -1
  4. package/npm/bin/qingflow-app-builder-mcp.mjs +33 -2
  5. package/npm/lib/runtime.mjs +101 -21
  6. package/npm/scripts/postinstall.mjs +10 -1
  7. package/package.json +2 -3
  8. package/pyproject.toml +1 -1
  9. package/skills/qingflow-app-builder/SKILL.md +51 -164
  10. package/skills/qingflow-app-builder/references/build-complete-system.md +234 -0
  11. package/skills/qingflow-app-builder/references/build-single-app.md +138 -0
  12. package/skills/qingflow-app-builder/references/create-app.md +24 -31
  13. package/skills/qingflow-app-builder/references/environments.md +1 -1
  14. package/skills/qingflow-app-builder/references/gotchas.md +13 -39
  15. package/skills/qingflow-app-builder/references/match-rules.md +18 -29
  16. package/skills/qingflow-app-builder/references/public-surface-sync.md +2 -2
  17. package/skills/qingflow-app-builder/references/solution-playbooks.md +0 -10
  18. package/skills/qingflow-app-builder/references/tool-selection.md +25 -42
  19. package/skills/qingflow-app-builder/references/update-flow.md +38 -22
  20. package/skills/qingflow-app-builder/references/update-schema.md +0 -14
  21. package/skills/qingflow-app-builder/references/update-views.md +14 -83
  22. package/skills/qingflow-app-builder-code-integrations/SKILL.md +3 -5
  23. package/skills/qingflow-app-builder-code-integrations/references/code-block.md +1 -1
  24. package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +1 -1
  25. package/skills/qingflow-mcp-setup/SKILL.md +111 -0
  26. package/skills/qingflow-mcp-setup/agents/openai.yaml +4 -0
  27. package/skills/qingflow-mcp-setup/references/claude-desktop.md +34 -0
  28. package/skills/qingflow-mcp-setup/references/environments.md +61 -0
  29. package/skills/qingflow-mcp-setup/references/generic-stdio.md +31 -0
  30. package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
  31. package/skills/qingflow-workflow-builder/SKILL.md +96 -0
  32. package/skills/qingflow-workflow-builder/manifest.yaml +8 -0
  33. package/skills/qingflow-workflow-builder/references/01-overview.md +45 -0
  34. package/skills/qingflow-workflow-builder/references/02-update-mode.md +53 -0
  35. package/skills/qingflow-workflow-builder/references/03-flow-patterns.md +57 -0
  36. package/skills/qingflow-workflow-builder/references/04-stage1-business-modeling.md +131 -0
  37. package/skills/qingflow-workflow-builder/references/05-stage2-members-roles.md +29 -0
  38. package/skills/qingflow-workflow-builder/references/06-stage3-build-spec.md +165 -0
  39. package/skills/qingflow-workflow-builder/references/07-stage4-validate-spec.md +33 -0
  40. package/skills/qingflow-workflow-builder/references/08-stage5-apply-verify.md +51 -0
  41. package/skills/qingflow-workflow-builder/references/09-stage6-summary.md +88 -0
  42. package/skills/qingflow-workflow-builder/references/10-node-config-reference.md +93 -0
  43. package/skills/qingflow-workflow-builder/references/11-troubleshooting.md +15 -0
  44. package/skills/qingflow-workflow-builder/scripts/diff_flow_spec.py +275 -0
  45. package/skills/qingflow-workflow-builder/scripts/validate_flow_spec.py +605 -0
  46. package/src/qingflow_mcp/__init__.py +1 -1
  47. package/src/qingflow_mcp/__main__.py +2 -6
  48. package/src/qingflow_mcp/builder_facade/models.py +49 -330
  49. package/src/qingflow_mcp/builder_facade/service.py +1621 -6149
  50. package/src/qingflow_mcp/cli/commands/builder.py +366 -322
  51. package/src/qingflow_mcp/cli/commands/chart.py +1 -1
  52. package/src/qingflow_mcp/cli/commands/common.py +3 -12
  53. package/src/qingflow_mcp/cli/commands/exports.py +2 -2
  54. package/src/qingflow_mcp/cli/commands/imports.py +3 -3
  55. package/src/qingflow_mcp/cli/commands/portal.py +2 -2
  56. package/src/qingflow_mcp/cli/commands/record.py +27 -101
  57. package/src/qingflow_mcp/cli/commands/task.py +47 -28
  58. package/src/qingflow_mcp/cli/commands/view.py +1 -1
  59. package/src/qingflow_mcp/cli/context.py +3 -0
  60. package/src/qingflow_mcp/cli/formatters.py +16 -784
  61. package/src/qingflow_mcp/cli/main.py +41 -120
  62. package/src/qingflow_mcp/errors.py +2 -43
  63. package/src/qingflow_mcp/public_surface.py +17 -26
  64. package/src/qingflow_mcp/response_trim.py +17 -81
  65. package/src/qingflow_mcp/server.py +12 -14
  66. package/src/qingflow_mcp/server_app_builder.py +39 -81
  67. package/src/qingflow_mcp/server_app_user.py +16 -22
  68. package/src/qingflow_mcp/session_store.py +7 -11
  69. package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
  70. package/src/qingflow_mcp/solution/executor.py +18 -245
  71. package/src/qingflow_mcp/tools/ai_builder_tools.py +1483 -3139
  72. package/src/qingflow_mcp/tools/app_tools.py +43 -184
  73. package/src/qingflow_mcp/tools/approval_tools.py +35 -197
  74. package/src/qingflow_mcp/tools/auth_tools.py +16 -92
  75. package/src/qingflow_mcp/tools/code_block_tools.py +40 -298
  76. package/src/qingflow_mcp/tools/custom_button_tools.py +10 -64
  77. package/src/qingflow_mcp/tools/directory_tools.py +72 -236
  78. package/src/qingflow_mcp/tools/export_tools.py +34 -244
  79. package/src/qingflow_mcp/tools/feedback_tools.py +0 -9
  80. package/src/qingflow_mcp/tools/file_tools.py +3 -9
  81. package/src/qingflow_mcp/tools/import_tools.py +49 -336
  82. package/src/qingflow_mcp/tools/navigation_tools.py +12 -91
  83. package/src/qingflow_mcp/tools/package_tools.py +6 -118
  84. package/src/qingflow_mcp/tools/portal_tools.py +3 -39
  85. package/src/qingflow_mcp/tools/qingbi_report_tools.py +7 -116
  86. package/src/qingflow_mcp/tools/record_tools.py +360 -1145
  87. package/src/qingflow_mcp/tools/resource_read_tools.py +39 -188
  88. package/src/qingflow_mcp/tools/role_tools.py +9 -80
  89. package/src/qingflow_mcp/tools/solution_tools.py +45 -59
  90. package/src/qingflow_mcp/tools/task_context_tools.py +158 -662
  91. package/src/qingflow_mcp/tools/task_tools.py +29 -113
  92. package/src/qingflow_mcp/tools/view_tools.py +3 -106
  93. package/src/qingflow_mcp/tools/workflow_tools.py +4 -48
  94. package/src/qingflow_mcp/tools/workspace_tools.py +3 -71
  95. package/src/qingflow_mcp/version.py +2 -0
  96. package/npm/bin/qingflow-skills.mjs +0 -5
  97. package/skills/qingflow-app-builder/references/complete-system-development-guide.md +0 -172
  98. package/skills/qingflow-app-builder/references/single-app-development-guide.md +0 -87
  99. package/skills/qingflow-app-builder/scripts/validate_system_build_summary.py +0 -124
  100. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 对比两个工作流 spec 的差异,输出新增/删除/修改的节点和边。
4
+ 用于更新模式下辅助 agent 判断是否遵循最小修改原则。
5
+
6
+ 用法:
7
+ python3 diff_flow_spec.py <old_spec.json> <new_spec.json>
8
+ python3 diff_flow_spec.py <old_spec.json> <new_spec.json> --json # JSON 输出
9
+ """
10
+
11
+ import json
12
+ import sys
13
+
14
+
15
+ def load_json(path):
16
+ with open(path, 'r', encoding='utf-8') as f:
17
+ return json.load(f)
18
+
19
+
20
+ def extract_edges(spec):
21
+ """从 spec 中提取边列表,兼容 edges 在不同层级的情况"""
22
+ e = spec.get('edges', [])
23
+ if isinstance(e, dict) and 'edges' in e:
24
+ return e['edges']
25
+ if isinstance(e, list):
26
+ return e
27
+ return []
28
+
29
+
30
+ def node_key(n):
31
+ """节点的唯一标识"""
32
+ return n.get('id', '')
33
+
34
+
35
+ def edge_key(e):
36
+ """边的唯一标识:from→to"""
37
+ return (e.get('from', ''), e.get('to', ''))
38
+
39
+
40
+ def node_identity(n):
41
+ """节点的完整内容(不含 id,用于判断是否修改)"""
42
+ return {
43
+ 'type': n.get('type', ''),
44
+ 'name': n.get('name', ''),
45
+ 'attrs': n.get('attrs', {}),
46
+ }
47
+
48
+
49
+ def edge_identity(e):
50
+ """边的完整内容(不含 from/to,用于判断是否修改)"""
51
+ return {
52
+ 'label': e.get('label', ''),
53
+ 'condition': e.get('condition', {}),
54
+ }
55
+
56
+
57
+ def diff_nodes(old_nodes, new_nodes):
58
+ """对比节点差异"""
59
+ old_by_id = {node_key(n): n for n in old_nodes}
60
+ new_by_id = {node_key(n): n for n in new_nodes}
61
+
62
+ old_ids = set(old_by_id.keys())
63
+ new_ids = set(new_by_id.keys())
64
+
65
+ deleted_ids = sorted(old_ids - new_ids)
66
+ added_ids = sorted(new_ids - old_ids)
67
+ common_ids = sorted(old_ids & new_ids)
68
+
69
+ modified = []
70
+ id_changes = []
71
+ for nid in common_ids:
72
+ old_ident = node_identity(old_by_id[nid])
73
+ new_ident = node_identity(new_by_id[nid])
74
+ if old_ident != new_ident:
75
+ modified.append({
76
+ 'id': nid,
77
+ 'old': {'type': old_ident['type'], 'name': old_ident['name']},
78
+ 'new': {'type': new_ident['type'], 'name': new_ident['name']},
79
+ 'attrs_changed': old_ident['attrs'] != new_ident['attrs'],
80
+ 'type_changed': old_ident['type'] != new_ident['type'],
81
+ 'name_changed': old_ident['name'] != new_ident['name'],
82
+ })
83
+
84
+ # 检测可能的不必要 ID 变更(内容相同但 ID 不同)
85
+ old_by_identity = {}
86
+ for n in old_nodes:
87
+ ident = json.dumps(node_identity(n), sort_keys=True, ensure_ascii=False)
88
+ old_by_identity.setdefault(ident, []).append(n)
89
+
90
+ for n in new_nodes:
91
+ if node_key(n) in added_ids:
92
+ ident = json.dumps(node_identity(n), sort_keys=True, ensure_ascii=False)
93
+ if ident in old_by_identity and old_by_identity[ident]:
94
+ old_match = old_by_identity[ident][0]
95
+ if node_key(old_match) in deleted_ids:
96
+ id_changes.append({
97
+ 'old_id': node_key(old_match),
98
+ 'new_id': node_key(n),
99
+ 'identity': node_identity(n),
100
+ 'warning': '节点内容未变但 ID 已变更,可能导致后端不支持配置丢失',
101
+ })
102
+
103
+ return {
104
+ 'deleted': [{'id': nid, 'name': old_by_id[nid].get('name', ''), 'type': old_by_id[nid].get('type', '')} for nid in deleted_ids],
105
+ 'added': [{'id': nid, 'name': new_by_id[nid].get('name', ''), 'type': new_by_id[nid].get('type', '')} for nid in added_ids],
106
+ 'modified': modified,
107
+ 'id_changes': id_changes,
108
+ 'unchanged': len(common_ids) - len(modified),
109
+ }
110
+
111
+
112
+ def diff_edges(old_edges, new_edges):
113
+ """对比边差异"""
114
+ old_by_key = {edge_key(e): e for e in old_edges}
115
+ new_by_key = {edge_key(e): e for e in new_edges}
116
+
117
+ old_keys = set(old_by_key.keys())
118
+ new_keys = set(new_by_key.keys())
119
+
120
+ deleted_keys = sorted(old_keys - new_keys)
121
+ added_keys = sorted(new_keys - old_keys)
122
+ common_keys = sorted(old_keys & new_keys)
123
+
124
+ modified = []
125
+ for key in common_keys:
126
+ old_ident = edge_identity(old_by_key[key])
127
+ new_ident = edge_identity(new_by_key[key])
128
+ if old_ident != new_ident:
129
+ modified.append({
130
+ 'from': key[0],
131
+ 'to': key[1],
132
+ 'old_condition': old_ident['condition'],
133
+ 'new_condition': new_ident['condition'],
134
+ 'label_changed': old_ident['label'] != new_ident['label'],
135
+ 'condition_changed': old_ident['condition'] != new_ident['condition'],
136
+ })
137
+
138
+ return {
139
+ 'deleted': [{'from': k[0], 'to': k[1]} for k in deleted_keys],
140
+ 'added': [{'from': k[0], 'to': k[1]} for k in added_keys],
141
+ 'modified': modified,
142
+ 'unchanged': len(common_keys) - len(modified),
143
+ }
144
+
145
+
146
+ def print_human(result):
147
+ """人类可读输出"""
148
+ nodes = result['nodes']
149
+ edges = result['edges']
150
+
151
+ print("=" * 60)
152
+ print("工作流 Spec 差异分析")
153
+ print("=" * 60)
154
+
155
+ # 节点汇总
156
+ print(f"\n📊 节点汇总:")
157
+ print(f" 删除: {len(nodes['deleted'])} | 新增: {len(nodes['added'])} | "
158
+ f"修改: {len(nodes['modified'])} | 未变: {nodes['unchanged']}")
159
+
160
+ if nodes['id_changes']:
161
+ print(f"\n⚠️ 检测到 {len(nodes['id_changes'])} 个可能的 ID 变更(内容相同但 ID 不同):")
162
+ for ic in nodes['id_changes']:
163
+ print(f" {ic['old_id']} → {ic['new_id']} ({ic['identity']['name']})")
164
+ print(f" ⚠ {ic['warning']}")
165
+
166
+ if nodes['deleted']:
167
+ print(f"\n🗑 删除的节点 ({len(nodes['deleted'])}):")
168
+ for d in nodes['deleted']:
169
+ print(f" - [{d['type']}] {d['id']} ({d['name']})")
170
+
171
+ if nodes['added']:
172
+ print(f"\n➕ 新增的节点 ({len(nodes['added'])}):")
173
+ for a in nodes['added']:
174
+ print(f" - [{a['type']}] {a['id']} ({a['name']})")
175
+
176
+ if nodes['modified']:
177
+ print(f"\n✏️ 修改的节点 ({len(nodes['modified'])}):")
178
+ for m in nodes['modified']:
179
+ changes = []
180
+ if m['type_changed']:
181
+ changes.append(f"type: {m['old']['type']} → {m['new']['type']}")
182
+ if m['name_changed']:
183
+ changes.append(f"name: {m['old']['name']} → {m['new']['name']}")
184
+ if m['attrs_changed']:
185
+ changes.append("attrs 已变更")
186
+ print(f" - {m['id']}: {', '.join(changes) if changes else '无实质变更'}")
187
+
188
+ # 边汇总
189
+ print(f"\n📊 边汇总:")
190
+ print(f" 删除: {len(edges['deleted'])} | 新增: {len(edges['added'])} | "
191
+ f"修改: {len(edges['modified'])} | 未变: {edges['unchanged']}")
192
+
193
+ if edges['deleted']:
194
+ print(f"\n🗑 删除的边 ({len(edges['deleted'])}):")
195
+ for d in edges['deleted']:
196
+ print(f" - {d['from']} → {d['to']}")
197
+
198
+ if edges['added']:
199
+ print(f"\n➕ 新增的边 ({len(edges['added'])}):")
200
+ for a in edges['added']:
201
+ print(f" - {a['from']} → {a['to']}")
202
+
203
+ if edges['modified']:
204
+ print(f"\n✏️ 修改的边 ({len(edges['modified'])}):")
205
+ for m in edges['modified']:
206
+ changes = []
207
+ if m['label_changed']:
208
+ changes.append("label 已变更")
209
+ if m['condition_changed']:
210
+ changes.append("condition 已变更")
211
+ if changes:
212
+ print(f" - {m['from']} → {m['to']}: {', '.join(changes)}")
213
+ else:
214
+ print(f" - {m['from']} → {m['to']}: 无实质变更")
215
+
216
+ # 最小修改原则评估
217
+ print(f"\n📋 最小修改原则评估:")
218
+ issues = 0
219
+ if nodes['id_changes']:
220
+ print(f" ❌ 存在 ID 变更({len(nodes['id_changes'])} 个节点),可能导致后端不支持配置丢失")
221
+ issues += 1
222
+ if nodes['deleted']:
223
+ print(f" ⚠️ 删除了 {len(nodes['deleted'])} 个节点,请确认是否为业务需要")
224
+ issues += 1
225
+ if not nodes['deleted'] and not nodes['id_changes'] and nodes['modified']:
226
+ print(f" ✅ 仅修改已有节点,未删除或变更 ID,符合最小修改原则")
227
+ if not nodes['deleted'] and not nodes['modified'] and not nodes['added'] and not nodes['id_changes']:
228
+ print(f" ✅ 无任何变更")
229
+
230
+ print(f"\n总结: 错误 {issues} 项")
231
+
232
+
233
+ def main():
234
+ if len(sys.argv) < 3:
235
+ print("用法: python3 diff_flow_spec.py <old_spec.json> <new_spec.json> [--json]")
236
+ sys.exit(1)
237
+
238
+ old_file = sys.argv[1]
239
+ new_file = sys.argv[2]
240
+ output_json = '--json' in sys.argv
241
+
242
+ try:
243
+ old_spec = load_json(old_file)
244
+ except Exception as e:
245
+ print(f"FATAL: 无法读取旧 spec 文件 {old_file}: {e}")
246
+ sys.exit(1)
247
+
248
+ try:
249
+ new_spec = load_json(new_file)
250
+ except Exception as e:
251
+ print(f"FATAL: 无法读取新 spec 文件 {new_file}: {e}")
252
+ sys.exit(1)
253
+
254
+ old_nodes = old_spec.get('nodes', [])
255
+ new_nodes = new_spec.get('nodes', [])
256
+ old_edges = extract_edges(old_spec)
257
+ new_edges = extract_edges(new_spec)
258
+
259
+ result = {
260
+ 'nodes': diff_nodes(old_nodes, new_nodes),
261
+ 'edges': diff_edges(old_edges, new_edges),
262
+ }
263
+
264
+ if output_json:
265
+ print(json.dumps(result, indent=2, ensure_ascii=False))
266
+ else:
267
+ print_human(result)
268
+
269
+ # 如果有 ID 变更,返回非零以便脚本判断
270
+ has_id_changes = len(result['nodes']['id_changes']) > 0
271
+ sys.exit(1 if has_id_changes else 0)
272
+
273
+
274
+ if __name__ == '__main__':
275
+ main()