@qingflow-tech/qingflow-app-user-mcp 1.0.2 → 1.0.4

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 (56) hide show
  1. package/README.md +2 -2
  2. package/docs/local-agent-install.md +9 -3
  3. package/npm/lib/runtime.mjs +10 -3
  4. package/package.json +1 -1
  5. package/pyproject.toml +1 -1
  6. package/skills/qingflow-app-user/SKILL.md +21 -12
  7. package/skills/qingflow-app-user/references/data-gotchas.md +1 -1
  8. package/skills/qingflow-app-user/references/public-surface-sync.md +70 -0
  9. package/skills/qingflow-app-user/references/record-patterns.md +1 -1
  10. package/skills/qingflow-record-analysis/SKILL.md +44 -2
  11. package/skills/qingflow-record-insert/SKILL.md +3 -0
  12. package/skills/qingflow-record-update/SKILL.md +3 -0
  13. package/skills/qingflow-task-ops/SKILL.md +31 -10
  14. package/src/qingflow_mcp/__init__.py +33 -1
  15. package/src/qingflow_mcp/backend_client.py +109 -0
  16. package/src/qingflow_mcp/builder_facade/button_style_catalog.py +282 -0
  17. package/src/qingflow_mcp/builder_facade/models.py +58 -9
  18. package/src/qingflow_mcp/builder_facade/service.py +1711 -240
  19. package/src/qingflow_mcp/cli/commands/__init__.py +2 -1
  20. package/src/qingflow_mcp/cli/commands/app.py +47 -1
  21. package/src/qingflow_mcp/cli/commands/auth.py +63 -0
  22. package/src/qingflow_mcp/cli/commands/builder.py +11 -3
  23. package/src/qingflow_mcp/cli/commands/exports.py +111 -0
  24. package/src/qingflow_mcp/cli/commands/record.py +5 -5
  25. package/src/qingflow_mcp/cli/commands/task.py +701 -27
  26. package/src/qingflow_mcp/cli/commands/workspace.py +84 -0
  27. package/src/qingflow_mcp/cli/context.py +3 -0
  28. package/src/qingflow_mcp/cli/formatters.py +424 -50
  29. package/src/qingflow_mcp/cli/interaction.py +72 -0
  30. package/src/qingflow_mcp/cli/main.py +11 -1
  31. package/src/qingflow_mcp/cli/qingflow_login.py +116 -0
  32. package/src/qingflow_mcp/cli/terminal_ui.py +218 -0
  33. package/src/qingflow_mcp/config.py +1 -1
  34. package/src/qingflow_mcp/errors.py +4 -4
  35. package/src/qingflow_mcp/export_store.py +14 -0
  36. package/src/qingflow_mcp/id_utils.py +49 -0
  37. package/src/qingflow_mcp/public_surface.py +16 -1
  38. package/src/qingflow_mcp/response_trim.py +394 -9
  39. package/src/qingflow_mcp/server.py +26 -0
  40. package/src/qingflow_mcp/server_app_builder.py +15 -1
  41. package/src/qingflow_mcp/server_app_user.py +113 -0
  42. package/src/qingflow_mcp/session_store.py +126 -21
  43. package/src/qingflow_mcp/solution/compiler/form_compiler.py +2 -2
  44. package/src/qingflow_mcp/solution/executor.py +2 -2
  45. package/src/qingflow_mcp/tools/ai_builder_tools.py +107 -34
  46. package/src/qingflow_mcp/tools/app_tools.py +1 -0
  47. package/src/qingflow_mcp/tools/auth_tools.py +243 -9
  48. package/src/qingflow_mcp/tools/base.py +6 -2
  49. package/src/qingflow_mcp/tools/code_block_tools.py +2 -2
  50. package/src/qingflow_mcp/tools/custom_button_tools.py +0 -2
  51. package/src/qingflow_mcp/tools/export_tools.py +1565 -0
  52. package/src/qingflow_mcp/tools/import_tools.py +78 -4
  53. package/src/qingflow_mcp/tools/record_tools.py +551 -165
  54. package/src/qingflow_mcp/tools/resource_read_tools.py +154 -33
  55. package/src/qingflow_mcp/tools/task_context_tools.py +917 -141
  56. package/src/qingflow_mcp/tools/workspace_tools.py +141 -0
@@ -3,6 +3,9 @@ from __future__ import annotations
3
3
  import argparse
4
4
 
5
5
  from ..context import CliContext
6
+ from ..interaction import cancelled_result, resolve_interactive_selection
7
+ from ..terminal_ui import SelectionOption
8
+ from .common import raise_config_error
6
9
 
7
10
 
8
11
  def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
@@ -15,6 +18,14 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
15
18
  list_parser.add_argument("--include-external", action="store_true")
16
19
  list_parser.set_defaults(handler=_handle_list, format_hint="workspace_list")
17
20
 
21
+ get_parser = workspace_subparsers.add_parser("get", help="读取工作区详情")
22
+ get_parser.add_argument("--ws-id", type=int, default=0)
23
+ get_parser.set_defaults(handler=_handle_get, format_hint="workspace_get")
24
+
25
+ select_parser = workspace_subparsers.add_parser("select", help="切换当前工作区")
26
+ select_parser.add_argument("--ws-id", type=int, default=0, help="不传时在交互终端中选择工作区")
27
+ select_parser.set_defaults(handler=_handle_select, format_hint="workspace_select")
28
+
18
29
 
