@josephyan/qingflow-app-user-mcp 1.1.24 → 1.1.26

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 (113) hide show
  1. package/README.md +7 -7
  2. package/docs/local-agent-install.md +6 -57
  3. package/entry_point.py +1 -1
  4. package/npm/bin/qingflow-app-user-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 +277 -0
  10. package/skills/qingflow-app-builder/agents/openai.yaml +4 -0
  11. package/skills/qingflow-app-builder/references/build-complete-system.md +428 -0
  12. package/skills/qingflow-app-builder/references/build-single-app.md +530 -0
  13. package/skills/qingflow-app-builder/references/create-app.md +149 -0
  14. package/skills/qingflow-app-builder/references/environments.md +63 -0
  15. package/skills/qingflow-app-builder/references/flow-actors-and-permissions.md +123 -0
  16. package/skills/qingflow-app-builder/references/gotchas.md +108 -0
  17. package/skills/qingflow-app-builder/references/match-rules.md +114 -0
  18. package/skills/qingflow-app-builder/references/public-surface-sync.md +75 -0
  19. package/skills/qingflow-app-builder/references/solution-playbooks.md +52 -0
  20. package/skills/qingflow-app-builder/references/tool-selection.md +107 -0
  21. package/skills/qingflow-app-builder/references/update-flow.md +186 -0
  22. package/skills/qingflow-app-builder/references/update-layout.md +68 -0
  23. package/skills/qingflow-app-builder/references/update-schema.md +72 -0
  24. package/skills/qingflow-app-builder/references/update-views.md +291 -0
  25. package/skills/qingflow-app-builder-code-integrations/SKILL.md +137 -0
  26. package/skills/qingflow-app-builder-code-integrations/agents/openai.yaml +4 -0
  27. package/skills/qingflow-app-builder-code-integrations/references/code-block.md +66 -0
  28. package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +77 -0
  29. package/skills/qingflow-app-user/SKILL.md +14 -17
  30. package/skills/qingflow-app-user/references/data-gotchas.md +2 -2
  31. package/skills/qingflow-app-user/references/public-surface-sync.md +3 -3
  32. package/skills/qingflow-app-user/references/record-patterns.md +5 -5
  33. package/skills/qingflow-app-user/references/workflow-usage.md +5 -4
  34. package/skills/qingflow-mcp-setup/SKILL.md +9 -13
  35. package/skills/qingflow-mcp-setup/references/claude-desktop.md +1 -1
  36. package/skills/qingflow-mcp-setup/references/environments.md +1 -2
  37. package/skills/qingflow-mcp-setup/references/generic-stdio.md +5 -6
  38. package/skills/qingflow-record-analysis/SKILL.md +8 -9
  39. package/skills/qingflow-record-delete/SKILL.md +3 -7
  40. package/skills/qingflow-record-import/SKILL.md +2 -35
  41. package/skills/qingflow-record-insert/SKILL.md +6 -78
  42. package/skills/qingflow-record-update/SKILL.md +24 -39
  43. package/skills/qingflow-task-ops/SKILL.md +27 -30
  44. package/skills/qingflow-task-ops/references/environments.md +1 -0
  45. package/skills/qingflow-task-ops/references/workflow-usage.md +6 -4
  46. package/skills/qingflow-workflow-builder/SKILL.md +96 -0
  47. package/skills/qingflow-workflow-builder/manifest.yaml +8 -0
  48. package/skills/qingflow-workflow-builder/references/01-overview.md +45 -0
  49. package/skills/qingflow-workflow-builder/references/02-update-mode.md +53 -0
  50. package/skills/qingflow-workflow-builder/references/03-flow-patterns.md +57 -0
  51. package/skills/qingflow-workflow-builder/references/04-stage1-business-modeling.md +131 -0
  52. package/skills/qingflow-workflow-builder/references/05-stage2-members-roles.md +29 -0
  53. package/skills/qingflow-workflow-builder/references/06-stage3-build-spec.md +165 -0
  54. package/skills/qingflow-workflow-builder/references/07-stage4-validate-spec.md +33 -0
  55. package/skills/qingflow-workflow-builder/references/08-stage5-apply-verify.md +51 -0
  56. package/skills/qingflow-workflow-builder/references/09-stage6-summary.md +88 -0
  57. package/skills/qingflow-workflow-builder/references/10-node-config-reference.md +93 -0
  58. package/skills/qingflow-workflow-builder/references/11-troubleshooting.md +15 -0
  59. package/skills/qingflow-workflow-builder/scripts/diff_flow_spec.py +275 -0
  60. package/skills/qingflow-workflow-builder/scripts/validate_flow_spec.py +605 -0
  61. package/src/qingflow_mcp/__init__.py +1 -1
  62. package/src/qingflow_mcp/__main__.py +2 -6
  63. package/src/qingflow_mcp/builder_facade/models.py +108 -485
  64. package/src/qingflow_mcp/builder_facade/service.py +1686 -6674
  65. package/src/qingflow_mcp/cli/commands/builder.py +364 -322
  66. package/src/qingflow_mcp/cli/commands/chart.py +1 -1
  67. package/src/qingflow_mcp/cli/commands/common.py +3 -12
  68. package/src/qingflow_mcp/cli/commands/exports.py +2 -2
  69. package/src/qingflow_mcp/cli/commands/imports.py +3 -3
  70. package/src/qingflow_mcp/cli/commands/portal.py +2 -2
  71. package/src/qingflow_mcp/cli/commands/record.py +27 -101
  72. package/src/qingflow_mcp/cli/commands/task.py +47 -28
  73. package/src/qingflow_mcp/cli/commands/view.py +1 -1
  74. package/src/qingflow_mcp/cli/context.py +3 -0
  75. package/src/qingflow_mcp/cli/formatters.py +16 -784
  76. package/src/qingflow_mcp/cli/main.py +33 -117
  77. package/src/qingflow_mcp/errors.py +2 -43
  78. package/src/qingflow_mcp/public_surface.py +17 -26
  79. package/src/qingflow_mcp/response_trim.py +17 -81
  80. package/src/qingflow_mcp/server.py +12 -14
  81. package/src/qingflow_mcp/server_app_builder.py +36 -79
  82. package/src/qingflow_mcp/server_app_user.py +16 -22
  83. package/src/qingflow_mcp/session_store.py +7 -11
  84. package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
  85. package/src/qingflow_mcp/solution/executor.py +18 -245
  86. package/src/qingflow_mcp/tools/ai_builder_tools.py +1396 -3100
  87. package/src/qingflow_mcp/tools/app_tools.py +43 -184
  88. package/src/qingflow_mcp/tools/approval_tools.py +35 -197
  89. package/src/qingflow_mcp/tools/auth_tools.py +16 -92
  90. package/src/qingflow_mcp/tools/code_block_tools.py +40 -298
  91. package/src/qingflow_mcp/tools/custom_button_tools.py +10 -64
  92. package/src/qingflow_mcp/tools/directory_tools.py +72 -236
  93. package/src/qingflow_mcp/tools/export_tools.py +34 -244
  94. package/src/qingflow_mcp/tools/feedback_tools.py +0 -9
  95. package/src/qingflow_mcp/tools/file_tools.py +3 -9
  96. package/src/qingflow_mcp/tools/import_tools.py +49 -336
  97. package/src/qingflow_mcp/tools/navigation_tools.py +12 -91
  98. package/src/qingflow_mcp/tools/package_tools.py +6 -118
  99. package/src/qingflow_mcp/tools/portal_tools.py +3 -39
  100. package/src/qingflow_mcp/tools/qingbi_report_tools.py +7 -116
  101. package/src/qingflow_mcp/tools/record_tools.py +360 -1145
  102. package/src/qingflow_mcp/tools/resource_read_tools.py +39 -188
  103. package/src/qingflow_mcp/tools/role_tools.py +9 -80
  104. package/src/qingflow_mcp/tools/solution_tools.py +45 -59
  105. package/src/qingflow_mcp/tools/task_context_tools.py +158 -662
  106. package/src/qingflow_mcp/tools/task_tools.py +29 -113
  107. package/src/qingflow_mcp/tools/view_tools.py +3 -106
  108. package/src/qingflow_mcp/tools/workflow_tools.py +4 -48
  109. package/src/qingflow_mcp/tools/workspace_tools.py +3 -71
  110. package/npm/bin/qingflow-skills.mjs +0 -5
  111. package/skills/qingflow-record-analysis/manifest.yaml +0 -10
  112. package/skills/qingflow-record-insert/manifest.yaml +0 -6
  113. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
