draftgo-cli 3.0.33 → 3.0.38

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 -269
  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 +13 -5
  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 +99 -23
  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 +37 -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 +250 -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,88 +1,103 @@
1
1
  'use strict';
2
2
 
3
+ const fs = require('fs');
3
4
  const path = require('path');
4
- const { exists } = require('../fsx');
5
5
  const log = require('../logger');
6
6
  const { confirm } = require('../prompt');
7
- const { findPython } = require('../python');
8
- const { spawnSync } = require('child_process');
9
- const { platforms } = require('../platforms');
7
+ const { loadProjectConfig } = require('../projectConfig');
8
+ const { TOOL_NAMES, openToolSession, callStructured } = require('../mcp/tools');
10
9
 
11
- function findDeleteScript(projectDir) {
12
- for (const platform of platforms) {
13
- const candidate = path.join(projectDir, platform.assetDir, 'scripts', 'draftgo_delete.py');
14
- if (exists(candidate)) return candidate;
15
- }
16
- const bundled = path.resolve(__dirname, '..', '..', 'resources', 'skill', 'scripts', 'draftgo_delete.py');
17
- return exists(bundled) ? bundled : null;
18
- }
10
+ const LEGACY_TYPES = new Set([
11
+ 'page', 'pages', 'nav', 'navigation', 'navigations', 'doc', 'docs', 'article',
12
+ 'db_meta', 'custom_scripts', 'doc_categories', 'aihub', 'roles', 'users', 'system_config',
13
+ ]);
19
14
 
20
- const TYPE_ALIASES = {
21
- page: 'pages',
22
- pages: 'pages',
23
- nav: 'nav',
24
- navigations: 'nav',
25
- db_meta: 'db_meta',
26
- script: 'custom_scripts',
27
- scripts: 'custom_scripts',
28
- custom_scripts: 'custom_scripts',
29
- doc: 'docs',
30
- docs: 'docs',
31
- doc_category: 'doc_categories',
32
- doc_categories: 'doc_categories',
33
- aihub: 'aihub',
34
- };
15
+ function resultItems(value) {
16
+ if (Array.isArray(value)) return value;
17
+ if (!value || typeof value !== 'object') return [];
18
+ if (Array.isArray(value.items)) return value.items;
19
+ if (Array.isArray(value.operations)) return value.operations;
20
+ return value.data ? resultItems(value.data) : [];
21
+ }
35
22
 
