cli-calctool 0.3.4 → 1.0.1
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 +9 -161
- package/cli.mjs +238 -0
- package/package.json +20 -7
- package/{skills/calctool → skill}/skill.json +1 -1
- package/LICENSE +0 -201
- package/install.mjs +0 -442
- package/skills/blueprint/SKILL.md +0 -47
- package/skills/blueprint/install-meta.json +0 -7
- package/skills/blueprint/skill.json +0 -10
- package/skills/calctool/install-meta.json +0 -7
- package/skills/calctool/platform-template/README.md +0 -74
- package/skills/calctool/platform-template/index.html +0 -12
- package/skills/calctool/platform-template/package.json +0 -26
- package/skills/calctool/platform-template/src/App.tsx +0 -233
- package/skills/calctool/platform-template/src/authz.ts +0 -103
- package/skills/calctool/platform-template/src/engine/evaluate.ts +0 -114
- package/skills/calctool/platform-template/src/engine-definition.json +0 -195
- package/skills/calctool/platform-template/src/main.tsx +0 -13
- package/skills/calctool/platform-template/src/pipeline.ts +0 -80
- package/skills/calctool/platform-template/src/store.ts +0 -53
- package/skills/calctool/platform-template/tsconfig.json +0 -15
- package/skills/calctool/platform-template/vite.config.ts +0 -7
- package/skills/calctool/references/declarative-pages.md +0 -69
- package/skills/calctool/references/engine-meta-model.md +0 -123
- package/skills/calctool/references/finance-example.md +0 -132
- package/skills/calctool/references/formula-dsl.md +0 -92
- package/skills/calctool/references/import-ocr.md +0 -72
- package/skills/calctool/templates/ecommerce-ops/README.md +0 -27
- package/skills/calctool/templates/ecommerce-ops/domain-reference.yaml +0 -172
- package/skills/swarm/SKILL.md +0 -120
- package/skills/swarm/install-meta.json +0 -7
- package/skills/swarm/references/ops-heartbeat.md +0 -45
- package/skills/swarm/references/org-chart.md +0 -50
- package/skills/swarm/references/security-guard.md +0 -56
- package/skills/swarm/references/task-lifecycle.md +0 -53
- package/skills/swarm/references/traffic-light.md +0 -52
- package/skills/swarm/skill.json +0 -10
- package/skills/swarm/swarm-runtime.mjs +0 -600
- package/sources.json +0 -25
- package/{skills/calctool → skill}/SKILL.md +5 -5
package/install.mjs
DELETED
|
@@ -1,442 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* dsh-skillpack — 多 IDE 通用 skill 插件包安装器
|
|
4
|
-
*
|
|
5
|
-
* 从本包 `skills/` 目录同步一组 skill 到全世界各 IDE/编码 agent 的 skills 根目录。
|
|
6
|
-
* 每个子目录(含 SKILL.md + skill.json)即一个 skill;新增/删除子目录后重跑即可扩展。
|
|
7
|
-
*
|
|
8
|
-
* 设计原则:
|
|
9
|
-
* - 内置「全世界已知 IDE 的 skills 安装位置表」(IDES,官方文档核实);
|
|
10
|
-
* - 默认「自动匹配」:检测本机真实安装的 IDE(which 命令 / 应用路径 / 配置文件),
|
|
11
|
-
* 只装给已安装的 IDE,让每个 IDE 自己识别需要的 skill;--all 才全量安装;
|
|
12
|
-
* - 每个 IDE 装到它自己的专属用户级根 → IDE 内部唯一、IDE 之间互不重复;
|
|
13
|
-
* - 跨工具互操作标准 ~/.agents/skills(agentskills.io)默认跳过——它会被多个工具
|
|
14
|
-
* 同时扫描(Codex 新约定/Gemini/Zed/OpenCode/Cursor/Antigravity),装进去会被重复发现;
|
|
15
|
-
* 需要时用 --agents 显式开启(会打印警告);
|
|
16
|
-
* - --ide / --skip 精确控制;新增 IDE 只需在 IDES 表加一行。
|
|
17
|
-
*
|
|
18
|
-
* 更新感知:
|
|
19
|
-
* - pull 时把 cli.tax 返回的 version 写入 skills/<slug>/install-meta.json;
|
|
20
|
-
* - install / update 时对比「已安装版本」与「远端最新版本」,有新版本会提示;
|
|
21
|
-
* - check 子命令:遍历本机已装 IDE 的 skill 目录,对比远端最新版本并报告更新。
|
|
22
|
-
*
|
|
23
|
-
* 用法:
|
|
24
|
-
* node install.mjs pull # 从 sources.json(cli.tax)拉取全部 skill 到 skills/
|
|
25
|
-
* node install.mjs install # 自动匹配:只装本机已安装的 IDE(推荐)
|
|
26
|
-
* # skills/ 为空时自动先 pull;--pull 强制重新拉取
|
|
27
|
-
* node install.mjs install --all # 装到所有已知 IDE(不管是否安装)
|
|
28
|
-
* node install.mjs install --ide codex # 仅安装到指定 IDE(可重复: --ide codex --ide dsh)
|
|
29
|
-
* node install.mjs install --skip cursor # 自动匹配但跳过指定 IDE(可重复)
|
|
30
|
-
* node install.mjs install --project # 额外安装到当前项目的项目级根(每个选中 IDE 一个)
|
|
31
|
-
* node install.mjs install --agents # 额外安装到共享 ~/.agents/skills(警告)
|
|
32
|
-
* node install.mjs install --target /abs/path # 仅自定义目录
|
|
33
|
-
* node install.mjs update # 同 install(幂等覆盖)
|
|
34
|
-
* node install.mjs check # 检查已安装 skill 是否有新版本(对比 cli.tax)
|
|
35
|
-
* node install.mjs uninstall # 从相同目标移除本包安装的 skills
|
|
36
|
-
* node install.mjs list # 列出本包包含的 skills
|
|
37
|
-
* node install.mjs ides # 列出已知 IDE 及本机检测结果
|
|
38
|
-
*
|
|
39
|
-
* 作为 npm 包发布后: npx dsh-skillpack@latest install
|
|
40
|
-
*/
|
|
41
|
-
import { cp, mkdir, readdir, rm, writeFile } from 'node:fs/promises'
|
|
42
|
-
import { existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
43
|
-
import { homedir } from 'node:os'
|
|
44
|
-
import { join, resolve } from 'node:path'
|
|
45
|
-
import { fileURLToPath } from 'node:url'
|
|
46
|
-
import { spawnSync } from 'node:child_process'
|
|
47
|
-
|
|
48
|
-
const __dirname = fileURLToPath(new URL('.', import.meta.url))
|
|
49
|
-
const SKILLS_SRC = join(__dirname, 'skills')
|
|
50
|
-
|
|
51
|
-
const HOME = homedir()
|
|
52
|
-
const CWD = process.cwd()
|
|
53
|
-
|
|
54
|
-
/** 每个 skill 安装/拉取时的版本元数据文件名(IDE 目录与 skills/ 内都会写) */
|
|
55
|
-
const INSTALL_META = 'install-meta.json'
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* 全世界已知 IDE/编码 agent 的 skills 安装位置表(官方文档核实,2026-08)。
|
|
59
|
-
* key: 命令行标识
|
|
60
|
-
* label: 显示名
|
|
61
|
-
* detect: 安装探测规则数组(任一命中即视为已安装):
|
|
62
|
-
* { cmd: 'codex' } —— which 命令存在
|
|
63
|
-
* { app: '/Applications/X.app' } —— 应用路径存在
|
|
64
|
-
* { file: '~/.codex/config.toml' } —— 配置文件存在(确认真实安装,非本工具创建)
|
|
65
|
-
* user: 用户级 skills 根(默认安装目标,每个 IDE 一份)
|
|
66
|
-
* project: 项目级 skills 根(--project 时使用)
|
|
67
|
-
* doc: 官方文档 URL
|
|
68
|
-
* 新增 IDE 只需在此加一行,其余逻辑自动生效。
|
|
69
|
-
*/
|
|
70
|
-
const IDES = [
|
|
71
|
-
// OpenAI / Anthropic / Google 系
|
|
72
|
-
{ key: 'codex', label: 'Codex CLI', detect: [{ cmd: 'codex' }, { file: join(HOME, '.codex', 'config.toml') }], user: join(HOME, '.codex', 'skills'), project: join(CWD, '.codex', 'skills'), doc: 'https://developers.openai.com/codex/skills', note: '新官方约定 ~/.agents/skills;此处用旧约定 ~/.codex/skills(skill-installer 仍默认写入),避免共享根重复' },
|
|
73
|
-
{ key: 'dsh', label: 'DSH', detect: [{ file: join(HOME, '.dsh', 'settings.yaml') }, { file: join(HOME, '.dsh', '.credentials.yaml') }], user: join(HOME, '.dsh', 'skills'), project: join(CWD, '.dsh', 'skills'), doc: 'https://github.com/deepseek-ai/Harness', note: 'skill-filesystem 扫描 ~/.dsh/skills + ~/.agents/skills' },
|
|
74
|
-
{ key: 'claude', label: 'Claude Code', detect: [{ cmd: 'claude' }, { file: join(HOME, '.claude', 'settings.json') }, { dir: join(HOME, '.claude') }], user: join(HOME, '.claude', 'skills'), project: join(CWD, '.claude', 'skills'), doc: 'https://code.claude.com/docs/en/skills', note: '含父目录向上扫描至 repo root;monorepo 子目录生效' },
|
|
75
|
-
{ key: 'gemini', label: 'Gemini CLI', detect: [{ cmd: 'gemini' }, { file: join(HOME, '.gemini', 'settings.json') }], user: join(HOME, '.gemini', 'skills'), project: join(CWD, '.gemini', 'skills'), doc: 'https://geminicli.com/docs/cli/skills/', note: '别名 .agents/skills 优先级更高(此处用专属根避免共享冲突)' },
|
|
76
|
-
{ key: 'antigravity', label: 'Google Antigravity', detect: [{ cmd: 'antigravity' }, { app: '/Applications/Antigravity.app' }], user: join(HOME, '.gemini', 'antigravity', 'skills'), project: join(CWD, '.agents', 'skills'), doc: 'https://antigravity.google/docs/ide/skills', note: 'workspace 级固定用 .agents/skills(需 --agents 才装项目级)' },
|
|
77
|
-
|
|
78
|
-
// 编辑器 / VS Code 生态
|
|
79
|
-
{ key: 'cursor', label: 'Cursor', detect: [{ cmd: 'cursor' }, { app: '/Applications/Cursor.app' }, { file: join(HOME, '.cursor', 'cursor.db') }], user: join(HOME, '.cursor', 'skills'), project: join(CWD, '.cursor', 'skills'), doc: 'https://cursor.com/help/customization/skills.md', note: '兼容加载 .claude/.codex/.agents/skills' },
|
|
80
|
-
{ key: 'cline', label: 'Cline', detect: [{ cmd: 'cline' }, { dir: join(HOME, '.cline') }], user: join(HOME, '.cline', 'skills'), project: join(CWD, '.cline', 'skills'), doc: 'https://cline.bot/blog/cline-3-48-0-skills-and-websearch-make-cline-smarter', note: '全局优先于项目' },
|
|
81
|
-
{ key: 'roo', label: 'Roo Code', detect: [{ cmd: 'roo' }, { dir: join(HOME, '.roo') }], user: join(HOME, '.roo', 'skills'), project: join(CWD, '.roo', 'skills'), doc: 'https://docs.roocode.com/features/skills', note: '项目覆盖全局,模式级覆盖通用' },
|
|
82
|
-
{ key: 'kilo', label: 'Kilo Code', detect: [{ cmd: 'kilo' }, { dir: join(HOME, '.kilo') }], user: join(HOME, '.kilo', 'skills'), project: join(CWD, '.kilo', 'skills'), doc: 'https://kilo.ai/docs/customize/skills', note: 'kilo.jsonc 可配 skills.paths/urls;另默认加载 .agents/skills' },
|
|
83
|
-
{ key: 'windsurf', label: 'Windsurf', detect: [{ cmd: 'windsurf' }, { app: '/Applications/Windsurf.app' }], user: join(HOME, '.codeium', 'windsurf', 'skills'), project: join(CWD, '.windsurf', 'skills'), doc: 'https://docs.windsurf.com/windsurf/cascade/skills', note: '全局 ~/.codeium/windsurf/skills,workspace .windsurf/skills' },
|
|
84
|
-
{ key: 'copilot', label: 'VS Code / Copilot', detect: [{ cmd: 'copilot' }, { app: '/Applications/Visual Studio Code.app' }, { file: join(HOME, '.copilot', 'accounts.json') }], user: join(HOME, '.copilot', 'skills'), project: join(CWD, '.github', 'skills'), doc: 'https://code.visualstudio.com/docs/agent-customization/agent-skills', note: 'workspace .github/skills + .claude/skills;user ~/.copilot/skills + ~/.claude/skills' },
|
|
85
|
-
{ key: 'trae', label: 'Trae', detect: [{ cmd: 'trae' }, { app: '/Applications/Trae.app' }, { dir: join(HOME, '.trae-cn') }], user: join(HOME, '.trae-cn', 'skills'), project: join(CWD, '.trae', 'skills'), doc: 'https://docs.trae.cn/work_skills', note: '国内版 ~/.trae-cn/skills;国际版用户级未确认;另有 .trae/rules' },
|
|
86
|
-
|
|
87
|
-
// CLI agent
|
|
88
|
-
{ key: 'qwen', label: 'Qwen Code', detect: [{ cmd: 'qwen' }, { file: join(HOME, '.qwen', 'settings.json') }], user: join(HOME, '.qwen', 'skills'), project: join(CWD, '.qwen', 'skills'), doc: 'https://qwenlm.github.io/qwen-code-docs/users/features/skills/', note: '模型自动调用;/learn 生成到项目 .qwen/skills' },
|
|
89
|
-
{ key: 'opencode', label: 'OpenCode', detect: [{ cmd: 'opencode' }, { dir: join(HOME, '.config', 'opencode') }], user: join(HOME, '.config', 'opencode', 'skills'), project: join(CWD, '.opencode', 'skills'), doc: 'https://github.com/anomalyco/opencode/blob/main/packages/web/src/content/docs/skills.mdx', note: '兼容 .claude/skills 与 .agents/skills' },
|
|
90
|
-
{ key: 'zed', label: 'Zed', detect: [{ cmd: 'zed' }, { app: '/Applications/Zed.app' }], user: join(HOME, '.agents', 'skills'), project: join(CWD, '.agents', 'skills'), doc: 'https://zed.dev/docs/ai/skills', note: '官方即用 .agents/skills 标准根,仅 --agents 时安装(避免默认触碰共享根)', agentsOnly: true },
|
|
91
|
-
// Amazon Q Developer:官方确认无 SKILL.md skills 机制(能力目录为 ~/.aws/amazonq/cli-agents/),故不入表
|
|
92
|
-
]
|
|
93
|
-
|
|
94
|
-
// 跨工具互操作标准根(默认跳过;--agents 显式开启)
|
|
95
|
-
const AGENTS = { label: '共享 agents 级 (.agents/skills 标准)', path: join(HOME, '.agents', 'skills') }
|
|
96
|
-
|
|
97
|
-
function print(line = '') { process.stdout.write(`${line}\n`) }
|
|
98
|
-
|
|
99
|
-
/** 探测 IDE 是否真实安装(which 命令 / 应用路径 / 配置文件,任一命中即已安装) */
|
|
100
|
-
function isIdeInstalled(ide) {
|
|
101
|
-
for (const probe of ide.detect) {
|
|
102
|
-
if (probe.cmd !== undefined) {
|
|
103
|
-
const r = spawnSync('which', [probe.cmd], { stdio: 'ignore' })
|
|
104
|
-
if (r.status === 0) return true
|
|
105
|
-
}
|
|
106
|
-
if (probe.app !== undefined && existsSync(probe.app)) return true
|
|
107
|
-
if (probe.file !== undefined && existsSync(probe.file)) return true
|
|
108
|
-
if (probe.dir !== undefined) {
|
|
109
|
-
// 目录存在且包含非 skills 的真实内容(避免本安装器创建的 skills 子目录误判)
|
|
110
|
-
try {
|
|
111
|
-
const entries = readdirSync(probe.dir)
|
|
112
|
-
if (entries.some((e) => e !== 'skills' && e !== '.git')) return true
|
|
113
|
-
} catch { /* 目录不存在或不可读,忽略 */ }
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return false
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async function listSkills() {
|
|
120
|
-
if (!existsSync(SKILLS_SRC)) return []
|
|
121
|
-
const entries = await readdir(SKILLS_SRC, { withFileTypes: true })
|
|
122
|
-
const skills = []
|
|
123
|
-
for (const e of entries) {
|
|
124
|
-
if (!e.isDirectory()) continue
|
|
125
|
-
const skillDir = join(SKILLS_SRC, e.name)
|
|
126
|
-
if (existsSync(join(skillDir, 'SKILL.md'))) skills.push(e.name)
|
|
127
|
-
}
|
|
128
|
-
return skills.sort()
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async function syncOne(name, targetRoot, { remove = false, silent = false } = {}) {
|
|
132
|
-
const dest = join(targetRoot, name)
|
|
133
|
-
if (remove) {
|
|
134
|
-
if (existsSync(dest)) { await rm(dest, { recursive: true, force: true }); print(` ✓ 已移除 ${name} ← ${targetRoot}`) }
|
|
135
|
-
return
|
|
136
|
-
}
|
|
137
|
-
await mkdir(dest, { recursive: true })
|
|
138
|
-
const sourceDir = join(SKILLS_SRC, name)
|
|
139
|
-
const destMeta = readMeta(dest) // 覆盖前读取旧版本
|
|
140
|
-
await cp(sourceDir, dest, { recursive: true, force: true })
|
|
141
|
-
const srcMeta = readMeta(sourceDir)
|
|
142
|
-
// 安装时注入版本横幅到 SKILL.md(IDE 每次读取即见版本与更新入口)
|
|
143
|
-
if (srcMeta?.version) {
|
|
144
|
-
const installedSkillMd = readFileSync(join(dest, 'SKILL.md'), 'utf8')
|
|
145
|
-
await writeFile(join(dest, 'SKILL.md'), injectVersionBanner(installedSkillMd, srcMeta.version, name))
|
|
146
|
-
}
|
|
147
|
-
if (srcMeta) {
|
|
148
|
-
await writeMeta(dest, { ...srcMeta, installedAt: new Date().toISOString() })
|
|
149
|
-
}
|
|
150
|
-
const upgrade = destMeta && srcMeta && destMeta.version && srcMeta.version && destMeta.version !== srcMeta.version
|
|
151
|
-
if (silent) return
|
|
152
|
-
if (upgrade) {
|
|
153
|
-
print(` ⤴ 已更新 ${name} ${destMeta.version} → ${srcMeta.version} → ${dest}`)
|
|
154
|
-
} else {
|
|
155
|
-
print(` ✓ 已安装 ${name} → ${dest}${srcMeta?.version ? `(${srcMeta.version})` : ''}`)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async function syncAll(targetRoot, { remove = false, reportUpdated, silent = false } = {}) {
|
|
160
|
-
const skills = await listSkills()
|
|
161
|
-
if (!remove) await mkdir(targetRoot, { recursive: true })
|
|
162
|
-
for (const s of skills) {
|
|
163
|
-
const before = reportUpdated ? readMeta(join(targetRoot, s)) : null
|
|
164
|
-
await syncOne(s, targetRoot, { remove, silent })
|
|
165
|
-
if (reportUpdated && before && before.version) {
|
|
166
|
-
const after = readMeta(join(targetRoot, s))
|
|
167
|
-
if (after && after.version && after.version !== before.version) reportUpdated(1)
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/** 解析目标列表:--target / --ide / --skip / 默认(全部已知 IDE) */
|
|
173
|
-
function parseTargets(args) {
|
|
174
|
-
const flags = args.filter((a) => a.startsWith('--'))
|
|
175
|
-
|
|
176
|
-
const targetFlag = args.indexOf('--target')
|
|
177
|
-
if (targetFlag !== -1) {
|
|
178
|
-
const p = args[targetFlag + 1]
|
|
179
|
-
if (!p) { print('--target 需要一个绝对路径'); process.exit(1) }
|
|
180
|
-
return [{ label: '自定义', path: resolve(p) }]
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const selected = []
|
|
184
|
-
const withProject = flags.includes('--project')
|
|
185
|
-
|
|
186
|
-
// 显式 --ide(可重复)
|
|
187
|
-
const ideKeys = []
|
|
188
|
-
for (let i = 0; i < args.length; i++) {
|
|
189
|
-
if (args[i] === '--ide') {
|
|
190
|
-
const k = args[i + 1]
|
|
191
|
-
if (!k) { print('--ide 需要一个 IDE key(如 codex)'); process.exit(1) }
|
|
192
|
-
ideKeys.push(k)
|
|
193
|
-
i++
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if (ideKeys.length) {
|
|
197
|
-
for (const k of ideKeys) {
|
|
198
|
-
const ide = IDES.find((x) => x.key === k)
|
|
199
|
-
if (!ide) { print(`未知 IDE: ${k}(可用: ${IDES.map((x) => x.key).join(', ')})`); process.exit(1) }
|
|
200
|
-
selected.push({ label: `${ide.label} 用户级`, path: ide.user })
|
|
201
|
-
if (withProject) selected.push({ label: `${ide.label} 项目级`, path: ide.project })
|
|
202
|
-
}
|
|
203
|
-
return selected
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// --skip(排除某些 IDE)
|
|
207
|
-
const skipped = []
|
|
208
|
-
for (let i = 0; i < args.length; i++) {
|
|
209
|
-
if (args[i] === '--skip') {
|
|
210
|
-
const k = args[i + 1]
|
|
211
|
-
if (!k) { print('--skip 需要一个 IDE key(如 cursor)'); process.exit(1) }
|
|
212
|
-
skipped.push(k)
|
|
213
|
-
i++
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// 默认:自动匹配——只装本机真实安装的 IDE;--all 则装全部已知 IDE。
|
|
218
|
-
const wantAgents = flags.includes('--agents')
|
|
219
|
-
const wantAll = flags.includes('--all')
|
|
220
|
-
for (const ide of IDES) {
|
|
221
|
-
if (skipped.includes(ide.key)) continue
|
|
222
|
-
// agentsOnly(如 Zed):仅 --agents 时安装,默认不触碰共享根
|
|
223
|
-
if (ide.agentsOnly && !wantAgents) continue
|
|
224
|
-
// 默认只装检测到已安装的 IDE;--all 忽略检测
|
|
225
|
-
if (!wantAll && !isIdeInstalled(ide)) continue
|
|
226
|
-
selected.push({ label: `${ide.label} 用户级`, path: ide.user })
|
|
227
|
-
if (withProject) selected.push({ label: `${ide.label} 项目级`, path: ide.project })
|
|
228
|
-
}
|
|
229
|
-
if (!selected.length) {
|
|
230
|
-
print('未检测到任何已安装的 IDE;可用 --all 安装全部已知 IDE,或 --ide <key> 指定。')
|
|
231
|
-
process.exit(1)
|
|
232
|
-
}
|
|
233
|
-
return selected
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const [cmd, ...rest] = process.argv.slice(2)
|
|
237
|
-
|
|
238
|
-
/** 读取 skill 目录的 install-meta.json(没有则返回 null) */
|
|
239
|
-
function readMeta(skillDir) {
|
|
240
|
-
const p = join(skillDir, INSTALL_META)
|
|
241
|
-
if (!existsSync(p)) return null
|
|
242
|
-
try { return JSON.parse(readFileSync(p, 'utf8')) } catch { return null }
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/** 写入 skill 目录的 install-meta.json(来源/版本/时间/地址) */
|
|
246
|
-
async function writeMeta(skillDir, meta) {
|
|
247
|
-
await writeFile(join(skillDir, INSTALL_META), `${JSON.stringify(meta, null, 2)}\n`)
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/** 在 SKILL.md frontmatter 之后注入版本横幅:IDE 每次读取即见版本与更新命令 */
|
|
251
|
-
function injectVersionBanner(skillMd, version, slug) {
|
|
252
|
-
if (!skillMd || !version) return skillMd
|
|
253
|
-
const banner = `<!-- cli-${slug}-installer: version ${version} · 检查更新见 install-meta.json / npx cli-${slug}@latest check -->\n`
|
|
254
|
-
if (skillMd.startsWith('---')) {
|
|
255
|
-
const end = skillMd.indexOf('\n---', 3)
|
|
256
|
-
if (end !== -1) {
|
|
257
|
-
return `${skillMd.slice(0, end + 5)}\n\n${banner}${skillMd.slice(end + 5).replace(/^\n+/, '')}`
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return `${banner}${skillMd}`
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/** 从 cli.tax 拉取一个 skill 到 skills/<slug>/(覆盖本地副本) */
|
|
264
|
-
async function pullSkill(source) {
|
|
265
|
-
const url = source.endpoint.replace('{code}', source.code)
|
|
266
|
-
const resp = await fetch(url)
|
|
267
|
-
if (!resp.ok) {
|
|
268
|
-
print(` ✗ 拉取失败 ${source.slug}(${url} → HTTP ${resp.status})`)
|
|
269
|
-
return false
|
|
270
|
-
}
|
|
271
|
-
const data = await resp.json()
|
|
272
|
-
const dir = join(SKILLS_SRC, data.slug || source.slug)
|
|
273
|
-
await mkdir(dir, { recursive: true })
|
|
274
|
-
await writeFile(join(dir, 'SKILL.md'), data.skillMd)
|
|
275
|
-
await writeFile(join(dir, 'skill.json'), data.skillJson)
|
|
276
|
-
const previous = readMeta(dir)
|
|
277
|
-
await writeMeta(dir, {
|
|
278
|
-
source: source.code,
|
|
279
|
-
slug: data.slug || source.slug,
|
|
280
|
-
version: data.version ?? '',
|
|
281
|
-
endpoint: url,
|
|
282
|
-
installedAt: new Date().toISOString(),
|
|
283
|
-
})
|
|
284
|
-
const changed = previous && previous.version && previous.version !== data.version
|
|
285
|
-
if (changed) {
|
|
286
|
-
print(` ⤴ 已更新 ${data.displayName} ${previous.version} → ${data.version ?? ''} → skills/${data.slug}/`)
|
|
287
|
-
} else {
|
|
288
|
-
print(` ✓ 已拉取 ${data.displayName} ${data.version ?? ''} → skills/${data.slug}/`)
|
|
289
|
-
}
|
|
290
|
-
return true
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/** 从 cli.tax 拉取单个 skill 的最新元数据(check 用,不写盘) */
|
|
294
|
-
async function fetchLatestMeta(source) {
|
|
295
|
-
const url = source.endpoint.replace('{code}', source.code)
|
|
296
|
-
try {
|
|
297
|
-
const resp = await fetch(url)
|
|
298
|
-
if (!resp.ok) return null
|
|
299
|
-
const data = await resp.json()
|
|
300
|
-
return {
|
|
301
|
-
source: source.code,
|
|
302
|
-
slug: data.slug || source.slug,
|
|
303
|
-
version: data.version ?? '',
|
|
304
|
-
displayName: data.displayName ?? data.slug ?? source.slug,
|
|
305
|
-
endpoint: url,
|
|
306
|
-
}
|
|
307
|
-
} catch {
|
|
308
|
-
return null
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/** check:遍历已安装目标的 skill 目录,对比远端最新版本,报告更新 */
|
|
313
|
-
async function checkUpdates(targets) {
|
|
314
|
-
const sources = loadSources()
|
|
315
|
-
if (!sources.length) {
|
|
316
|
-
print('⚠ sources.json 无源,无法检查更新。')
|
|
317
|
-
return 1
|
|
318
|
-
}
|
|
319
|
-
let found = 0
|
|
320
|
-
for (const t of targets) {
|
|
321
|
-
print(`[${t.label}] ${t.path}`)
|
|
322
|
-
if (!existsSync(t.path)) { print(' · 未安装'); continue }
|
|
323
|
-
const entries = (await readdir(t.path, { withFileTypes: true }).catch(() => []))
|
|
324
|
-
.filter((e) => e.isDirectory())
|
|
325
|
-
if (!entries.length) { print(' · 无已安装 skill'); continue }
|
|
326
|
-
for (const e of entries) {
|
|
327
|
-
const skillDir = join(t.path, e.name)
|
|
328
|
-
const local = readMeta(skillDir)
|
|
329
|
-
const source = sources.find((s) => (s.slug === e.name)
|
|
330
|
-
|| (local && local.source === s.code))
|
|
331
|
-
if (!source) continue
|
|
332
|
-
const remote = await fetchLatestMeta(source)
|
|
333
|
-
if (!remote) { print(` · ${e.name}:远端查询失败,跳过`); continue }
|
|
334
|
-
if (!local || !local.version) {
|
|
335
|
-
print(` · ${e.name}:本地无版本记录(旧安装),远端最新 ${remote.version} —— 运行 install 补齐`)
|
|
336
|
-
found++
|
|
337
|
-
} else if (local.version === remote.version) {
|
|
338
|
-
print(` ✓ ${e.name} 已是最新(${remote.version})`)
|
|
339
|
-
} else {
|
|
340
|
-
print(` ⤴ ${e.name} 有新版本 ${local.version} → ${remote.version} —— 运行 install 更新`)
|
|
341
|
-
found++
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
if (found) {
|
|
346
|
-
print(`\n发现 ${found} 个 skill 可更新。执行: node install.mjs install`)
|
|
347
|
-
} else {
|
|
348
|
-
print('\n全部已是最新。')
|
|
349
|
-
}
|
|
350
|
-
return 0
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/** 读取 sources.json 中的源列表 */
|
|
354
|
-
function loadSources() {
|
|
355
|
-
const p = join(__dirname, 'sources.json')
|
|
356
|
-
if (!existsSync(p)) return []
|
|
357
|
-
try {
|
|
358
|
-
const raw = readFileSync(p, 'utf8')
|
|
359
|
-
return JSON.parse(raw).cliTax ?? []
|
|
360
|
-
} catch {
|
|
361
|
-
print('⚠ sources.json 解析失败,忽略源。')
|
|
362
|
-
return []
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/** 拉取全部源;失败不影响本地已有 skills */
|
|
367
|
-
async function pullAllSources() {
|
|
368
|
-
const sources = loadSources()
|
|
369
|
-
if (!sources.length) return false
|
|
370
|
-
print(`从 cli.tax 拉取 ${sources.length} 个 skill 源:`)
|
|
371
|
-
let ok = 0
|
|
372
|
-
for (const s of sources) { if (await pullSkill(s)) ok++ }
|
|
373
|
-
print(`拉取完成(${ok}/${sources.length})。`)
|
|
374
|
-
return ok > 0
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (cmd === 'list') {
|
|
378
|
-
const skills = await listSkills()
|
|
379
|
-
print(`本包包含 ${skills.length} 个 skills:`)
|
|
380
|
-
for (const s of skills) print(` - ${s}`)
|
|
381
|
-
} else if (cmd === 'ides') {
|
|
382
|
-
print(`已知 IDE 及本机检测结果(共 ${IDES.length} 个):`)
|
|
383
|
-
for (const ide of IDES) {
|
|
384
|
-
const installed = isIdeInstalled(ide)
|
|
385
|
-
print(` ${installed ? '✓' : '·'} ${ide.key.padEnd(11)} ${ide.label.padEnd(20)} ${ide.user}`)
|
|
386
|
-
}
|
|
387
|
-
print(` · agents 共享 agents 级 ${AGENTS.path}(默认跳过,--agents 开启)`)
|
|
388
|
-
print('默认 install 只装 ✓ 标记的 IDE;--all 装全部。')
|
|
389
|
-
} else if (cmd === 'pull') {
|
|
390
|
-
const ok = await pullAllSources()
|
|
391
|
-
if (ok) print('已更新 skills/ 目录;可用 git add/commit 提交到仓库。')
|
|
392
|
-
process.exit(ok ? 0 : 1)
|
|
393
|
-
} else if (cmd === 'install' || cmd === 'update') {
|
|
394
|
-
// 自动拉取:--pull 强制拉取;skills/ 为空时也自动拉取
|
|
395
|
-
if (rest.includes('--pull') || !existsSync(SKILLS_SRC) || (await readdir(SKILLS_SRC).catch(() => [])).length === 0) {
|
|
396
|
-
await pullAllSources()
|
|
397
|
-
}
|
|
398
|
-
const targets = parseTargets(rest)
|
|
399
|
-
const skills = await listSkills()
|
|
400
|
-
if (!skills.length) { print('skills/ 目录为空,无法安装'); process.exit(1) }
|
|
401
|
-
if (rest.includes('--agents') && !targets.some((t) => t.path === AGENTS.path)) targets.push(AGENTS)
|
|
402
|
-
const silent = rest.includes('--silent') || rest.includes('--yes')
|
|
403
|
-
if (!silent) print(`将安装 ${skills.length} 个 skills → ${targets.length} 个目标`)
|
|
404
|
-
let updated = 0
|
|
405
|
-
for (const t of targets) {
|
|
406
|
-
if (!silent) print(`[${t.label}] ${t.path}`)
|
|
407
|
-
await syncAll(t.path, { reportUpdated: (n) => { updated += n }, silent })
|
|
408
|
-
}
|
|
409
|
-
if (rest.includes('--agents')) {
|
|
410
|
-
print('⚠ 已安装到共享 ~/.agents/skills:该目录会被 Codex/Gemini/Zed/OpenCode/Cursor 等同时扫描,同一 skill 可能被重复发现。')
|
|
411
|
-
}
|
|
412
|
-
if (!silent) {
|
|
413
|
-
print('完成。各 IDE 会自动发现各自用户级根中的新目录(可立即在新会话中调用)。')
|
|
414
|
-
if (updated) {
|
|
415
|
-
print(`提示:${updated} 个 skill 有更新。以后可用 node install.mjs check 检查新版本。`)
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
} else if (cmd === 'check') {
|
|
419
|
-
const targets = parseTargets(rest)
|
|
420
|
-
if (rest.includes('--agents') && !targets.some((t) => t.path === AGENTS.path)) targets.push(AGENTS)
|
|
421
|
-
process.exitCode = await checkUpdates(targets)
|
|
422
|
-
} else if (cmd === 'uninstall') {
|
|
423
|
-
const targets = parseTargets(rest)
|
|
424
|
-
if (rest.includes('--agents') && !targets.some((t) => t.path === AGENTS.path)) targets.push(AGENTS)
|
|
425
|
-
for (const t of targets) {
|
|
426
|
-
print(`[${t.label}] ${t.path}`)
|
|
427
|
-
await syncAll(t.path, { remove: true })
|
|
428
|
-
}
|
|
429
|
-
print('完成。')
|
|
430
|
-
} else {
|
|
431
|
-
print(`用法:
|
|
432
|
-
node install.mjs install [--all|--ide <key>|--skip <key>|--project|--agents|--pull|--target <dir>]
|
|
433
|
-
node install.mjs pull
|
|
434
|
-
node install.mjs update
|
|
435
|
-
node install.mjs check
|
|
436
|
-
node install.mjs uninstall
|
|
437
|
-
node install.mjs list
|
|
438
|
-
node install.mjs ides
|
|
439
|
-
|
|
440
|
-
npm 安装后(npx cli-blueprint@latest / npx cli-calctool@latest)用法相同。`)
|
|
441
|
-
process.exit(1)
|
|
442
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: blueprint
|
|
3
|
-
description: '把一个目标编译为可执行、可验证、可追溯的工程蓝图(确定性检查节点/依赖/验收标准/合同)。Compile one goal into an executable, verifiable, traceable engineering blueprint with deterministic checks. Превращает цель в исполняемый, проверяемый инженерный план с узлами, зависимостями, критериями приёмки и валидацией.'
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Blueprint Skill
|
|
7
|
-
|
|
8
|
-
Endpoint: https://cli.tax/wvz6zmRWmX
|
|
9
|
-
Request schema: blueprint.skill.request/1.0
|
|
10
|
-
Response schema: blueprint.skill.response/1.0
|
|
11
|
-
|
|
12
|
-
## Request envelope
|
|
13
|
-
|
|
14
|
-
POST JSON to the endpoint with an `input` wrapper:
|
|
15
|
-
|
|
16
|
-
```json
|
|
17
|
-
{
|
|
18
|
-
"input": {
|
|
19
|
-
"schemaVersion": "blueprint.skill.request/1.0",
|
|
20
|
-
"requestId": "<unique-id>",
|
|
21
|
-
"operation": "<operation>",
|
|
22
|
-
"input": {}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Operations
|
|
28
|
-
|
|
29
|
-
- `capabilities`: discover the operation list, per-operation inputs, and the recommended next step.
|
|
30
|
-
- `help`: return the usage guide, operation catalog, and request examples.
|
|
31
|
-
- `intake`: return the questions the IDE must ask the user before building the Blueprint.
|
|
32
|
-
- `validate`: deterministically validate a Blueprint object conforming to `blueprint.ir/1.0`.
|
|
33
|
-
- `compile-inline`: validate and compile a Blueprint, returning the generated artifacts inline.
|
|
34
|
-
|
|
35
|
-
## Required flow
|
|
36
|
-
|
|
37
|
-
1. Call `capabilities` first and read the returned `nextStep`.
|
|
38
|
-
2. Call `intake` and ask the user the returned questions one at a time, waiting for each answer.
|
|
39
|
-
3. Do not compile a Blueprint until all required questions are answered.
|
|
40
|
-
4. Build a Blueprint conforming to `blueprint.ir/1.0`, then call `validate`.
|
|
41
|
-
5. Fix every validation finding until the report is green, then call `compile-inline` and save the artifacts.
|
|
42
|
-
|
|
43
|
-
## Safety rules
|
|
44
|
-
|
|
45
|
-
- Never send credentials, model keys, provider endpoints, or personal secrets inside the request envelope or `input`.
|
|
46
|
-
- The response `status` must be `succeeded`; a `failed` response is an error, not a result.
|
|
47
|
-
- Public responses never prove that code was developed, tested, or deployed.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "blueprint",
|
|
3
|
-
"displayName": "Blueprint",
|
|
4
|
-
"description": "Blueprint 是一个跨工具的工程规划 CLI,把明确目标编译为可执行、可追溯、可验收的全站工程蓝图,并通过确定性规则检查每个节点、分支、合同、任务与验收闭环。默认由当前 IDE 的多智能体协同推演,也可连接用户自行安装的本地 Hermes;平台不托管模型密钥。",
|
|
5
|
-
"schemaVersion": "cli.request/1.0",
|
|
6
|
-
"endpoint": "https://cli.tax/wvz6zmRWmX",
|
|
7
|
-
"method": "POST",
|
|
8
|
-
"version": "v0.4.0",
|
|
9
|
-
"type": "Skill"
|
|
10
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# calctool 平台模板
|
|
2
|
-
|
|
3
|
-
由 calctool 生成的**可运行计算工具**工程模板(配置驱动、框架不变)。
|
|
4
|
-
|
|
5
|
-
## 一键运行
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install # 或 pnpm install / yarn install(环境自动适配)
|
|
9
|
-
npm run dev # 启动开发服务器 http://localhost:5173
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## 结构
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
├── src/
|
|
16
|
-
│ ├── main.tsx # 入口(Ant Design X ConfigProvider)
|
|
17
|
-
│ ├── App.tsx # 应用壳(录入/指标卡/链路/报告/授权 五页)
|
|
18
|
-
│ ├── engine-definition.json # ★ 引擎定义 + 链路(改这里 = 改工具,框架不动)
|
|
19
|
-
│ ├── engine/
|
|
20
|
-
│ │ ├── evaluate.ts # JSON AST + decimal.js 确定性求值
|
|
21
|
-
│ │ └── (recompute.ts 依赖图增量重算,按需扩展)
|
|
22
|
-
│ ├── pipeline.ts # ★ 链路编排(联通节点,非单页孤岛)
|
|
23
|
-
│ ├── authz.ts # ★ 授权管理(首次使用同意 + 逐能力授权,一次授权持久化)
|
|
24
|
-
│ └── store.ts # 存储层(localStorage,可换 sqlite)
|
|
25
|
-
├── vite.config.ts
|
|
26
|
-
└── package.json
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## 整套工具(联通节点,非单页孤岛)
|
|
30
|
-
|
|
31
|
-
工具由**联通节点**组成,数据流在节点间流动:
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
录入/导入 → 数据校验 → 确定性计算 → 数据存储 → 指标卡/报告
|
|
35
|
-
↘ 自动化(可选)
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
- 改动指标/公式:只改 `engine-definition.json`,链路自动感知
|
|
39
|
-
- 新增能力:在 `pipeline.ts` 加节点(如导入、导出、告警),页面无需改
|
|
40
|
-
- 页面「链路」Tab 可视化每个节点及其上下游,改工具时定位影响范围
|
|
41
|
-
|
|
42
|
-
## 授权与消耗提示
|
|
43
|
-
|
|
44
|
-
1. **首次使用总提示**:打开工具时弹窗告知"可能调用其他技能辅助、可能增加消耗",同意后持久化不再提示(可按工具分别记录)
|
|
45
|
-
2. **逐能力授权**:实际调用外部能力(Blueprint/搜索/模型/存储/自动化)前询问,一次授权永久记住
|
|
46
|
-
3. **授权管理页**:随时查看已授权能力、撤销授权
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
## 改指标/公式(需求变更,框架不变)
|
|
50
|
-
|
|
51
|
-
编辑 `src/engine-definition.json`:
|
|
52
|
-
|
|
53
|
-
```json
|
|
54
|
-
{
|
|
55
|
-
"fields": [{ "key": "visitors", "label": "访客数", "type": "integer", "unit": "人" }],
|
|
56
|
-
"formulas": [
|
|
57
|
-
{ "key": "conversionRate", "label": "转化率",
|
|
58
|
-
"expression": { "op": "safeDivide", "args": [{ "ref": "orders" }, { "ref": "visitors" }] } }
|
|
59
|
-
]
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
保存即热更新——**不用改任何代码**。
|
|
64
|
-
|
|
65
|
-
## 公式运算符(详见 formula-dsl.md)
|
|
66
|
-
|
|
67
|
-
`add / sub / mul / div(除零报错)/ safeDivide(除零回退)/ percentOf / round / if / ref / lit`
|
|
68
|
-
|
|
69
|
-
## 环境适配
|
|
70
|
-
|
|
71
|
-
- Node ≥18:全功能(可换 better-sqlite3 持久化)
|
|
72
|
-
- Node 16:兼容(sql.js WASM)
|
|
73
|
-
- Node <16:建议只用 L0 配置预览
|
|
74
|
-
- 包管理器:npm/pnpm/yarn 命令自动适配
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="zh-CN">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>calctool 计算工具</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="root"></div>
|
|
10
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "calctool-tool",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"description": "calctool 生成的可运行计算工具(配置驱动,Ant Design X + decimal.js)",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"dev": "vite",
|
|
9
|
-
"build": "tsc -b && vite build",
|
|
10
|
-
"preview": "vite preview",
|
|
11
|
-
"typecheck": "tsc --noEmit"
|
|
12
|
-
},
|
|
13
|
-
"dependencies": {
|
|
14
|
-
"react": "^19.0.0",
|
|
15
|
-
"react-dom": "^19.0.0",
|
|
16
|
-
"antd": "^6.0.0",
|
|
17
|
-
"@ant-design/x": "^2.9.0",
|
|
18
|
-
"decimal.js": "^10.4.3",
|
|
19
|
-
"lucide-react": "^0.469.0"
|
|
20
|
-
},
|
|
21
|
-
"devDependencies": {
|
|
22
|
-
"@vitejs/plugin-react": "^4.3.4",
|
|
23
|
-
"typescript": "^5.6.0",
|
|
24
|
-
"vite": "^6.0.0"
|
|
25
|
-
}
|
|
26
|
-
}
|