draftgo-cli 3.0.35 → 3.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +220 -272
  2. package/package.json +6 -2
  3. package/resources/skill/SKILL.md +114 -55
  4. package/resources/skill/init/SKILL.md +29 -15
  5. package/resources/skill/manifest.json +5 -4
  6. package/resources/skill/push/SKILL.md +41 -29
  7. package/resources/skill/references/aihub.md +8 -5
  8. package/resources/skill/references/api-endpoints.md +5 -3
  9. package/resources/skill/references/architecture.md +1 -1
  10. package/resources/skill/references/checkout.md +116 -0
  11. package/resources/skill/references/custom-services.md +9 -10
  12. package/resources/skill/references/data.md +4 -2
  13. package/resources/skill/references/frontend.md +1 -1
  14. package/resources/skill/references/mcp.md +101 -0
  15. package/resources/skill/references/modules.md +8 -8
  16. package/resources/skill/references/parallel.md +6 -3
  17. package/resources/skill/references/runtime.md +7 -10
  18. package/resources/skill/scripts/README.md +8 -0
  19. package/resources/skill/story/SKILL.md +8 -8
  20. package/src/cli.js +5 -0
  21. package/src/commandRegistry.js +7 -1
  22. package/src/commands/api.js +24 -187
  23. package/src/commands/autoPush.js +48 -17
  24. package/src/commands/check.js +17 -47
  25. package/src/commands/checkout.js +18 -0
  26. package/src/commands/commit.js +21 -0
  27. package/src/commands/conflict.js +30 -0
  28. package/src/commands/conflicts.js +16 -0
  29. package/src/commands/connect.js +60 -48
  30. package/src/commands/delete.js +79 -64
  31. package/src/commands/deploy.js +18 -10
  32. package/src/commands/diff.js +23 -0
  33. package/src/commands/help.js +99 -75
  34. package/src/commands/init.js +4 -10
  35. package/src/commands/local.js +23 -6
  36. package/src/commands/map.js +89 -89
  37. package/src/commands/mcp.js +126 -0
  38. package/src/commands/sync.js +28 -43
  39. package/src/commands/verifyUi.js +3 -2
  40. package/src/localdev/index.js +37 -7
  41. package/src/localdev/mysqlClient.js +1 -1
  42. package/src/mcp/client.js +275 -0
  43. package/src/mcp/hosts.js +520 -0
  44. package/src/mcp/protocol.js +173 -0
  45. package/src/mcp/stdio.js +300 -0
  46. package/src/mcp/tools.js +47 -0
  47. package/src/platforms.js +3 -4
  48. package/src/projectConfig.js +91 -49
  49. package/src/projectMap.js +123 -460
  50. package/src/skill.js +6 -28
  51. package/src/worktree/backend.js +326 -0
  52. package/src/worktree/errors.js +28 -0
  53. package/src/worktree/index.js +461 -0
  54. package/src/worktree/manifest.js +75 -0
  55. package/src/worktree/streams.js +200 -0
  56. package/src/worktree/types.js +103 -0
  57. package/src/worktree/validate.js +37 -0
  58. package/resources/skill/pull/SKILL.md +0 -33
  59. package/resources/skill/references/api.json +0 -20248
  60. package/resources/skill/scripts/draftgo_delete.py +0 -149
  61. package/resources/skill/scripts/draftgo_init.py +0 -80
  62. package/resources/skill/scripts/draftgo_pull.py +0 -427
  63. package/resources/skill/scripts/draftgo_push.py +0 -1022
  64. package/src/python.js +0 -27
@@ -1,107 +1,131 @@
1
1
  'use strict';
2
2
 
3
3
  const { all } = require('../installers');
4
+ const { HOSTS } = require('../mcp/hosts');
4
5
  const { getPackageVersion } = require('../skill');
5
6
 
