cli-blueprint 5.0.1 → 5.0.3

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/package.json CHANGED
@@ -5,7 +5,6 @@
5
5
  "description": "Blueprint skill installer for CLI.Tax: compile one goal into an executable, verifiable engineering blueprint.",
6
6
  "files": [
7
7
  "cli.mjs",
8
- "installer.mjs",
9
8
  "README.md",
10
9
  "skill/SKILL.md",
11
10
  "skill/skill.json"
@@ -18,5 +17,5 @@
18
17
  "url": "https://github.com/88208555/blueprint-clitax.git"
19
18
  },
20
19
  "type": "module",
21
- "version": "5.0.1"
20
+ "version": "5.0.3"
22
21
  }
package/skill/SKILL.md CHANGED
@@ -42,7 +42,7 @@ POST JSON to the endpoint with an `input` wrapper:
42
42
 
43
43
  ## Official catalog hops
44
44
 
45
- 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.
45
+ 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.
46
46
 
47
47
  ## Safety rules
48
48
 
package/skill/skill.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
- "description": "Blueprint \u628a\u4e00\u53e5\u5df2\u7ecf\u8bf4\u6e05\u695a\u7684\u76ee\u6807\u7f16\u8bd1\u6210\u53ef\u6267\u884c\u3001\u53ef\u9a8c\u8bc1\u3001\u53ef\u8ffd\u6eaf\u7684\u5de5\u7a0b\u84dd\u56fe\u3002\u9ed8\u8ba4\u7531\u5f53\u524d IDE \u7684\u591a\u667a\u80fd\u4f53\u534f\u540c\u5b8c\u6210\u9700\u6c42\u63a8\u6f14\uff0c\u4e5f\u53ef\u8fde\u63a5\u7528\u6237\u81ea\u884c\u5b89\u88c5\u7684\u672c\u5730 Hermes\uff1b\u5e73\u53f0\u4e0d\u6258\u7ba1\u6a21\u578b\u5bc6\u94a5\uff0c\u4e0d\u5728\u4ecb\u7ecd\u6587\u6848\u4e2d\u5c55\u793a\u5916\u90e8\u7533\u8bf7\u5730\u5740\u3002\u84dd\u56fe\u4e0d\u662f\u5907\u5fd8\u5f55\uff1a\u6bcf\u4e2a\u8282\u70b9\u3001\u5206\u652f\u3001\u4f9d\u8d56\u3001\u5408\u540c\u3001\u4efb\u52a1\u4e0e\u9a8c\u6536\u6807\u51c6\u90fd\u5fc5\u987b\u80fd\u88ab\u786e\u5b9a\u6027\u89c4\u5219\u68c0\u67e5\u3002\u7f3a\u8282\u70b9\u3001\u65ad\u94fe\u8def\u3001\u65e0\u9a8c\u6536\u3001\u628a\u201c\u770b\u8d77\u6765\u505a\u5b8c\u201d\u5f53\u6210\u5b8c\u6210\uff0c\u4e00\u5f8b\u5224\u5931\u8d25\u3002\u8c03\u7528\u987a\u5e8f\u4e3a capabilities\u3001intake\u3001validate\u3001compile-inline\uff1a\u5148\u5728\u5bf9\u8bdd\u4e2d\u95ee\u6e05\u8303\u56f4\u3001\u7ea6\u675f\u3001\u4ea4\u4ed8\u7269\u4e0e\u9a8c\u6536\u53e3\u5f84\uff0c\u518d\u751f\u6210\u7ed3\u6784\u5316\u84dd\u56fe\uff1b\u6821\u9a8c\u5168\u90e8\u53d8\u7eff\u540e\u624d\u5141\u8bb8\u4ea7\u51fa\u53ef\u843d\u5730\u5de5\u4ef6\u3002\u9002\u5408\u8de8\u5de5\u5177\u534f\u4f5c\u7684\u5de5\u7a0b\u89c4\u5212\uff0c\u8ba9\u60f3\u6cd5\u8fdb\u5165\u53ef\u5b9e\u65bd\u72b6\u6001\u800c\u4e0d\u662f\u505c\u5728\u53e3\u5934\u627f\u8bfa\u3002\u5168\u8fc7\u7a0b\u53ef\u5ba1\u8ba1\u3001\u53ef\u590d\u9a8c\uff0c\u7981\u6b62\u9759\u9ed8\u8df3\u6b65\u3001\u7981\u6b62\u4f2a\u9020\u9a8c\u6536\u3001\u7981\u6b62\u7528\u5360\u4f4d\u6587\u6863\u5192\u5145\u7f16\u8bd1\u7ed3\u679c\u3002\u9700\u8981\u672c\u5730\u6a21\u578b\u6216\u5bc6\u94a5\u65f6\uff0c\u7531\u5bf9\u8bdd\u5411\u7528\u6237\u63d0\u95ee\uff0c\u7528\u6237\u81ea\u884c\u914d\u7f6e\u540e\u518d\u7ee7\u7eed\u3002\u672c\u6280\u80fd\u9762\u5411\u771f\u5b9e\u4ea4\u4ed8\uff1a\u6bcf\u4e00\u6b65\u90fd\u6709\u8f93\u5165\u3001\u89c4\u5219\u4e0e\u5931\u8d25\u9762\uff0c\u7981\u6b62\u628a\u52a0\u8f7d\u4e2d\u3001\u8d85\u65f6\u6216\u672a\u77e5\u72b6\u6001\u5f53\u6210\u7a7a\u6210\u529f\u3002\u7528\u6237\u53ef\u89c1\u8bf4\u660e\u53ea\u8bb2\u80fd\u529b\u4e0e\u5bf9\u8bdd\u914d\u7f6e\u65b9\u5f0f\uff0c\u4e0d\u51fa\u73b0\u5916\u94fe\u3002\u8c03\u7528\u524d\u5fc5\u987b\u5148\u8d70 capabilities\uff0c\u518d\u6309 nextStep \u524d\u8fdb\uff1b\u5fc5\u586b\u9879\u672a\u56de\u7b54\u4e0d\u5f97\u8fdb\u5165\u4e0b\u4e00\u64cd\u4f5c\u3002\u65e5\u5fd7\u53ea\u4fdd\u5b58\u5fc5\u8981\u5143\u6570\u636e\uff0c\u5bc6\u94a5\u4e0d\u5f97\u5199\u5165\u516c\u5f00\u9875\u9762\u3002",
2
+ "description": "Blueprint 把一句已经说清楚的目标编译成可执行、可验证、可追溯的工程蓝图。默认由当前 IDE 的多智能体协同完成需求推演,也可连接用户自行安装的本地 Hermes;平台不托管模型密钥,不在介绍文案中展示外部申请地址。蓝图不是备忘录:每个节点、分支、依赖、合同、任务与验收标准都必须能被确定性规则检查。缺节点、断链路、无验收、把“看起来做完”当成完成,一律判失败。调用顺序为 capabilities、intake、validate、compile-inline:先在对话中问清范围、约束、交付物与验收口径,再生成结构化蓝图;校验全部变绿后才允许产出可落地工件。适合跨工具协作的工程规划,让想法进入可实施状态而不是停在口头承诺。全过程可审计、可复验,禁止静默跳步、禁止伪造验收、禁止用占位文档冒充编译结果。需要本地模型或密钥时,由对话向用户提问,用户自行配置后再继续。本技能面向真实交付:每一步都有输入、规则与失败面,禁止把加载中、超时或未知状态当成空成功。用户可见说明只讲能力与对话配置方式,不出现外链。调用前必须先走 capabilities,再按 nextStep 前进;必填项未回答不得进入下一操作。日志只保存必要元数据,密钥不得写入公开页面。",
3
3
  "displayName": "Blueprint",
4
4
  "endpoint": "https://cli.tax/wvz6zmRWmX",
5
5
  "method": "POST",
6
6
  "name": "blueprint",
7
7
  "schemaVersion": "blueprint.skill.request/1.0",
8
8
  "type": "Skill",
9
- "version": "v5.0.1"
9
+ "version": "v5.0.3"
10
10
  }
package/installer.mjs DELETED
@@ -1,251 +0,0 @@
1
- /**
2
- * 五个官方技能共用这一份安装器。packages/*-cli/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
- }