package/README.md CHANGED
@@ -3,13 +3,13 @@
3
3
  Install:
4
4
 
5
5
  ```bash
6
- npm install @josephyan/qingflow-app-user-mcp@1.1.24
6
+ npm install @josephyan/qingflow-app-user-mcp@1.1.26
7
7
  ```
8
8
 
9
9
  Run:
10
10
 
11
11
  ```bash
12
- npx -y -p @josephyan/qingflow-app-user-mcp@1.1.24 qingflow-app-user-mcp
12
+ npx -y -p @josephyan/qingflow-app-user-mcp@1.1.26 qingflow-app-user-mcp
13
13
  ```
14
14
 
15
15
  Environment:
@@ -23,19 +23,19 @@ This package bootstraps a local Python runtime on first install and then starts
23
23
  Bundled skills:
24
24
 
25
25
  - `skills/qingflow-app-user`
26
- - `skills/qingflow-mcp-setup`
27
26
  - `skills/qingflow-record-insert`
28
27
  - `skills/qingflow-record-update`
29
28
  - `skills/qingflow-record-delete`
30
29
  - `skills/qingflow-record-import`
31
30
  - `skills/qingflow-task-ops`
32
31
  - `skills/qingflow-record-analysis`
32
+ - `skills/qingflow-mcp-setup`
33
+ - `skills/qingflow-app-builder`
34
+ - `skills/qingflow-app-builder-code-integrations`
35
+ - `skills/qingflow-workflow-builder`
33
36
 
