draftgo-cli 1.0.4 → 1.0.5
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 +3 -3
- package/package.json +4 -3
- package/resources/skill/manifest.json +1 -1
- package/resources/skill/references/mcp.md +2 -2
- package/src/commands/help.js +125 -125
- package/src/commands/map.js +60 -17
- package/src/commands/uninstall.js +16 -9
- package/src/mcp/hosts.js +68 -23
- package/src/platforms.js +16 -4
- package/src/skill.js +26 -6
- package/src/targets.js +2 -2
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ draftgo connect [target...]
|
|
|
85
85
|
|
|
86
86
|
`draftgo status` 会通过只读 MCP 诊断显示服务连接健康状态、服务版本和 Registry revision;`--output json` 适合读取 `connection.health` 与 `connection.registry_revision`。
|
|
87
87
|
|
|
88
|
-
`draftgo update` 和 `draftgo update all` 只刷新项目中已经安装的 DraftGo Skill,不会升级全局 CLI,也不会因为检测到 `.cursor`、`AGENTS.md` 等工具痕迹而新增配置。`draftgo init` 的自动识别只看宿主自有目录或文件(例如 `.cursor`、`.codex`),不把 `AGENTS.md`、`CLAUDE.md`、`GEMINI.md` 这类可共享说明文件当成安装目标。显式指定 target 时(例如 `draftgo update cursor` 或 `draftgo init cursor`),如果该 Skill 尚未安装,会按你的明确请求直接安装;首次接入请使用 `draftgo init <target>`。
|
|
88
|
+
`draftgo update` 和 `draftgo update all` 只刷新项目中已经安装的 DraftGo Skill,不会升级全局 CLI,也不会因为检测到 `.cursor`、`AGENTS.md` 等工具痕迹而新增配置。`draftgo init` 的自动识别只看宿主自有目录或文件(例如 `.cursor`、`.codex`、`.zcode`),不把 `AGENTS.md`、`CLAUDE.md`、`GEMINI.md` 这类可共享说明文件当成安装目标。显式指定 target 时(例如 `draftgo update cursor` 或 `draftgo init cursor`),如果该 Skill 尚未安装,会按你的明确请求直接安装;首次接入请使用 `draftgo init <target>`。
|
|
89
89
|
|
|
90
90
|
### 发现与正文
|
|
91
91
|
|
|
@@ -105,7 +105,7 @@ draftgo conflict resolve <type> <id>
|
|
|
105
105
|
|
|
106
106
|
`checkout` 下载正文和经过 hash 校验的 base。`commit` 先并发完成所有目标的本地与远端 preflight,全部通过后再并发上传;任何 409/412 都保留 base/local/remote 冲突材料,不 force、不覆盖、不自动合并。
|
|
107
107
|
|
|
108
|
-
`--route`、`--title` 为精确匹配,同时给出时取交集。CLI
|
|
108
|
+
`--route`、`--title` 为精确匹配,同时给出时取交集。CLI 可用资源搜索缩小候选,但最终在本地做精确判定。生产 DraftGo MCP 没有 `resource_search` 时,`map --type pages --route` 会改走 `listPageByRoute`。Windows Git Bash 传入的 `/admin/...` 会被还原成页面路由,而不是 Git 安装目录下的绝对路径。`map --summary` 不输出 project overview、资源列表或 hash。`diff --stat` 输出文件与增删行数,`--summary` 输出资源、版本和变更概要;只有不带这两个参数的 diff 才展开正文。
|
|
109
109
|
|
|
110
110
|
### Page 组件库
|
|
111
111
|
|
|
@@ -221,7 +221,7 @@ draftgo clean [--dry-run|--yes]
|
|
|
221
221
|
|
|
222
222
|
Skill 只放稳定的领域知识、项目边界和操作规则;动态 operation schema 由 MCP 提供并缓存在项目运行时。Reference 不拆成更小文件,保留按任务路由、前端运行时、安全、checkout 和 MCP 契约所需的完整上下文,避免过度拆分损失效果。
|
|
223
223
|
|
|
224
|
-
支持的 Skill target 包括 Claude Code、Cursor、Windsurf、Antigravity、Kiro、GitHub Copilot、Codex CLI、Gemini CLI 和
|
|
224
|
+
支持的 Skill target 包括 Claude Code、Cursor、Windsurf、Antigravity、Kiro、GitHub Copilot、Codex CLI、Gemini CLI、Pi 和 ZCode。Codex 与 Pi 的项目级 Skill 都安装到 `.agents/skills/draftgo/`,符合 Agent Skills 仓库级发现路径;Codex 仍用 `.codex` 识别并写入 `.codex/config.toml`,旧的 `.codex/skills/draftgo/` 会在 update 时迁走。ZCode 安装到 `.zcode/skills/draftgo/`,MCP 写入 `.zcode/config.json` 的 `mcp.servers`。
|
|
225
225
|
|
|
226
226
|
## 开发与测试
|
|
227
227
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "draftgo-cli",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Install and manage the DraftGo skill across AI coding agents (Claude Code, Codex, Cursor, Windsurf, Antigravity, Copilot, Gemini, Kiro, Pi).",
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "Install and manage the DraftGo skill across AI coding agents (Claude Code, Codex, Cursor, Windsurf, Antigravity, Copilot, Gemini, Kiro, Pi, ZCode).",
|
|
5
5
|
"bin": {
|
|
6
6
|
"draftgo": "bin/draftgo.js"
|
|
7
7
|
},
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"copilot",
|
|
31
31
|
"gemini",
|
|
32
32
|
"kiro",
|
|
33
|
-
"pi"
|
|
33
|
+
"pi",
|
|
34
|
+
"zcode"
|
|
34
35
|
],
|
|
35
36
|
"author": "draftgo <cabinai@163.com> (https://github.com/draftgo)",
|
|
36
37
|
"license": "MIT",
|
|
@@ -86,7 +86,7 @@ draftgo mcp serve
|
|
|
86
86
|
- `test` 建立独立新连接并验证关键工具;它不证明宿主持有的旧 session 仍有效。
|
|
87
87
|
- `serve` 是 stdio bridge。远端 session 明确失效时重新 initialize 并重放当前请求一次;普通超时、服务错误和工具错误不重试。
|
|
88
88
|
|
|
89
|
-
支持项目级 setup:Codex、Claude Code、Cursor、Gemini CLI、Kiro、GitHub Copilot。Windsurf 和 Antigravity 没有可靠项目级 MCP 配置,CLI 应明确提示不支持。
|
|
89
|
+
支持项目级 setup:Codex、Claude Code、Cursor、Gemini CLI、Kiro、GitHub Copilot、ZCode。Windsurf 和 Antigravity 没有可靠项目级 MCP 配置,CLI 应明确提示不支持。
|
|
90
90
|
|
|
91
91
|
宿主配置只运行:
|
|
92
92
|
|
|
@@ -94,7 +94,7 @@ draftgo mcp serve
|
|
|
94
94
|
{"command":"draftgo","args":["mcp","serve"]}
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
Codex 使用 `[mcp_servers.draftgo]`;GitHub Copilot 使用 `servers` 且声明 `type="stdio"`;其他支持宿主使用各自 `mcpServers`。配置不得包含 API Key、token、Authorization、headers、远端 `/mcp` URL、`env` 或带凭据命令。bridge 从当前项目 `.draftgo/config.json` 读取 server/API Key。
|
|
97
|
+
Codex 使用 `.codex/config.toml` 的 `[mcp_servers.draftgo]`;GitHub Copilot 使用 `servers` 且声明 `type="stdio"`;ZCode 使用 `.zcode/config.json` 的 `mcp.servers.draftgo` 且声明 `type="stdio"`;其他支持宿主使用各自 `mcpServers`。配置不得包含 API Key、token、Authorization、headers、远端 `/mcp` URL、`env` 或带凭据命令。bridge 从当前项目 `.draftgo/config.json` 读取 server/API Key。
|
|
98
98
|
|
|
99
99
|
`draftgo connect` 只保存并验证连接,不下载业务资源或创建本地镜像。MCP 不可用时运行 `draftgo mcp test` 收集证据。
|
|
100
100
|
|
package/src/commands/help.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { all } = require('../targets');
|
|
3
|
+
const { all } = require('../targets');
|
|
4
4
|
const { HOSTS } = require('../mcp/hosts');
|
|
5
5
|
const { getPackageVersion } = require('../skill');
|
|
6
6
|
|
|
@@ -13,158 +13,158 @@ function help() {
|
|
|
13
13
|
|
|
14
14
|
console.log(`draftgo v${getPackageVersion()} - DraftGo workbench CLI for AI coding agents
|
|
15
15
|
|
|
16
|
-
DraftGo shell baseline: React + Vite. Database Pages default to native HTML,
|
|
17
|
-
Tailwind CSS 4, and the DraftGo built-in component library (draftgo/*).
|
|
18
|
-
Provider brands and shared icons are served from the local /assets/providers and
|
|
19
|
-
/assets/icons directories; the Page runtime does not require third-party CDNs.
|
|
20
|
-
System MCP handles live discovery and structured resources; complete page,
|
|
21
|
-
navigation, and document bodies use checkout/commit outside MCP context.
|
|
22
|
-
The installed Skill routes agents to task-specific References and MCP tools.
|
|
16
|
+
DraftGo shell baseline: React + Vite. Database Pages default to native HTML,
|
|
17
|
+
Tailwind CSS 4, and the DraftGo built-in component library (draftgo/*).
|
|
18
|
+
Provider brands and shared icons are served from the local /assets/providers and
|
|
19
|
+
/assets/icons directories; the Page runtime does not require third-party CDNs.
|
|
20
|
+
System MCP handles live discovery and structured resources; complete page,
|
|
21
|
+
navigation, and document bodies use checkout/commit outside MCP context.
|
|
22
|
+
The installed Skill routes agents to task-specific References and MCP tools.
|
|
23
23
|
|
|
24
24
|
Usage:
|
|
25
|
-
draftgo init [<target>...] Install the DraftGo Skill. Omit the target
|
|
26
|
-
to detect host-owned directories such as
|
|
27
|
-
.cursor or .
|
|
28
|
-
AGENTS.md. Use "all" for every Skill target.
|
|
29
|
-
draftgo update [<target>...] Refresh already installed Skills. Does not
|
|
30
|
-
upgrade the global CLI. An explicit target
|
|
31
|
-
may also install that target when missing.
|
|
25
|
+
draftgo init [<target>...] Install the DraftGo Skill. Omit the target
|
|
26
|
+
to detect host-owned directories such as
|
|
27
|
+
.cursor, .codex, or .zcode, not shared files like
|
|
28
|
+
AGENTS.md. Use "all" for every Skill target.
|
|
29
|
+
draftgo update [<target>...] Refresh already installed Skills. Does not
|
|
30
|
+
upgrade the global CLI. An explicit target
|
|
31
|
+
may also install that target when missing.
|
|
32
32
|
draftgo uninstall <target|all> Remove Skill files. --purge also removes
|
|
33
33
|
the entire .draftgo/ runtime directory.
|
|
34
|
-
draftgo status Show connection health, Registry revision, targets, and Skill version.
|
|
34
|
+
draftgo status Show connection health, Registry revision, targets, and Skill version.
|
|
35
35
|
|
|
36
|
-
draftgo connect [<target>...] Verify and save server/API Key, then configure
|
|
36
|
+
draftgo connect [<target>...] Verify and save server/API Key, then configure
|
|
37
37
|
a detected or explicit MCP host. This never
|
|
38
38
|
downloads DraftGo business resources.
|
|
39
39
|
draftgo mcp setup [<target>...] Merge project-level stdio MCP configuration.
|
|
40
40
|
draftgo mcp status [<target>...] Check host configuration and secret safety.
|
|
41
|
-
draftgo mcp test Test project, resource, and API MCP calls.
|
|
42
|
-
draftgo mcp serve Bridge local stdio to the remote /mcp endpoint.
|
|
43
|
-
|
|
44
|
-
draftgo map [--type <type>] Locate bounded remote resource metadata and
|
|
45
|
-
overlay matching local checkout state.
|
|
46
|
-
Use --route/--title for exact selection.
|
|
47
|
-
draftgo checkout <type> <id...> Download a long-form body + verified base,
|
|
48
|
-
--force explicitly replaces local changes.
|
|
49
|
-
draftgo check Validate all checked-out content.
|
|
50
|
-
--remote also compares remote hash/version.
|
|
51
|
-
draftgo verify [<type> <id...>] Run the default delivery verification.
|
|
52
|
-
Browser options are explicit and optional.
|
|
53
|
-
draftgo diff <type> <id> Show checkout base versus local body/files.
|
|
54
|
-
--stat/--summary omit the full patch.
|
|
55
|
-
draftgo commit <type> <id...> Validate and upload complete checked-out bodies/files.
|
|
56
|
-
draftgo components search <query> Search the live component catalog.
|
|
57
|
-
draftgo components show <library/component>
|
|
58
|
-
Show HTML, props, slots, CSS variables and revision.
|
|
59
|
-
draftgo components expand --page <id> --instance <data-dg-instance>
|
|
60
|
-
Expand one component in a checked-out Page worktree.
|
|
61
|
-
draftgo components libraries list|show|create|update|delete
|
|
62
|
-
Manage component libraries.
|
|
63
|
-
draftgo components list|create|copy|delete
|
|
64
|
-
Manage component catalog entries.
|
|
65
|
-
draftgo components checkout|diff|verify|commit|publish <library/component>
|
|
66
|
-
Develop a component draft locally, then publish explicitly.
|
|
67
|
-
draftgo components import <zip> [--dry-run]
|
|
68
|
-
draftgo components export <library> [--file <zip>]
|
|
69
|
-
Transfer standard DraftGo component archives.
|
|
70
|
-
draftgo refresh <type> <id...> Safely refresh a clean checkout from the cloud.
|
|
71
|
-
draftgo reconcile <type> <id...> Repair stale metadata only when local equals remote.
|
|
41
|
+
draftgo mcp test Test project, resource, and API MCP calls.
|
|
42
|
+
draftgo mcp serve Bridge local stdio to the remote /mcp endpoint.
|
|
43
|
+
|
|
44
|
+
draftgo map [--type <type>] Locate bounded remote resource metadata and
|
|
45
|
+
overlay matching local checkout state.
|
|
46
|
+
Use --route/--title for exact selection.
|
|
47
|
+
draftgo checkout <type> <id...> Download a long-form body + verified base,
|
|
48
|
+
--force explicitly replaces local changes.
|
|
49
|
+
draftgo check Validate all checked-out content.
|
|
50
|
+
--remote also compares remote hash/version.
|
|
51
|
+
draftgo verify [<type> <id...>] Run the default delivery verification.
|
|
52
|
+
Browser options are explicit and optional.
|
|
53
|
+
draftgo diff <type> <id> Show checkout base versus local body/files.
|
|
54
|
+
--stat/--summary omit the full patch.
|
|
55
|
+
draftgo commit <type> <id...> Validate and upload complete checked-out bodies/files.
|
|
56
|
+
draftgo components search <query> Search the live component catalog.
|
|
57
|
+
draftgo components show <library/component>
|
|
58
|
+
Show HTML, props, slots, CSS variables and revision.
|
|
59
|
+
draftgo components expand --page <id> --instance <data-dg-instance>
|
|
60
|
+
Expand one component in a checked-out Page worktree.
|
|
61
|
+
draftgo components libraries list|show|create|update|delete
|
|
62
|
+
Manage component libraries.
|
|
63
|
+
draftgo components list|create|copy|delete
|
|
64
|
+
Manage component catalog entries.
|
|
65
|
+
draftgo components checkout|diff|verify|commit|publish <library/component>
|
|
66
|
+
Develop a component draft locally, then publish explicitly.
|
|
67
|
+
draftgo components import <zip> [--dry-run]
|
|
68
|
+
draftgo components export <library> [--file <zip>]
|
|
69
|
+
Transfer standard DraftGo component archives.
|
|
70
|
+
draftgo refresh <type> <id...> Safely refresh a clean checkout from the cloud.
|
|
71
|
+
draftgo reconcile <type> <id...> Repair stale metadata only when local equals remote.
|
|
72
72
|
draftgo conflicts List unresolved conflicts; --all includes resolved.
|
|
73
73
|
draftgo conflict show <type> <id>
|
|
74
74
|
Show preserved base/local/remote paths.
|
|
75
75
|
draftgo conflict resolve <type> <id>
|
|
76
76
|
Mark a merged worktree file ready against the
|
|
77
77
|
preserved remote base; then check/diff/commit.
|
|
78
|
-
draftgo clean [--dry-run|--yes] Plan or remove all tmp and registered artifacts.
|
|
79
|
-
draftgo work start <item> Add an active item to .draftgo/worklog.md.
|
|
80
|
-
draftgo work add <item> Add an item awaiting a decision.
|
|
81
|
-
draftgo work wait <ref> Mark an item waiting; --note records the reason.
|
|
82
|
-
draftgo work start-item <ref> Mark an item active; ref is number or date#number.
|
|
83
|
-
draftgo work complete <ref> Mark an item complete; --note appends evidence.
|
|
84
|
-
draftgo work show [<ref>] Show all history or one item with notes.
|
|
85
|
-
draftgo work list Latest first; --status active|waiting|completed,
|
|
86
|
-
--date YYYY-MM-DD, --limit 1-100 (20), --offset N.
|
|
78
|
+
draftgo clean [--dry-run|--yes] Plan or remove all tmp and registered artifacts.
|
|
79
|
+
draftgo work start <item> Add an active item to .draftgo/worklog.md.
|
|
80
|
+
draftgo work add <item> Add an item awaiting a decision.
|
|
81
|
+
draftgo work wait <ref> Mark an item waiting; --note records the reason.
|
|
82
|
+
draftgo work start-item <ref> Mark an item active; ref is number or date#number.
|
|
83
|
+
draftgo work complete <ref> Mark an item complete; --note appends evidence.
|
|
84
|
+
draftgo work show [<ref>] Show all history or one item with notes.
|
|
85
|
+
draftgo work list Latest first; --status active|waiting|completed,
|
|
86
|
+
--date YYYY-MM-DD, --limit 1-100 (20), --offset N.
|
|
87
87
|
|
|
88
|
-
draftgo api <query> Search the live API contract through MCP.
|
|
89
|
-
draftgo api search <query> Search the live operation registry explicitly.
|
|
90
|
-
draftgo api describe <operation_id>
|
|
91
|
-
Describe one live API operation through MCP.
|
|
92
|
-
draftgo api call <operation_id> --input <json-file>
|
|
93
|
-
Call one described operation with UTF-8 JSON.
|
|
94
|
-
draftgo capabilities list|search|show|audit
|
|
95
|
-
Discover and audit the current server's live operation catalog.
|
|
96
|
-
draftgo group <action> Manage RBAC groups and group members.
|
|
97
|
-
draftgo api-key status|create|update|delete|rotate
|
|
98
|
-
Manage the current user's API key through MCP.
|
|
99
|
-
draftgo role permissions|list|create|get|update|delete
|
|
100
|
-
Manage structured permission templates.
|
|
101
|
-
draftgo delete <operation_id> [id]
|
|
102
|
-
Confirm and call a live delete operation.
|
|
88
|
+
draftgo api <query> Search the live API contract through MCP.
|
|
89
|
+
draftgo api search <query> Search the live operation registry explicitly.
|
|
90
|
+
draftgo api describe <operation_id>
|
|
91
|
+
Describe one live API operation through MCP.
|
|
92
|
+
draftgo api call <operation_id> --input <json-file>
|
|
93
|
+
Call one described operation with UTF-8 JSON.
|
|
94
|
+
draftgo capabilities list|search|show|audit
|
|
95
|
+
Discover and audit the current server's live operation catalog.
|
|
96
|
+
draftgo group <action> Manage RBAC groups and group members.
|
|
97
|
+
draftgo api-key status|create|update|delete|rotate
|
|
98
|
+
Manage the current user's API key through MCP.
|
|
99
|
+
draftgo role permissions|list|create|get|update|delete
|
|
100
|
+
Manage structured permission templates.
|
|
101
|
+
draftgo delete <operation_id> [id]
|
|
102
|
+
Confirm and call a live delete operation.
|
|
103
103
|
draftgo deploy [<type> <id...>] Check, then diff or commit checked-out content.
|
|
104
104
|
--delivery local needs no IDs; preview/deploy
|
|
105
105
|
require an explicit type and IDs.
|
|
106
|
-
draftgo auto-push [<type> <id...>]
|
|
107
|
-
With config.auto_push=true, check and commit
|
|
108
|
-
changed checkouts. Any conflict stops the run.
|
|
109
|
-
draftgo local setup|start|stop|logs|status
|
|
110
|
-
Manage the project-local Docker stack.
|
|
106
|
+
draftgo auto-push [<type> <id...>]
|
|
107
|
+
With config.auto_push=true, check and commit
|
|
108
|
+
changed checkouts. Any conflict stops the run.
|
|
109
|
+
draftgo local setup|start|stop|logs|status
|
|
110
|
+
Manage the project-local Docker stack.
|
|
111
111
|
draftgo list-targets List Skill installation targets.
|
|
112
112
|
draftgo -v | --version Print CLI version.
|
|
113
113
|
draftgo -h | --help Show this help.
|
|
114
114
|
|
|
115
|
-
Resource types:
|
|
116
|
-
pages | nav/navigations | docs/articles
|
|
115
|
+
Resource types:
|
|
116
|
+
pages | nav/navigations | docs/articles
|
|
117
117
|
|
|
118
118
|
Important flags:
|
|
119
119
|
--project <dir> Operate on <dir> instead of the current directory.
|
|
120
120
|
--target <name,...> Select one or more MCP setup/status targets.
|
|
121
|
-
--server <url> (connect) DraftGo base URL.
|
|
122
|
-
--mcp-url <url> (connect) Explicit endpoint; standard /mcp derives the
|
|
123
|
-
base URL; custom paths also need --server.
|
|
124
|
-
--api-key <key> (connect) DraftGo user API Key for non-interactive use.
|
|
125
|
-
--timeout <ms> (connect/mcp test) Network timeout.
|
|
121
|
+
--server <url> (connect) DraftGo base URL.
|
|
122
|
+
--mcp-url <url> (connect) Explicit endpoint; standard /mcp derives the
|
|
123
|
+
base URL; custom paths also need --server.
|
|
124
|
+
--api-key <key> (connect) DraftGo user API Key for non-interactive use.
|
|
125
|
+
--timeout <ms> (connect/mcp test) Network timeout.
|
|
126
126
|
--allow-offline (connect) Save only after explicitly accepting a
|
|
127
127
|
failed MCP validation.
|
|
128
128
|
--no-mcp-setup (connect) Do not write host MCP configuration.
|
|
129
129
|
--force (init/checkout) Overwrite Skill or local changes.
|
|
130
130
|
--purge (uninstall) Also remove the .draftgo/ directory.
|
|
131
|
-
--skip-update-check (update) Compatibility flag; update never upgrades CLI.
|
|
132
|
-
--connect (init) Continue into DraftGo server connection.
|
|
133
|
-
--no-setup (init) Install the Skill without either setup flow.
|
|
134
|
-
--output json Print machine-readable output where supported.
|
|
135
|
-
--type <type> (map) pages | nav/navigations | docs/articles;
|
|
136
|
-
(capabilities) alias for a Registry module filter.
|
|
137
|
-
--route <path> (map) Select resources with this exact route.
|
|
138
|
-
--title <title> (map) Select resources with this exact title.
|
|
139
|
-
--limit <1-100> (map) Maximum resources returned per type/page;
|
|
140
|
-
defaults to 20.
|
|
141
|
-
--cursor <cursor> (map) Continue one typed map result page.
|
|
142
|
-
--summary (map/diff) Print bounded resource or change summary.
|
|
143
|
-
--stat (diff) Print per-file and total change statistics.
|
|
144
|
-
--strict Treat check warnings as failures.
|
|
145
|
-
--remote (check) Compare checkout hashes/versions with remote.
|
|
131
|
+
--skip-update-check (update) Compatibility flag; update never upgrades CLI.
|
|
132
|
+
--connect (init) Continue into DraftGo server connection.
|
|
133
|
+
--no-setup (init) Install the Skill without either setup flow.
|
|
134
|
+
--output json Print machine-readable output where supported.
|
|
135
|
+
--type <type> (map) pages | nav/navigations | docs/articles;
|
|
136
|
+
(capabilities) alias for a Registry module filter.
|
|
137
|
+
--route <path> (map) Select resources with this exact route.
|
|
138
|
+
--title <title> (map) Select resources with this exact title.
|
|
139
|
+
--limit <1-100> (map) Maximum resources returned per type/page;
|
|
140
|
+
defaults to 20.
|
|
141
|
+
--cursor <cursor> (map) Continue one typed map result page.
|
|
142
|
+
--summary (map/diff) Print bounded resource or change summary.
|
|
143
|
+
--stat (diff) Print per-file and total change statistics.
|
|
144
|
+
--strict Treat check warnings as failures.
|
|
145
|
+
--remote (check) Compare checkout hashes/versions with remote.
|
|
146
146
|
--yes Skip supported confirmation prompts.
|
|
147
147
|
--operation-id <id> (delete) Select an operation explicitly.
|
|
148
|
-
--params <json> (api call/delete) Pass an API parameter object.
|
|
149
|
-
--input <file> (api call/delete) Read a UTF-8 JSON object.
|
|
150
|
-
--delivery <mode> (deploy) local | preview | deploy.
|
|
151
|
-
--dry-run Preview supported cleanup or delivery operations.
|
|
152
|
-
--ui <mode> (verify) always | never; default never.
|
|
153
|
-
--remote (check/verify) Compare checkout hashes with remote.
|
|
154
|
-
--viewport <mode> (verify) mobile | desktop | both.
|
|
155
|
-
--frame <mode> (verify) auto | top | all | <iframe-selector>.
|
|
156
|
-
--token <mode> (verify) auto | never; auto appends the configured
|
|
157
|
-
API Key to same-origin URLs as the token query parameter.
|
|
158
|
-
--screenshot <mode> (verify) always | never; always alone captures a screenshot.
|
|
159
|
-
--browser <name> (verify) chromium | chrome | msedge.
|
|
160
|
-
--browser-path <file> (verify) Explicit browser executable; environment
|
|
161
|
-
fallback: DRAFTGO_BROWSER_PATH.
|
|
162
|
-
--selector <css> (verify) Require a visible element in top or iframe DOM.
|
|
163
|
-
--width/--height <px> (verify) Override the default 1440x900 desktop viewport.
|
|
148
|
+
--params <json> (api call/delete) Pass an API parameter object.
|
|
149
|
+
--input <file> (api call/delete) Read a UTF-8 JSON object.
|
|
150
|
+
--delivery <mode> (deploy) local | preview | deploy.
|
|
151
|
+
--dry-run Preview supported cleanup or delivery operations.
|
|
152
|
+
--ui <mode> (verify) always | never; default never.
|
|
153
|
+
--remote (check/verify) Compare checkout hashes with remote.
|
|
154
|
+
--viewport <mode> (verify) mobile | desktop | both.
|
|
155
|
+
--frame <mode> (verify) auto | top | all | <iframe-selector>.
|
|
156
|
+
--token <mode> (verify) auto | never; auto appends the configured
|
|
157
|
+
API Key to same-origin URLs as the token query parameter.
|
|
158
|
+
--screenshot <mode> (verify) always | never; always alone captures a screenshot.
|
|
159
|
+
--browser <name> (verify) chromium | chrome | msedge.
|
|
160
|
+
--browser-path <file> (verify) Explicit browser executable; environment
|
|
161
|
+
fallback: DRAFTGO_BROWSER_PATH.
|
|
162
|
+
--selector <css> (verify) Require a visible element in top or iframe DOM.
|
|
163
|
+
--width/--height <px> (verify) Override the default 1440x900 desktop viewport.
|
|
164
164
|
|
|
165
165
|
Security:
|
|
166
|
-
.draftgo/config.json stores the server and API Key and is gitignored. Host MCP
|
|
167
|
-
files contain only "draftgo mcp serve"; they never contain the API Key.
|
|
166
|
+
.draftgo/config.json stores the server and API Key and is gitignored. Host MCP
|
|
167
|
+
files contain only "draftgo mcp serve"; they never contain the API Key.
|
|
168
168
|
|
|
169
169
|
MCP targets (project config or status):
|
|
170
170
|
${mcpTargets}
|
|
@@ -174,15 +174,15 @@ ${targets}
|
|
|
174
174
|
|
|
175
175
|
Examples:
|
|
176
176
|
draftgo init codex
|
|
177
|
-
draftgo connect codex --server https://draftgo.example --api-key <key>
|
|
178
|
-
draftgo mcp test
|
|
179
|
-
draftgo map --type pages --route /admin/channel-ops --summary --output json
|
|
177
|
+
draftgo connect codex --server https://draftgo.example --api-key <key>
|
|
178
|
+
draftgo mcp test
|
|
179
|
+
draftgo map --type pages --route /admin/channel-ops --summary --output json
|
|
180
180
|
draftgo checkout pages 42
|
|
181
|
-
draftgo verify pages 42
|
|
182
|
-
draftgo diff pages 42 --stat
|
|
183
|
-
draftgo commit pages 42
|
|
184
|
-
draftgo work start "Build document management and role permissions"
|
|
185
|
-
draftgo work complete 1 --note "Verification and delivery passed"
|
|
181
|
+
draftgo verify pages 42
|
|
182
|
+
draftgo diff pages 42 --stat
|
|
183
|
+
draftgo commit pages 42
|
|
184
|
+
draftgo work start "Build document management and role permissions"
|
|
185
|
+
draftgo work complete 1 --note "Verification and delivery passed"
|
|
186
186
|
draftgo deploy docs 7 --delivery preview
|
|
187
187
|
`);
|
|
188
188
|
}
|
package/src/commands/map.js
CHANGED
|
@@ -19,6 +19,9 @@ const LIST_OPERATIONS = Object.freeze({
|
|
|
19
19
|
navigations: 'listNavigation',
|
|
20
20
|
'docs/articles': 'listDocAdminArticle',
|
|
21
21
|
});
|
|
22
|
+
const EXACT_ROUTE_OPERATIONS = Object.freeze({
|
|
23
|
+
pages: 'listPageByRoute',
|
|
24
|
+
});
|
|
22
25
|
|
|
23
26
|
function itemsFrom(value) {
|
|
24
27
|
if (Array.isArray(value)) return value;
|
|
@@ -29,6 +32,15 @@ function itemsFrom(value) {
|
|
|
29
32
|
return [];
|
|
30
33
|
}
|
|
31
34
|
|
|
35
|
+
function resourcesFrom(value) {
|
|
36
|
+
const items = itemsFrom(value);
|
|
37
|
+
if (items.length) return items;
|
|
38
|
+
if (value && typeof value === 'object' && (value.id != null || value.resource_id != null)) {
|
|
39
|
+
return [value];
|
|
40
|
+
}
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
|
|
32
44
|
function nextCursor(value) {
|
|
33
45
|
if (!value || typeof value !== 'object') return null;
|
|
34
46
|
if (Object.prototype.hasOwnProperty.call(value, 'next_cursor')) return value.next_cursor;
|
|
@@ -70,20 +82,28 @@ function queryProperties(operation) {
|
|
|
70
82
|
return query && query.properties && typeof query.properties === 'object' ? query.properties : {};
|
|
71
83
|
}
|
|
72
84
|
|
|
85
|
+
function registryOperationID(type, flags) {
|
|
86
|
+
return flags.route != null && EXACT_ROUTE_OPERATIONS[type]
|
|
87
|
+
? EXACT_ROUTE_OPERATIONS[type]
|
|
88
|
+
: LIST_OPERATIONS[type];
|
|
89
|
+
}
|
|
90
|
+
|
|
73
91
|
function registryQuery(operation, operationID, flags) {
|
|
74
92
|
const properties = queryProperties(operation);
|
|
75
93
|
const query = {};
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (cursor)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
if (operationID !== 'listPageByRoute') {
|
|
95
|
+
const limit = positiveLimit(flags.limit);
|
|
96
|
+
const cursor = flags.cursor == null ? '' : String(flags.cursor);
|
|
97
|
+
if (Object.prototype.hasOwnProperty.call(properties, 'cursor')) {
|
|
98
|
+
if (cursor) query.cursor = cursor;
|
|
99
|
+
if (Object.prototype.hasOwnProperty.call(properties, 'limit')) query.limit = limit;
|
|
100
|
+
} else if (Object.prototype.hasOwnProperty.call(properties, 'page')
|
|
101
|
+
&& Object.prototype.hasOwnProperty.call(properties, 'page_size')) {
|
|
102
|
+
query.page = registryPageFromCursor(cursor, operationID);
|
|
103
|
+
query.page_size = limit;
|
|
104
|
+
} else if (cursor) {
|
|
105
|
+
throw new Error(`${operationID} does not expose a paginated Registry contract.`);
|
|
106
|
+
}
|
|
87
107
|
}
|
|
88
108
|
|
|
89
109
|
const search = flags.route != null ? normalizeRoute(flags.route)
|
|
@@ -93,6 +113,7 @@ function registryQuery(operation, operationID, flags) {
|
|
|
93
113
|
? 'route'
|
|
94
114
|
: ['search', 'q', 'query'].find((candidate) => Object.prototype.hasOwnProperty.call(properties, candidate));
|
|
95
115
|
if (key) query[key] = search;
|
|
116
|
+
else if (operationID === 'listPageByRoute') query.route = search;
|
|
96
117
|
}
|
|
97
118
|
return query;
|
|
98
119
|
}
|
|
@@ -131,10 +152,22 @@ function requestedResourceTypes(flags = {}) {
|
|
|
131
152
|
: [normalizeMapResourceType(flags.type)];
|
|
132
153
|
}
|
|
133
154
|
|
|
155
|
+
function recoverCliRoute(value) {
|
|
156
|
+
// Git Bash converts POSIX paths like /admin/channel-ops into a Windows path
|
|
157
|
+
// under the Git install root. Recover the original route before matching.
|
|
158
|
+
const text = String(value == null ? '' : value).trim().replace(/\\/g, '/');
|
|
159
|
+
const converted = text.match(/^[A-Za-z]:\/(?:[^/]+\/)*Git(\/.*)$/i);
|
|
160
|
+
return converted ? converted[1] : text;
|
|
161
|
+
}
|
|
162
|
+
|
|
134
163
|
function normalizeRoute(value) {
|
|
135
|
-
// DraftGo stores page routes
|
|
136
|
-
//
|
|
137
|
-
|
|
164
|
+
// DraftGo stores page routes with a leading slash and no trailing slash,
|
|
165
|
+
// except for `/`. Accept stored, browser, and Git-Bash-converted forms.
|
|
166
|
+
let route = recoverCliRoute(value);
|
|
167
|
+
if (!route) return '';
|
|
168
|
+
if (!route.startsWith('/')) route = `/${route}`;
|
|
169
|
+
if (route !== '/') route = route.replace(/\/+$/g, '');
|
|
170
|
+
return route;
|
|
138
171
|
}
|
|
139
172
|
|
|
140
173
|
function positiveLimit(value, fallback = 20) {
|
|
@@ -209,7 +242,7 @@ async function listRegistryResources(projectDir, config, session, resourceTypes,
|
|
|
209
242
|
throw new Error('Map --cursor requires exactly one --type.');
|
|
210
243
|
}
|
|
211
244
|
const groups = await allWithAbort(resourceTypes.map((type) => async (options) => {
|
|
212
|
-
const operationID =
|
|
245
|
+
const operationID = registryOperationID(type, flags);
|
|
213
246
|
const revision = await registryRevision(session, operationID);
|
|
214
247
|
const description = await descriptionForRevision(projectDir, config, session, operationID, revision);
|
|
215
248
|
const operation = description.operation || {};
|
|
@@ -220,16 +253,25 @@ async function listRegistryResources(projectDir, config, session, resourceTypes,
|
|
|
220
253
|
};
|
|
221
254
|
if (Object.keys(query).length) invoke.query = query;
|
|
222
255
|
const payload = await callStructured(session, TOOL_NAMES.apiCall, invoke, options);
|
|
256
|
+
const status = payload && (payload.status_code ?? payload.statusCode);
|
|
257
|
+
if (Number(status) === 404) {
|
|
258
|
+
return {
|
|
259
|
+
resources: [],
|
|
260
|
+
pagination: { next_cursor: null, has_more: false, total: 0 },
|
|
261
|
+
};
|
|
262
|
+
}
|
|
223
263
|
const response = payload && payload.response !== undefined ? payload.response
|
|
224
264
|
: payload && payload.data !== undefined ? payload.data : payload;
|
|
225
|
-
const responseItems =
|
|
265
|
+
const responseItems = resourcesFrom(response);
|
|
226
266
|
const resources = responseItems
|
|
227
267
|
.map((item) => ({ ...item, resource_type: type, resource_id: String(item.resource_id ?? item.id) }))
|
|
228
268
|
.filter((item) => item.resource_id && item.resource_id !== 'undefined')
|
|
229
269
|
.filter((item) => resourceMatches(item, flags));
|
|
230
270
|
return {
|
|
231
271
|
resources,
|
|
232
|
-
pagination:
|
|
272
|
+
pagination: operationID === 'listPageByRoute'
|
|
273
|
+
? { next_cursor: null, has_more: false, total: resources.length }
|
|
274
|
+
: registryPagination(response, operationID, query, responseItems.length),
|
|
233
275
|
};
|
|
234
276
|
}));
|
|
235
277
|
return {
|
|
@@ -378,6 +420,7 @@ async function mapCommand(projectDir, flags = {}) {
|
|
|
378
420
|
|
|
379
421
|
module.exports = mapCommand;
|
|
380
422
|
module.exports.itemsFrom = itemsFrom;
|
|
423
|
+
module.exports.resourcesFrom = resourcesFrom;
|
|
381
424
|
module.exports.normalizeMapResourceType = normalizeMapResourceType;
|
|
382
425
|
module.exports.requestedResourceTypes = requestedResourceTypes;
|
|
383
426
|
module.exports.listRemoteResources = listRemoteResources;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const log = require('../logger');
|
|
4
|
-
const { all, resolveTargets } = require('../targets');
|
|
4
|
+
const { all, resolveTargets } = require('../targets');
|
|
5
5
|
const { removePath } = require('../fsx');
|
|
6
6
|
const { dgDir } = require('../paths');
|
|
7
7
|
|
|
@@ -25,11 +25,18 @@ async function uninstall(projectDir, positional, flags) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
const resolvedNames = new Set(resolved.map((target) => target.name));
|
|
28
29
|
log.step('移除入口文件');
|
|
29
30
|
for (const t of resolved) {
|
|
30
31
|
try {
|
|
31
|
-
const
|
|
32
|
-
|
|
32
|
+
const keepSharedSkill = all.some((other) =>
|
|
33
|
+
other.name !== t.name
|
|
34
|
+
&& !resolvedNames.has(other.name)
|
|
35
|
+
&& other.assetDir === t.assetDir
|
|
36
|
+
&& other.mainFile === t.mainFile);
|
|
37
|
+
const removed = t.uninstall(projectDir, { keepSharedSkill });
|
|
38
|
+
if (keepSharedSkill) log.dim(`${t.displayName} 与其他宿主共用 Skill 目录,已保留`);
|
|
39
|
+
else if (removed) log.ok(`${t.displayName} 入口已移除`);
|
|
33
40
|
else log.dim(`${t.displayName} 入口不存在,跳过`);
|
|
34
41
|
} catch (e) {
|
|
35
42
|
log.err(`${t.displayName} 卸载失败:${e.message}`);
|
|
@@ -38,12 +45,12 @@ async function uninstall(projectDir, positional, flags) {
|
|
|
38
45
|
|
|
39
46
|
// Each AI-tool skill directory is self-contained, so per-target uninstall
|
|
40
47
|
// already wiped its content above. Only --purge nukes runtime data under
|
|
41
|
-
// .draftgo/ (config/worklog/lessons/cache etc.).
|
|
42
|
-
if (flags.purge) {
|
|
43
|
-
if (removePath(dgDir(projectDir))) {
|
|
44
|
-
log.warn('已使用 --purge:.draftgo/(含 config/worklog/lessons 等运行时数据)已删除。');
|
|
45
|
-
}
|
|
46
|
-
}
|
|
48
|
+
// .draftgo/ (config/worklog/lessons/cache etc.).
|
|
49
|
+
if (flags.purge) {
|
|
50
|
+
if (removePath(dgDir(projectDir))) {
|
|
51
|
+
log.warn('已使用 --purge:.draftgo/(含 config/worklog/lessons 等运行时数据)已删除。');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
47
54
|
|
|
48
55
|
log.title('完成');
|
|
49
56
|
return 0;
|
package/src/mcp/hosts.js
CHANGED
|
@@ -22,6 +22,7 @@ const HOSTS = Object.freeze([
|
|
|
22
22
|
{ name: 'gemini', displayName: 'Gemini CLI', path: '.gemini/settings.json', format: 'jsonc', registry: 'mcpServers', signals: ['.gemini'] },
|
|
23
23
|
{ name: 'kiro', displayName: 'Kiro', path: '.kiro/settings/mcp.json', format: 'jsonc', registry: 'mcpServers', signals: ['.kiro'] },
|
|
24
24
|
{ name: 'copilot', aliases: ['github-copilot'], displayName: 'GitHub Copilot', path: '.vscode/mcp.json', format: 'jsonc', registry: 'servers', vscode: true, signals: ['.vscode', '.github/prompts', '.github/copilot-instructions.md'] },
|
|
25
|
+
{ name: 'zcode', displayName: 'ZCode', path: '.zcode/config.json', format: 'jsonc', registry: 'mcp.servers', vscode: true, signals: ['.zcode'] },
|
|
25
26
|
{ name: 'windsurf', displayName: 'Windsurf', supported: false, reason: 'Windsurf has no supported project-level MCP configuration adapter.', signals: ['.windsurf'] },
|
|
26
27
|
{ name: 'antigravity', displayName: 'Antigravity', supported: false, reason: 'Antigravity has no supported project-level MCP configuration adapter.', signals: ['.agent'] },
|
|
27
28
|
]);
|
|
@@ -249,41 +250,85 @@ function insertObjectProperty(source, open, close, key, value, newline) {
|
|
|
249
250
|
);
|
|
250
251
|
}
|
|
251
252
|
|
|
253
|
+
function registryPath(registry) {
|
|
254
|
+
return String(registry || '').split('.').map((part) => part.trim()).filter(Boolean);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function nestedValue(value, keys) {
|
|
258
|
+
let current = value;
|
|
259
|
+
for (const key of keys) {
|
|
260
|
+
if (!current || typeof current !== 'object' || Array.isArray(current) || !Object.prototype.hasOwnProperty.call(current, key)) {
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
current = current[key];
|
|
264
|
+
}
|
|
265
|
+
return current;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function assertRegistryObjects(parsed, keys, file) {
|
|
269
|
+
let current = parsed;
|
|
270
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
271
|
+
const key = keys[index];
|
|
272
|
+
if (current[key] === undefined) return;
|
|
273
|
+
if (!current[key] || typeof current[key] !== 'object' || Array.isArray(current[key])) {
|
|
274
|
+
throw new Error(`Invalid ${file}: ${keys.slice(0, index + 1).join('.')} must be an object.`);
|
|
275
|
+
}
|
|
276
|
+
current = current[key];
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function findNestedObject(source, open, close, keys) {
|
|
281
|
+
let currentOpen = open;
|
|
282
|
+
let currentClose = close;
|
|
283
|
+
let matched = 0;
|
|
284
|
+
for (const key of keys) {
|
|
285
|
+
const property = objectProperties(source, currentOpen, currentClose).find((item) => item.key === key);
|
|
286
|
+
if (!property) break;
|
|
287
|
+
currentOpen = property.valueStart;
|
|
288
|
+
currentClose = scanComposite(source, currentOpen) - 1;
|
|
289
|
+
matched += 1;
|
|
290
|
+
}
|
|
291
|
+
return { open: currentOpen, close: currentClose, matched };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function upsertDraftGoEntry(source, open, close, entry, newline) {
|
|
295
|
+
const serverProperties = objectProperties(source, open, close);
|
|
296
|
+
const draftgo = serverProperties.find((item) => item.key === 'draftgo');
|
|
297
|
+
if (draftgo) {
|
|
298
|
+
const indent = lineIndent(source, draftgo.propertyStart);
|
|
299
|
+
return replaceRange(
|
|
300
|
+
source,
|
|
301
|
+
draftgo.valueStart,
|
|
302
|
+
draftgo.valueEnd,
|
|
303
|
+
formatJsonValue(entry, indent, newline),
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
return insertObjectProperty(source, open, close, 'draftgo', entry, newline);
|
|
307
|
+
}
|
|
308
|
+
|
|
252
309
|
function mergeJsoncConfig(source, registry, entry, file = 'MCP config') {
|
|
310
|
+
const keys = registryPath(registry);
|
|
311
|
+
if (!keys.length) throw new Error(`Invalid ${file}: MCP registry path is missing.`);
|
|
253
312
|
const initial = source.trim() ? source : '{\n}\n';
|
|
254
313
|
const parsed = parseJsonc(initial, file);
|
|
255
|
-
|
|
256
|
-
&& (!parsed[registry] || typeof parsed[registry] !== 'object' || Array.isArray(parsed[registry]))) {
|
|
257
|
-
throw new Error(`Invalid ${file}: ${registry} must be an object.`);
|
|
258
|
-
}
|
|
314
|
+
assertRegistryObjects(parsed, keys, file);
|
|
259
315
|
|
|
260
316
|
const newline = initial.includes('\r\n') ? '\r\n' : '\n';
|
|
261
317
|
const rootOffset = initial.charCodeAt(0) === 0xfeff ? 1 : 0;
|
|
262
318
|
const rootOpen = skipTrivia(initial, rootOffset);
|
|
263
319
|
const rootEnd = scanComposite(initial, rootOpen);
|
|
264
320
|
const rootClose = rootEnd - 1;
|
|
265
|
-
const
|
|
266
|
-
const registryProperty = rootProperties.find((item) => item.key === registry);
|
|
321
|
+
const nested = findNestedObject(initial, rootOpen, rootClose, keys);
|
|
267
322
|
let result;
|
|
268
323
|
|
|
269
|
-
if (
|
|
270
|
-
result =
|
|
324
|
+
if (nested.matched === keys.length) {
|
|
325
|
+
result = upsertDraftGoEntry(initial, nested.open, nested.close, entry, newline);
|
|
271
326
|
} else {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const draftgo = serverProperties.find((item) => item.key === 'draftgo');
|
|
276
|
-
if (draftgo) {
|
|
277
|
-
const indent = lineIndent(initial, draftgo.propertyStart);
|
|
278
|
-
result = replaceRange(
|
|
279
|
-
initial,
|
|
280
|
-
draftgo.valueStart,
|
|
281
|
-
draftgo.valueEnd,
|
|
282
|
-
formatJsonValue(entry, indent, newline),
|
|
283
|
-
);
|
|
284
|
-
} else {
|
|
285
|
-
result = insertObjectProperty(initial, registryOpen, registryClose, 'draftgo', entry, newline);
|
|
327
|
+
let value = { draftgo: entry };
|
|
328
|
+
for (let index = keys.length - 1; index > nested.matched; index -= 1) {
|
|
329
|
+
value = { [keys[index]]: value };
|
|
286
330
|
}
|
|
331
|
+
result = insertObjectProperty(initial, nested.open, nested.close, keys[nested.matched], value, newline);
|
|
287
332
|
}
|
|
288
333
|
|
|
289
334
|
parseJsonc(result, file);
|
|
@@ -482,7 +527,7 @@ function statusHost(projectDir, target) {
|
|
|
482
527
|
if (host.format === 'toml') return { host, supported: true, path: file, ...codexStatus(source) };
|
|
483
528
|
try {
|
|
484
529
|
const value = parseJsonc(source, host.path);
|
|
485
|
-
const registry = value
|
|
530
|
+
const registry = nestedValue(value, registryPath(host.registry));
|
|
486
531
|
const entry = registry && typeof registry === 'object' && !Array.isArray(registry)
|
|
487
532
|
? registry.draftgo
|
|
488
533
|
: undefined;
|
package/src/platforms.js
CHANGED
|
@@ -79,10 +79,13 @@ const platforms = [
|
|
|
79
79
|
{
|
|
80
80
|
name: 'codex',
|
|
81
81
|
displayName: 'Codex CLI',
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
// Repo-level Codex skills now follow the Agent Skills path.
|
|
83
|
+
// MCP config still lives in .codex/config.toml, so detection stays on .codex.
|
|
84
|
+
mainFile: '.agents/skills/draftgo/SKILL.md',
|
|
85
|
+
assetDir: '.agents/skills/draftgo',
|
|
86
|
+
skillDir: '.agents/skills/draftgo',
|
|
85
87
|
signals: ['.codex'],
|
|
88
|
+
legacyAssetDirs: ['.codex/skills/draftgo'],
|
|
86
89
|
frontmatter: { name: SKILL_NAME, description: DESCRIPTION },
|
|
87
90
|
},
|
|
88
91
|
{
|
|
@@ -100,7 +103,16 @@ const platforms = [
|
|
|
100
103
|
mainFile: '.agents/skills/draftgo/SKILL.md',
|
|
101
104
|
assetDir: '.agents/skills/draftgo',
|
|
102
105
|
skillDir: '.agents/skills/draftgo',
|
|
103
|
-
signals: ['.
|
|
106
|
+
signals: ['.pi'],
|
|
107
|
+
frontmatter: { name: SKILL_NAME, description: DESCRIPTION },
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'zcode',
|
|
111
|
+
displayName: 'ZCode',
|
|
112
|
+
mainFile: '.zcode/skills/draftgo/SKILL.md',
|
|
113
|
+
assetDir: '.zcode/skills/draftgo',
|
|
114
|
+
skillDir: '.zcode/skills/draftgo',
|
|
115
|
+
signals: ['.zcode'],
|
|
104
116
|
frontmatter: { name: SKILL_NAME, description: DESCRIPTION },
|
|
105
117
|
},
|
|
106
118
|
];
|
package/src/skill.js
CHANGED
|
@@ -193,6 +193,19 @@ function ensureRuntime(projectDir) {
|
|
|
193
193
|
appendGitignoreLine(projectDir, '.draftgo/conflicts/');
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
function legacySkillDirs(projectDir, platform) {
|
|
197
|
+
const destAsset = path.resolve(path.join(projectDir, platform.assetDir));
|
|
198
|
+
return (platform.legacyAssetDirs || [])
|
|
199
|
+
.map((relative) => path.join(projectDir, relative))
|
|
200
|
+
.filter((absolute) => path.resolve(absolute) !== destAsset && exists(absolute));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function skillAlreadyInstalled(projectDir, platform) {
|
|
204
|
+
const destAsset = path.join(projectDir, platform.assetDir);
|
|
205
|
+
const destMain = path.join(projectDir, platform.mainFile);
|
|
206
|
+
return exists(destAsset) || exists(destMain) || legacySkillDirs(projectDir, platform).length > 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
196
209
|
function installPlatform(projectDir, platform, opts = {}) {
|
|
197
210
|
if (!exists(SKILL_SOURCE_DIR)) {
|
|
198
211
|
throw new Error(
|
|
@@ -202,7 +215,7 @@ function installPlatform(projectDir, platform, opts = {}) {
|
|
|
202
215
|
const destAsset = path.join(projectDir, platform.assetDir);
|
|
203
216
|
const destMain = path.join(projectDir, platform.mainFile);
|
|
204
217
|
const mainOutsideAsset = path.relative(destAsset, destMain).startsWith('..');
|
|
205
|
-
if ((
|
|
218
|
+
if (skillAlreadyInstalled(projectDir, platform) && !opts.force) {
|
|
206
219
|
return { path: platform.mainFile, skipped: true };
|
|
207
220
|
}
|
|
208
221
|
|
|
@@ -251,24 +264,31 @@ function installPlatform(projectDir, platform, opts = {}) {
|
|
|
251
264
|
|
|
252
265
|
if (exists(backupAsset)) removePath(backupAsset);
|
|
253
266
|
if (backupMain && exists(backupMain)) removePath(backupMain);
|
|
267
|
+
for (const legacyDir of legacySkillDirs(projectDir, platform)) removePath(legacyDir);
|
|
254
268
|
if (!exists(destMain)) throw new Error(`安装后缺少入口文件:${platform.mainFile}`);
|
|
255
269
|
|
|
256
270
|
return { path: platform.mainFile };
|
|
257
271
|
}
|
|
258
272
|
|
|
259
|
-
function uninstallPlatform(projectDir, platform) {
|
|
273
|
+
function uninstallPlatform(projectDir, platform, opts = {}) {
|
|
260
274
|
const destAsset = path.join(projectDir, platform.assetDir);
|
|
261
275
|
const destMain = path.join(projectDir, platform.mainFile);
|
|
262
276
|
let removed = false;
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
277
|
+
if (!opts.keepSharedSkill) {
|
|
278
|
+
if (exists(destAsset)) { removePath(destAsset); removed = true; }
|
|
279
|
+
if (path.relative(destAsset, destMain).startsWith('..') && exists(destMain)) {
|
|
280
|
+
removePath(destMain); removed = true;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
for (const legacyDir of legacySkillDirs(projectDir, platform)) {
|
|
284
|
+
removePath(legacyDir);
|
|
285
|
+
removed = true;
|
|
266
286
|
}
|
|
267
287
|
return removed;
|
|
268
288
|
}
|
|
269
289
|
|
|
270
290
|
function statusPlatform(projectDir, platform) {
|
|
271
|
-
return { installed:
|
|
291
|
+
return { installed: skillAlreadyInstalled(projectDir, platform) };
|
|
272
292
|
}
|
|
273
293
|
|
|
274
294
|
function installAll(projectDir, platforms, opts = {}) {
|
package/src/targets.js
CHANGED
|
@@ -14,8 +14,8 @@ function makeInstaller(p) {
|
|
|
14
14
|
install(projectDir, opts) {
|
|
15
15
|
return skill.installPlatform(projectDir, p, opts || {});
|
|
16
16
|
},
|
|
17
|
-
uninstall(projectDir) {
|
|
18
|
-
return skill.uninstallPlatform(projectDir, p);
|
|
17
|
+
uninstall(projectDir, opts) {
|
|
18
|
+
return skill.uninstallPlatform(projectDir, p, opts || {});
|
|
19
19
|
},
|
|
20
20
|
status(projectDir) {
|
|
21
21
|
return skill.statusPlatform(projectDir, p);
|