6
7
  function help() {
7
8
  const targets = all.map((i) => ` ${i.name.padEnd(12)} ${i.displayName}`).join('\n');
8
- console.log(`draftgo v${getPackageVersion()} DraftGo Next workbench CLI for AI coding agents
9
+ const mcpTargets = HOSTS.map((host) => {
10
+ const status = host.supported === false ? 'unsupported' : host.path;
11
+ return ` ${host.name.padEnd(12)} ${status}`;
12
+ }).join('\n');
13
+
14
+ console.log(`draftgo v${getPackageVersion()} - DraftGo workbench CLI for AI coding agents
9
15
 
10
16
  DraftGo Next frontend baseline: React + Vite.
11
17
  Database pages use standard HTML + Tailwind CSS.
12
- The CLI is the workbench layer for local runtime, resource sync, checks, and push/pull flows.
18
+ MCP handles live discovery and structured resources; complete page, navigation,
19
+ and document bodies use checkout/commit outside MCP context.
13
20
 
14
21
  Usage:
15
- draftgo init [<target>...] Install skill. No target = auto-detect.
16
- Use "all" to install every target.
22
+ draftgo init [<target>...] Install the DraftGo Skill. No target = detect.
23
+ Use "all" to install every Skill target.
17
24
  draftgo update [<target>...] Fully update the global CLI, then atomically
18
- refresh skill + entry files.
19
- draftgo uninstall <target|all> Remove entry files for target(s).
20
- Use explicit "all" to remove every target.
21
- --purge also removes .draftgo/ (runtime data).
22
- draftgo status Show installed targets and skill version.
23
- draftgo map Print a local DraftGo project resource map
24
- for fast AI orientation.
25
- draftgo check Check page entry binding, page/navigation
26
- HTML parsing, and unregistered local files.
25
+ refresh installed or detected Skills.
26
+ draftgo uninstall <target|all> Remove Skill files. --purge also removes
27
+ the entire .draftgo/ runtime directory.
28
+ draftgo status Show installed targets and Skill version.
29
+
30
+ draftgo connect [<target>...] Verify and save server/SAT, then configure
31
+ a detected or explicit MCP host. This never
32
+ downloads DraftGo business resources.
33
+ draftgo mcp setup [<target>...] Merge project-level stdio MCP configuration.
34
+ draftgo mcp status [<target>...] Check host configuration and secret safety.
35
+ draftgo mcp test Test initialize, tools/list, and tools/call.
36
+ draftgo mcp serve Bridge local stdio to the remote /mcp endpoint.
37
+
38
+ draftgo map Read remote overview/resources through MCP and
39
+ overlay local checkout state.
40
+ draftgo checkout <type> <id...> Download pages/nav/docs body + verified base.
41
+ --force explicitly replaces local changes.
42
+ draftgo check Validate checked-out worktree/base files only.
43
+ draftgo diff <type> <id> Show checkout base versus local body.
44
+ draftgo commit <type> <id...> Validate and upload complete checked-out bodies.
45
+ draftgo conflicts List unresolved conflicts; --all includes resolved.
46
+ draftgo conflict show <type> <id>
47
+ Show preserved base/local/remote paths.
48
+ draftgo conflict resolve <type> <id>
49
+ Mark a merged worktree file ready against the
50
+ preserved remote base; then check/diff/commit.
27
51
  draftgo verify-ui <url> Run a deterministic browser smoke check.
28
- Uses one viewport and screenshots on failure.
29
- draftgo api <keyword> Search DraftGo base APIs and show request/
30
- response/authentication contracts.
31
- Custom service routes come from app.Route;
32
- they are not part of the bundled OpenAPI.
33
- draftgo delete <type> <id> Delete resource from server + local index.
34
- Types: pages | nav | db_meta | custom_scripts
35
- | docs | doc_categories | aihub
36
- Alias: del, rm. Flags: --yes / -y skip confirm.
37
- draftgo pull [type] [id...] Pull DraftGo resources via the bundled
38
- sync script. Defaults to --all.
39
- draftgo push <type> [id...] Push DraftGo resources via the bundled
40
- sync script.
41
- draftgo deploy [type] [id...] Run check, then push explicitly.
42
- --delivery local|preview|deploy
43
- draftgo auto-push [type] [id...] Run check, then push only when
44
- config.auto_push is true.
45
- Supports --batch <type> <ids> ... too.
52
+
53
+ draftgo api <query> Search the live API contract through MCP.
54
+ draftgo api describe <operation_id>
55
+ Describe one live API operation through MCP.
56
+ draftgo delete <operation_id> [id]
57
+ Confirm and call a live delete operation.
58
+ Legacy <type> <id> works only when unambiguous.
59
+ draftgo deploy [<type> <id...>] Check, then diff or commit checked-out content.
60
+ --delivery local needs no IDs; preview/deploy
61
+ require an explicit type and IDs.
62
+ draftgo auto-push [<type> <id...>]
63
+ With config.auto_push=true, check and commit
64
+ changed checkouts. Any conflict stops the run.
65
+ draftgo pull Migration notice only; always downloads nothing.
66
+ draftgo push <type> <id...> Deprecated alias to commit for pages/nav/docs.
67
+ Push-all and structured-resource push are removed.
68
+
46
69
  draftgo local setup|start|stop|logs|status
47
- Manage .draftgo/docker/docker-compose.yaml
48
- generated by local setup. up/down remain
49
- compatible aliases for start/stop.
50
- draftgo list-targets List supported AI tools.
51
- draftgo connect Bind this project to an existing DraftGo
52
- server. Prompts for BaseURL + access
53
- token (or pass --server / --token).
54
- draftgo local setup One-click local DraftGo stack via Docker
55
- (shared MySQL, Redis, Milvus + app). It
56
- generates an app-only project compose,
57
- <project>/.draftgo/docker/docker-compose.yaml,
58
- then guides you to finish setup and paste
59
- an access token.
70
+ Manage the existing local Docker stack. up/down
71
+ remain aliases for start/stop; status also has ps.
60
72
  draftgo local-dev Compatibility alias for \`draftgo local setup\`.
73
+ draftgo list-targets List Skill installation targets.
61
74
  draftgo -v | --version Print CLI version.
62
75
  draftgo -h | --help Show this help.
63
76
 
64
- v3 Workbench:
65
- draftgo local start|stop|logs Local stack lifecycle commands.
66
- draftgo check Local resource closure gate.
67
- draftgo pull|push First-class resource sync wrappers.
77
+ Resource types:
78
+ pages | nav/navigations | docs/articles
68
79
 
69
- Flags:
80
+ Important flags:
70
81
  --project <dir> Operate on <dir> instead of the current directory.
71
- --force Overwrite existing skill body during install/update.
72
- --purge (uninstall) Also remove the entire .draftgo/ directory.
73
- --skip-update-check Do not contact npm to check for a newer CLI.
74
- --yes Assume "yes" for interactive prompts.
75
- --connect (init) Skip the prompt and run \`draftgo connect\`.
76
- --local-dev (init) Skip the prompt and run \`draftgo local setup\`.
77
- --no-setup (init) Don't offer either flow after installing.
78
- --server <url> (connect) Provide the DraftGo BaseURL non-interactively.
79
- --token <sat> (connect) Provide the access token non-interactively.
80
- --output <json> (map/check) Print machine-readable JSON.
81
- --strict (check) Treat warnings as failures.
82
- --mobile-check <mode> (verify-ui) auto | always | never. Default: auto.
82
+ --target <name,...> Select one or more MCP setup/status targets.
83
+ --server <url> (connect) DraftGo base URL.
84
+ --token <sat> (connect) SAT for non-interactive use.
85
+ --timeout <ms> (connect/mcp test) Network timeout.
86
+ --allow-offline (connect) Save only after explicitly accepting a
87
+ failed MCP validation.
88
+ --no-mcp-setup (connect) Do not write host MCP configuration.
89
+ --force (init/checkout) Overwrite Skill or local changes.
90
+ --purge (uninstall) Also remove the .draftgo/ directory.
91
+ --skip-update-check (update) Refresh Skills without contacting npm.
92
+ --connect (init) Continue into DraftGo server connection.
93
+ --local-dev (init) Continue into local Docker setup.
94
+ --no-setup (init) Install the Skill without either setup flow.
95
+ --output json Print machine-readable output where supported.
96
+ --strict Treat check warnings as failures.
97
+ --yes Skip supported confirmation prompts.
98
+ --operation-id <id> (delete) Select an operation explicitly.
99
+ --params <json> (delete) Pass an API parameter object.
100
+ --input <file> (delete) Read the parameter object from JSON.
101
+ --delivery <mode> (deploy) local | preview | deploy.
102
+ --dry-run (push) Show diffs without committing.
103
+ --mobile-check <mode> (verify-ui) auto | always | never.
83
104
  --screenshot <mode> (verify-ui) on-failure | always | never.
84
105
  --browser <name> (verify-ui) chromium | chrome | msedge.
85
- --selector <css> (verify-ui) Require a key element to be visible.
106
+ --selector <css> (verify-ui) Require a visible key element.
86
107
  --width/--height <px> (verify-ui) Override the default 390x844 viewport.
87
- --delivery <mode> (deploy) local | preview | deploy.
88
108
 
89
- Environment:
90
- DRAFTGO_NO_UPDATE_CHECK=1 Disable the cached CLI version check.
109
+ Security:
110
+ .draftgo/config.json stores the server and SAT and is gitignored. Host MCP
111
+ files contain only "draftgo mcp serve"; they never contain the SAT.
112
+
113
+ MCP targets (project config or status):
114
+ ${mcpTargets}
91
115
 
92
- Targets:
116
+ Skill targets:
93
117
  ${targets}
94
118
 
95
119
  Examples:
96
- draftgo init # auto-detect AI tools in this project
97
- draftgo init claudecode # install for Claude Code only
98
- draftgo init claudecode kiro # install for both
99
- draftgo init all # install for every supported target
100
- draftgo update # fully update CLI and installed skills
101
- draftgo map # inspect pages/nav/db/scripts before development
102
- draftgo check --strict # fail on any resource-check warning before push
103
- draftgo verify-ui http://localhost:5173 --mobile-check auto
104
- draftgo uninstall all --purge # full removal incl. runtime data
120
+ draftgo init codex
121
+ draftgo connect codex --server https://draftgo.example --token <sat>
122
+ draftgo mcp test
123
+ draftgo map --output json
124
+ draftgo checkout pages 42
125
+ draftgo check --strict
126
+ draftgo diff pages 42
127
+ draftgo commit pages 42
128
+ draftgo deploy docs 7 --delivery preview
105
129
  `);
106
130
  }