34
37
  Note:
35
38
 
36
39
  - The skill files are included in the npm package.
37
- - Installing the npm package does not overwrite agent skills automatically.
38
- - To mount bundled skills from an installed package, run `qingflow-app-user-mcp-skills install --agent codex --scope user`.
39
- - For one-shot `npx -p` installs, prefer `--copy` because symlinks would point into the npm execution cache.
40
- - Use `qingflow-app-user-mcp-skills list` to inspect bundled skills. The installer defaults to symlinks for stable package installs and refuses to overwrite existing skills unless `--force` is provided.
40
+ - On install, the package copies them to `$CODEX_HOME/skills` (or `~/.codex/skills` if `CODEX_HOME` is unset).
41
41
  - If a stdio MCP client reports `Transport closed`, delete `.npm-python`, reinstall the package, and make sure CLI/user/builder packages are on the same version. The stdio entrypoints refuse runtime bootstrap so install logs never corrupt MCP stdout.
@@ -101,51 +101,7 @@ npm install /absolute/path/to/dist/npm/qingflow-tech-qingflow-app-builder-mcp-<v
101
101
  1. 创建 `.npm-python/`
102
102
  2. 在其中建立 Python 虚拟环境
103
103
  3. 执行 `pip install .`
104
- 4. 在安装位置暴露对应入口:CLI 包暴露 `qingflow` / `qingflow-skills`;app-user 包暴露 `qingflow-app-user-mcp` / `qingflow-app-user-mcp-skills`;app-builder 包暴露 `qingflow-app-builder-mcp` / `qingflow-app-builder-mcp-skills`
105
- 5. 携带 `skills/<skill-name>/SKILL.md`,但不在 `postinstall` 阶段自动覆盖本机 agent skills
106
-
107
- ## Skills 挂载
108
-
109
- 显式查看包内 skills:
110
-
111
- ```bash
112
- qingflow-skills list
113
- ```
114
-
115
- 独立 MCP split 包使用包专属 skills 命令,避免全局安装多个包时发生 bin 覆盖:
116
-
117
- ```bash
118
- qingflow-app-user-mcp-skills list
119
- qingflow-app-builder-mcp-skills list
120
- ```
121
-
122
- 显式挂载到 Codex 用户目录:
123
-
124
- ```bash
125
- qingflow-skills install --agent codex --scope user
126
- ```
127
-
128
- 如果通过一次性 `npx -p <package>` 执行安装,请加 `--copy`,避免 symlink 指向 npm 临时执行缓存:
129
-
130
- ```bash
131
- npx -y -p @qingflow-tech/qingflow-cli qingflow-skills install --agent codex --scope user --copy
132
- ```
133
-
134
- 也可以挂载到项目级 agent 目录:
135
-
136
- ```bash
137
- qingflow-skills install --agent claude-code --scope project
138
- qingflow-skills install --agent cursor --scope project --copy
139
- qingflow-skills install --agent all --scope project
140
- ```
141
-
142
- 默认行为:
143
-
144
- - `--mode symlink`:使用 symlink,让 npm 包版本成为单一来源
145
- - `--scope user`:安装到用户级 agent skills 目录
146
- - `--agent codex`:目标 agent 为 Codex
147
- - 不覆盖已有同名 skill;需要替换时显式加 `--force`
148
- - 每次安装会在目标 skills 目录下写入 `.qingflow-skill-sources/<skill>.json`,记录 package、version、source、destination、agent、scope、mode
104
+ 4. 在安装位置暴露 `qingflow`、`qingflow-app-user-mcp`、`qingflow-app-builder-mcp` 命令
149
105
 