19
30
  def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
20
31
  return context.workspace.workspace_list(
@@ -23,3 +34,76 @@ def _handle_list(args: argparse.Namespace, context: CliContext) -> dict:
23
34
  page_size=args.page_size,
24
35
  include_external=bool(args.include_external),
25
36
  )
37
+
38
+
39
+ def _handle_get(args: argparse.Namespace, context: CliContext) -> dict:
40
+ return context.workspace.workspace_get(
41
+ profile=args.profile,
42
+ ws_id=args.ws_id if int(args.ws_id or 0) > 0 else None,
43
+ )
44
+
45
+
46
+ def _handle_select(args: argparse.Namespace, context: CliContext) -> dict:
47
+ if int(args.ws_id or 0) <= 0:
48
+ selection = _choose_workspace_interactively(args, context)
49
+ if selection.status == "unavailable":
50
+ raise_config_error(
51
+ "workspace select requires --ws-id, or an interactive terminal to choose a workspace",
52
+ fix_hint="Retry in an interactive terminal, or pass `--ws-id WS_ID` explicitly.",
53
+ )
54
+ if selection.status == "empty":
55
+ raise_config_error(
56
+ selection.message or "workspace select could not open a selector because no workspaces are available.",
57
+ fix_hint="Run `workspace list` to confirm visible workspaces, or retry with `--ws-id WS_ID`.",
58
+ )
59
+ if selection.status == "cancelled":
60
+ return cancelled_result(selection.message or "已取消")
61
+ args.ws_id = int(selection.value or 0)
62
+ return context.workspace.workspace_select(
63
+ profile=args.profile,
64
+ ws_id=int(args.ws_id),
65
+ )
66
+
67
+
68
+ def _choose_workspace_interactively(args: argparse.Namespace, context: CliContext):
69
+ current_ws_id = None
70
+ sessions = getattr(context, "sessions", None)
71
+ if sessions is not None and hasattr(sessions, "get_profile"):
72
+ try:
73
+ session_profile = sessions.get_profile(args.profile)
74
+ except Exception:
75
+ session_profile = None
76
+ current_ws_id = getattr(session_profile, "selected_ws_id", None) if session_profile is not None else None
77
+ def load_options() -> list[SelectionOption[int]]:
78
+ page = context.workspace.workspace_list(
79
+ profile=args.profile,
80
+ page_num=1,
81
+ page_size=100,
82
+ include_external=False,
83
+ ).get("page")
84
+ items = page.get("list") if isinstance(page, dict) and isinstance(page.get("list"), list) else []
85
+ options: list[SelectionOption[int]] = []
86
+ for item in items:
87
+ if not isinstance(item, dict):
88
+ continue
89
+ ws_id = int(item.get("wsId") or 0)
90
+ if ws_id <= 0:
91
+ continue
92
+ workspace_name = str(item.get("workspaceName") or item.get("wsName") or f"Workspace {ws_id}")
93
+ remark = str(item.get("remark") or "").strip()
94
+ label = workspace_name
95
+ if remark:
96
+ label = f"{workspace_name} - {remark}"
97
+ hint = f"ws_id={ws_id}"
98
+ if current_ws_id == ws_id:
99
+ hint += " · 当前"
100
+ options.append(SelectionOption(value=ws_id, label=label, hint=hint))
101
+ return options
102
+
103
+ return resolve_interactive_selection(
104
+ args,
105
+ title="选择工作区",
106
+ unavailable_message="workspace select requires --ws-id, or an interactive terminal to choose a workspace",
107
+ empty_message="workspace select could not open a selector because no visible workspaces were returned.",
108
+ load_options=load_options,
109
+ )
@@ -8,6 +8,7 @@ from ..tools.ai_builder_tools import AiBuilderTools
8
8
  from ..tools.app_tools import AppTools
9
9
  from ..tools.auth_tools import AuthTools
10
10
  from ..tools.code_block_tools import CodeBlockTools
11
+ from ..tools.export_tools import ExportTools
11
12
  from ..tools.feedback_tools import FeedbackTools
12
13
  from ..tools.file_tools import FileTools
13
14
  from ..tools.import_tools import ImportTools
@@ -29,6 +30,7 @@ class CliContext:
29
30
  record: RecordTools
30
31
  code_block: CodeBlockTools
31
32
  imports: ImportTools
33
+ exports: ExportTools
32
34
  task: TaskContextTools
33
35
  files: FileTools
34
36
  builder_feedback: FeedbackTools
@@ -52,6 +54,7 @@ def build_cli_context() -> CliContext:
52
54
  record=RecordTools(sessions, backend),
53
55
  code_block=CodeBlockTools(sessions, backend),
54
56
  imports=ImportTools(sessions, backend),
57
+ exports=ExportTools(sessions, backend),
55
58
  task=TaskContextTools(sessions, backend),
56
59
  files=FileTools(sessions, backend),
57
60
  builder_feedback=FeedbackTools(backend, mcp_side="App Builder MCP"),