107
131
 
@@ -5,18 +5,12 @@ const log = require('../logger');
5
5
  const { detectTargets } = require('../detect');
6
6
  const { resolveTargets, all } = require('../installers');
7
7
  const { ensureRuntime, writeInstalledVersion, getPackageVersion } = require('../skill');
8
- const { findPython } = require('../python');
9
8
  const { exists } = require('../fsx');
10
9
  const { ask } = require('../prompt');
11
10
 
12
- function warnIfNoPython() {
13
- const py = findPython();
14
- if (!py) {
15
- log.warn('Python 未检测到。DraftGo 资源同步需要 Python 3.9+。');
16
- log.dim(' 安装 Python 3.9+ 后运行 `draftgo pull` 获取项目资源。');
17
- } else {
18
- log.dim(` detected ${py.bin} (${py.version})`);
19
- }
11
+ function showEnvironment() {
12
+ log.dim(` Node.js ${process.versions.node}`);
13
+ log.dim(' DraftGo resources use MCP discovery; long content uses checkout/commit.');
20
14
  }
21
15
 
22
16
  async function init(projectDir, positional, flags) {
@@ -77,7 +71,7 @@ async function init(projectDir, positional, flags) {
77
71
 
78
72
  // 3) Environment check (advisory).
79
73
  log.step('环境检查');
80
- warnIfNoPython();
74
+ showEnvironment();
81
75
 
82
76
  // 4) Optional server binding flow.
83
77
  const cfgPath = path.join(projectDir, '.draftgo', 'config.json');
@@ -27,12 +27,29 @@ function runCompose(projectDir, args, stdio = 'inherit') {
27
27
  return 1;
28
28
  }
29
29
 
30
- const r = spawnSync(docker.composeCmd, [...docker.composeArgs, '-f', file, ...args], {
31
- cwd: path.dirname(file),
32
- stdio,
33
- shell: false,
34
- });
35
- return r.status || 0;
30
+ let result;
31
+ try {
32
+ result = spawnSync(docker.composeCmd, [...docker.composeArgs, '-f', file, ...args], {
33
+ cwd: path.dirname(file),
34
+ stdio,
35
+ shell: false,
36
+ });
37
+ } catch (error) {
38
+ log.err(`Failed to start Docker Compose: ${error.message}`);
39
+ return 1;
40
+ }
41
+
42
+ if (result.error) {
43
+ log.err(`Failed to start Docker Compose: ${result.error.message}`);
44
+ return 1;
45
+ }
46
+ if (!Number.isInteger(result.status)) {
47
+ log.err(result.signal
48
+ ? `Docker Compose terminated by signal ${result.signal}.`
49
+ : 'Docker Compose did not report an exit status.');
50
+ return 1;
51
+ }
52
+ return result.status;
36
53
  }
37
54
 
38
55
  function local(projectDir, positional, flags = {}) {
@@ -1,105 +1,105 @@
1
1
  'use strict';
2
2
 
3
+ const fs = require('fs');
4
+ const path = require('path');
3
5
  const log = require('../logger');
4
- const { buildProjectMap } = require('../projectMap');
6
+ const { loadProjectConfig } = require('../projectConfig');
7
+ const { loadManifest, absolutePath } = require('../worktree/manifest');
8
+ const { hashFile } = require('../worktree/streams');
9
+ const { TOOL_NAMES, openToolSession, callStructured } = require('../mcp/tools');
10
+
11
+ function itemsFrom(value) {
12
+ if (Array.isArray(value)) return value;
13
+ if (!value || typeof value !== 'object') return [];
14
+ if (Array.isArray(value.items)) return value.items;
15
+ if (Array.isArray(value.resources)) return value.resources;
16
+ if (value.data) return itemsFrom(value.data);
17
+ return [];
18
+ }
19
+
20
+ function nextCursor(value) {
21
+ return value && (value.next_cursor || value.nextCursor || value.cursor && value.has_more && value.cursor);
22
+ }
23
+
24
+ async function listRemoteResources(session, flags) {
25
+ const resources = [];
26
+ const seen = new Set();
27
+ let cursor = null;
28
+ for (let page = 0; page < 100; page += 1) {
29
+ const args = {};
30
+ if (cursor) args.cursor = cursor;
31
+ if (flags.type) args.resource_type = String(flags.type);
32
+ const payload = await callStructured(session, TOOL_NAMES.resourceList, args);
33
+ resources.push(...itemsFrom(payload));
34
+ cursor = nextCursor(payload);
35
+ if (!cursor) break;
36
+ if (seen.has(cursor)) throw new Error('DraftGo resource_list repeated a cursor.');
37
+ seen.add(cursor);
38
+ }
39
+ if (cursor) throw new Error('DraftGo resource_list exceeded 100 pages.');
40
+ return resources;
41
+ }
5
42
 
6
- function printList(title, rows, render) {
7
- console.log('');
8
- log.info(`${title}:${rows.length}`);
9
- if (rows.length === 0) {
10
- log.dim(' (无)');
11
- return;
43
+ async function localCheckouts(projectDir) {
44
+ const manifest = loadManifest(projectDir);
45
+ const entries = [];
46
+ for (const entry of Object.values(manifest.entries)) {
47
+ const local = absolutePath(projectDir, entry.local_path);
48
+ let currentHash = null;
49
+ if (fs.existsSync(local)) currentHash = (await hashFile(local)).hash;
50
+ entries.push({
51
+ ...entry,
52
+ exists: currentHash !== null,
53
+ current_hash: currentHash,
54
+ changed: currentHash !== null && currentHash !== entry.base_hash,
55
+ });
12
56
  }
13
- for (const row of rows) log.dim(` • ${render(row)}`);
57
+ return entries;
14
58
  }
15
59
 
16
- function mapCommand(projectDir, flags = {}) {
17
- const map = buildProjectMap(projectDir);
60
+ function legacyCaches(projectDir) {
61
+ return ['pages', 'navigations', 'docs', 'db_meta', 'custom_scripts', 'aihub', 'roles', 'users', 'system_config']
62
+ .filter((name) => fs.existsSync(path.join(projectDir, '.draftgo', name, 'index.json')));
63
+ }
64
+
65
+ async function mapCommand(projectDir, flags = {}) {
66
+ const config = loadProjectConfig(projectDir);
67
+ const session = await openToolSession(config, [TOOL_NAMES.projectOverview, TOOL_NAMES.resourceList]);
68
+ const [overview, resources, checkouts] = await Promise.all([
69
+ callStructured(session, TOOL_NAMES.projectOverview, {}),
70
+ listRemoteResources(session, flags),
71
+ localCheckouts(projectDir),
72
+ ]);
73
+ const result = {
74
+ server: config.server,
75
+ overview,
76
+ resources,
77
+ checkouts,
78
+ legacy_cache: { ignored: true, detected: legacyCaches(projectDir) },
79
+ };
18
80
 
19
81
  if (flags.output === 'json') {
20
- console.log(JSON.stringify(map, null, 2));
82
+ console.log(JSON.stringify(result, null, 2));
21
83
  return 0;
22
84
  }
23
-
24
85
  log.title('draftgo map');
25
- log.info(`项目目录:${projectDir}`);
26
-
27
- printList('页面', map.pages, (p) => {
28
- const id = p.id == null ? 'new' : p.id;
29
- return `${String(id).padEnd(4)} ${p.route || '(无 route)'} ${p.title || '未命名'}${p.html_file ? ` ${p.html_file}` : ''}`;
30
- });
31
-
32
- printList('导航', map.navigations, (n) => {
33
- const id = n.id == null ? 'new' : n.id;
34
- return `${String(id).padEnd(4)} ${n.code || 'default'} ${n.name || '未命名'}${n.html_file ? ` ${n.html_file}` : ''}`;
35
- });
36
-
37
- printList('动态 DB', map.db_meta, (m) => {
38
- const fields = m.fields.length ? ` (${m.fields.slice(0, 8).join(', ')}${m.fields.length > 8 ? ', ...' : ''})` : '';
39
- return `${m.type || '(无 type)'} ${m.label || ''}${fields}`;
40
- });
41
-
42
- printList('自定义脚本', map.custom_scripts, (s) => {
43
- const bindings = [
44
- ...(s.routes || []),
45
- ...(s.events || []).map((event) => `EVENT ${event}`),
46
- ...(s.schedules || []).map((cron) => `CRON ${cron}`),
47
- ];
48
- const summary = bindings.length ? ` [${bindings.join(' | ')}]` : '';
49
- return `${s.slug || '(无 slug)'} ${s.mode || 'mode?'} ${s.name || ''}${summary}${s.code_file ? ` ${s.code_file}` : ''}`;
50
- });
51
-
52
- printList('文档', map.docs, (d) => {
53
- return `${d.id || 'new'} ${d.slug || '(无 slug)'} ${d.title || '未命名'}${d.content_file ? ` ${d.content_file}` : ''}`;
54
- });
55
-
56
- printList('文档分类', map.doc_categories, (c) => {
57
- const parent = c.parent_id == null ? 'root' : `parent:${c.parent_id}`;
58
- return `${c.id || 'new'} ${c.slug || '(无 slug)'} ${c.name || '未命名'} ${parent}`;
59
- });
60
-
61
- printList('系统配置', map.system_config, (c) => {
62
- const sensitive = c.is_sensitive ? ' sensitive' : '';
63
- return `${c.config_key || '(无 key)'} ${c.category || 'default'} ${c.value_type || 'value'}${sensitive}`;
64
- });
65
-
66
- printList('AIHub', map.aihub, (a) => {
67
- const extra = [];
68
- if (a.type === 'agent') {
69
- if (a.mode) extra.push(a.mode);
70
- if (a.output_format && a.output_format.mode === 'json') extra.push(`json:${a.output_format.json_strategy || 'auto'}`);
71
- if (a.model_selection && a.model_selection.user_selectable) extra.push('user-model');
72
- if (a.tools && a.tools.sources.length) extra.push(`tools:${a.tools.sources.length}`);
73
- } else if (a.type === 'model') {
74
- extra.push(`models:${a.models_count || 0}`);
75
- if (a.supports_response_format === true) extra.push('response_format');
76
- if (a.supports_json_schema === true) extra.push('json_schema');
77
- } else if (a.type === 'mcp') {
78
- extra.push(a.transport || 'transport?');
79
- extra.push(`tools:${a.tools_count || 0}`);
80
- }
81
- return `${a.id || 'new'} ${a.type || ''} ${a.name || ''}${extra.length ? ` [${extra.join(', ')}]` : ''}`;
82
- });
83
- printList('角色', map.roles, (r) => `${r.code || r.id || 'new'} ${r.name || ''}`);
84
-
85
- console.log('');
86
- log.info('页面分区:');
87
- const areaRows = [
88
- ['home', map.pageGroups.home.length],
89
- ['admin', map.pageGroups.admin.length],
90
- ['business', map.pageGroups.business.length],
91
- ['system', map.pageGroups.system.length],
92
- ['unknown', map.pageGroups.unknown.length],
93
- ];
94
- log.dim(' ' + areaRows.map(([k, v]) => `${k} ${v}`).join(' '));
95
-
96
- console.log('');
97
- log.info('入口引用:');
98
- const routes = Object.keys(map.routeRefs).sort();
99
- if (routes.length === 0) log.dim(' (未发现 data-page-route / href 引用)');
100
- else routes.forEach((route) => log.dim(` • ${route} ← ${map.routeRefs[route].join(', ')}`));
101
-
86
+ log.info(`Remote resources: ${resources.length}`);
87
+ const counts = new Map();
88
+ for (const resource of resources) {
89
+ const type = resource.resource_type || resource.type || 'unknown';
90
+ counts.set(type, (counts.get(type) || 0) + 1);
91
+ }
92
+ for (const [type, count] of [...counts.entries()].sort()) log.plain(` ${type}: ${count}`);
93
+ log.info(`Local checkouts: ${checkouts.length}`);
94
+ for (const entry of checkouts) {
95
+ const state = !entry.exists ? 'missing' : entry.changed ? 'modified' : 'clean';
96
+ log.plain(` ${entry.resource_type} ${entry.resource_id}: ${state} (${entry.local_path})`);
97
+ }
98
+ if (result.legacy_cache.detected.length) {
99
+ log.warn(`Ignored legacy cache indexes: ${result.legacy_cache.detected.join(', ')}`);
100
+ }
102
101
  return 0;
103
102
  }
104
103
 
105
104
  module.exports = mapCommand;
105
+ module.exports.itemsFrom = itemsFrom;
@@ -0,0 +1,126 @@
1
+ 'use strict';
2
+
3
+ const log = require('../logger');
4
+ const { loadProjectConfig } = require('../projectConfig');
5
+ const { testConnection } = require('../mcp/client');
6
+ const { redactText } = require('../mcp/protocol');
7
+ const {
8
+ HOSTS,
9
+ detectHostTargets,
10
+ setupHosts,
11
+ statusHosts,
12
+ } = require('../mcp/hosts');
13
+ const { serveStdio } = require('../mcp/stdio');
14
+
15
+ function targetArgs(positional, flags) {
16
+ const values = positional.slice();
17
+ if (flags.target) values.push(...String(flags.target).split(','));
18
+ return values.map((value) => String(value).trim()).filter(Boolean);
19
+ }
20
+
21
+ function printUsage() {
22
+ log.plain('Usage:');
23
+ log.plain(' draftgo mcp setup [target...]');
24
+ log.plain(' draftgo mcp status [target...]');
25
+ log.plain(' draftgo mcp test');
26
+ log.plain(' draftgo mcp serve');
27
+ log.dim(` targets: ${HOSTS.map((host) => host.name).join(', ')}`);
28
+ }
29
+
30
+ function setup(projectDir, positional = [], flags = {}) {
31
+ const targets = targetArgs(positional, flags);
32
+ const detected = targets.length ? null : detectHostTargets(projectDir);
33
+ if (!targets.length && detected.length === 0) {
34
+ log.err('No supported AI host was detected in this project.');
35
+ log.dim(' Run `draftgo mcp setup <target>` to select one explicitly.');
36
+ return 1;
37
+ }
38
+
39
+ const results = setupHosts(projectDir, targets.length ? targets : detected.map((host) => host.name));
40
+ let configured = 0;
41
+ let unsupported = 0;
42
+ for (const result of results) {
43
+ if (!result.supported) {
44
+ unsupported += 1;
45
+ log.warn(`${result.host.displayName}: ${result.reason}`);
46
+ continue;
47
+ }
48
+ configured += 1;
49
+ if (result.changed) log.ok(`${result.host.displayName}: ${result.host.path}`);
50
+ else log.info(`${result.host.displayName}: already configured`);
51
+ }
52
+ if (!configured && unsupported) return 1;
53
+ if (!configured) {
54
+ log.err('No MCP host configuration was written.');
55
+ return 1;
56
+ }
57
+ return 0;
58
+ }
59
+
60
+ function status(projectDir, positional = [], flags = {}) {
61
+ const targets = targetArgs(positional, flags);
62
+ const results = statusHosts(projectDir, targets);
63
+ log.title('draftgo mcp status');
64
+ let exitCode = 0;
65
+ try {
66
+ const config = loadProjectConfig(projectDir);
67
+ log.plain(` project configured (${config.server}; SAT present)`);
68
+ } catch (error) {
69
+ log.plain(` project not ready (${error.message})`);
70
+ exitCode = 1;
71
+ }
72
+ for (const result of results) {
73
+ if (!result.supported) {
74
+ log.plain(` - ${result.host.name.padEnd(12)} unsupported`);
75
+ } else if (result.configured && result.secure) {
76
+ log.plain(` OK ${result.host.name.padEnd(12)} ${result.host.path}`);
77
+ } else if (result.error) {
78
+ log.plain(` x ${result.host.name.padEnd(12)} invalid config`);
79
+ exitCode = 1;
80
+ } else {
81
+ log.plain(` - ${result.host.name.padEnd(12)} not configured`);
82
+ }
83
+ }
84
+ return exitCode;
85
+ }
86
+
87
+ async function test(projectDir, _positional = [], flags = {}) {
88
+ let config;
89
+ try {
90
+ config = loadProjectConfig(projectDir);
91
+ const result = await testConnection(config, {
92
+ timeoutMs: flags.timeout ? Number(flags.timeout) : undefined,
93
+ });
94
+ log.ok('DraftGo MCP initialize succeeded.');
95
+ log.ok(`tools/list returned ${result.tools.length} tools.`);
96
+ log.ok(`tools/call succeeded: ${result.testedTool}`);
97
+ log.dim(` protocol: ${result.protocolVersion}`);
98
+ return 0;
99
+ } catch (error) {
100
+ const token = config && String(config.token || config.sat || '');
101
+ log.err(redactText(error && error.message ? error.message : error, [token]));
102
+ return 1;
103
+ }
104
+ }
105
+
106
+ async function serve(projectDir, _positional = [], _flags = {}) {
107
+ return serveStdio(projectDir);
108
+ }
109
+
110
+ async function mcp(projectDir, positional = [], flags = {}) {
111
+ const [action, ...rest] = positional;
112
+ if (action === 'setup') return setup(projectDir, rest, flags);
113
+ if (action === 'status') return status(projectDir, rest, flags);
114
+ if (action === 'test') return test(projectDir, rest, flags);
115
+ if (action === 'serve') return serve(projectDir, rest, flags);
116
+ printUsage();
117
+ return 1;
118
+ }
119
+
120
+ mcp.setup = setup;
121
+ mcp.status = status;
122
+ mcp.test = test;
123
+ mcp.serve = serve;
124
+ mcp.printUsage = printUsage;
125
+
126
+ module.exports = mcp;