150
106
  ## 本地验证
151
107
 
@@ -154,31 +110,24 @@ qingflow-skills install --agent all --scope project
154
110
  ```bash
155
111
  cd qingflow-support/mcp-server
156
112
  node ./npm/bin/qingflow.mjs --help
157
- node ./npm/bin/qingflow-skills.mjs list
158
113
  node ./npm/bin/qingflow-app-user-mcp.mjs
159
114
  node ./npm/bin/qingflow-app-builder-mcp.mjs
160
115
  ```
161
116
 
162
- 如果你是全局安装对应包:
117
+ 如果你是全局安装:
163
118
 
164
119
  ```bash
165
120
  qingflow --help
166
- qingflow-skills list
167
121
  qingflow-app-user-mcp
168
- qingflow-app-user-mcp-skills list
169
122
  qingflow-app-builder-mcp
170
- qingflow-app-builder-mcp-skills list
171
123
  ```
172
124
 
173
- 如果你是把包安装到了某个本地 agent workspace,安装对应包后命令通常位于:
125
+ 如果你是把包安装到了某个本地 agent workspace,命令通常位于:
174
126
 
175
127
  ```bash
176
128
  /absolute/path/to/agent-workspace/node_modules/.bin/qingflow
177
- /absolute/path/to/agent-workspace/node_modules/.bin/qingflow-skills
178
129
  /absolute/path/to/agent-workspace/node_modules/.bin/qingflow-app-user-mcp
179
- /absolute/path/to/agent-workspace/node_modules/.bin/qingflow-app-user-mcp-skills
180
130
  /absolute/path/to/agent-workspace/node_modules/.bin/qingflow-app-builder-mcp
181
- /absolute/path/to/agent-workspace/node_modules/.bin/qingflow-app-builder-mcp-skills
182
131
  ```
183
132
 
184
133
  如果你是从 tgz 安装到某个空目录,命令通常位于:
@@ -268,7 +217,7 @@ qingflow-app-builder-mcp-skills list
268
217
  "command": "npx",
269
218
  "args": [
270
219
  "-y",
271
- "@qingflow-tech/qingflow-app-user-mcp"
220
+ "@josephyan/qingflow-app-user-mcp"
272
221
  ],
273
222
  "env": {
274
223
  "QINGFLOW_MCP_DEFAULT_BASE_URL": "https://qingflow.com/api",
@@ -281,7 +230,7 @@ qingflow-app-builder-mcp-skills list
281
230
  "command": "npx",
282
231
  "args": [
283
232
  "-y",
284
- "@qingflow-tech/qingflow-app-builder-mcp"
233
+ "@josephyan/qingflow-app-builder-mcp"
285
234
  ],
286
235
  "env": {
287
236
  "QINGFLOW_MCP_DEFAULT_BASE_URL": "https://qingflow.com/api",
@@ -323,7 +272,7 @@ npm install
323
272
 
324
273
  如果 MCP 客户端一调用工具就报 `Transport closed`,优先检查这几件事:
325
274
 
326
- 1. 不要混用不同版本的 `@qingflow-tech/qingflow-cli`、`@qingflow-tech/qingflow-app-user-mcp`、`@qingflow-tech/qingflow-app-builder-mcp`
275
+ 1. 不要混用不同版本的 `@josephyan/qingflow-cli`、`@josephyan/qingflow-app-user-mcp`、`@josephyan/qingflow-app-builder-mcp`
327
276
  2. 删除安装目录下的 `.npm-python`
328
277
  3. 重新执行 `npm install` 或重新安装对应 tgz/npm 包
329
278
  4. 再启动 MCP 客户端
package/entry_point.py CHANGED
@@ -7,7 +7,7 @@ src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "src"))
7
7
  if src_path not in sys.path:
