draftgo-cli 1.0.0

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/src/index.js ADDED
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { parse } = require('./cli');
5
+ const log = require('./logger');
6
+
7
+ async function run(argv) {
8
+ const { command, positional, flags } = parse(argv);
9
+
10
+ // --version / --help shortcuts
11
+ if (flags.version || flags.v || command === 'version') {
12
+ console.log(require('./skill').getPackageVersion());
13
+ return 0;
14
+ }
15
+ if (!command || flags.help || flags.h || command === 'help') {
16
+ require('./commands/help')();
17
+ return 0;
18
+ }
19
+
20
+ const projectDir = flags.project
21
+ ? path.resolve(String(flags.project))
22
+ : process.cwd();
23
+
24
+ try {
25
+ switch (command) {
26
+ case 'init':
27
+ return await require('./commands/init')(projectDir, positional, flags);
28
+ case 'update':
29
+ case 'upgrade':
30
+ return await require('./commands/update')(projectDir, positional, flags);
31
+ case 'uninstall':
32
+ case 'remove':
33
+ return await require('./commands/uninstall')(projectDir, positional, flags);
34
+ case 'status':
35
+ return require('./commands/status')(projectDir);
36
+ case 'doctor':
37
+ return require('./commands/doctor')(projectDir);
38
+ case 'list-targets':
39
+ case 'targets':
40
+ return require('./commands/listTargets')();
41
+ default:
42
+ log.err(`未知命令:${command}`);
43
+ require('./commands/help')();
44
+ return 1;
45
+ }
46
+ } catch (e) {
47
+ log.err(e.message || String(e));
48
+ if (process.env.DEBUG) console.error(e.stack);
49
+ return 1;
50
+ }
51
+ }
52
+
53
+ module.exports = { run };
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ // Shared body content for AI-tool entry files.
4
+ // Each entry file is a thin shell that tells the agent: "read the shared
5
+ // skill body at .draftgo/skill/ and follow it". The skill body is the single
6
+ // source of truth and lives outside any AI-tool-specific folder.
7
+
8
+ function entryMarkdown({ command = '/draftgo' } = {}) {
9
+ return `---
10
+ name: draftgo
11
+ description: DraftGo 开发助手(由 draftgo-cli 安装)。用户谈及 DraftGo 开发、页面、导航、同步等场景时激活。
12
+ ---
13
+
14
+ # DraftGo
15
+
16
+ 此文件由 \`draftgo-cli\` 管理,是 DraftGo skill 的入口。实际内容在本项目的共享目录:
17
+
18
+ - 主提示:\`.draftgo/skill/SKILL.md\`
19
+ - 初始化子技能:\`.draftgo/skill/init/SKILL.md\`
20
+ - 同步子技能:\`.draftgo/skill/sync/SKILL.md\`
21
+ - Bug 记录子技能:\`.draftgo/skill/bug/SKILL.md\`
22
+ - 前端开发规范:\`.draftgo/skill/rules/frontend.md\`
23
+ - 页面排错指南:\`.draftgo/skill/rules/debugging-syntax.md\`
24
+ - 运行时脚本:\`.draftgo/skill/scripts/draftgo_init.py\`、\`.draftgo/skill/scripts/draftgo_sync.py\`
25
+
26
+ ## 使用
27
+
28
+ - 初始化项目:\`${command} init\`
29
+ - 同步到服务器:\`${command} sync pages|nav|db_meta [id]\`
30
+ - 记录 bug:由 \`bug\` 子技能自动处理
31
+
32
+ ## 运行脚本的正确方式
33
+
34
+ \`\`\`bash
35
+ python .draftgo/skill/scripts/draftgo_init.py --server <server> --token <token>
36
+ python .draftgo/skill/scripts/draftgo_sync.py pages [page_id]
37
+ python .draftgo/skill/scripts/draftgo_sync.py nav [nav_id]
38
+ python .draftgo/skill/scripts/draftgo_sync.py db_meta [db_meta_id]
39
+ \`\`\`
40
+
41
+ > 禁止绕过脚本手动调用 API。详细规范以 \`.draftgo/skill/\` 下文档为准。
42
+ `;
43
+ }
44
+
45
+ module.exports = { entryMarkdown };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { exists } = require('../fsx');
5
+ const { writeManagedFile, removeIfExists } = require('./base');
6
+ const { entryMarkdown } = require('./_entryContent');
7
+
8
+ const NAME = 'antigravity';
9
+ const DISPLAY = 'Antigravity';
10
+
11
+ function entryFile(projectDir) {
12
+ return path.join(projectDir, '.agent', 'workflows', 'draftgo.md');
13
+ }
14
+
15
+ function install(projectDir) {
16
+ writeManagedFile(entryFile(projectDir), entryMarkdown({ command: '/draftgo' }));
17
+ return { path: '.agent/workflows/draftgo.md' };
18
+ }
19
+
20
+ function uninstall(projectDir) {
21
+ return removeIfExists(entryFile(projectDir));
22
+ }
23
+
24
+ function status(projectDir) {
25
+ return { installed: exists(entryFile(projectDir)) };
26
+ }
27
+
28
+ module.exports = { name: NAME, displayName: DISPLAY, install, uninstall, status };
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ // Base helpers for all AI-tool installers.
4
+ // Each installer exports: { name, displayName, install, uninstall, status }
5
+
6
+ const path = require('path');
7
+ const { exists, writeText, removePath } = require('../fsx');
8
+
9
+ // Managed-region markers for appending to shared markdown files
10
+ // (used by codex/gemini/copilot targets).
11
+ const BEGIN = '<!-- DRAFTGO:START -->';
12
+ const END = '<!-- DRAFTGO:END -->';
13
+
14
+ function writeManagedFile(filePath, body) {
15
+ writeText(filePath, body);
16
+ }
17
+
18
+ function writeManagedBlock(filePath, body) {
19
+ const block = `${BEGIN}\n${body}\n${END}\n`;
20
+ let current = '';
21
+ try { current = require('fs').readFileSync(filePath, 'utf8'); } catch { current = ''; }
22
+ if (!current) {
23
+ writeText(filePath, block);
24
+ return;
25
+ }
26
+ const re = new RegExp(`${BEGIN}[\\s\\S]*?${END}\\n?`);
27
+ const next = re.test(current)
28
+ ? current.replace(re, block)
29
+ : (current.endsWith('\n') ? current : current + '\n') + '\n' + block;
30
+ writeText(filePath, next);
31
+ }
32
+
33
+ function removeManagedBlock(filePath) {
34
+ if (!exists(filePath)) return false;
35
+ const current = require('fs').readFileSync(filePath, 'utf8');
36
+ const re = new RegExp(`\\n*${BEGIN}[\\s\\S]*?${END}\\n?`);
37
+ if (!re.test(current)) return false;
38
+ const next = current.replace(re, '').replace(/\n{3,}/g, '\n\n');
39
+ writeText(filePath, next.trimEnd() + '\n');
40
+ return true;
41
+ }
42
+
43
+ function removeIfExists(p) {
44
+ return removePath(p);
45
+ }
46
+
47
+ module.exports = {
48
+ BEGIN,
49
+ END,
50
+ writeManagedFile,
51
+ writeManagedBlock,
52
+ removeManagedBlock,
53
+ removeIfExists,
54
+ };
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { exists } = require('../fsx');
5
+ const { writeManagedFile, removeIfExists } = require('./base');
6
+ const { entryMarkdown } = require('./_entryContent');
7
+
8
+ const NAME = 'claudecode';
9
+ const DISPLAY = 'Claude Code';
10
+
11
+ function entryFile(projectDir) {
12
+ return path.join(projectDir, '.claude', 'skills', 'draftgo', 'SKILL.md');
13
+ }
14
+
15
+ function install(projectDir) {
16
+ writeManagedFile(entryFile(projectDir), entryMarkdown({ command: '/draftgo' }));
17
+ return { path: '.claude/skills/draftgo/SKILL.md' };
18
+ }
19
+
20
+ function uninstall(projectDir) {
21
+ const root = path.join(projectDir, '.claude', 'skills', 'draftgo');
22
+ return removeIfExists(root);
23
+ }
24
+
25
+ function status(projectDir) {
26
+ return { installed: exists(entryFile(projectDir)) };
27
+ }
28
+
29
+ module.exports = { name: NAME, displayName: DISPLAY, install, uninstall, status };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { exists } = require('../fsx');
5
+ const { writeManagedBlock, removeManagedBlock } = require('./base');
6
+ const { entryMarkdown } = require('./_entryContent');
7
+
8
+ const NAME = 'codex';
9
+ const DISPLAY = 'Codex CLI';
10
+
11
+ function entryFile(projectDir) {
12
+ return path.join(projectDir, 'AGENTS.md');
13
+ }
14
+
15
+ function install(projectDir) {
16
+ writeManagedBlock(entryFile(projectDir), entryMarkdown({ command: '/draftgo' }));
17
+ return { path: 'AGENTS.md (managed block)' };
18
+ }
19
+
20
+ function uninstall(projectDir) {
21
+ return removeManagedBlock(entryFile(projectDir));
22
+ }
23
+
24
+ function status(projectDir) {
25
+ const f = entryFile(projectDir);
26
+ if (!exists(f)) return { installed: false };
27
+ const content = require('fs').readFileSync(f, 'utf8');
28
+ return { installed: /DRAFTGO:START[\s\S]*DRAFTGO:END/.test(content) };
29
+ }
30
+
31
+ module.exports = { name: NAME, displayName: DISPLAY, install, uninstall, status };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { exists } = require('../fsx');
5
+ const { writeManagedFile, removeIfExists } = require('./base');
6
+ const { entryMarkdown } = require('./_entryContent');
7
+
8
+ const NAME = 'copilot';
9
+ const DISPLAY = 'GitHub Copilot';
10
+
11
+ function entryFile(projectDir) {
12
+ return path.join(projectDir, '.github', 'prompts', 'draftgo.prompt.md');
13
+ }
14
+
15
+ function install(projectDir) {
16
+ writeManagedFile(entryFile(projectDir), entryMarkdown({ command: '/draftgo' }));
17
+ return { path: '.github/prompts/draftgo.prompt.md' };
18
+ }
19
+
20
+ function uninstall(projectDir) {
21
+ return removeIfExists(entryFile(projectDir));
22
+ }
23
+
24
+ function status(projectDir) {
25
+ return { installed: exists(entryFile(projectDir)) };
26
+ }
27
+
28
+ module.exports = { name: NAME, displayName: DISPLAY, install, uninstall, status };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { exists } = require('../fsx');
5
+ const { writeManagedFile, removeIfExists } = require('./base');
6
+ const { entryMarkdown } = require('./_entryContent');
7
+
8
+ const NAME = 'cursor';
9
+ const DISPLAY = 'Cursor';
10
+
11
+ function entryFile(projectDir) {
12
+ return path.join(projectDir, '.cursor', 'commands', 'draftgo.md');
13
+ }
14
+
15
+ function install(projectDir) {
16
+ writeManagedFile(entryFile(projectDir), entryMarkdown({ command: '/draftgo' }));
17
+ return { path: '.cursor/commands/draftgo.md' };
18
+ }
19
+
20
+ function uninstall(projectDir) {
21
+ return removeIfExists(entryFile(projectDir));
22
+ }
23
+
24
+ function status(projectDir) {
25
+ return { installed: exists(entryFile(projectDir)) };
26
+ }
27
+
28
+ module.exports = { name: NAME, displayName: DISPLAY, install, uninstall, status };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { exists } = require('../fsx');
5
+ const { writeManagedBlock, removeManagedBlock } = require('./base');
6
+ const { entryMarkdown } = require('./_entryContent');
7
+
8
+ const NAME = 'gemini';
9
+ const DISPLAY = 'Gemini CLI';
10
+
11
+ function entryFile(projectDir) {
12
+ return path.join(projectDir, 'GEMINI.md');
13
+ }
14
+
15
+ function install(projectDir) {
16
+ writeManagedBlock(entryFile(projectDir), entryMarkdown({ command: '/draftgo' }));
17
+ return { path: 'GEMINI.md (managed block)' };
18
+ }
19
+
20
+ function uninstall(projectDir) {
21
+ return removeManagedBlock(entryFile(projectDir));
22
+ }
23
+
24
+ function status(projectDir) {
25
+ const f = entryFile(projectDir);
26
+ if (!exists(f)) return { installed: false };
27
+ const content = require('fs').readFileSync(f, 'utf8');
28
+ return { installed: /DRAFTGO:START[\s\S]*DRAFTGO:END/.test(content) };
29
+ }
30
+
31
+ module.exports = { name: NAME, displayName: DISPLAY, install, uninstall, status };
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ const claudecode = require('./claudecode');
4
+ const cursor = require('./cursor');
5
+ const windsurf = require('./windsurf');
6
+ const antigravity = require('./antigravity');
7
+ const copilot = require('./copilot');
8
+ const kiro = require('./kiro');
9
+ const codex = require('./codex');
10
+ const gemini = require('./gemini');
11
+
12
+ const all = [claudecode, cursor, windsurf, antigravity, copilot, kiro, codex, gemini];
13
+ const byName = Object.fromEntries(all.map((i) => [i.name, i]));
14
+
15
+ function resolveTargets(input) {
16
+ // input: string | string[] | undefined
17
+ // undefined -> { resolved: [], unknown: [] } (caller triggers auto-detect)
18
+ // 'all' -> all installers
19
+ // name(s) -> matching installers + any unknown names
20
+ if (!input) return { resolved: [], unknown: [] };
21
+ if (input === 'all') return { resolved: all.slice(), unknown: [] };
22
+ const list = Array.isArray(input) ? input : [input];
23
+ if (list.includes('all')) return { resolved: all.slice(), unknown: [] };
24
+ const resolved = [];
25
+ const unknown = [];
26
+ for (const name of list) {
27
+ if (byName[name]) resolved.push(byName[name]);
28
+ else unknown.push(name);
29
+ }
30
+ return { resolved, unknown };
31
+ }
32
+
33
+ module.exports = { all, byName, resolveTargets };
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { exists } = require('../fsx');
5
+ const { writeManagedFile, removeIfExists } = require('./base');
6
+ const { entryMarkdown } = require('./_entryContent');
7
+
8
+ const NAME = 'kiro';
9
+ const DISPLAY = 'Kiro';
10
+
11
+ function entryFile(projectDir) {
12
+ return path.join(projectDir, '.kiro', 'steering', 'draftgo.md');
13
+ }
14
+
15
+ function install(projectDir) {
16
+ const body = `---
17
+ inclusion: manual
18
+ ---
19
+
20
+ ` + entryMarkdown({ command: '/draftgo' });
21
+ writeManagedFile(entryFile(projectDir), body);
22
+ return { path: '.kiro/steering/draftgo.md' };
23
+ }
24
+
25
+ function uninstall(projectDir) {
26
+ return removeIfExists(entryFile(projectDir));
27
+ }
28
+
29
+ function status(projectDir) {
30
+ return { installed: exists(entryFile(projectDir)) };
31
+ }
32
+
33
+ module.exports = { name: NAME, displayName: DISPLAY, install, uninstall, status };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const { exists } = require('../fsx');
5
+ const { writeManagedFile, removeIfExists } = require('./base');
6
+ const { entryMarkdown } = require('./_entryContent');
7
+
8
+ const NAME = 'windsurf';
9
+ const DISPLAY = 'Windsurf';
10
+
11
+ function entryFile(projectDir) {
12
+ return path.join(projectDir, '.windsurf', 'workflows', 'draftgo.md');
13
+ }
14
+
15
+ function install(projectDir) {
16
+ writeManagedFile(entryFile(projectDir), entryMarkdown({ command: '/draftgo' }));
17
+ return { path: '.windsurf/workflows/draftgo.md' };
18
+ }
19
+
20
+ function uninstall(projectDir) {
21
+ return removeIfExists(entryFile(projectDir));
22
+ }
23
+
24
+ function status(projectDir) {
25
+ return { installed: exists(entryFile(projectDir)) };
26
+ }
27
+
28
+ module.exports = { name: NAME, displayName: DISPLAY, install, uninstall, status };
package/src/logger.js ADDED
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ // Minimal ANSI color helpers — no external deps.
4
+ const isTTY = process.stdout && process.stdout.isTTY;
5
+ const supportsColor = isTTY && !process.env.NO_COLOR;
6
+
7
+ const wrap = (code) => (s) => supportsColor ? `\x1b[${code}m${s}\x1b[0m` : String(s);
8
+
9
+ const c = {
10
+ bold: wrap(1),
11
+ dim: wrap(2),
12
+ red: wrap(31),
13
+ green: wrap(32),
14
+ yellow: wrap(33),
15
+ blue: wrap(34),
16
+ magenta: wrap(35),
17
+ cyan: wrap(36),
18
+ gray: wrap(90),
19
+ };
20
+
21
+ function info(msg) { console.log(`${c.cyan('ℹ')} ${msg}`); }
22
+ function ok(msg) { console.log(`${c.green('✓')} ${msg}`); }
23
+ function warn(msg) { console.warn(`${c.yellow('!')} ${msg}`); }
24
+ function err(msg) { console.error(`${c.red('✗')} ${msg}`); }
25
+ function step(msg) { console.log(`${c.blue('→')} ${msg}`); }
26
+ function title(msg) { console.log(`\n${c.bold(c.magenta(msg))}`); }
27
+ function plain(msg) { console.log(msg); }
28
+ function dim(msg) { console.log(c.dim(msg)); }
29
+
30
+ module.exports = { c, info, ok, warn, err, step, title, plain, dim };
package/src/paths.js ADDED
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+
5
+ // Resource dir inside the package (bundled with npm publish)
6
+ const PACKAGE_ROOT = path.resolve(__dirname, '..');
7
+ const RESOURCES_DIR = path.join(PACKAGE_ROOT, 'resources');
8
+
9
+ // In-project paths
10
+ const DG_DIR_NAME = '.draftgo'; // <project>/.draftgo
11
+ const SKILL_SUBDIR = 'skill'; // <project>/.draftgo/skill
12
+ const VERSION_FILE = '.version';
13
+
14
+ function dgDir(projectDir) {
15
+ return path.join(projectDir, DG_DIR_NAME);
16
+ }
17
+
18
+ function skillDir(projectDir) {
19
+ return path.join(dgDir(projectDir), SKILL_SUBDIR);
20
+ }
21
+
22
+ function versionFile(projectDir) {
23
+ return path.join(skillDir(projectDir), VERSION_FILE);
24
+ }
25
+
26
+ module.exports = {
27
+ PACKAGE_ROOT,
28
+ RESOURCES_DIR,
29
+ DG_DIR_NAME,
30
+ SKILL_SUBDIR,
31
+ VERSION_FILE,
32
+ dgDir,
33
+ skillDir,
34
+ versionFile,
35
+ };
package/src/python.js ADDED
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ const { spawnSync } = require('child_process');
4
+
5
+ function tryBin(bin) {
6
+ try {
7
+ const r = spawnSync(bin, ['--version'], { encoding: 'utf8' });
8
+ if (r.status === 0 || (r.stdout || '').toLowerCase().includes('python')) {
9
+ return { bin, version: (r.stdout || r.stderr || '').trim() };
10
+ }
11
+ } catch { /* ignore */ }
12
+ return null;
13
+ }
14
+
15
+ // Return a usable python command ('python' or 'python3') if found, else null.
16
+ function findPython() {
17
+ const candidates = process.platform === 'win32'
18
+ ? ['python', 'python3', 'py']
19
+ : ['python3', 'python'];
20
+ for (const b of candidates) {
21
+ const r = tryBin(b);
22
+ if (r) return r;
23
+ }
24
+ return null;
25
+ }
26
+
27
+ module.exports = { findPython };
package/src/skill.js ADDED
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ // Install/update/uninstall the shared skill body at <project>/.draftgo/skill/.
4
+ // This is the single source that all AI-tool entry files reference.
5
+
6
+ const path = require('path');
7
+ const { RESOURCES_DIR } = require('./paths');
8
+ const {
9
+ exists, ensureDir, readText, writeText, copyDir, removePath, appendGitignoreLine,
10
+ } = require('./fsx');
11
+ const paths = require('./paths');
12
+ const log = require('./logger');
13
+
14
+ // Files/dirs under resources/skill/ that form the skill body.
15
+ const SKILL_SOURCE_DIR = path.join(RESOURCES_DIR, 'skill');
16
+
17
+ function getPackageVersion() {
18
+ try {
19
+ return require(path.join(paths.PACKAGE_ROOT, 'package.json')).version;
20
+ } catch {
21
+ return '0.0.0';
22
+ }
23
+ }
24
+
25
+ function readInstalledVersion(projectDir) {
26
+ const f = paths.versionFile(projectDir);
27
+ return exists(f) ? readText(f).trim() : null;
28
+ }
29
+
30
+ function writeInstalledVersion(projectDir) {
31
+ writeText(paths.versionFile(projectDir), getPackageVersion() + '\n');
32
+ }
33
+
34
+ function installSkillBody(projectDir, { force = false } = {}) {
35
+ if (!exists(SKILL_SOURCE_DIR)) {
36
+ throw new Error(
37
+ `Skill source missing: ${SKILL_SOURCE_DIR}. The CLI package may be broken.`
38
+ );
39
+ }
40
+ const dest = paths.skillDir(projectDir);
41
+ const alreadyInstalled = exists(dest);
42
+ if (alreadyInstalled && !force) {
43
+ log.dim(` skill body already at .draftgo/skill (use --force to overwrite)`);
44
+ } else {
45
+ // Clean install of the skill body (preserves sibling .draftgo/* runtime data).
46
+ if (alreadyInstalled) removePath(dest);
47
+ ensureDir(dest);
48
+ copyDir(SKILL_SOURCE_DIR, dest);
49
+ writeInstalledVersion(projectDir);
50
+ log.ok(`.draftgo/skill/ installed (v${getPackageVersion()})`);
51
+ }
52
+
53
+ // Ignore runtime/local files, not the skill body.
54
+ appendGitignoreLine(projectDir, '.draftgo/config.json');
55
+ appendGitignoreLine(projectDir, '.draftgo/token');
56
+
57
+ return dest;
58
+ }
59
+
60
+ function updateSkillBody(projectDir) {
61
+ return installSkillBody(projectDir, { force: true });
62
+ }
63
+
64
+ function uninstallSkillBody(projectDir, { purge = false } = {}) {
65
+ const skill = paths.skillDir(projectDir);
66
+ const removed = removePath(skill);
67
+ if (removed) log.ok(`removed .draftgo/skill/`);
68
+ if (purge) {
69
+ const dg = paths.dgDir(projectDir);
70
+ if (removePath(dg)) log.ok(`purged .draftgo/`);
71
+ }
72
+ return removed;
73
+ }
74
+
75
+ module.exports = {
76
+ SKILL_SOURCE_DIR,
77
+ getPackageVersion,
78
+ readInstalledVersion,
79
+ writeInstalledVersion,
80
+ installSkillBody,
81
+ updateSkillBody,
82
+ uninstallSkillBody,
83
+ };