@josephyan/qingflow-app-user-mcp 1.0.9 → 1.1.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 (88) hide show
  1. package/README.md +6 -2
  2. package/npm/bin/qingflow-app-user-mcp.mjs +31 -2
  3. package/npm/lib/runtime.mjs +43 -2
  4. package/package.json +1 -1
  5. package/pyproject.toml +2 -1
  6. package/skills/qingflow-app-builder/SKILL.md +276 -0
  7. package/skills/qingflow-app-builder/agents/openai.yaml +4 -0
  8. package/skills/qingflow-app-builder/references/build-complete-system.md +428 -0
  9. package/skills/qingflow-app-builder/references/build-single-app.md +530 -0
  10. package/skills/qingflow-app-builder/references/create-app.md +149 -0
  11. package/skills/qingflow-app-builder/references/environments.md +63 -0
  12. package/skills/qingflow-app-builder/references/flow-actors-and-permissions.md +123 -0
  13. package/skills/qingflow-app-builder/references/gotchas.md +108 -0
  14. package/skills/qingflow-app-builder/references/match-rules.md +114 -0
  15. package/skills/qingflow-app-builder/references/public-surface-sync.md +75 -0
  16. package/skills/qingflow-app-builder/references/solution-playbooks.md +52 -0
  17. package/skills/qingflow-app-builder/references/tool-selection.md +107 -0
  18. package/skills/qingflow-app-builder/references/update-flow.md +186 -0
  19. package/skills/qingflow-app-builder/references/update-layout.md +68 -0
  20. package/skills/qingflow-app-builder/references/update-schema.md +72 -0
  21. package/skills/qingflow-app-builder/references/update-views.md +291 -0
  22. package/skills/qingflow-app-builder-code-integrations/SKILL.md +137 -0
  23. package/skills/qingflow-app-builder-code-integrations/agents/openai.yaml +4 -0
  24. package/skills/qingflow-app-builder-code-integrations/references/code-block.md +66 -0
  25. package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +77 -0
  26. package/skills/qingflow-app-user/SKILL.md +2 -1
  27. package/skills/qingflow-app-user/references/data-gotchas.md +5 -3
  28. package/skills/qingflow-app-user/references/public-surface-sync.md +6 -3
  29. package/skills/qingflow-app-user/references/record-patterns.md +14 -4
  30. package/skills/qingflow-mcp-setup/SKILL.md +111 -0
  31. package/skills/qingflow-mcp-setup/agents/openai.yaml +4 -0
  32. package/skills/qingflow-mcp-setup/references/claude-desktop.md +34 -0
  33. package/skills/qingflow-mcp-setup/references/environments.md +61 -0
  34. package/skills/qingflow-mcp-setup/references/generic-stdio.md +31 -0
  35. package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
  36. package/skills/qingflow-record-analysis/SKILL.md +94 -186
  37. package/skills/qingflow-record-analysis/agents/openai.yaml +2 -2
  38. package/skills/qingflow-record-analysis/references/analysis-gotchas.md +54 -113
  39. package/skills/qingflow-record-analysis/references/analysis-patterns.md +95 -134
  40. package/skills/qingflow-record-analysis/references/business-context.md +74 -0
  41. package/skills/qingflow-record-analysis/references/confidence-reporting.md +47 -74
  42. package/skills/qingflow-record-analysis/references/data-access-playbook.md +106 -0
  43. package/skills/qingflow-record-analysis/references/pandas-recipes.md +172 -0
  44. package/skills/qingflow-record-analysis/references/report-format.md +76 -0
  45. package/skills/qingflow-record-insert/SKILL.md +28 -7
  46. package/skills/qingflow-record-update/SKILL.md +3 -1
  47. package/skills/qingflow-workflow-builder/SKILL.md +96 -0
  48. package/skills/qingflow-workflow-builder/manifest.yaml +8 -0
  49. package/skills/qingflow-workflow-builder/references/01-overview.md +45 -0
  50. package/skills/qingflow-workflow-builder/references/02-update-mode.md +53 -0
  51. package/skills/qingflow-workflow-builder/references/03-flow-patterns.md +57 -0
  52. package/skills/qingflow-workflow-builder/references/04-stage1-business-modeling.md +131 -0
  53. package/skills/qingflow-workflow-builder/references/05-stage2-members-roles.md +29 -0
  54. package/skills/qingflow-workflow-builder/references/06-stage3-build-spec.md +165 -0
  55. package/skills/qingflow-workflow-builder/references/07-stage4-validate-spec.md +33 -0
  56. package/skills/qingflow-workflow-builder/references/08-stage5-apply-verify.md +51 -0
  57. package/skills/qingflow-workflow-builder/references/09-stage6-summary.md +88 -0
  58. package/skills/qingflow-workflow-builder/references/10-node-config-reference.md +93 -0
  59. package/skills/qingflow-workflow-builder/references/11-troubleshooting.md +15 -0
  60. package/skills/qingflow-workflow-builder/scripts/diff_flow_spec.py +275 -0
  61. package/skills/qingflow-workflow-builder/scripts/validate_flow_spec.py +605 -0
  62. package/src/qingflow_mcp/__init__.py +1 -1
  63. package/src/qingflow_mcp/backend_client.py +55 -1
  64. package/src/qingflow_mcp/builder_facade/models.py +532 -48
  65. package/src/qingflow_mcp/builder_facade/service.py +9194 -2384
  66. package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
  67. package/src/qingflow_mcp/cli/commands/app.py +3 -16
  68. package/src/qingflow_mcp/cli/commands/builder.py +354 -56
  69. package/src/qingflow_mcp/cli/commands/record.py +112 -6
  70. package/src/qingflow_mcp/cli/formatters.py +102 -2
  71. package/src/qingflow_mcp/cli/main.py +204 -3
  72. package/src/qingflow_mcp/public_surface.py +12 -9
  73. package/src/qingflow_mcp/response_trim.py +287 -22
  74. package/src/qingflow_mcp/server.py +29 -20
  75. package/src/qingflow_mcp/server_app_builder.py +108 -30
  76. package/src/qingflow_mcp/server_app_user.py +28 -23
  77. package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
  78. package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
  79. package/src/qingflow_mcp/solution/executor.py +3 -133
  80. package/src/qingflow_mcp/tools/ai_builder_tools.py +2617 -440
  81. package/src/qingflow_mcp/tools/app_tools.py +53 -8
  82. package/src/qingflow_mcp/tools/package_tools.py +16 -2
  83. package/src/qingflow_mcp/tools/record_tools.py +14283 -9162
  84. package/src/qingflow_mcp/tools/resource_read_tools.py +3 -0
  85. package/src/qingflow_mcp/tools/solution_tools.py +30 -2
  86. package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
  87. package/skills/qingflow-record-analysis/references/dsl-templates.md +0 -93
  88. 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()