cli-calctool 1.0.4 → 5.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/cli.mjs CHANGED
@@ -1,16 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { copyFile, mkdir, writeFile } from 'node:fs/promises'
3
- import { createInterface } from 'node:readline/promises'
4
- import { stdin, stdout } from 'node:process'
5
- import { dirname, join, resolve } from 'node:path'
2
+ import { dirname } from 'node:path'
6
3
  import { fileURLToPath } from 'node:url'
7
- import { existsSync, readFileSync } from 'node:fs'
8
-
9
- const ENDPOINT = 'https://cli.tax/KKyA6xljUX'
10
- const SCHEMA_VERSION = 'calctool.skill.request/1.0'
11
- const PACKAGE_SKILL_DIR = join(dirname(fileURLToPath(import.meta.url)), 'skill')
12
- const INSTALL_META = 'install-meta.json'
13
- const LATEST_ENDPOINT = 'https://cli.tax/api/public/skills/KKyA6xljUX'
4
+ import { dispatchOfficialSkillCli, runIntakeHandshake } from './installer.mjs'
14
5
 
15
6
  const INTAKE_QUESTIONS = [
16
7
  {
@@ -51,186 +42,14 @@ const INTAKE_QUESTIONS = [
51
42
  },
52
43
  ]
53
44
 
54
- function usage() {
55
- return [
56
- 'clitax-KKyA6xljUX install and run the calctool skill from CLI.Tax',
57
- '',
58
- 'Usage:',
59
- ' npx <package-url> install [directory]',
60
- ' Install the calctool skill for the current IDE (Codex skills directory by default).',
61
- ' If an older version is already installed, it reports and upgrades it.',
62
- ' npx <package-url> check [directory]',
63
- ' Check whether the installed skill has a newer version on cli.tax.',
64
- ' npx <package-url> run',
65
- ' Run the skill handshake: discover capabilities, answer the intake questions,',
66
- ' and save CALCTOOL-REQUIREMENTS.json for the IDE agent.',
67
- '',
68
- `Endpoint: ${ENDPOINT}`,
69
- ].join('\n')
70
- }
71
-
72
- /** 读取已安装目录的版本元数据 */
73
- function readMeta(dir) {
74
- const p = join(dir, INSTALL_META)
75
- if (!existsSync(p)) return null
76
- try { return JSON.parse(readFileSync(p, 'utf8')) } catch { return null }
77
- }
78
-
79
- /** 查询 cli.tax 最新版本(GET /api/public/skills/{code}) */
80
- async function fetchLatestVersion() {
81
- try {
82
- const resp = await fetch(LATEST_ENDPOINT)
83
- if (!resp.ok) return null
84
- const data = await resp.json()
85
- return { version: data.version ?? '', displayName: data.displayName ?? 'calctool' }
86
- } catch {
87
- return null
88
- }
89
- }
90
-
91
- async function postRequest(operation, requestId) {
92
- const response = await fetch(ENDPOINT, {
93
- method: 'POST',
94
- headers: { 'Content-Type': 'application/json' },
95
- body: JSON.stringify({
96
- input: {
97
- schemaVersion: SCHEMA_VERSION,
98
- requestId,
99
- operation,
100
- input: {},
101
- },
102
- }),
103
- })
104
- let payload
105
- try {
106
- payload = await response.json()
107
- } catch {
108
- throw new Error(`calctool ${operation} failed: the runtime host returned a non-JSON response (HTTP ${response.status}). Check that the CLI runtime is deployed on ${ENDPOINT}.`)
109
- }
110
- if (!response.ok || payload?.ok !== true) {
111
- const message = payload?.error?.message ?? payload?.error ?? `HTTP ${response.status}`
112
- throw new Error(`calctool ${operation} failed: ${message}`)
113
- }
114
- return payload
115
- }
116
-
117
- function installTarget(explicit) {
118
- if (explicit) return resolve(explicit)
119
- const codexHome = process.env.CODEX_HOME?.trim()
120
- if (codexHome) return join(codexHome, 'skills', 'calctool')
121
- const projectCodex = join(process.cwd(), '.codex', 'skills', 'calctool')
122
- return projectCodex
123
- }
124
-
125
- async function install(explicit) {
126
- const target = installTarget(explicit)
127
- await mkdir(target, { recursive: true })
128
- const previous = readMeta(target)
129
- await copyFile(join(PACKAGE_SKILL_DIR, 'SKILL.md'), join(target, 'SKILL.md'))
130
- await copyFile(join(PACKAGE_SKILL_DIR, 'skill.json'), join(target, 'skill.json'))
131
- const installedVersion = JSON.parse(readFileSync(join(target, 'skill.json'), 'utf8')).version
132
- if (!installedVersion) throw new Error('skill.json is missing version')
133
- const localVersion = process.env.npm_package_version ?? ''
134
- await writeFile(join(target, INSTALL_META), `${JSON.stringify({
135
- source: 'KKyA6xljUX',
136
- slug: 'calctool',
137
- version: installedVersion,
138
- packageVersion: localVersion,
139
- endpoint: ENDPOINT,
140
- installedAt: new Date().toISOString(),
141
- }, null, 2)}\n`)
142
- if (previous?.version && previous.version !== installedVersion) {
143
- console.log(`calctool skill updated: ${target}`)
144
- console.log(` ⤴ ${previous.version} → ${installedVersion}`)
145
- } else {
146
- console.log(`calctool skill installed: ${target} (${installedVersion})`)
147
- }
148
- console.log('Next: return to your IDE conversation and describe the calculator tool you want.')
149
- console.log('The IDE agent reads the installed SKILL.md, asks you the intake questions,')
150
- console.log('and then calls the calctool protocol to generate and validate the engine definition.')
151
- }
152
-
153
- async function check(explicit) {
154
- const target = installTarget(explicit)
155
- const local = readMeta(target)
156
- const latest = await fetchLatestVersion()
157
- if (!latest) {
158
- console.log('calctool: cannot reach cli.tax to check updates.')
159
- process.exitCode = 1
160
- return
161
- }
162
- if (!local?.version) {
163
- console.log(`calctool: no version record in ${target} (installed before update tracking).`)
164
- console.log(`Latest on cli.tax: ${latest.version}. Run install to record and refresh.`)
165
- process.exitCode = 1
166
- return
167
- }
168
- if (local.version === latest.version) {
169
- console.log(`calctool is up to date (${latest.version}) at ${target}`)
170
- } else {
171
- console.log(`calctool update available: ${local.version} → ${latest.version} at ${target}`)
172
- console.log('Run: npx cli-calctool@latest install')
173
- process.exitCode = 1
174
- }
175
- }
176
-
177
- async function askOne(question, readline) {
178
- const requiredMark = question.required ? ' (required)' : ''
179
- console.log(`\n${question.prompt}${requiredMark}`)
180
- console.log(`Example: ${question.example}`)
181
- for (;;) {
182
- const answer = (await readline.question('> ')).trim()
183
- if (answer || !question.required) return answer || ''
184
- console.log('This question is required. Please answer before continuing.')
185
- }
186
- }
187
-
188
- async function run() {
189
- const capabilities = await postRequest('capabilities', 'cli-1')
190
- const skill = capabilities.output?.skill ?? {}
191
- const nextStep = capabilities.output?.nextStep ?? {}
192
- console.log(`calctool ${skill.version ?? ''} — ${skill.description ?? 'generate online calculator tools'}`)
193
- if (nextStep.instruction) console.log(nextStep.instruction)
194
-
195
- const readline = createInterface({ input: stdin, output: stdout })
196
- const answers = []
197
- try {
198
- for (const question of INTAKE_QUESTIONS) {
199
- answers.push({ id: question.id, prompt: question.prompt, answer: await askOne(question, readline) })
200
- }
201
- } finally {
202
- readline.close()
203
- }
204
-
205
- const requirements = {
206
- schemaVersion: SCHEMA_VERSION,
207
- endpoint: ENDPOINT,
208
- createdAt: new Date().toISOString(),
209
- answers,
210
- }
211
- const target = join(process.cwd(), 'CALCTOOL-REQUIREMENTS.json')
212
- await writeFile(target, `${JSON.stringify(requirements, null, 2)}\n`)
213
- console.log(`\nRequirements saved: ${target}`)
214
- console.log('Next: continue in your IDE agent with this file.')
215
- }
216
-
217
- const command = process.argv[2] ?? 'help'
218
- const argument = process.argv[3] ?? ''
219
- try {
220
- if (command === 'install') {
221
- await install(argument)
222
- } else if (command === 'check') {
223
- await check(argument)
224
- } else if (command === 'run') {
225
- await run()
226
- } else if (command === '--help' || command === '-h' || command === 'help') {
227
- console.log(usage())
228
- } else {
229
- console.error(`Unknown command: ${command}\n`)
230
- console.log(usage())
231
- process.exitCode = 1
232
- }
233
- } catch (error) {
234
- console.error(error instanceof Error ? error.message : error)
235
- process.exitCode = 1
236
- }
45
+ await dispatchOfficialSkillCli({
46
+ packageRoot: dirname(fileURLToPath(import.meta.url)),
47
+ runCommand: (context) => runIntakeHandshake(context, {
48
+ questions: INTAKE_QUESTIONS,
49
+ outputFile: 'CALCTOOL-REQUIREMENTS.json',
50
+ afterCapabilities(output) {
51
+ const instruction = output.nextStep?.instruction
52
+ if (typeof instruction === 'string' && instruction.trim()) console.log(instruction)
53
+ },
54
+ }),
55
+ })
package/installer.mjs ADDED
@@ -0,0 +1,251 @@
1
+ /**
2
+ * 五个官方技能共用这一份安装器。packages/*/installer.mjs 必须与本文件字节一致。
3
+ * 禁止第二套超时、第二套版本来源、第二套 bin 名。
4
+ */
5
+ import { copyFile, mkdir, writeFile } from 'node:fs/promises'
6
+ import { existsSync, readFileSync } from 'node:fs'
7
+ import { dirname, join, resolve } from 'node:path'
8
+ import { stdin, stdout } from 'node:process'
9
+ import { createInterface } from 'node:readline/promises'
10
+ import { fileURLToPath } from 'node:url'
11
+
12
+ export const LOOKUP_TIMEOUT_MS = 8000
13
+ export const CALL_TIMEOUT_MS = 120_000
14
+ const INSTALL_META = 'install-meta.json'
15
+
16
+ function asObject(value, label) {
17
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
18
+ throw new Error(`${label} must be an object`)
19
+ }
20
+ return value
21
+ }
22
+
23
+ function requiredString(value, label) {
24
+ const text = typeof value === 'string' ? value.trim() : ''
25
+ if (!text) throw new Error(`${label} is required`)
26
+ return text
27
+ }
28
+
29
+ export function loadOfficialSkillContext(packageRoot) {
30
+ const pkg = asObject(JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8')), 'package.json')
31
+ const skill = asObject(JSON.parse(readFileSync(join(packageRoot, 'skill/skill.json'), 'utf8')), 'skill.json')
32
+ const npmName = requiredString(pkg.name, 'package.json name')
33
+ const packageVersion = requiredString(pkg.version, 'package.json version')
34
+ const displayName = requiredString(skill.displayName, 'skill.json displayName')
35
+ const skillName = requiredString(skill.name, 'skill.json name')
36
+ const schemaVersion = requiredString(skill.schemaVersion, 'skill.json schemaVersion')
37
+ const endpoint = requiredString(skill.endpoint, 'skill.json endpoint')
38
+ const skillVersion = requiredString(skill.version, 'skill.json version')
39
+ const runtimeCode = requiredString(endpoint.replace(/^https:\/\/cli\.tax\//, ''), 'runtime code')
40
+ if (!/^[A-Za-z0-9]{10}$/.test(runtimeCode)) {
41
+ throw new Error(`skill.json endpoint must be https://cli.tax/{10-char-code}: ${endpoint}`)
42
+ }
43
+ if (skillVersion.replace(/^v/i, '') !== packageVersion.replace(/^v/i, '')) {
44
+ throw new Error(`skill.json ${skillVersion} must match package.json ${packageVersion}`)
45
+ }
46
+ return {
47
+ packageRoot,
48
+ npmName,
49
+ packageVersion,
50
+ displayName,
51
+ skillName,
52
+ schemaVersion,
53
+ endpoint,
54
+ skillVersion,
55
+ runtimeCode,
56
+ latestEndpoint: `https://cli.tax/api/public/skills/${runtimeCode}`,
57
+ skillDir: join(packageRoot, 'skill'),
58
+ }
59
+ }
60
+
61
+ export function readInstallMeta(target) {
62
+ const path = join(target, INSTALL_META)
63
+ if (!existsSync(path)) return null
64
+ return asObject(JSON.parse(readFileSync(path, 'utf8')), INSTALL_META)
65
+ }
66
+
67
+ export function installTarget(skillName, explicit) {
68
+ if (explicit) return resolve(explicit)
69
+ const codexHome = process.env.CODEX_HOME?.trim()
70
+ if (codexHome) return join(codexHome, 'skills', skillName)
71
+ return join(process.cwd(), '.codex', 'skills', skillName)
72
+ }
73
+
74
+ export async function fetchLatestVersion(context) {
75
+ const response = await fetch(context.latestEndpoint, { signal: AbortSignal.timeout(LOOKUP_TIMEOUT_MS) })
76
+ if (!response.ok) throw new Error(`cli.tax skill lookup failed: HTTP ${response.status}`)
77
+ const data = asObject(await response.json(), 'cli.tax skill lookup')
78
+ return {
79
+ version: requiredString(data.version, 'cli.tax skill lookup version'),
80
+ displayName: requiredString(data.displayName, 'cli.tax skill lookup displayName'),
81
+ }
82
+ }
83
+
84
+ export async function callOfficialSkill(context, operation, input) {
85
+ const requestId = `${context.npmName}-${Date.now()}`
86
+ const response = await fetch(context.endpoint, {
87
+ method: 'POST',
88
+ headers: { 'Content-Type': 'application/json' },
89
+ body: JSON.stringify({
90
+ input: {
91
+ schemaVersion: context.schemaVersion,
92
+ requestId,
93
+ operation,
94
+ input,
95
+ },
96
+ }),
97
+ signal: AbortSignal.timeout(CALL_TIMEOUT_MS),
98
+ })
99
+ let payload
100
+ try {
101
+ payload = await response.json()
102
+ } catch {
103
+ throw new Error(`${context.displayName} ${operation} failed: non-JSON response (HTTP ${response.status}). Check ${context.endpoint}.`)
104
+ }
105
+ if (!response.ok || payload?.ok !== true) {
106
+ const message = payload?.error?.message
107
+ if (typeof message !== 'string' || !message.trim()) {
108
+ throw new Error(`${context.displayName} ${operation} failed: HTTP ${response.status}`)
109
+ }
110
+ throw new Error(`${context.displayName} ${operation} failed: ${message}`)
111
+ }
112
+ return payload
113
+ }
114
+
115
+ export async function installOfficialSkill(context, explicit) {
116
+ const target = installTarget(context.skillName, explicit)
117
+ await mkdir(target, { recursive: true })
118
+ const previous = readInstallMeta(target)
119
+ await copyFile(join(context.skillDir, 'SKILL.md'), join(target, 'SKILL.md'))
120
+ await copyFile(join(context.skillDir, 'skill.json'), join(target, 'skill.json'))
121
+ const installed = asObject(JSON.parse(readFileSync(join(target, 'skill.json'), 'utf8')), 'installed skill.json')
122
+ const installedVersion = requiredString(installed.version, 'installed skill.json version')
123
+ await writeFile(join(target, INSTALL_META), `${JSON.stringify({
124
+ source: context.runtimeCode,
125
+ slug: context.skillName,
126
+ version: installedVersion,
127
+ packageVersion: context.packageVersion,
128
+ endpoint: context.endpoint,
129
+ installedAt: new Date().toISOString(),
130
+ }, null, 2)}\n`)
131
+ if (previous?.version && previous.version !== installedVersion) {
132
+ console.log(`${context.displayName} skill updated: ${target}`)
133
+ console.log(` ${previous.version} → ${installedVersion}`)
134
+ } else {
135
+ console.log(`${context.displayName} skill installed: ${target} (${installedVersion})`)
136
+ }
137
+ if (context.npmName === 'cli-images') {
138
+ console.log('npm package: cli-images. Never install cli-image (third-party terminal viewer, not CLI.Tax).')
139
+ }
140
+ console.log('Next: return to your IDE and state the goal. The agent reads the installed SKILL.md.')
141
+ }
142
+
143
+ export async function checkOfficialSkill(context, explicit) {
144
+ const target = installTarget(context.skillName, explicit)
145
+ const current = readInstallMeta(target)
146
+ if (!current) {
147
+ console.log(`${context.displayName} skill is not installed. Run: npx ${context.npmName}@latest install`)
148
+ process.exitCode = 1
149
+ return
150
+ }
151
+ const installedVersion = requiredString(current.version, 'install-meta.json version')
152
+ const packageVersion = requiredString(current.packageVersion, 'install-meta.json packageVersion')
153
+ console.log(`Installed: ${installedVersion} (package ${packageVersion})`)
154
+ const latest = await fetchLatestVersion(context)
155
+ console.log(`Latest on cli.tax: ${latest.version}`)
156
+ if (installedVersion === latest.version) {
157
+ console.log('Up to date.')
158
+ return
159
+ }
160
+ console.log(`Update available: ${installedVersion} → ${latest.version}`)
161
+ console.log(`Run: npx ${context.npmName}@latest install`)
162
+ process.exitCode = 1
163
+ }
164
+
165
+ export function defaultUsage(context, extraLines) {
166
+ const lines = [
167
+ `${context.npmName} — install and run the ${context.displayName} skill from CLI.Tax`,
168
+ '',
169
+ 'Usage:',
170
+ ` npx ${context.npmName}@latest install [directory]`,
171
+ ` Install the ${context.displayName} skill for the current IDE.`,
172
+ ` npx ${context.npmName}@latest check [directory]`,
173
+ ' Check whether the installed skill has a newer version.',
174
+ ` npx ${context.npmName}@latest run`,
175
+ ' Run the skill handshake: discover capabilities and collect intake answers.',
176
+ '',
177
+ `Endpoint: ${context.endpoint}`,
178
+ ]
179
+ if (context.npmName === 'cli-images') {
180
+ lines.push(
181
+ '',
182
+ 'The npm package name is cli-images (plural).',
183
+ 'Never install cli-image: that is a third-party terminal image viewer, not CLI.Tax.',
184
+ )
185
+ }
186
+ if (extraLines?.length) lines.push('', ...extraLines)
187
+ return lines.join('\n')
188
+ }
189
+
190
+ export async function runIntakeHandshake(context, spec) {
191
+ const capabilities = await callOfficialSkill(context, 'capabilities', {})
192
+ const output = capabilities.output && typeof capabilities.output === 'object' ? capabilities.output : {}
193
+ const skill = output.skill && typeof output.skill === 'object' ? output.skill : {}
194
+ const version = typeof skill.version === 'string' && skill.version.trim()
195
+ ? skill.version.trim()
196
+ : context.skillVersion
197
+ console.log(`${context.displayName} ${version}`)
198
+ if (typeof spec.afterCapabilities === 'function') spec.afterCapabilities(output)
199
+ const readline = createInterface({ input: stdin, output: stdout })
200
+ const answers = []
201
+ try {
202
+ for (const question of spec.questions) {
203
+ const requiredMark = question.required ? ' (required)' : ''
204
+ console.log(`\n${question.prompt}${requiredMark}`)
205
+ console.log(`Example: ${question.example}`)
206
+ for (;;) {
207
+ const answer = (await readline.question('> ')).trim()
208
+ if (answer) {
209
+ answers.push({ id: question.id, prompt: question.prompt, answer })
210
+ break
211
+ }
212
+ if (!question.required) break
213
+ console.log('This question is required. Please answer before continuing.')
214
+ }
215
+ }
216
+ } finally {
217
+ readline.close()
218
+ }
219
+ const target = join(process.cwd(), spec.outputFile)
220
+ await writeFile(target, `${JSON.stringify({
221
+ schemaVersion: context.schemaVersion,
222
+ endpoint: context.endpoint,
223
+ createdAt: new Date().toISOString(),
224
+ answers,
225
+ }, null, 2)}\n`)
226
+ console.log(`\nRequirements saved: ${target}`)
227
+ console.log('Next: continue in your IDE agent with this file.')
228
+ }
229
+
230
+ export async function dispatchOfficialSkillCli(options) {
231
+ const packageRoot = options.packageRoot ?? dirname(fileURLToPath(options.importMetaUrl))
232
+ const context = loadOfficialSkillContext(packageRoot)
233
+ const args = process.argv.slice(2)
234
+ const command = args[0] ?? 'help'
235
+ const argument = args[1]
236
+ try {
237
+ if (command === 'install') await installOfficialSkill(context, argument)
238
+ else if (command === 'check') await checkOfficialSkill(context, argument)
239
+ else if (command === 'run') await options.runCommand(context)
240
+ else if (command === 'help' || command === '--help' || command === '-h') {
241
+ console.log(options.usage ? options.usage(context) : defaultUsage(context, options.extraUsageLines))
242
+ } else {
243
+ console.error(`Unknown command: ${command}`)
244
+ console.log(options.usage ? options.usage(context) : defaultUsage(context, options.extraUsageLines))
245
+ process.exitCode = 1
246
+ }
247
+ } catch (error) {
248
+ console.error(error instanceof Error ? error.message : error)
249
+ process.exitCode = 1
250
+ }
251
+ }
package/package.json CHANGED
@@ -1,29 +1,21 @@
1
1
  {
2
- "name": "cli-calctool",
3
- "version": "1.0.4",
4
- "description": "Calctool skill installer for CLI.Tax: generate a runnable online calculator from a domain need.",
5
- "type": "module",
6
2
  "bin": {
7
3
  "cli-calctool": "./cli.mjs"
8
4
  },
5
+ "description": "Calctool skill installer for CLI.Tax: generate a runnable online calculator from a domain need.",
9
6
  "files": [
10
7
  "cli.mjs",
8
+ "installer.mjs",
11
9
  "README.md",
12
10
  "skill/SKILL.md",
13
11
  "skill/skill.json"
14
12
  ],
15
- "engines": {
16
- "node": ">=18"
17
- },
13
+ "license": "UNLICENSED",
14
+ "name": "cli-calctool",
18
15
  "repository": {
19
16
  "type": "git",
20
17
  "url": "https://github.com/88208555/calctool-clitax.git"
21
18
  },
22
- "license": "UNLICENSED",
23
- "keywords": [
24
- "cli.tax",
25
- "skill",
26
- "installer",
27
- "agent"
28
- ]
19
+ "type": "module",
20
+ "version": "5.0.0"
29
21
  }
package/skill/SKILL.md CHANGED
@@ -53,7 +53,7 @@ description: '按需生成「万能计算工具」:用户输入一个领域需
53
53
 
54
54
  ## Official catalog hops
55
55
 
56
- After `capabilities`, read `officialCatalog`. Default allowlist is official skills. Call another skill only when its capability matches this demand. User-named extras enter only when the user names them; then confirm that skill's capabilities before invoke. Do not call chain-unrelated or self-extended skills. Image npm package is `cli-images`. Quick start: `npx cli-images@latest install`. Never install `cli-image`: that is a third-party terminal image viewer, unrelated to CLI.Tax.
56
+ After `capabilities`, read `officialCatalog`. Default allowlist is official skills. Call another skill only when its capability matches this demand. User-named extras enter only when the user names them; then confirm that skill's capabilities before invoke. Do not call chain-unrelated or self-extended skills. Images npm package is `cli-images`. Quick start: `npx cli-images@latest install`. Never install `cli-image`: that is a third-party terminal image viewer, unrelated to CLI.Tax.
57
57
 
58
58
  ## 核心原则
59
59
 
package/skill/skill.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
- "name": "calctool",
2
+ "description": "Calctool 按需生成可执行、可验证、可发布的在线计算工具。用户用一句话说明领域需求,例如财务经营健康诊断、报价测算或指标看板;技能通过对话逐项确认指标定义、公式逻辑、输入方式与输出形式,禁止把未确认的口径写成假完成。生成结果支持自定义指标、自定义公式,以及用户上传内容的自动识别:表格字段映射与图片文字识别,最终输出可复核的计算报告。调用顺序为 capabilities、intake、validate、compile-inline,校验未通过不得发布。密钥与模型若需要,一律在对话中由用户自行填写,平台不发放密钥、不代持免费额度,也不在描述里展示外部申请网址。工具必须能被再次运行,并在同一套规则下得到同一结论。公式、口径与样本数据全部可追溯;缺字段、映射失败或识别失败必须显式报错,不得用空表或占位数字冒充计算结果。发布前须完成确定性校验,未通过即停止。本技能面向真实交付:每一步都有输入、规则与失败面,禁止把加载中、超时或未知状态当成空成功。用户可见说明只讲能力与对话配置方式,不出现外链。调用前必须先走 capabilities,再按 nextStep 前进;必填项未回答不得进入下一操作。日志只保存必要元数据,密钥不得写入公开页面。",
3
3
  "displayName": "Calctool",
4
- "description": "按需生成「万能计算工具」:输入领域需求(如财务经营健康诊断),通过提问明确指标/公式/输入方式,生成可执行、可验证、可发布的在线计算工具——支持自定义指标、自定义公式、上传内容自动识别(Excel/OCR)、报告输出。",
5
- "schemaVersion": "calctool.skill.request/1.0",
6
4
  "endpoint": "https://cli.tax/KKyA6xljUX",
7
5
  "method": "POST",
8
- "version": "v1.0.4",
9
- "type": "Skill"
10
- }
6
+ "name": "calctool",
7
+ "schemaVersion": "calctool.skill.request/1.0",
8
+ "type": "Skill",
9
+ "version": "v5.0.0"
10
+ }