36
- async function deleteResource(projectDir, positional, flags) {
37
- const [typeRaw, id] = positional;
23
+ function operationId(value) {
24
+ return value && (value.operation_id || value.operationId || value.id);
25
+ }
38
26
 
39
- if (!typeRaw || !id) {
40
- log.err('用法:draftgo delete <type> <id>');
41
- log.dim(' 支持类型:pages | nav | db_meta | custom_scripts | docs | doc_categories | aihub');
42
- return 1;
27
+ function readParams(projectDir, flags) {
28
+ const raw = flags.params ? String(flags.params)
29
+ : flags.input ? fs.readFileSync(path.resolve(projectDir, String(flags.input)), 'utf8') : '';
30
+ if (!raw) return {};
31
+ const parsed = JSON.parse(raw);
32
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
33
+ throw new Error('Delete MCP parameters must be a JSON object.');
43
34
  }
35
+ return parsed;
36
+ }
44
37
 
45
- const type = TYPE_ALIASES[typeRaw];
46
- if (!type) {
47
- log.err(`未知类型:${typeRaw}`);
48
- return 1;
49
- }
38
+ function pathParameterName(description) {
39
+ const source = description && (description.parameters || description.params
40
+ || description.operation && description.operation.parameters) || [];
41
+ const pathParameters = source.filter((parameter) => parameter && parameter.in === 'path');
42
+ return pathParameters.length === 1 ? pathParameters[0].name : null;
43
+ }
50
44
 
51
- if (!exists(path.join(projectDir, '.draftgo', 'config.json'))) {
52
- log.err('未找到 .draftgo/config.json,请先运行 /draftgo init');
45
+ async function deleteResource(projectDir, positional, flags = {}) {
46
+ if (!positional.length && !flags['operation-id']) {
47
+ log.err('Usage: draftgo delete <operation_id> [id] [--params JSON]');
48
+ log.dim(' Legacy form is supported when discovery is unambiguous: draftgo delete <type> <id>.');
53
49
  return 1;
54
50
  }
51
+ const config = loadProjectConfig(projectDir);
52
+ const session = await openToolSession(config, [
53
+ TOOL_NAMES.apiSearch, TOOL_NAMES.apiDescribe, TOOL_NAMES.apiCall,
54
+ ]);
55
55
 
56
- const py = findPython();
57
- if (!py) {
58
- log.err('未检测到 Python,无法运行删除脚本');
59
- return 1;
56
+ const first = String(positional[0] || '');
57
+ const legacy = LEGACY_TYPES.has(first.toLowerCase()) && !flags['operation-id'];
58
+ let selectedOperation = flags['operation-id'] ? String(flags['operation-id']) : (legacy ? null : first);
59
+ const resourceId = legacy ? positional[1] : positional[1];
60
+ if (legacy) {
61
+ if (!resourceId) {
62
+ log.err('Legacy delete form requires a resource id.');
63
+ return 1;
64
+ }
65
+ const search = await callStructured(session, TOOL_NAMES.apiSearch, { query: `delete ${first}` });
66
+ const candidates = resultItems(search).filter((item) => operationId(item)
67
+ && (!item.method || String(item.method).toUpperCase() === 'DELETE'));
68
+ if (candidates.length !== 1) {
69
+ log.err(`Delete operation discovery was ambiguous (${candidates.length} matches).`);
70
+ log.dim(` Run \`draftgo api delete ${first}\`, then pass --operation-id explicitly.`);
71
+ return 1;
72
+ }
73
+ selectedOperation = operationId(candidates[0]);
60
74
  }
61
75
 
62
- const script = findDeleteScript(projectDir);
63
- if (!script) {
64
- log.err('未找到 draftgo_delete.py,请运行 draftgo update 更新');
65
- return 1;
76
+ const description = await callStructured(session, TOOL_NAMES.apiDescribe, {
77
+ operation_id: selectedOperation,
78
+ });
79
+ const params = readParams(projectDir, flags);
80
+ if (resourceId && Object.keys(params).length === 0) {
81
+ const parameterName = pathParameterName(description);
82
+ if (!parameterName) {
83
+ log.err('The operation does not expose exactly one path parameter; provide --params JSON explicitly.');
84
+ return 1;
85
+ }
86
+ params.path_params = { [parameterName]: resourceId };
66
87
  }
67
-
68
88
  if (!flags.yes && !flags.y) {
69
- const ok = await confirm(`确认删除 ${type} id=${id}?此操作不可恢复`);
70
- if (!ok) {
71
- log.dim('已取消');
72
- return 0;
73
- }
89
+ const approved = await confirm(`Call destructive DraftGo operation ${selectedOperation}?`, { default: false });
90
+ if (!approved) return 0;
74
91
  }
75
-
76
- const r = spawnSync(py.bin, [script, type, id], {
77
- cwd: projectDir,
78
- stdio: 'inherit',
79
- shell: false,
92
+ const result = await callStructured(session, TOOL_NAMES.apiCall, {
93
+ ...params,
94
+ operation_id: selectedOperation,
95
+ confirm: true,
80
96
  });
81
- if (r.error) {
82
- log.err(`删除脚本启动失败:${r.error.message}`);
83
- return 1;
84
- }
85
- return Number.isInteger(r.status) ? r.status : 1;
97
+ if (flags.output === 'json') console.log(JSON.stringify(result, null, 2));
98
+ else log.ok(`DraftGo operation completed: ${selectedOperation}`);
99
+ return 0;
86
100
  }
87
101
 
88
102
  module.exports = deleteResource;
103
+ module.exports.pathParameterName = pathParameterName;
@@ -2,30 +2,38 @@
2
2
 
3
3
  const log = require('../logger');
4
4
  const check = require('./check');
5
- const sync = require('./sync');
6
5
 
7
- function deploy(projectDir, positional, flags = {}) {
6
+ async function deploy(projectDir, positional, flags = {}) {
8
7
  const delivery = String(flags.delivery || 'deploy').toLowerCase();
9
8
  if (!['local', 'preview', 'deploy'].includes(delivery)) {
10
- log.err('--delivery 只支持 localpreviewdeploy');
9
+ log.err('--delivery accepts local, preview, or deploy.');
11
10
  return 1;
12
11
  }
13
- log.step('deploy: 先执行 check …');
12
+
13
+ log.step('deploy: validating checked-out content...');
14
14
  const checkCode = check(projectDir, { strict: flags.strict });
15
15
  if (checkCode !== 0) {
16
- log.err('deploy 终止:check 未通过,请修复后重试。');
16
+ log.err('Deploy stopped because local validation failed.');
17
17
  return checkCode;
18
18
  }
19
19
  if (delivery === 'local') {
20
- log.ok('delivery=local:本地检查通过,未修改云端。');
20
+ log.ok('Local validation passed; no remote content was changed.');
21
21
  return 0;
22
22
  }
23
+
24
+ const [resourceType, ...ids] = positional;
25
+ if (!resourceType || !ids.length) {
26
+ log.err('Deploy no longer commits every legacy index. Specify <pages|nav|docs> <id...>.');
27
+ return 1;
28
+ }
23
29
  if (delivery === 'preview') {
24
- log.step('delivery=preview:执行 push dry-run …');
25
- return sync(projectDir, 'push', positional, { ...flags, 'dry-run': true });
30
+ for (const id of ids) {
31
+ const code = require('./diff')(projectDir, [resourceType, id], flags);
32
+ if (code !== 0) return code;
33
+ }
34
+ return 0;
26
35
  }
27
- log.step('delivery=deploy:开始 push …');
28
- return sync(projectDir, 'push', positional, flags);
36
+ return require('./commit')(projectDir, [resourceType, ...ids], flags);
29
37
  }
30
38
 
31
39
  module.exports = deploy;
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ const log = require('../logger');
4
+ const { diffResource } = require('../worktree');
5
+
6
+ function diff(projectDir, positional, flags = {}) {
7
+ const [resourceType, resourceId] = positional;
8
+ if (!resourceType || !resourceId) {
9
+ log.err('Usage: draftgo diff <pages|nav|docs> <id>');
10
+ return 1;
11
+ }
12
+ const result = diffResource(projectDir, resourceType, resourceId);
13
+ if (flags.output === 'json') {
14
+ console.log(JSON.stringify({ changed: result.changed, entry: result.entry, diff: result.output }, null, 2));
15
+ } else if (result.changed) {
16
+ process.stdout.write(result.output);
17
+ } else {
18
+ log.dim(`${result.entry.resource_type} ${result.entry.resource_id}: no local changes`);
19
+ }
20
+ return 0;
21
+ }
22
+
23
+ module.exports = diff;
@@ -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 = {}) {