8
8
  sys.path.insert(0, src_path)
9
9
 
10
- from qingflow_mcp.server_app_user import main
10
+ from qingflow_mcp.server import main
11
11
 
12
12
  if __name__ == "__main__":
13
13
  main()
@@ -1,7 +1,38 @@
1
1
  #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
2
5
 
3
- import { getPackageRoot, spawnServer } from "../lib/runtime.mjs";
6
+ const PKG_BY_BIN = {
7
+ qingflow: "qingflow-cli",
8
+ "qingflow-app-user-mcp": "qingflow-app-user-mcp",
9
+ "qingflow-app-builder-mcp": "qingflow-app-builder-mcp",
10
+ };
4
11
 
5
- const packageRoot = getPackageRoot(import.meta.url);
12
+ function resolvePackageModule(metaUrl, ...segments) {
13
+ const scriptPath = fileURLToPath(metaUrl);
14
+ const scriptDir = path.dirname(scriptPath);
15
+ // bin files live in <pkg>/npm/bin/, runtime.mjs lives in the sibling <pkg>/npm/lib/,
16
+ // so step up one level before joining the requested segments.
17
+ const direct = path.join(scriptDir, "..", ...segments);
18
+ if (fs.existsSync(direct)) {
19
+ return pathToFileURL(direct).href;
20
+ }
21
+ if (path.basename(scriptDir) === ".bin") {
22
+ const binName = path.basename(scriptPath);
23
+ const pkgName = PKG_BY_BIN[binName];
24
+ if (!pkgName) {
25
+ throw new Error(`Unknown qingflow command: ${binName}`);
26
+ }
27
+ const scope = process.env.QINGFLOW_NPM_SCOPE || "@josephyan";
28
+ const fromBin = path.join(scriptDir, "..", scope, pkgName, "npm", ...segments);
29
+ if (fs.existsSync(fromBin)) {
30
+ return pathToFileURL(fromBin).href;
31
+ }
32
+ }
33
+ throw new Error(`Cannot locate ${segments.join("/")} from ${scriptPath}`);
34
+ }
6
35
 
36
+ const { getPackageRoot, spawnServer } = await import(resolvePackageModule(import.meta.url, "lib", "runtime.mjs"));
37
+ const packageRoot = getPackageRoot(import.meta.url);
7
38
  spawnServer(packageRoot, process.argv.slice(2), "qingflow-app-user-mcp", { allowRuntimeBootstrap: false });
@@ -5,6 +5,12 @@ import { fileURLToPath } from "node:url";
5
5
 
6
6
  const WINDOWS = process.platform === "win32";
7
7
 
