@qingflow-tech/qingflow-app-builder-mcp 1.0.11 → 1.0.13
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.
- package/README.md +6 -3
- package/docs/local-agent-install.md +54 -3
- package/entry_point.py +1 -1
- package/npm/bin/qingflow-skills.mjs +5 -0
- package/npm/lib/runtime.mjs +304 -13
- package/npm/scripts/postinstall.mjs +1 -5
- package/package.json +3 -2
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-builder/SKILL.md +12 -12
- package/skills/qingflow-app-builder/references/create-app.md +3 -3
- package/skills/qingflow-app-builder/references/environments.md +1 -1
- package/skills/qingflow-app-builder/references/gotchas.md +1 -1
- package/skills/qingflow-app-builder/references/public-surface-sync.md +75 -0
- package/skills/qingflow-app-builder/references/tool-selection.md +6 -5
- package/skills/qingflow-app-builder/references/update-views.md +1 -1
- package/skills/qingflow-app-builder-code-integrations/SKILL.md +3 -3
- package/skills/qingflow-app-builder-code-integrations/references/code-block.md +1 -1
- package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +1 -1
- package/src/qingflow_mcp/__main__.py +6 -2
- package/src/qingflow_mcp/builder_facade/models.py +11 -0
- package/src/qingflow_mcp/builder_facade/service.py +1488 -288
- package/src/qingflow_mcp/cli/commands/builder.py +2 -2
- package/src/qingflow_mcp/cli/commands/exports.py +2 -2
- package/src/qingflow_mcp/cli/commands/imports.py +1 -1
- package/src/qingflow_mcp/cli/commands/record.py +91 -19
- package/src/qingflow_mcp/cli/context.py +0 -3
- package/src/qingflow_mcp/cli/formatters.py +206 -7
- package/src/qingflow_mcp/cli/main.py +47 -3
- package/src/qingflow_mcp/errors.py +43 -2
- package/src/qingflow_mcp/public_surface.py +21 -15
- package/src/qingflow_mcp/response_trim.py +74 -13
- package/src/qingflow_mcp/server.py +11 -9
- package/src/qingflow_mcp/server_app_builder.py +3 -2
- package/src/qingflow_mcp/server_app_user.py +19 -13
- package/src/qingflow_mcp/session_store.py +11 -7
- package/src/qingflow_mcp/solution/executor.py +112 -15
- package/src/qingflow_mcp/tools/ai_builder_tools.py +36 -11
- package/src/qingflow_mcp/tools/app_tools.py +184 -43
- package/src/qingflow_mcp/tools/approval_tools.py +196 -34
- package/src/qingflow_mcp/tools/auth_tools.py +92 -16
- package/src/qingflow_mcp/tools/code_block_tools.py +298 -40
- package/src/qingflow_mcp/tools/custom_button_tools.py +64 -10
- package/src/qingflow_mcp/tools/directory_tools.py +236 -72
- package/src/qingflow_mcp/tools/export_tools.py +244 -34
- package/src/qingflow_mcp/tools/file_tools.py +7 -3
- package/src/qingflow_mcp/tools/import_tools.py +336 -49
- package/src/qingflow_mcp/tools/navigation_tools.py +91 -12
- package/src/qingflow_mcp/tools/package_tools.py +118 -6
- package/src/qingflow_mcp/tools/portal_tools.py +39 -3
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +116 -7
- package/src/qingflow_mcp/tools/record_tools.py +1067 -349
- package/src/qingflow_mcp/tools/resource_read_tools.py +188 -39
- package/src/qingflow_mcp/tools/role_tools.py +80 -9
- package/src/qingflow_mcp/tools/solution_tools.py +57 -15
- package/src/qingflow_mcp/tools/task_context_tools.py +569 -119
- package/src/qingflow_mcp/tools/task_tools.py +113 -29
- package/src/qingflow_mcp/tools/view_tools.py +106 -3
- package/src/qingflow_mcp/tools/workflow_tools.py +17 -1
- package/src/qingflow_mcp/tools/workspace_tools.py +71 -3
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.
|
|
6
|
+
npm install @qingflow-tech/qingflow-app-builder-mcp@1.0.13
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.
|
|
12
|
+
npx -y -p @qingflow-tech/qingflow-app-builder-mcp@1.0.13 qingflow-app-builder-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
|
@@ -28,5 +28,8 @@ Bundled skills:
|
|
|
28
28
|
Note:
|
|
29
29
|
|
|
30
30
|
- The skill files are included in the npm package.
|
|
31
|
-
-
|
|
31
|
+
- Installing the npm package does not overwrite agent skills automatically.
|
|
32
|
+
- To mount bundled skills from an installed package, run `qingflow-app-builder-mcp-skills install --agent codex --scope user`.
|
|
33
|
+
- For one-shot `npx -p` installs, prefer `--copy` because symlinks would point into the npm execution cache.
|
|
34
|
+
- Use `qingflow-app-builder-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.
|
|
32
35
|
- 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,7 +101,51 @@ 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.
|
|
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 @josephyan/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
|
|
105
149
|
|
|
106
150
|
## 本地验证
|
|
107
151
|
|
|
@@ -110,24 +154,31 @@ npm install /absolute/path/to/dist/npm/qingflow-tech-qingflow-app-builder-mcp-<v
|
|
|
110
154
|
```bash
|
|
111
155
|
cd qingflow-support/mcp-server
|
|
112
156
|
node ./npm/bin/qingflow.mjs --help
|
|
157
|
+
node ./npm/bin/qingflow-skills.mjs list
|
|
113
158
|
node ./npm/bin/qingflow-app-user-mcp.mjs
|
|
114
159
|
node ./npm/bin/qingflow-app-builder-mcp.mjs
|
|
115
160
|
```
|
|
116
161
|
|
|
117
|
-
|
|
162
|
+
如果你是全局安装对应包:
|
|
118
163
|
|
|
119
164
|
```bash
|
|
120
165
|
qingflow --help
|
|
166
|
+
qingflow-skills list
|
|
121
167
|
qingflow-app-user-mcp
|
|
168
|
+
qingflow-app-user-mcp-skills list
|
|
122
169
|
qingflow-app-builder-mcp
|
|
170
|
+
qingflow-app-builder-mcp-skills list
|
|
123
171
|
```
|
|
124
172
|
|
|
125
|
-
如果你是把包安装到了某个本地 agent workspace
|
|
173
|
+
如果你是把包安装到了某个本地 agent workspace,安装对应包后命令通常位于:
|
|
126
174
|
|
|
127
175
|
```bash
|
|
128
176
|
/absolute/path/to/agent-workspace/node_modules/.bin/qingflow
|
|
177
|
+
/absolute/path/to/agent-workspace/node_modules/.bin/qingflow-skills
|
|
129
178
|
/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
|
|
130
180
|
/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
|
|
131
182
|
```
|
|
132
183
|
|
|
133
184
|
如果你是从 tgz 安装到某个空目录,命令通常位于:
|
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.
|
|
10
|
+
from qingflow_mcp.server_app_user import main
|
|
11
11
|
|
|
12
12
|
if __name__ == "__main__":
|
|
13
13
|
main()
|
package/npm/lib/runtime.mjs
CHANGED
|
@@ -43,29 +43,320 @@ export function getCodexHome() {
|
|
|
43
43
|
return path.join(home, ".codex");
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
function getHomeDir() {
|
|
47
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
48
|
+
if (!home) {
|
|
49
|
+
throw new Error("Cannot resolve user home because HOME is not set.");
|
|
50
|
+
}
|
|
51
|
+
return home;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getAgentSkillDest(agent, scope, cwd = process.cwd()) {
|
|
55
|
+
const normalizedAgent = agent.trim().toLowerCase();
|
|
56
|
+
const normalizedScope = scope.trim().toLowerCase();
|
|
57
|
+
const home = getHomeDir();
|
|
58
|
+
if (!["user", "project"].includes(normalizedScope)) {
|
|
59
|
+
throw new Error(`Unsupported skills scope '${scope}'. Expected 'user' or 'project'.`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const projectRoots = {
|
|
63
|
+
codex: [cwd, ".codex", "skills"],
|
|
64
|
+
claude: [cwd, ".claude", "skills"],
|
|
65
|
+
"claude-code": [cwd, ".claude", "skills"],
|
|
66
|
+
cursor: [cwd, ".cursor", "skills"],
|
|
67
|
+
generic: [cwd, ".agents", "skills"],
|
|
68
|
+
};
|
|
69
|
+
const userRoots = {
|
|
70
|
+
codex: [process.env.CODEX_HOME?.trim() || path.join(home, ".codex"), "skills"],
|
|
71
|
+
claude: [home, ".claude", "skills"],
|
|
72
|
+
"claude-code": [home, ".claude", "skills"],
|
|
73
|
+
cursor: [home, ".cursor", "skills"],
|
|
74
|
+
generic: [home, ".agents", "skills"],
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const roots = normalizedScope === "project" ? projectRoots : userRoots;
|
|
78
|
+
const parts = roots[normalizedAgent];
|
|
79
|
+
if (!parts) {
|
|
80
|
+
throw new Error(`Unsupported skills agent '${agent}'. Expected one of: codex, claude, claude-code, cursor, generic.`);
|
|
81
|
+
}
|
|
82
|
+
return path.resolve(...parts);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function listBundledSkills(packageRoot) {
|
|
47
86
|
const skillsSrc = path.join(packageRoot, "skills");
|
|
48
87
|
if (!fs.existsSync(skillsSrc)) {
|
|
49
|
-
return
|
|
88
|
+
return [];
|
|
50
89
|
}
|
|
51
90
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
91
|
+
return fs
|
|
92
|
+
.readdirSync(skillsSrc, { withFileTypes: true })
|
|
93
|
+
.filter((entry) => entry.isDirectory() && fs.existsSync(path.join(skillsSrc, entry.name, "SKILL.md")))
|
|
94
|
+
.map((entry) => entry.name)
|
|
95
|
+
.sort();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function sameRealPath(a, b) {
|
|
99
|
+
try {
|
|
100
|
+
return fs.realpathSync(a) === fs.realpathSync(b);
|
|
101
|
+
} catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
55
105
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
106
|
+
function installOneSkill(src, dest, { force, mode }) {
|
|
107
|
+
let existingStat = null;
|
|
108
|
+
try {
|
|
109
|
+
existingStat = fs.lstatSync(dest);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (error.code !== "ENOENT") {
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (existingStat) {
|
|
116
|
+
if (existingStat.isSymbolicLink() && sameRealPath(dest, src)) {
|
|
117
|
+
return { status: "unchanged" };
|
|
118
|
+
}
|
|
119
|
+
if (!force) {
|
|
120
|
+
return { status: "conflict" };
|
|
60
121
|
}
|
|
61
|
-
const src = path.join(skillsSrc, entry.name);
|
|
62
|
-
const dest = path.join(skillsDestRoot, entry.name);
|
|
63
122
|
fs.rmSync(dest, { recursive: true, force: true });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (mode === "copy") {
|
|
64
126
|
fs.cpSync(src, dest, { recursive: true });
|
|
65
|
-
installed
|
|
127
|
+
return { status: "installed" };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fs.symlinkSync(src, dest, "dir");
|
|
131
|
+
return { status: "installed" };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function writeSkillProvenance(skillsDestRoot, skillName, payload) {
|
|
135
|
+
const provenanceRoot = path.join(skillsDestRoot, ".qingflow-skill-sources");
|
|
136
|
+
fs.mkdirSync(provenanceRoot, { recursive: true });
|
|
137
|
+
fs.writeFileSync(path.join(provenanceRoot, `${skillName}.json`), `${JSON.stringify(payload, null, 2)}\n`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function installBundledSkills(
|
|
141
|
+
packageRoot,
|
|
142
|
+
{
|
|
143
|
+
agent = "codex",
|
|
144
|
+
scope = "user",
|
|
145
|
+
mode = "symlink",
|
|
146
|
+
force = false,
|
|
147
|
+
skills = [],
|
|
148
|
+
cwd = process.cwd(),
|
|
149
|
+
} = {},
|
|
150
|
+
) {
|
|
151
|
+
const skillsSrc = path.join(packageRoot, "skills");
|
|
152
|
+
if (!fs.existsSync(skillsSrc)) {
|
|
153
|
+
return { installed: [], unchanged: [], conflicts: [], skipped: true, destination: null };
|
|
66
154
|
}
|
|
67
155
|
|
|
68
|
-
|
|
156
|
+
const validModes = new Set(["symlink", "copy"]);
|
|
157
|
+
if (!validModes.has(mode)) {
|
|
158
|
+
throw new Error(`Unsupported skills install mode '${mode}'. Expected 'symlink' or 'copy'.`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const available = listBundledSkills(packageRoot);
|
|
162
|
+
const wanted = skills.length ? skills : available;
|
|
163
|
+
const unknown = wanted.filter((skillName) => !available.includes(skillName));
|
|
164
|
+
if (unknown.length) {
|
|
165
|
+
throw new Error(`Unknown bundled skill(s): ${unknown.join(", ")}. Available: ${available.join(", ")}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const skillsDestRoot = getAgentSkillDest(agent, scope, cwd);
|
|
169
|
+
fs.mkdirSync(skillsDestRoot, { recursive: true });
|
|
170
|
+
|
|
171
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
172
|
+
const result = {
|
|
173
|
+
installed: [],
|
|
174
|
+
unchanged: [],
|
|
175
|
+
conflicts: [],
|
|
176
|
+
skipped: false,
|
|
177
|
+
destination: skillsDestRoot,
|
|
178
|
+
agent,
|
|
179
|
+
scope,
|
|
180
|
+
mode,
|
|
181
|
+
};
|
|
182
|
+
|
|
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
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function parseSkillsCliArgs(args) {
|
|
207
|
+
const parsed = {
|
|
208
|
+
command: "help",
|
|
209
|
+
agent: "codex",
|
|
210
|
+
scope: "user",
|
|
211
|
+
mode: "symlink",
|
|
212
|
+
force: false,
|
|
213
|
+
json: false,
|
|
214
|
+
skills: [],
|
|
215
|
+
};
|
|
216
|
+
const rest = [...args];
|
|
217
|
+
if (rest.length && !rest[0].startsWith("-")) {
|
|
218
|
+
parsed.command = rest.shift();
|
|
219
|
+
}
|
|
220
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
221
|
+
const arg = rest[index];
|
|
222
|
+
const next = () => {
|
|
223
|
+
index += 1;
|
|
224
|
+
if (index >= rest.length) {
|
|
225
|
+
throw new Error(`Missing value for ${arg}`);
|
|
226
|
+
}
|
|
227
|
+
return rest[index];
|
|
228
|
+
};
|
|
229
|
+
switch (arg) {
|
|
230
|
+
case "--agent":
|
|
231
|
+
case "-a":
|
|
232
|
+
parsed.agent = next();
|
|
233
|
+
break;
|
|
234
|
+
case "--scope":
|
|
235
|
+
case "-s":
|
|
236
|
+
parsed.scope = next();
|
|
237
|
+
break;
|
|
238
|
+
case "--mode":
|
|
239
|
+
case "-m":
|
|
240
|
+
parsed.mode = next();
|
|
241
|
+
break;
|
|
242
|
+
case "--skill":
|
|
243
|
+
parsed.skills.push(next());
|
|
244
|
+
break;
|
|
245
|
+
case "--all":
|
|
246
|
+
parsed.agent = "all";
|
|
247
|
+
break;
|
|
248
|
+
case "--force":
|
|
249
|
+
case "-f":
|
|
250
|
+
parsed.force = true;
|
|
251
|
+
break;
|
|
252
|
+
case "--copy":
|
|
253
|
+
parsed.mode = "copy";
|
|
254
|
+
break;
|
|
255
|
+
case "--json":
|
|
256
|
+
parsed.json = true;
|
|
257
|
+
break;
|
|
258
|
+
case "--help":
|
|
259
|
+
case "-h":
|
|
260
|
+
parsed.command = "help";
|
|
261
|
+
break;
|
|
262
|
+
default:
|
|
263
|
+
if (arg.startsWith("-")) {
|
|
264
|
+
throw new Error(`Unknown option ${arg}`);
|
|
265
|
+
}
|
|
266
|
+
parsed.skills.push(arg);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return parsed;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function printSkillsHelp() {
|
|
274
|
+
console.log(`Qingflow bundled skills
|
|
275
|
+
|
|
276
|
+
Usage:
|
|
277
|
+
qingflow-skills list [--json]
|
|
278
|
+
qingflow-skills install [--agent codex|claude|claude-code|cursor|generic|all] [--scope user|project] [--mode symlink|copy] [--skill name] [--force] [--json]
|
|
279
|
+
|
|
280
|
+
Examples:
|
|
281
|
+
qingflow-skills list
|
|
282
|
+
qingflow-skills install --agent codex --scope user
|
|
283
|
+
qingflow-skills install --agent claude-code --scope project --copy
|
|
284
|
+
qingflow-skills install --agent all --scope project
|
|
285
|
+
|
|
286
|
+
Defaults:
|
|
287
|
+
--agent codex
|
|
288
|
+
--scope user
|
|
289
|
+
--mode symlink
|
|
290
|
+
|
|
291
|
+
Existing skills are not overwritten unless --force is provided.`);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function printInstallSummary(result) {
|
|
295
|
+
console.log(`[qingflow-skills] destination: ${result.destination}`);
|
|
296
|
+
if (result.installed.length) {
|
|
297
|
+
console.log(`[qingflow-skills] installed: ${result.installed.join(", ")}`);
|
|
298
|
+
}
|
|
299
|
+
if (result.unchanged.length) {
|
|
300
|
+
console.log(`[qingflow-skills] unchanged: ${result.unchanged.join(", ")}`);
|
|
301
|
+
}
|
|
302
|
+
if (result.conflicts.length) {
|
|
303
|
+
console.log(`[qingflow-skills] conflicts: ${result.conflicts.join(", ")}`);
|
|
304
|
+
console.log("[qingflow-skills] Re-run with --force to replace conflicting skills.");
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function runSkillsCli(packageRoot, args) {
|
|
309
|
+
let parsed;
|
|
310
|
+
try {
|
|
311
|
+
parsed = parseSkillsCliArgs(args);
|
|
312
|
+
if (parsed.command === "help") {
|
|
313
|
+
printSkillsHelp();
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (parsed.command === "list") {
|
|
317
|
+
const skills = listBundledSkills(packageRoot);
|
|
318
|
+
if (parsed.json) {
|
|
319
|
+
console.log(JSON.stringify({ skills }, null, 2));
|
|
320
|
+
} else {
|
|
321
|
+
for (const skill of skills) {
|
|
322
|
+
console.log(skill);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (parsed.command !== "install") {
|
|
328
|
+
throw new Error(`Unknown qingflow skills command '${parsed.command}'.`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const agents = parsed.agent === "all" ? ["codex", "claude-code", "cursor", "generic"] : [parsed.agent];
|
|
332
|
+
const results = agents.map((agent) =>
|
|
333
|
+
installBundledSkills(packageRoot, {
|
|
334
|
+
agent,
|
|
335
|
+
scope: parsed.scope,
|
|
336
|
+
mode: parsed.mode,
|
|
337
|
+
force: parsed.force,
|
|
338
|
+
skills: parsed.skills,
|
|
339
|
+
}),
|
|
340
|
+
);
|
|
341
|
+
if (parsed.json) {
|
|
342
|
+
console.log(JSON.stringify({ results }, null, 2));
|
|
343
|
+
} else {
|
|
344
|
+
for (const result of results) {
|
|
345
|
+
printInstallSummary(result);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (results.some((result) => result.conflicts.length)) {
|
|
349
|
+
process.exit(2);
|
|
350
|
+
}
|
|
351
|
+
} catch (error) {
|
|
352
|
+
if (parsed?.json) {
|
|
353
|
+
console.error(JSON.stringify({ error: error.message }, null, 2));
|
|
354
|
+
} else {
|
|
355
|
+
console.error(`[qingflow-skills] ${error.message}`);
|
|
356
|
+
console.error("Run 'qingflow-skills --help' for usage.");
|
|
357
|
+
}
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
69
360
|
}
|
|
70
361
|
|
|
71
362
|
export function getVenvDir(packageRoot) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ensurePythonEnv, getPackageRoot
|
|
1
|
+
import { ensurePythonEnv, getPackageRoot } from "../lib/runtime.mjs";
|
|
2
2
|
|
|
3
3
|
const packageRoot = getPackageRoot(import.meta.url);
|
|
4
4
|
|
|
@@ -6,10 +6,6 @@ try {
|
|
|
6
6
|
console.log("[qingflow-mcp] Bootstrapping Python runtime...");
|
|
7
7
|
ensurePythonEnv(packageRoot, { commandName: "qingflow-app-builder-mcp" });
|
|
8
8
|
console.log("[qingflow-mcp] Python runtime is ready.");
|
|
9
|
-
const skills = installBundledSkills(packageRoot);
|
|
10
|
-
if (!skills.skipped) {
|
|
11
|
-
console.log(`[qingflow-mcp] Installed skills to ${skills.destination}: ${skills.installed.join(", ")}`);
|
|
12
|
-
}
|
|
13
9
|
} catch (error) {
|
|
14
10
|
console.error(`[qingflow-mcp] postinstall failed: ${error.message}`);
|
|
15
11
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qingflow-tech/qingflow-app-builder-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "Builder MCP for Qingflow app/package/system design and staged solution workflows.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"qingflow-app-builder-mcp": "./npm/bin/qingflow-app-builder-mcp.mjs"
|
|
8
|
+
"qingflow-app-builder-mcp": "./npm/bin/qingflow-app-builder-mcp.mjs",
|
|
9
|
+
"qingflow-app-builder-mcp-skills": "./npm/bin/qingflow-skills.mjs"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
12
|
"postinstall": "node ./npm/scripts/postinstall.mjs"
|
package/pyproject.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: qingflow-app-builder
|
|
3
|
-
description: Build, configure, and modify Qingflow apps and systems
|
|
3
|
+
description: Build, configure, and modify Qingflow apps and systems using the current Wingent Momo runtime MCP session. Use when the user wants to apply or repair an existing SolutionSpec, modify an existing package with app, view, workflow, portal, navigation, or reporting tools, verify builder-side results, or troubleshoot system-building behavior. Do not use this skill to install the MCP or to author a brand new SolutionSpec from scratch.
|
|
4
4
|
metadata:
|
|
5
5
|
short-description: Build and modify Qingflow apps and systems
|
|
6
6
|
---
|
|
@@ -10,11 +10,11 @@ metadata:
|
|
|
10
10
|
## Overview
|
|
11
11
|
|
|
12
12
|
This skill is for the current **public builder surface**, not for legacy low-level builder writes.
|
|
13
|
-
|
|
13
|
+
In Wingent Momo runtime, trust the injected MCP session, credentials, workspace, and route context until a tool explicitly reports otherwise.
|
|
14
14
|
|
|
15
15
|
Before any write or verification flow, identify whether the task targets `test` or `prod` and read [references/environments.md](references/environments.md). If the user did not specify one, default to `prod`.
|
|
16
16
|
|
|
17
|
-
Before choosing a route, skim the shared maintenance baseline: [public-surface-sync.md](
|
|
17
|
+
Before choosing a route, skim the shared maintenance baseline: [public-surface-sync.md](references/public-surface-sync.md).
|
|
18
18
|
|
|
19
19
|
## Current Public Mental Model
|
|
20
20
|
|
|
@@ -105,14 +105,13 @@ Treat these as the official surface. Do not default to `package_create`, `packag
|
|
|
105
105
|
|
|
106
106
|
## Standard Operating Order
|
|
107
107
|
|
|
108
|
-
1.
|
|
109
|
-
2.
|
|
110
|
-
3.
|
|
111
|
-
4. Resolve the smallest stable target:
|
|
108
|
+
1. Trust the current MCP/session when it is already injected by the runtime; only run auth/workspace recovery after a tool explicitly reports an auth, credential, or workspace error
|
|
109
|
+
2. Confirm whether the task is read-only or write-impacting
|
|
110
|
+
3. Resolve the smallest stable target:
|
|
112
111
|
- app-scoped work -> `app_resolve`
|
|
113
112
|
- package-scoped work with known id -> `package_get`
|
|
114
113
|
- portal inventory -> `portal_list`
|
|
115
|
-
|
|
114
|
+
4. Read only the smallest config slice needed:
|
|
116
115
|
- app map -> `app_get` (default entry; includes compact views, charts, custom buttons, and associated resource pool)
|
|
117
116
|
- fields -> `app_get_fields`
|
|
118
117
|
- layout -> `app_get_layout`
|
|
@@ -120,8 +119,8 @@ Treat these as the official surface. Do not default to `package_create`, `packag
|
|
|
120
119
|
- flow -> `app_get_flow`
|
|
121
120
|
- charts -> `app_get_charts` only when the app_get compact list is not enough
|
|
122
121
|
- portal -> `portal_get`
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
5. If the public shape is unclear, call `builder_tool_contract`
|
|
123
|
+
6. Apply the smallest patch tool that fits:
|
|
125
124
|
- fields -> `app_schema_apply`
|
|
126
125
|
- layout -> `app_layout_apply`
|
|
127
126
|
- flow -> `app_flow_apply`
|
|
@@ -131,11 +130,12 @@ Treat these as the official surface. Do not default to `package_create`, `packag
|
|
|
131
130
|
- existing charts -> `app_charts_apply.patch_charts`; new/full charts -> `app_charts_apply.upsert_charts`
|
|
132
131
|
- portal -> `portal_apply`
|
|
133
132
|
- package metadata/layout -> `package_apply`
|
|
134
|
-
|
|
133
|
+
7. Use `app_publish_verify` only when the user explicitly wants final publish/live verification or you need a dedicated verification pass
|
|
135
134
|
|
|
136
135
|
## Safe Usage Rules
|
|
137
136
|
|
|
138
137
|
- Do not guess package identity from a loose name. Public package work assumes a known `package_id`, or an explicit create/update intent through `package_apply`.
|
|
138
|
+
- Do not perform routine auth probes before every builder action in runtime contexts with injected MCP credentials; recover auth only after an actual auth/workspace failure.
|
|
139
139
|
- If `package_id` is unknown, derive it from related app/portal readback when possible; otherwise ask the user instead of falling back to hidden package lookup tools.
|
|
140
140
|
- Do not use `package_create` or `package_attach_app` as a public default path. If they still appear in low-level code, treat them as internal/legacy implementation details.
|
|
141
141
|
- Do not use raw `portal_*` writes or raw `qingbi_report_*` writes as the default builder strategy.
|
|
@@ -241,7 +241,7 @@ For add-data buttons that create a child record linked back to the current recor
|
|
|
241
241
|
|
|
242
242
|
## Resources
|
|
243
243
|
|
|
244
|
-
- Shared public-surface baseline: [public-surface-sync.md](
|
|
244
|
+
- Shared public-surface baseline: [public-surface-sync.md](references/public-surface-sync.md)
|
|
245
245
|
- Environment switching: [references/environments.md](references/environments.md)
|
|
246
246
|
- Tool choice and sequencing: [references/tool-selection.md](references/tool-selection.md)
|
|
247
247
|
- Field matching rules: [references/match-rules.md](references/match-rules.md)
|
|
@@ -6,9 +6,9 @@ This playbook follows the current public builder surface, not legacy package hel
|
|
|
6
6
|
Do not use this playbook when the user is really asking for a system/package with multiple forms or modules. In that case:
|
|
7
7
|
|
|
8
8
|
1. read or create the package through `package_get` / `package_apply`
|
|
9
|
-
2. create
|
|
9
|
+
2. create the related apps in one `app_schema_apply(apps=[...])` call
|
|
10
10
|
3. keep package ownership on the public `package_id` path instead of a separate attach step
|
|
11
|
-
4. add relation fields
|
|
11
|
+
4. add same-call relation fields with `target_app_ref` when one new app references another new app
|
|
12
12
|
|
|
13
13
|
Hierarchy reminder:
|
|
14
14
|
|
|
@@ -138,7 +138,7 @@ Expected on create. Continue with `create_if_missing=true`.
|
|
|
138
138
|
|
|
139
139
|
### `CREATE_APP_ROUTE_NOT_FOUND`
|
|
140
140
|
|
|
141
|
-
The create route did not resolve in the current backend route context.
|
|
141
|
+
The create route did not resolve in the current backend route context. In Wingent Momo runtime, first retry the same `app_schema_apply` after confirming the injected session is still active. Only run `workspace_select` if a business tool explicitly reports a missing or wrong workspace.
|
|
142
142
|
|
|
143
143
|
### Hierarchy modeling mistake
|
|
144
144
|
|
|
@@ -16,7 +16,7 @@ If the user did not specify an environment, default to `prod`.
|
|
|
16
16
|
Use test for:
|
|
17
17
|
|
|
18
18
|
- first application of a new `SolutionSpec`
|
|
19
|
-
- trying `
|
|
19
|
+
- trying builder apply flows end-to-end with readback verification, or `solution_install` when the user is installing an existing packaged solution
|
|
20
20
|
- package initialization and schema evolution experiments
|
|
21
21
|
- mock data creation, with at least `5` records per relevant entity unless the user asks for fewer
|
|
22
22
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Auth and workspace
|
|
4
4
|
|
|
5
5
|
- `auth_*` success does not mean a workspace is selected
|
|
6
|
-
-
|
|
6
|
+
- In Wingent Momo runtime, do not run `workspace_select` before builder work. Re-run it only after an explicit auth/route recovery or when a business tool reports the workspace is missing.
|
|
7
7
|
|
|
8
8
|
## Package ownership
|
|
9
9
|
|