openyida 2026.8.26 → 2026.8.27-2
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 +11 -27
- package/bin/yida.js +12 -78
- package/lib/app/builder-ai-source.js +0 -2
- package/lib/asset/ai-image.js +1 -1
- package/lib/core/browser-handoff.js +1 -1
- package/lib/core/check-update.js +66 -21
- package/lib/core/command-manifest.js +0 -18
- package/lib/core/copy.js +37 -76
- package/lib/core/doctor.js +0 -1
- package/lib/core/env-cmd.js +0 -10
- package/lib/core/env.js +2 -10
- package/lib/core/locales/en.js +7 -11
- package/lib/core/locales/zh.js +7 -11
- package/lib/core/update.js +324 -15
- package/lib/core/utils.js +58 -115
- package/lib/report/chart-builder.js +26 -0
- package/lib/report/data-model.js +44 -48
- package/lib/report/field-utils.js +47 -3
- package/package.json +2 -9
- package/scripts/postinstall.js +2 -17
- package/yida-skills/references/task-retrospective.md +1 -1
- package/yida-skills/skills/yida-form-detail/SKILL.md +0 -1
- package/yida-skills/skills/yida-report/references/schema-builder-details.md +1 -1
- package/docs/capabilities.md +0 -222
- package/scripts/build-skills-package.js +0 -513
- package/scripts/check-release-risks.js +0 -254
- package/scripts/check-syntax.js +0 -112
- package/scripts/demo-dws.sh +0 -106
- package/scripts/generate-command-docs.js +0 -155
- package/scripts/i18n-baseline.json +0 -72
- package/scripts/nightly-smoke.js +0 -116
- package/scripts/solution-center-runner.js +0 -368
- package/scripts/validate-ci.sh +0 -64
- package/scripts/validate-command-manifest.js +0 -403
- package/scripts/validate-i18n.js +0 -227
- package/scripts/validate-package-size.js +0 -109
- package/scripts/validate-skills.js +0 -608
- package/scripts/validate-structure.js +0 -81
package/README.md
CHANGED
|
@@ -50,6 +50,10 @@ npm install -g openyida
|
|
|
50
50
|
|
|
51
51
|
OpenYida requires Node.js 18 or later. The package exposes both `openyida` and `yida` commands.
|
|
52
52
|
|
|
53
|
+
For npm global installations, OpenYida checks the npm registry before a normal command at most once every 24 hours. When a newer version is available, it installs that exact version and then reruns the original command. This behavior is identical whether the local command is launched from a terminal, Codex, Claude Code, or Qoder. Managed cloud Agent runtimes are excluded before any cache access, registry request, or npm invocation.
|
|
54
|
+
|
|
55
|
+
Set `OPENYIDA_NO_AUTO_UPDATE=1` to disable automatic updates. `OPENYIDA_AUTO_UPDATE_SECS` can override the check interval for development and testing. The explicit `openyida update` command remains available.
|
|
56
|
+
|
|
53
57
|
If Codex is already installed, OpenYida also imports a local Codex plugin during postinstall. Restart Codex after installation, then type `@宜搭` or `@openyida` in the composer to attach the OpenYida context.
|
|
54
58
|
|
|
55
59
|
### 2. Check Your Environment
|
|
@@ -105,21 +109,7 @@ Build an IPD workflow for chip production, including approval nodes and dashboar
|
|
|
105
109
|
Generate a public landing page and publish it to my Yida app.
|
|
106
110
|
```
|
|
107
111
|
|
|
108
|
-
The agent can then call OpenYida commands to create the application, generate source files, publish pages, and return the final Yida URLs. In Codex, QwenWork,
|
|
109
|
-
|
|
110
|
-
## Wukong Installation
|
|
111
|
-
|
|
112
|
-
Wukong uses manual skill package installation instead of npm:
|
|
113
|
-
|
|
114
|
-
1. Download the latest `.zip` skill package from [GitHub Releases](https://github.com/openyida/openyida/releases).
|
|
115
|
-
2. Open Wukong.
|
|
116
|
-
3. Go to **Skill Center** > **Upload Skill** and select the downloaded package.
|
|
117
|
-
|
|
118
|
-
For Wukong terminal work, make sure its bundled Node.js path is active before running `node`, `npm`, or `npx` commands:
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
export PATH="$HOME/.real/.bin/node/bin:$PATH"
|
|
122
|
-
```
|
|
112
|
+
The agent can then call OpenYida commands to create the application, generate source files, publish pages, and return the final Yida URLs. In Codex, QwenWork, Qoder, Qoder IDE, and QoderWork environments, successful creation and publish commands also include a browser handoff so the agent can open the resulting Yida page in the in-app browser. Use `--open` to force this handoff or `--no-open` to suppress it.
|
|
123
113
|
|
|
124
114
|
## Supported AI Coding Tools
|
|
125
115
|
|
|
@@ -128,14 +118,13 @@ export PATH="$HOME/.real/.bin/node/bin:$PATH"
|
|
|
128
118
|
| [Codex](https://openai.com/codex/) | Full support |
|
|
129
119
|
| [Claude Code](https://claude.ai/code) | Full support |
|
|
130
120
|
| [MuleRun](https://mulerun.com) | Full support |
|
|
131
|
-
| [Aone Copilot](https://copilot.code.alibaba-inc.com) | Full support |
|
|
132
121
|
| [OpenCode](https://opencode.ai) | Full support |
|
|
133
122
|
| [Cursor](https://cursor.com/) | Full support |
|
|
134
123
|
| [Visual Studio Code](https://code.visualstudio.com/) | Full support |
|
|
135
124
|
| QwenWork(千问办公) | Full support |
|
|
136
|
-
| [
|
|
137
|
-
| [Qoder](https://qoder.com) | Full support |
|
|
138
|
-
| [
|
|
125
|
+
| [Qoder](https://qoder.com/download) | Full support |
|
|
126
|
+
| [Qoder IDE](https://qoder.com/ide) | Full support |
|
|
127
|
+
| [QoderWork](https://qoder.com/download) | Full support |
|
|
139
128
|
|
|
140
129
|
## How It Works
|
|
141
130
|
|
|
@@ -320,8 +309,6 @@ OPENYIDA_E2E=1 npm run eval:all -- --skill yida-dashboard --screenshot
|
|
|
320
309
|
# whitelist. Use the Node version you start it with, so run `nvm use 20` first.
|
|
321
310
|
npm run eval:dashboard # http://127.0.0.1:4500
|
|
322
311
|
|
|
323
|
-
# You can also run eval directly via the CLI:
|
|
324
|
-
openyida eval --mode comprehensive --skill yida-dashboard
|
|
325
312
|
```
|
|
326
313
|
|
|
327
314
|
Configuration precedence is `CLI flag > env (OPENYIDA_EVAL_*) > scripts/eval/eval.config.json > defaults`.
|
|
@@ -501,7 +488,6 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
501
488
|
| `openyida copy [--force]` | Copy project working directory |
|
|
502
489
|
| `openyida sample [--list]` | Output code samples/templates |
|
|
503
490
|
| `openyida doctor [--fix]` | Environment diagnostics & auto-fix |
|
|
504
|
-
| `openyida eval --mode <mode> [--skill <name>] [--runs N]` | Multi-dimensional skill evaluation (doc quality, routing accuracy, safety, etc.) |
|
|
505
491
|
| `openyida db-seq-fix [--fix]` | Detect and repair PostgreSQL sequence drift |
|
|
506
492
|
| `openyida formula evaluate <formula\|file> [--schema file]` | Static-check Yida formula syntax and field refs |
|
|
507
493
|
| `openyida update` | Check and update to latest version |
|
|
@@ -545,22 +531,20 @@ Form field definitions can include `alias` or `componentAlias` to populate Yida
|
|
|
545
531
|
|
|
546
532
|
## Agent Skills
|
|
547
533
|
|
|
548
|
-
The `yida-skills/` directory is the source skill library used by OpenYida during development
|
|
534
|
+
The `yida-skills/` directory is the source skill library used by OpenYida during development and distributed with the npm package.
|
|
549
535
|
|
|
550
536
|
| Path | Purpose |
|
|
551
537
|
|------|---------|
|
|
552
538
|
| `yida-skills/SKILL.md` | Entry point and skill index |
|
|
553
539
|
| `yida-skills/skills/` | Self-contained sub-skills for app, form, process, page, data, and integration work |
|
|
554
540
|
| `yida-skills/references/` | Shared Yida API, model API, and query-condition references |
|
|
555
|
-
| `dist/skills/openyida/` | Generated Wukong upload package root; contains one root `SKILL.md` and reference-only subskill docs |
|
|
556
|
-
| `openyida-skills.zip` | Generated Wukong upload package; upload this file in Wukong |
|
|
557
541
|
|
|
558
542
|
When OpenYida is used inside a supported AI coding environment, these skills help the agent choose the right command sequence and file conventions.
|
|
559
543
|
|
|
560
|
-
For Wukong manual import, upload the generated `openyida-skills.zip`. The package follows Wukong's custom skill rules: folder name and `frontmatter.name` are both `openyida`, root frontmatter only contains `name` and `description`, and long references live under `references/`.
|
|
561
|
-
|
|
562
544
|
For QwenWork, use the same user-level global skills layout as QoderWork: `~/.qwenworkcn/skills/yida-skills/`. Skip QwenWork setup when `~/.qwenworkcn` is not present.
|
|
563
545
|
|
|
546
|
+
Qoder and Qoder IDE share the user-level `~/.qoder/skills/yida-skills/` directory. QoderWork remains a separate product and uses `~/.qoderwork/skills/yida-skills/`.
|
|
547
|
+
|
|
564
548
|
For Codex, `npm install -g openyida` additionally creates a local plugin marketplace under `~/.openyida/codex-plugin` and enables `openyida@openyida` in `~/.codex/config.toml` when Codex is detected. This makes OpenYida show up in Codex's `@` plugin menu as **宜搭** after Codex reloads.
|
|
565
549
|
|
|
566
550
|
## Examples
|
package/bin/yida.js
CHANGED
|
@@ -20,77 +20,6 @@ const command = process.argv[2];
|
|
|
20
20
|
const args = process.argv.slice(3);
|
|
21
21
|
const rawArgs = process.argv.slice(3);
|
|
22
22
|
|
|
23
|
-
function isAgentEnvironment(env) {
|
|
24
|
-
return !!(
|
|
25
|
-
env.CODEX_SHELL ||
|
|
26
|
-
env.CODEX_CI ||
|
|
27
|
-
env.CODEX_THREAD_ID ||
|
|
28
|
-
env.CODEX_HOME ||
|
|
29
|
-
env.CLAUDE_CODE ||
|
|
30
|
-
env.CLAUDE_CODE_ENTRYPOINT ||
|
|
31
|
-
env.MULERUN_CHAT_ID ||
|
|
32
|
-
env.MULE_DATA_DIR ||
|
|
33
|
-
env.MULE_WORKSPACE_DIR ||
|
|
34
|
-
env.OPENCODE ||
|
|
35
|
-
env.OPENCODE_CLIENT ||
|
|
36
|
-
env.QODER_IDE ||
|
|
37
|
-
env.QODER_AGENT ||
|
|
38
|
-
env.QODERCLI_INTEGRATION_MODE ||
|
|
39
|
-
env.QODER_WORK_INTEGRATION_PRODUCT ||
|
|
40
|
-
env.QWENWORK_INTEGRATION_MODE ||
|
|
41
|
-
env.QWENWORKCN_INTEGRATION_MODE ||
|
|
42
|
-
env.QWENWORK ||
|
|
43
|
-
env.QWENWORK_CLIENT ||
|
|
44
|
-
env.QWENWORK_WORKSPACE_DIR ||
|
|
45
|
-
env.QWENWORK_SANDBOX_ID ||
|
|
46
|
-
env.QWENWORK_PREVIEW_URL ||
|
|
47
|
-
env.QWENWORK_VNC_URL ||
|
|
48
|
-
(env.AGENT_PLATFORM || '').toLowerCase().includes('qwenwork') ||
|
|
49
|
-
env.CURSOR_TRACE_ID ||
|
|
50
|
-
env.AGENT_WORK_ROOT ||
|
|
51
|
-
env.OPENYIDA_AGENT_MODE ||
|
|
52
|
-
(env.__CFBundleIdentifier || '').toLowerCase().includes('codex') ||
|
|
53
|
-
(env.__CFBundleIdentifier || '').toLowerCase().includes('qoder') ||
|
|
54
|
-
(env.__CFBundleIdentifier || '').toLowerCase().includes('qwenwork') ||
|
|
55
|
-
(env.__CFBundleIdentifier || '').toLowerCase().includes('qwen-work')
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function shouldRunUpdateCheck() {
|
|
60
|
-
if (process.env.OPENYIDA_SKIP_UPDATE_CHECK || process.env.NO_UPDATE_NOTIFIER) {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
if (process.env.CI || isAgentEnvironment(process.env)) {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
if (!process.stderr.isTTY) {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
if (!command || command === '--help' || command === '-h' || command === '--version' || command === '-v') {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
if (command === 'commands' || command === 'agent-capabilities' || command === 'mcp') {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
if (args.includes('--json') || args.includes('--check-only')) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function maybeCheckForUpdate() {
|
|
85
|
-
if (!shouldRunUpdateCheck()) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
const { checkUpdate } = require('../lib/core/check-update');
|
|
89
|
-
checkUpdate(currentVersion).catch(() => {});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
maybeCheckForUpdate();
|
|
93
|
-
|
|
94
23
|
function shouldUseEnvManagement(argsList) {
|
|
95
24
|
const subCommand = argsList[0];
|
|
96
25
|
return !!subCommand && subCommand !== '--json';
|
|
@@ -369,7 +298,6 @@ const UNSUPPORTED_LEGACY_LOGIN_FLAGS = new Set([
|
|
|
369
298
|
'--browser',
|
|
370
299
|
'--codex',
|
|
371
300
|
'--qoder',
|
|
372
|
-
'--wukong',
|
|
373
301
|
]);
|
|
374
302
|
|
|
375
303
|
function findUnsupportedLegacyLoginFlag(...argLists) {
|
|
@@ -598,6 +526,18 @@ async function main() {
|
|
|
598
526
|
applyQuietFlag();
|
|
599
527
|
applyGlobalEnvironmentFlags();
|
|
600
528
|
|
|
529
|
+
const { maybeAutoUpdate } = require('../lib/core/update');
|
|
530
|
+
const updateResult = await maybeAutoUpdate({
|
|
531
|
+
currentVersion,
|
|
532
|
+
command,
|
|
533
|
+
args,
|
|
534
|
+
argv: process.argv,
|
|
535
|
+
});
|
|
536
|
+
if (updateResult.reexecuted) {
|
|
537
|
+
process.exitCode = updateResult.exitCode;
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
|
|
601
541
|
if (!command || command === '--help' || command === '-h') {
|
|
602
542
|
handleFirstRunGuide();
|
|
603
543
|
printHelp();
|
|
@@ -1258,12 +1198,6 @@ async function main() {
|
|
|
1258
1198
|
break;
|
|
1259
1199
|
}
|
|
1260
1200
|
|
|
1261
|
-
case 'eval': {
|
|
1262
|
-
const { main: evalMain } = require('../scripts/eval/runner');
|
|
1263
|
-
await evalMain(args);
|
|
1264
|
-
break;
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
1201
|
default: {
|
|
1268
1202
|
throwUnknownCommand(command, args);
|
|
1269
1203
|
}
|
package/lib/asset/ai-image.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* 因此本模块不假装能生成图片,而是:
|
|
9
9
|
* 1. 诚实地检测「是否存在内置图片生成能力」(目前恒为 false);
|
|
10
|
-
* 2. 把「生成/寻找素材」这件事交回给正在驱动本 CLI 的智能体(Claude
|
|
10
|
+
* 2. 把「生成/寻找素材」这件事交回给正在驱动本 CLI 的智能体(Claude/Codex 等),
|
|
11
11
|
* 由 skill 文档引导它:能自己生成就生成,否则去免费素材库搜真实图片;
|
|
12
12
|
* 3. 提供推荐的免费/可商用素材库清单与使用纪律,供 skill / agent 参考。
|
|
13
13
|
*
|
package/lib/core/check-update.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* check-update.js - openyida 版本更新检查
|
|
3
3
|
*
|
|
4
|
-
* 向 npm registry
|
|
5
|
-
* 全程异步,不阻塞主命令流程。
|
|
4
|
+
* 向 npm registry 查询 latest dist-tag,并提供 SemVer 比较。
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
'use strict';
|
|
@@ -32,32 +31,71 @@ function fetchLatestVersion() {
|
|
|
32
31
|
});
|
|
33
32
|
req.on('error', () => resolve(null));
|
|
34
33
|
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
35
|
-
req.on('socket', (socket) => {
|
|
36
|
-
if (socket && typeof socket.unref === 'function') {
|
|
37
|
-
socket.unref();
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
if (typeof req.unref === 'function') {
|
|
41
|
-
req.unref();
|
|
42
|
-
}
|
|
43
34
|
});
|
|
44
35
|
}
|
|
45
36
|
|
|
46
37
|
/**
|
|
47
38
|
* 比较版本号,返回 latestVersion 是否比 currentVersion 更新。
|
|
48
|
-
*
|
|
49
|
-
* 仅比较数字部分。
|
|
39
|
+
* 实现 SemVer 2.0.0 的优先级规则,构建元数据不参与比较。
|
|
50
40
|
*/
|
|
51
41
|
function isNewer(currentVersion, latestVersion) {
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
const [cMajor, cMinor, cPatch] = parseParts(currentVersion);
|
|
56
|
-
const [lMajor, lMinor, lPatch] = parseParts(latestVersion);
|
|
42
|
+
const current = parseSemver(currentVersion);
|
|
43
|
+
const latest = parseSemver(latestVersion);
|
|
44
|
+
if (!current || !latest) {return false;}
|
|
57
45
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
46
|
+
for (const key of ['major', 'minor', 'patch']) {
|
|
47
|
+
const compared = compareNumericIdentifier(latest[key], current[key]);
|
|
48
|
+
if (compared !== 0) {return compared > 0;}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return comparePrerelease(latest.prerelease, current.prerelease) > 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function parseSemver(version) {
|
|
55
|
+
if (typeof version !== 'string') {return null;}
|
|
56
|
+
const match = version.trim().match(
|
|
57
|
+
/^(?:v)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/
|
|
58
|
+
);
|
|
59
|
+
if (!match) {return null;}
|
|
60
|
+
return {
|
|
61
|
+
major: match[1],
|
|
62
|
+
minor: match[2],
|
|
63
|
+
patch: match[3],
|
|
64
|
+
prerelease: match[4] ? match[4].split('.') : [],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function comparePrerelease(left, right) {
|
|
69
|
+
if (left.length === 0 || right.length === 0) {
|
|
70
|
+
if (left.length === right.length) {return 0;}
|
|
71
|
+
return left.length === 0 ? 1 : -1;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const length = Math.max(left.length, right.length);
|
|
75
|
+
for (let i = 0; i < length; i++) {
|
|
76
|
+
if (left[i] === undefined) {return -1;}
|
|
77
|
+
if (right[i] === undefined) {return 1;}
|
|
78
|
+
if (left[i] === right[i]) {continue;}
|
|
79
|
+
|
|
80
|
+
const leftNumeric = /^\d+$/.test(left[i]);
|
|
81
|
+
const rightNumeric = /^\d+$/.test(right[i]);
|
|
82
|
+
if (leftNumeric && rightNumeric) {
|
|
83
|
+
return compareNumericIdentifier(left[i], right[i]);
|
|
84
|
+
}
|
|
85
|
+
if (leftNumeric !== rightNumeric) {
|
|
86
|
+
return leftNumeric ? -1 : 1;
|
|
87
|
+
}
|
|
88
|
+
return left[i] > right[i] ? 1 : -1;
|
|
89
|
+
}
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function compareNumericIdentifier(left, right) {
|
|
94
|
+
if (left.length !== right.length) {
|
|
95
|
+
return left.length > right.length ? 1 : -1;
|
|
96
|
+
}
|
|
97
|
+
if (left === right) {return 0;}
|
|
98
|
+
return left > right ? 1 : -1;
|
|
61
99
|
}
|
|
62
100
|
|
|
63
101
|
/**
|
|
@@ -79,4 +117,11 @@ async function checkUpdate(currentVersion) {
|
|
|
79
117
|
}
|
|
80
118
|
}
|
|
81
119
|
|
|
82
|
-
module.exports = {
|
|
120
|
+
module.exports = {
|
|
121
|
+
checkUpdate,
|
|
122
|
+
isNewer,
|
|
123
|
+
fetchLatestVersion,
|
|
124
|
+
parseSemver,
|
|
125
|
+
comparePrerelease,
|
|
126
|
+
compareNumericIdentifier,
|
|
127
|
+
};
|
|
@@ -387,13 +387,6 @@ const COMMAND_SIDE_EFFECTS = new Map([
|
|
|
387
387
|
'update',
|
|
388
388
|
], sideEffect('local_write')),
|
|
389
389
|
|
|
390
|
-
...sideEffectEntries([
|
|
391
|
-
'eval',
|
|
392
|
-
], sideEffect('mixed', {
|
|
393
|
-
read_actions: ['doc-quality', 'coverage', 'routing', 'safety', 'comprehensive', 'baseline', 'pipeline'],
|
|
394
|
-
mutating_actions: ['e2e', 'generate'],
|
|
395
|
-
})),
|
|
396
|
-
|
|
397
390
|
...sideEffectEntries([
|
|
398
391
|
'app-list',
|
|
399
392
|
'connector.detail',
|
|
@@ -666,13 +659,6 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
666
659
|
}],
|
|
667
660
|
})],
|
|
668
661
|
|
|
669
|
-
...permissionEntries([
|
|
670
|
-
'eval',
|
|
671
|
-
], actionDependentPermission({
|
|
672
|
-
preauthorized_actions: ['doc-quality', 'coverage', 'routing', 'safety', 'comprehensive', 'baseline', 'pipeline'],
|
|
673
|
-
ask_actions: ['e2e', 'generate'],
|
|
674
|
-
})),
|
|
675
|
-
|
|
676
662
|
...permissionEntries([
|
|
677
663
|
'add-validation',
|
|
678
664
|
'app-online',
|
|
@@ -1298,10 +1284,6 @@ const COMMAND_GROUPS = [
|
|
|
1298
1284
|
command('copy', ['copy'], 'copy [--force]', 'help.cmd_copy', { requiresLogin: false }),
|
|
1299
1285
|
command('sample', ['sample'], 'sample [--list]', 'help.cmd_sample', { requiresLogin: false }),
|
|
1300
1286
|
command('doctor', ['doctor'], 'doctor [--fix]', 'help.cmd_doctor', { requiresLogin: false }),
|
|
1301
|
-
command('eval', ['eval'], 'eval --mode <mode> [--skill <name>] [--runs N]', 'help.cmd_eval', {
|
|
1302
|
-
requiresLogin: false,
|
|
1303
|
-
output: 'text|json',
|
|
1304
|
-
}),
|
|
1305
1287
|
command('db-seq-fix', ['db-seq-fix'], 'db-seq-fix [--fix]', 'help.cmd_db_seq_fix'),
|
|
1306
1288
|
command('formula.evaluate', ['formula', 'evaluate'], 'formula evaluate <formula|file> [--schema file]', 'help.cmd_formula_evaluate', {
|
|
1307
1289
|
requiresLogin: false,
|
package/lib/core/copy.js
CHANGED
|
@@ -5,20 +5,17 @@
|
|
|
5
5
|
* openyida copy → 复制 project/ 目录模板(默认,合并模式)
|
|
6
6
|
* openyida copy --force → 复制 project/ 目录模板(强制覆盖,先清空目标目录)
|
|
7
7
|
* openyida copy -skills → 复制 yida-skills/ 到当前 AI 工具的 skills 目录
|
|
8
|
-
* 悟空环境下:删除已有 yida-skills/(悟空通过手动上传技能)
|
|
9
8
|
* openyida copy -project → 复制 project/ 目录模板(与默认行为相同,显式指定)
|
|
10
9
|
* openyida copy -project --force → 复制 project/ 目录模板(强制覆盖)
|
|
11
10
|
*
|
|
12
11
|
* 目标策略:
|
|
13
|
-
* -
|
|
14
|
-
* - 其他 AI 工具:复制/链接到当前工程目录(process.cwd())下
|
|
12
|
+
* - 复制/链接到当前工程目录(process.cwd())或宿主显式提供的工作区
|
|
15
13
|
*
|
|
16
14
|
* 源路径:npm 全局安装包根目录(通过 require.resolve 定位)
|
|
17
15
|
*
|
|
18
16
|
* project/ 合并模式(默认):已存在的文件强制覆盖,目标目录中多余的文件保留不动
|
|
19
17
|
* project/ 强制模式(--force):先清空目标目录,再完整复制
|
|
20
|
-
* yida-skills
|
|
21
|
-
* yida-skills/(悟空):删除已有软链或目录(悟空通过手动上传技能,不需要复制)
|
|
18
|
+
* yida-skills/:复制到宿主的 skills 目录,如目标已存在则先清理
|
|
22
19
|
*/
|
|
23
20
|
|
|
24
21
|
'use strict';
|
|
@@ -173,8 +170,7 @@ function ensureProjectWorkspaceDirs(projectDir) {
|
|
|
173
170
|
}
|
|
174
171
|
|
|
175
172
|
/**
|
|
176
|
-
* 删除已有的 yida-skills
|
|
177
|
-
* 悟空通过手动上传技能,不需要软链,执行 -skills 时只做清理。
|
|
173
|
+
* 删除已有的 yida-skills 软链接或目录。
|
|
178
174
|
* 使用 lstatSync 而非 existsSync,可以检测到悬空软链(目标不存在但链接本身存在)。
|
|
179
175
|
* @returns {boolean} 是否执行了删除操作
|
|
180
176
|
*/
|
|
@@ -184,7 +180,6 @@ function removeSkillsLink(destLink) {
|
|
|
184
180
|
stats = fs.lstatSync(destLink);
|
|
185
181
|
} catch {
|
|
186
182
|
// 路径不存在(包括悬空软链也不存在的情况)
|
|
187
|
-
console.log(t('copy.wukong_skills_not_found', destLink));
|
|
188
183
|
return false;
|
|
189
184
|
}
|
|
190
185
|
|
|
@@ -217,20 +212,9 @@ function removeSkillsLink(destLink) {
|
|
|
217
212
|
*/
|
|
218
213
|
function resolveDestBaseFromEnv(activeToolName, activeProjectRoot, envResults, options = {}) {
|
|
219
214
|
const activeResult = envResults.find((r) => r.displayName === activeToolName);
|
|
220
|
-
const isWukong = (activeResult && activeResult.dirName === '.real') ||
|
|
221
|
-
(options.activeTool && options.activeTool.tool === 'wukong');
|
|
222
215
|
const runtimeSource = (options.activeTool && options.activeTool.workspaceRootSource) ||
|
|
223
216
|
(activeResult && activeResult.workspaceRootSource);
|
|
224
217
|
|
|
225
|
-
if (isWukong) {
|
|
226
|
-
if (activeProjectRoot) {
|
|
227
|
-
return path.basename(activeProjectRoot) === 'project'
|
|
228
|
-
? path.dirname(activeProjectRoot)
|
|
229
|
-
: activeProjectRoot;
|
|
230
|
-
}
|
|
231
|
-
return process.env.AGENT_WORK_ROOT || path.join(os.homedir(), '.real', 'workspace');
|
|
232
|
-
}
|
|
233
|
-
|
|
234
218
|
if (
|
|
235
219
|
(activeResult || options.activeTool) &&
|
|
236
220
|
activeProjectRoot &&
|
|
@@ -292,7 +276,6 @@ function run(args = process.argv.slice(3)) {
|
|
|
292
276
|
label('Package', packageRoot, { stderr: false });
|
|
293
277
|
|
|
294
278
|
// 2. 确定目标根目录(检测 AI 工具环境)
|
|
295
|
-
// 同时获取 isWukong 标志,避免后续重复调用 detectEnvironment()
|
|
296
279
|
const {
|
|
297
280
|
activeTool,
|
|
298
281
|
activeToolName,
|
|
@@ -302,9 +285,6 @@ function run(args = process.argv.slice(3)) {
|
|
|
302
285
|
const runtimeOptions = activeTool ? { activeTool } : {};
|
|
303
286
|
const projectResolution = resolveProjectRoot(runtimeOptions);
|
|
304
287
|
const activeProjectRoot = projectResolution.projectRoot || detectedProjectRoot;
|
|
305
|
-
const activeEnvResult = envResults.find((r) => r.isActive);
|
|
306
|
-
const isWukong = (activeEnvResult && activeEnvResult.dirName === '.real') ||
|
|
307
|
-
(activeTool && activeTool.tool === 'wukong');
|
|
308
288
|
const destBase = resolveDestBaseFromEnv(activeToolName, activeProjectRoot, envResults, {
|
|
309
289
|
allowCurrentDir: isForce,
|
|
310
290
|
activeTool,
|
|
@@ -315,9 +295,7 @@ function run(args = process.argv.slice(3)) {
|
|
|
315
295
|
}
|
|
316
296
|
|
|
317
297
|
// 3. 确定要复制/链接的内容
|
|
318
|
-
// - 指定了 -skills
|
|
319
|
-
// 悟空环境:删除已有的 yida-skills/ 软链(悟空手动上传技能,不需要软链)
|
|
320
|
-
// 其他环境:创建 yida-skills/ 软链接(如果存在实际目录则先删除)
|
|
298
|
+
// - 指定了 -skills:复制到 AI 工具可发现的 skills 目录
|
|
321
299
|
// - 指定了 -project:只复制 project/
|
|
322
300
|
// - 两者都没指定(默认):只复制 project/
|
|
323
301
|
// - 两者都指定:同时处理两项
|
|
@@ -329,14 +307,13 @@ function run(args = process.argv.slice(3)) {
|
|
|
329
307
|
|
|
330
308
|
if (shouldCopyProject) {
|
|
331
309
|
// 检查 destBase 是否为空目录:
|
|
332
|
-
// -
|
|
310
|
+
// - 空目录 → 直接把 project/ 内容铺进 destBase,不创建 project/ 这层
|
|
333
311
|
// - 非空目录(已有其他文件)→ 复制整个 project/ 目录(含目录本身)
|
|
334
312
|
const destBaseEntries = fs.existsSync(destBase)
|
|
335
313
|
? fs.readdirSync(destBase).filter((name) => name !== '.DS_Store')
|
|
336
314
|
: [];
|
|
337
315
|
const isDestBaseEmpty = destBaseEntries.length === 0;
|
|
338
|
-
const shouldUseProjectRootDirectly =
|
|
339
|
-
isExplicitRuntimeWorkspaceSource(projectResolution.source);
|
|
316
|
+
const shouldUseProjectRootDirectly = isExplicitRuntimeWorkspaceSource(projectResolution.source);
|
|
340
317
|
|
|
341
318
|
const projectDestDir = (isDestBaseEmpty || shouldUseProjectRootDirectly)
|
|
342
319
|
? destBase
|
|
@@ -355,54 +332,41 @@ function run(args = process.argv.slice(3)) {
|
|
|
355
332
|
}
|
|
356
333
|
|
|
357
334
|
if (shouldLinkSkills) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
335
|
+
// 复制到 AI 工具配置目录的 skills/yida-skills/;
|
|
336
|
+
// 若宿主用户级目录不可写,按 capabilities 诊断降级到工作区可发现目录。
|
|
337
|
+
const activeResult = envResults.find((r) => r.isActive);
|
|
338
|
+
const skillsDiagnostics = buildSkillsDiagnostics({ ...runtimeOptions, projectResolution });
|
|
339
|
+
const selectedSkills = skillsDiagnostics.selected;
|
|
340
|
+
|
|
341
|
+
if (selectedSkills && selectedSkills.path) {
|
|
342
|
+
// 清理旧版遗留在根目录的错误安装
|
|
343
|
+
if (activeResult && selectedSkills.scope === 'user') {
|
|
344
|
+
removeSkillsLink(path.join(os.homedir(), activeResult.dirName, 'yida-skills'));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// 清理已有的 skills/yida-skills/(旧软链或旧目录)
|
|
348
|
+
removeSkillsLink(selectedSkills.path);
|
|
349
|
+
|
|
350
|
+
// 复制文件
|
|
351
|
+
fs.mkdirSync(path.dirname(selectedSkills.path), { recursive: true });
|
|
352
|
+
const count = mergeCopyDir(packageYidaSkillsDir, selectedSkills.path);
|
|
363
353
|
results.push({
|
|
364
354
|
label: 'yida-skills/',
|
|
365
|
-
dest:
|
|
366
|
-
count
|
|
367
|
-
type: '
|
|
355
|
+
dest: selectedSkills.path,
|
|
356
|
+
count,
|
|
357
|
+
type: selectedSkills.workspace_only ? 'copy-workspace' : 'copy'
|
|
368
358
|
});
|
|
369
359
|
} else {
|
|
370
|
-
//
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// 清理已有的 skills/yida-skills/(旧软链或旧目录)
|
|
383
|
-
removeSkillsLink(selectedSkills.path);
|
|
384
|
-
|
|
385
|
-
// 复制文件
|
|
386
|
-
fs.mkdirSync(path.dirname(selectedSkills.path), { recursive: true });
|
|
387
|
-
const count = mergeCopyDir(packageYidaSkillsDir, selectedSkills.path);
|
|
388
|
-
results.push({
|
|
389
|
-
label: 'yida-skills/',
|
|
390
|
-
dest: selectedSkills.path,
|
|
391
|
-
count,
|
|
392
|
-
type: selectedSkills.workspace_only ? 'copy-workspace' : 'copy'
|
|
393
|
-
});
|
|
394
|
-
} else {
|
|
395
|
-
// 未检测到 AI 工具,复制到当前目录下
|
|
396
|
-
const destSkillsDest = path.join(destBase, 'yida-skills');
|
|
397
|
-
removeSkillsLink(destSkillsDest);
|
|
398
|
-
const count = mergeCopyDir(packageYidaSkillsDir, destSkillsDest);
|
|
399
|
-
results.push({
|
|
400
|
-
label: 'yida-skills/',
|
|
401
|
-
dest: destSkillsDest,
|
|
402
|
-
count,
|
|
403
|
-
type: 'copy'
|
|
404
|
-
});
|
|
405
|
-
}
|
|
360
|
+
// 未检测到 AI 工具,复制到当前目录下
|
|
361
|
+
const destSkillsDest = path.join(destBase, 'yida-skills');
|
|
362
|
+
removeSkillsLink(destSkillsDest);
|
|
363
|
+
const count = mergeCopyDir(packageYidaSkillsDir, destSkillsDest);
|
|
364
|
+
results.push({
|
|
365
|
+
label: 'yida-skills/',
|
|
366
|
+
dest: destSkillsDest,
|
|
367
|
+
count,
|
|
368
|
+
type: 'copy'
|
|
369
|
+
});
|
|
406
370
|
}
|
|
407
371
|
}
|
|
408
372
|
|
|
@@ -423,9 +387,6 @@ function run(args = process.argv.slice(3)) {
|
|
|
423
387
|
results.forEach((r) => {
|
|
424
388
|
if (r.type === 'symlink') {
|
|
425
389
|
listItem(`${c.cyan}${r.label.padEnd(14)}${c.reset} ${c.dim}→${c.reset} ${r.dest} ${c.dim}(${t('copy.symlink_label')})${c.reset}`, { stderr: false });
|
|
426
|
-
} else if (r.type === 'wukong-cleanup') {
|
|
427
|
-
const statusText = r.count > 0 ? t('copy.wukong_skills_cleaned') : t('copy.wukong_skills_not_found', r.dest);
|
|
428
|
-
listItem(`${c.cyan}${r.label.padEnd(14)}${c.reset} ${c.dim}→${c.reset} ${r.dest} ${c.dim}(${statusText})${c.reset}`, { stderr: false });
|
|
429
390
|
} else {
|
|
430
391
|
listItem(`${c.cyan}${r.label.padEnd(14)}${c.reset} ${c.dim}→${c.reset} ${r.dest} ${c.dim}(${t('copy.files_count', r.count)})${c.reset}`, { stderr: false });
|
|
431
392
|
}
|
package/lib/core/doctor.js
CHANGED
|
@@ -351,7 +351,6 @@ class VersionChecker {
|
|
|
351
351
|
const skillPaths = [
|
|
352
352
|
path.join(this.projectRoot, 'yida-skills', 'SKILL.md'),
|
|
353
353
|
path.join(require('os').homedir(), '.claude', 'yida-skills', 'SKILL.md'),
|
|
354
|
-
path.join(require('os').homedir(), '.aone_copilot', 'yida-skills', 'SKILL.md'),
|
|
355
354
|
];
|
|
356
355
|
for (const skillPath of skillPaths) {
|
|
357
356
|
if (!fs.existsSync(skillPath)) {continue;}
|
package/lib/core/env-cmd.js
CHANGED
|
@@ -112,16 +112,6 @@ function detectSetupRecommendation() {
|
|
|
112
112
|
return 'intl';
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
let activeTool = null;
|
|
116
|
-
try {
|
|
117
|
-
activeTool = require('./utils').detectActiveTool();
|
|
118
|
-
} catch {
|
|
119
|
-
activeTool = null;
|
|
120
|
-
}
|
|
121
|
-
if (activeTool && activeTool.tool === 'wukong') {
|
|
122
|
-
return 'intl';
|
|
123
|
-
}
|
|
124
|
-
|
|
125
115
|
return 'public';
|
|
126
116
|
}
|
|
127
117
|
|