8
+ const PKG_BY_BIN = {
9
+ qingflow: "qingflow-cli",
10
+ "qingflow-app-user-mcp": "qingflow-app-user-mcp",
11
+ "qingflow-app-builder-mcp": "qingflow-app-builder-mcp",
12
+ };
13
+
8
14
  function runChecked(command, args, options = {}) {
9
15
  const result = spawnSync(command, args, {
10
16
  encoding: "utf8",
@@ -28,7 +34,18 @@ function commandWorks(command, args) {
28
34
  }
29
35
 
30
36
  export function getPackageRoot(metaUrl) {
31
- return path.resolve(path.dirname(fileURLToPath(metaUrl)), "..", "..");
37
+ const scriptPath = fileURLToPath(metaUrl);
38
+ const scriptDir = path.dirname(scriptPath);
39
+ if (path.basename(scriptDir) === ".bin") {
40
+ const binName = path.basename(scriptPath);
41
+ const pkgName = PKG_BY_BIN[binName];
42
+ if (!pkgName) {
43
+ throw new Error(`Unknown qingflow command: ${binName}`);
44
+ }
45
+ const scope = process.env.QINGFLOW_NPM_SCOPE || "@josephyan";
46
+ return path.join(scriptDir, "..", scope, pkgName);
47
+ }
48
+ return path.resolve(scriptDir, "..", "..");
32
49
  }
33
50
 
34
51
  export function getCodexHome() {
@@ -137,6 +154,43 @@ function writeSkillProvenance(skillsDestRoot, skillName, payload) {
137
154
  fs.writeFileSync(path.join(provenanceRoot, `${skillName}.json`), `${JSON.stringify(payload, null, 2)}\n`);
138
155
  }
139
156
 
157
+ function sleepMs(ms) {
158
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
159
+ }
160
+
161
+ function withSkillInstallLock(skillsDestRoot, callback) {
162
+ const lockDir = path.join(skillsDestRoot, ".qingflow-skill-install.lock");
163
+ const startedAt = Date.now();
164
+ while (true) {
165
+ try {
166
+ fs.mkdirSync(lockDir);
167
+ break;
168
+ } catch (error) {
169
+ if (error.code !== "EEXIST") {
170
+ throw error;
171
+ }
172
+ try {
173
+ const stat = fs.statSync(lockDir);
174
+ if (Date.now() - stat.mtimeMs > 300_000) {
175
+ fs.rmSync(lockDir, { recursive: true, force: true });
176
+ continue;
177
+ }
178
+ } catch {
179
+ continue;
180
+ }
181
+ if (Date.now() - startedAt > 30_000) {
182
+ throw new Error(`Timed out waiting for skill install lock: ${lockDir}`);
183
+ }
184
+ sleepMs(100);
185
+ }
186
+ }
187
+ try {
188
+ return callback();
189
+ } finally {
190
+ fs.rmSync(lockDir, { recursive: true, force: true });
191
+ }
192
+ }
193
+
140
194
  export function installBundledSkills(
141
195
  packageRoot,
142
196
  {
@@ -180,27 +234,29 @@ export function installBundledSkills(
180
234
  mode,
181
235
  };
182
236
 
183
- for (const skillName of wanted) {
184
- const src = path.join(skillsSrc, skillName);
185
- const dest = path.join(skillsDestRoot, skillName);
186
- const installResult = installOneSkill(src, dest, { force, mode });
187
- result[installResult.status === "conflict" ? "conflicts" : installResult.status].push(skillName);
188
- if (installResult.status !== "conflict") {
189
- writeSkillProvenance(skillsDestRoot, skillName, {
190
- package: packageJson.name ?? null,
191
- version: packageJson.version ?? null,
192
- skill: skillName,
193
- source: src,
194
- destination: dest,
195
- agent,
196
- scope,
197
- mode,
198
- installed_at: new Date().toISOString(),
199
- });
237
+ return withSkillInstallLock(skillsDestRoot, () => {
238
+ for (const skillName of wanted) {
239
+ const src = path.join(skillsSrc, skillName);
240
+ const dest = path.join(skillsDestRoot, skillName);
241
+ const installResult = installOneSkill(src, dest, { force, mode });
242
+ result[installResult.status === "conflict" ? "conflicts" : installResult.status].push(skillName);
243
+ if (installResult.status !== "conflict") {
244
+ writeSkillProvenance(skillsDestRoot, skillName, {
245
+ package: packageJson.name ?? null,
246
+ version: packageJson.version ?? null,
247
+ skill: skillName,
248
+ source: src,
249
+ destination: dest,
250
+ agent,
251
+ scope,
252
+ mode,
253
+ installed_at: new Date().toISOString(),
254
+ });
255
+ }
200
256
  }
201
- }
202
257
 
203
- return result;
258
+ return result;
259
+ });
204
260
  }
205
261
 
206
262
  function parseSkillsCliArgs(args) {
@@ -480,6 +536,17 @@ function getVenvPip(packageRoot) {
480
536
  : path.join(getVenvDir(packageRoot), "bin", "pip");
481
537
  }
482
538
 
539
+ function findOfflineProjectWheel(findLinksDir) {
540
+ const wheels = fs
541
+ .readdirSync(findLinksDir)
542
+ .filter((name) => /^qingflow_mcp-.*\.whl$/i.test(name))
543
+ .sort();
544
+ if (wheels.length === 0) {
545
+ throw new Error(`Offline install expected a qingflow_mcp wheel in ${findLinksDir}`);
546
+ }
547
+ return path.join(findLinksDir, wheels[wheels.length - 1]);
548
+ }
549
+
483
550
  export function findPython() {
484
551
  const preferred = process.env.QINGFLOW_MCP_PYTHON?.trim();
485
552
  const candidates = preferred
@@ -528,7 +595,20 @@ export function ensurePythonEnv(packageRoot, { force = false, commandName = "qin
528
595
  }
529
596
 
530
597
  const pip = getVenvPip(packageRoot);
531
- runChecked(pip, ["install", "--disable-pip-version-check", "."], { cwd: packageRoot });
598
+ const pipArgs = ["install", "--disable-pip-version-check"];
599
+ const offlineFindLinks = process.env.QINGFLOW_MCP_PIP_FIND_LINKS?.trim();
600
+ if (process.env.QINGFLOW_MCP_PIP_NO_INDEX === "1") {
601
+ pipArgs.push("--no-index");
602
+ }
603
+ if (offlineFindLinks) {
604
+ pipArgs.push("--find-links", offlineFindLinks);
605
+ }
606
+ if (process.env.QINGFLOW_MCP_PIP_NO_INDEX === "1" && offlineFindLinks) {
607
+ pipArgs.push(findOfflineProjectWheel(offlineFindLinks));
608
+ } else {
609
+ pipArgs.push(".");
610
+ }
611
+ runChecked(pip, pipArgs, { cwd: packageRoot });
532
612
 
533
613
  fs.writeFileSync(
534
614
  stampPath,
@@ -1,4 +1,4 @@
1
- import { ensurePythonEnv, getPackageRoot } from "../lib/runtime.mjs";
1
+ import { ensurePythonEnv, getPackageRoot, installBundledSkills } from "../lib/runtime.mjs";
2
2
 
3
3
  const packageRoot = getPackageRoot(import.meta.url);
4
4
 
@@ -6,6 +6,15 @@ try {
6
6
  console.log("[qingflow-mcp] Bootstrapping Python runtime...");
7
7
  ensurePythonEnv(packageRoot, { commandName: "qingflow-app-user-mcp" });
8
8
  console.log("[qingflow-mcp] Python runtime is ready.");
9
+ const skills = installBundledSkills(packageRoot, { force: true });
10
+ if (!skills.skipped) {
11
+ const changed = [
12
+ skills.installed.length ? `installed=${skills.installed.join(",")}` : "",
13
+ skills.unchanged.length ? `unchanged=${skills.unchanged.join(",")}` : "",
14
+ skills.conflicts.length ? `conflicts=${skills.conflicts.join(",")}` : "",
15
+ ].filter(Boolean).join(" ");
16
+ console.log(`[qingflow-mcp] Installed skills to ${skills.destination}: ${changed || "none"}`);
17
+ }
9
18
  } catch (error) {
10
19
  console.error(`[qingflow-mcp] postinstall failed: ${error.message}`);
11
20
  process.exit(1);
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@josephyan/qingflow-app-user-mcp",
3
- "version": "1.1.24",
3
+ "version": "1.1.26",
4
4
  "description": "Operational end-user MCP for Qingflow records, tasks, comments, and directory workflows.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
8
- "qingflow-app-user-mcp": "./npm/bin/qingflow-app-user-mcp.mjs",
9
- "qingflow-app-user-mcp-skills": "./npm/bin/qingflow-skills.mjs"
8
+ "qingflow-app-user-mcp": "./npm/bin/qingflow-app-user-mcp.mjs"
10
9
  },
11
10
  "scripts": {
12
11
  "postinstall": "node ./npm/scripts/postinstall.mjs"
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qingflow-mcp"
7
- version = "1.1.24"
7
+ version = "1.1.26"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"