cli-swarm 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 +13 -190
- package/installer.mjs +251 -0
- package/package.json +7 -14
- package/skill/SKILL.md +1 -1
- package/skill/skill.json +5 -5
package/cli.mjs
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
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 {
|
|
8
|
-
|
|
9
|
-
const ENDPOINT = 'https://cli.tax/zj7fTPVh4p'
|
|
10
|
-
const SCHEMA_VERSION = 'swarm.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/zj7fTPVh4p'
|
|
4
|
+
import { dispatchOfficialSkillCli, runIntakeHandshake } from './installer.mjs'
|
|
14
5
|
|
|
15
6
|
const INTAKE_QUESTIONS = [
|
|
16
7
|
{
|
|
@@ -39,182 +30,14 @@ const INTAKE_QUESTIONS = [
|
|
|
39
30
|
},
|
|
40
31
|
]
|
|
41
32
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
' Run the skill handshake: discover capabilities, answer the intake questions,',
|
|
54
|
-
' and save SWARM-REQUIREMENTS.json for the IDE agent.',
|
|
55
|
-
'',
|
|
56
|
-
`Endpoint: ${ENDPOINT}`,
|
|
57
|
-
].join('\n')
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function readMeta(dir) {
|
|
61
|
-
const p = join(dir, INSTALL_META)
|
|
62
|
-
if (!existsSync(p)) return null
|
|
63
|
-
try { return JSON.parse(readFileSync(p, 'utf8')) } catch { return null }
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function fetchLatestVersion() {
|
|
67
|
-
try {
|
|
68
|
-
const resp = await fetch(LATEST_ENDPOINT)
|
|
69
|
-
if (!resp.ok) return null
|
|
70
|
-
const data = await resp.json()
|
|
71
|
-
return { version: data.version ?? '', displayName: data.displayName ?? 'swarm' }
|
|
72
|
-
} catch {
|
|
73
|
-
return null
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function postRequest(operation, requestId) {
|
|
78
|
-
const response = await fetch(ENDPOINT, {
|
|
79
|
-
method: 'POST',
|
|
80
|
-
headers: { 'Content-Type': 'application/json' },
|
|
81
|
-
body: JSON.stringify({
|
|
82
|
-
input: {
|
|
83
|
-
schemaVersion: SCHEMA_VERSION,
|
|
84
|
-
requestId,
|
|
85
|
-
operation,
|
|
86
|
-
input: {},
|
|
87
|
-
},
|
|
88
|
-
}),
|
|
89
|
-
})
|
|
90
|
-
let payload
|
|
91
|
-
try {
|
|
92
|
-
payload = await response.json()
|
|
93
|
-
} catch {
|
|
94
|
-
throw new Error(`swarm ${operation} failed: the runtime host returned a non-JSON response (HTTP ${response.status}). Check that the CLI runtime is deployed on ${ENDPOINT}.`)
|
|
95
|
-
}
|
|
96
|
-
if (!response.ok || payload?.ok !== true) {
|
|
97
|
-
const message = payload?.error?.message ?? payload?.error ?? `HTTP ${response.status}`
|
|
98
|
-
throw new Error(`swarm ${operation} failed: ${message}`)
|
|
99
|
-
}
|
|
100
|
-
return payload
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function installTarget(explicit) {
|
|
104
|
-
if (explicit) return resolve(explicit)
|
|
105
|
-
const codexHome = process.env.CODEX_HOME?.trim()
|
|
106
|
-
if (codexHome) return join(codexHome, 'skills', 'swarm')
|
|
107
|
-
const projectCodex = join(process.cwd(), '.codex', 'skills', 'swarm')
|
|
108
|
-
return projectCodex
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function install(explicit) {
|
|
112
|
-
const target = installTarget(explicit)
|
|
113
|
-
await mkdir(target, { recursive: true })
|
|
114
|
-
const previous = readMeta(target)
|
|
115
|
-
await copyFile(join(PACKAGE_SKILL_DIR, 'SKILL.md'), join(target, 'SKILL.md'))
|
|
116
|
-
await copyFile(join(PACKAGE_SKILL_DIR, 'skill.json'), join(target, 'skill.json'))
|
|
117
|
-
const installedVersion = JSON.parse(readFileSync(join(target, 'skill.json'), 'utf8')).version
|
|
118
|
-
if (!installedVersion) throw new Error('skill.json is missing version')
|
|
119
|
-
await writeFile(join(target, INSTALL_META), `${JSON.stringify({
|
|
120
|
-
source: 'zj7fTPVh4p',
|
|
121
|
-
slug: 'swarm',
|
|
122
|
-
version: installedVersion,
|
|
123
|
-
endpoint: ENDPOINT,
|
|
124
|
-
installedAt: new Date().toISOString(),
|
|
125
|
-
}, null, 2)}\n`)
|
|
126
|
-
if (previous?.version && previous.version !== installedVersion) {
|
|
127
|
-
console.log(`swarm skill updated: ${target}`)
|
|
128
|
-
console.log(` ⤴ ${previous.version} → ${installedVersion}`)
|
|
129
|
-
} else {
|
|
130
|
-
console.log(`swarm skill installed: ${target} (${installedVersion})`)
|
|
131
|
-
}
|
|
132
|
-
console.log('Next: return to your IDE conversation and describe the swarm goal.')
|
|
133
|
-
console.log('The IDE agent reads the installed SKILL.md, asks the intake questions,')
|
|
134
|
-
console.log('and then orchestrates the swarm: org-chart → dispatch → traffic-light → ops → security-guard.')
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async function check(explicit) {
|
|
138
|
-
const target = installTarget(explicit)
|
|
139
|
-
const local = readMeta(target)
|
|
140
|
-
const latest = await fetchLatestVersion()
|
|
141
|
-
if (!latest) {
|
|
142
|
-
console.log('swarm: cannot reach cli.tax to check updates.')
|
|
143
|
-
process.exitCode = 1
|
|
144
|
-
return
|
|
145
|
-
}
|
|
146
|
-
if (!local?.version) {
|
|
147
|
-
console.log(`swarm: no version record in ${target} (installed before update tracking).`)
|
|
148
|
-
console.log(`Latest on cli.tax: ${latest.version}. Run install to record and refresh.`)
|
|
149
|
-
process.exitCode = 1
|
|
150
|
-
return
|
|
151
|
-
}
|
|
152
|
-
if (local.version === latest.version) {
|
|
153
|
-
console.log(`swarm is up to date (${latest.version}) at ${target}`)
|
|
154
|
-
} else {
|
|
155
|
-
console.log(`swarm update available: ${local.version} → ${latest.version} at ${target}`)
|
|
156
|
-
console.log('Run: npx cli-swarm@latest install')
|
|
157
|
-
process.exitCode = 1
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async function askOne(question, readline) {
|
|
162
|
-
const requiredMark = question.required ? ' (required)' : ''
|
|
163
|
-
console.log(`\n${question.prompt}${requiredMark}`)
|
|
164
|
-
console.log(`Example: ${question.example}`)
|
|
165
|
-
for (;;) {
|
|
166
|
-
const answer = (await readline.question('> ')).trim()
|
|
167
|
-
if (answer || !question.required) return answer || ''
|
|
168
|
-
console.log('This question is required. Please answer before continuing.')
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async function run() {
|
|
173
|
-
const capabilities = await postRequest('capabilities', 'cli-1')
|
|
174
|
-
const skill = capabilities.output?.skill ?? {}
|
|
175
|
-
const nextStep = capabilities.output?.nextStep ?? {}
|
|
176
|
-
console.log(`swarm ${skill.version ?? ''} — ${skill.description ?? 'orchestrate an agent swarm'}`)
|
|
177
|
-
if (nextStep.instruction) console.log(nextStep.instruction)
|
|
178
|
-
|
|
179
|
-
const readline = createInterface({ input: stdin, output: stdout })
|
|
180
|
-
const answers = []
|
|
181
|
-
try {
|
|
182
|
-
for (const question of INTAKE_QUESTIONS) {
|
|
183
|
-
answers.push({ id: question.id, prompt: question.prompt, answer: await askOne(question, readline) })
|
|
184
|
-
}
|
|
185
|
-
} finally {
|
|
186
|
-
readline.close()
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const requirements = {
|
|
190
|
-
schemaVersion: SCHEMA_VERSION,
|
|
191
|
-
endpoint: ENDPOINT,
|
|
192
|
-
createdAt: new Date().toISOString(),
|
|
193
|
-
answers,
|
|
194
|
-
}
|
|
195
|
-
const target = join(process.cwd(), 'SWARM-REQUIREMENTS.json')
|
|
196
|
-
await writeFile(target, `${JSON.stringify(requirements, null, 2)}\n`)
|
|
197
|
-
console.log(`\nRequirements saved: ${target}`)
|
|
198
|
-
console.log('Next: continue in your IDE agent with this file.')
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const command = process.argv[2] ?? 'help'
|
|
202
|
-
const argument = process.argv[3] ?? ''
|
|
203
|
-
try {
|
|
204
|
-
if (command === 'install') {
|
|
205
|
-
await install(argument)
|
|
206
|
-
} else if (command === 'check') {
|
|
207
|
-
await check(argument)
|
|
208
|
-
} else if (command === 'run') {
|
|
209
|
-
await run()
|
|
210
|
-
} else if (command === '--help' || command === '-h' || command === 'help') {
|
|
211
|
-
console.log(usage())
|
|
212
|
-
} else {
|
|
213
|
-
console.error(`Unknown command: ${command}\n`)
|
|
214
|
-
console.log(usage())
|
|
215
|
-
process.exitCode = 1
|
|
216
|
-
}
|
|
217
|
-
} catch (error) {
|
|
218
|
-
console.error(error instanceof Error ? error.message : error)
|
|
219
|
-
process.exitCode = 1
|
|
220
|
-
}
|
|
33
|
+
await dispatchOfficialSkillCli({
|
|
34
|
+
packageRoot: dirname(fileURLToPath(import.meta.url)),
|
|
35
|
+
runCommand: (context) => runIntakeHandshake(context, {
|
|
36
|
+
questions: INTAKE_QUESTIONS,
|
|
37
|
+
outputFile: 'SWARM-REQUIREMENTS.json',
|
|
38
|
+
afterCapabilities(output) {
|
|
39
|
+
const instruction = output.nextStep?.instruction
|
|
40
|
+
if (typeof instruction === 'string' && instruction.trim()) console.log(instruction)
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
})
|
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,22 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "cli-swarm",
|
|
3
|
-
"version": "1.0.4",
|
|
4
|
-
"description": "Swarm skill installer for CLI.Tax: orchestrate N sub-agents with enterprise org-chart rules.",
|
|
5
|
-
"type": "module",
|
|
6
2
|
"bin": {
|
|
7
3
|
"cli-swarm": "./cli.mjs"
|
|
8
4
|
},
|
|
5
|
+
"description": "Swarm skill installer for CLI.Tax: orchestrate N sub-agents with enterprise org-chart rules.",
|
|
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
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
"license": "UNLICENSED",
|
|
14
|
+
"name": "cli-swarm",
|
|
15
|
+
"private": false,
|
|
18
16
|
"repository": {
|
|
19
17
|
"type": "git",
|
|
20
18
|
"url": "https://github.com/88208555/swarm-clitax.git"
|
|
21
19
|
},
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"cli.tax",
|
|
25
|
-
"skill",
|
|
26
|
-
"installer",
|
|
27
|
-
"agent"
|
|
28
|
-
]
|
|
20
|
+
"type": "module",
|
|
21
|
+
"version": "5.0.0"
|
|
29
22
|
}
|
package/skill/SKILL.md
CHANGED
|
@@ -52,7 +52,7 @@ intake 时可选择 `blueprintEnabled`:任务先交给 Blueprint 技能规划
|
|
|
52
52
|
|
|
53
53
|
## Official catalog hops
|
|
54
54
|
|
|
55
|
-
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.
|
|
55
|
+
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.
|
|
56
56
|
|
|
57
57
|
## 核心原则
|
|
58
58
|
|
package/skill/skill.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"description": "Swarm 用智能体大脑调度创建 N 个子智能体,把企业级组织架构写成协作规则,围绕一份项目 JSON 完成任务派单、认领与回传。每个任务带红绿灯状态,进度与错误必须显式汇报,禁止把卡住写成仍在进行。固定运维智能体负责心跳检测、回收超时或卡死的智能体,并派新智能体继承未完成任务;固定安全守卫负责异常警报与恶意注入检测。调用方先通过对话确认组织层级、角色权限、任务粒度和汇报节奏,再进入编排。平台不在技能描述中展示外部网址;密钥与运行时接入若需要,一律在对话里向用户提问后由用户自行填写。编排过程可观测、可接管、可追责,失败必须落到明确责任人与可恢复动作,而不是静默消失。派单不得跳过认领,回传必须带证据;心跳超时即回收,禁止幽灵智能体占用任务。安全守卫发现注入或越权时立即阻断并告警,不得降级为继续执行。任务继承必须携带原上下文。本技能面向真实交付:每一步都有输入、规则与失败面,禁止把加载中、超时或未知状态当成空成功。用户可见说明只讲能力与对话配置方式,不出现外链。调用前必须先走 capabilities,再按 nextStep 前进;必填项未回答不得进入下一操作。日志只保存必要元数据,密钥不得写入公开页面。",
|
|
3
3
|
"displayName": "Swarm",
|
|
4
|
-
"description": "智能体蜂群编排:大脑调度 N 个子智能体,企业级组织架构规则 + 项目 JSON 任务派单/认领/回传 + 红绿灯状态 + 固定运维(心跳/回收/接替/继承)与安全守卫(注入检测/警报)。",
|
|
5
|
-
"schemaVersion": "swarm.skill.request/1.0",
|
|
6
4
|
"endpoint": "https://cli.tax/zj7fTPVh4p",
|
|
7
5
|
"method": "POST",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
6
|
+
"name": "swarm",
|
|
7
|
+
"schemaVersion": "swarm.skill.request/1.0",
|
|
8
|
+
"type": "Skill",
|
|
9
|
+
"version": "v5.0.0"
|
|
10
10
|
}
|