incur 0.3.14 → 0.3.16
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/dist/Cli.d.ts.map +1 -1
- package/dist/Cli.js +55 -4
- package/dist/Cli.js.map +1 -1
- package/dist/Fetch.d.ts.map +1 -1
- package/dist/Fetch.js +10 -9
- package/dist/Fetch.js.map +1 -1
- package/dist/Formatter.d.ts.map +1 -1
- package/dist/Formatter.js +6 -1
- package/dist/Formatter.js.map +1 -1
- package/dist/Help.d.ts +1 -1
- package/dist/Help.d.ts.map +1 -1
- package/dist/Help.js +4 -3
- package/dist/Help.js.map +1 -1
- package/dist/Openapi.js +20 -12
- package/dist/Openapi.js.map +1 -1
- package/dist/Parser.d.ts +2 -0
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +1 -1
- package/dist/Parser.js.map +1 -1
- package/dist/Schema.d.ts +5 -1
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +13 -2
- package/dist/Schema.js.map +1 -1
- package/dist/Skill.d.ts.map +1 -1
- package/dist/Skill.js +1 -1
- package/dist/Skill.js.map +1 -1
- package/dist/SyncSkills.d.ts +24 -0
- package/dist/SyncSkills.d.ts.map +1 -1
- package/dist/SyncSkills.js +46 -0
- package/dist/SyncSkills.js.map +1 -1
- package/dist/internal/command.d.ts +4 -2
- package/dist/internal/command.d.ts.map +1 -1
- package/dist/internal/command.js +4 -0
- package/dist/internal/command.js.map +1 -1
- package/package.json +4 -1
- package/src/Cli.test.ts +52 -3
- package/src/Cli.ts +62 -4
- package/src/Fetch.test.ts +21 -0
- package/src/Fetch.ts +8 -10
- package/src/Formatter.test.ts +15 -2
- package/src/Formatter.ts +5 -1
- package/src/Help.test.ts +39 -1
- package/src/Help.ts +6 -5
- package/src/Openapi.test.ts +7 -0
- package/src/Openapi.ts +19 -13
- package/src/Parser.ts +1 -1
- package/src/Schema.test.ts +29 -0
- package/src/Schema.ts +12 -2
- package/src/Skill.ts +2 -1
- package/src/SyncSkills.test.ts +63 -0
- package/src/SyncSkills.ts +78 -0
- package/src/e2e.test.ts +2 -2
- package/src/internal/command.ts +4 -0
package/src/Schema.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Converts a Zod schema to a JSON Schema object. Strips the `$schema`
|
|
5
|
+
* meta-property. Represents bigints and dates as `{ type: "string" }`
|
|
6
|
+
* since JSON lacks native types for them.
|
|
7
|
+
*/
|
|
4
8
|
export function toJsonSchema(schema: z.ZodType): Record<string, unknown> {
|
|
5
|
-
const result = z.toJSONSchema(schema
|
|
9
|
+
const result = z.toJSONSchema(schema, {
|
|
10
|
+
unrepresentable: 'any',
|
|
11
|
+
override: (ctx) => {
|
|
12
|
+
const type = ctx.zodSchema._zod?.def?.type
|
|
13
|
+
if (type === 'bigint' || type === 'date') ctx.jsonSchema.type = 'string'
|
|
14
|
+
},
|
|
15
|
+
}) as Record<string, unknown>
|
|
6
16
|
delete result.$schema
|
|
7
17
|
return result
|
|
8
18
|
}
|
package/src/Skill.ts
CHANGED
|
@@ -133,7 +133,8 @@ function renderGroup(
|
|
|
133
133
|
const childDescs = cmds.map((c) => c.description).filter(Boolean) as string[]
|
|
134
134
|
const descParts: string[] = []
|
|
135
135
|
if (groupDesc) descParts.push(groupDesc.replace(/\.$/, ''))
|
|
136
|
-
if (childDescs.length > 0)
|
|
136
|
+
if (childDescs.length > 0)
|
|
137
|
+
descParts.push(childDescs.map((d) => d.replace(/\.$/, '')).join(', '))
|
|
137
138
|
const description =
|
|
138
139
|
descParts.length > 0
|
|
139
140
|
? `${descParts.join('. ')}. Run \`${title} --help\` for usage details.`
|
package/src/SyncSkills.test.ts
CHANGED
|
@@ -125,3 +125,66 @@ test('installed SKILL.md contains frontmatter', async () => {
|
|
|
125
125
|
|
|
126
126
|
rmSync(tmp, { recursive: true, force: true })
|
|
127
127
|
})
|
|
128
|
+
|
|
129
|
+
test('list returns skills from command map', async () => {
|
|
130
|
+
const cli = Cli.create('test', { description: 'A test CLI' })
|
|
131
|
+
cli.command('ping', { description: 'Health check', run: () => ({}) })
|
|
132
|
+
cli.command('greet', { description: 'Say hello', run: () => ({}) })
|
|
133
|
+
|
|
134
|
+
const commands = Cli.toCommands.get(cli)!
|
|
135
|
+
const result = await SyncSkills.list('test', commands)
|
|
136
|
+
|
|
137
|
+
expect(result.length).toBeGreaterThan(0)
|
|
138
|
+
const names = result.map((s) => s.name)
|
|
139
|
+
expect(names).toContain('test-ping')
|
|
140
|
+
expect(names).toContain('test-greet')
|
|
141
|
+
for (const s of result) {
|
|
142
|
+
expect(s.installed).toBe(false)
|
|
143
|
+
expect(s.description).toBeDefined()
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test('list shows installed status after sync', async () => {
|
|
148
|
+
const tmp = join(tmpdir(), `clac-list-test-${Date.now()}`)
|
|
149
|
+
mkdirSync(tmp, { recursive: true })
|
|
150
|
+
process.env.XDG_DATA_HOME = tmp
|
|
151
|
+
|
|
152
|
+
const cli = Cli.create('test')
|
|
153
|
+
cli.command('ping', { description: 'Ping', run: () => ({}) })
|
|
154
|
+
|
|
155
|
+
const commands = Cli.toCommands.get(cli)!
|
|
156
|
+
const installDir = join(tmp, 'install')
|
|
157
|
+
mkdirSync(join(installDir, '.agents', 'skills'), { recursive: true })
|
|
158
|
+
|
|
159
|
+
// Sync first to install
|
|
160
|
+
await SyncSkills.sync('test', commands, {
|
|
161
|
+
global: false,
|
|
162
|
+
cwd: installDir,
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
// Now list should show installed
|
|
166
|
+
const result = await SyncSkills.list('test', commands)
|
|
167
|
+
expect(result.length).toBeGreaterThan(0)
|
|
168
|
+
for (const s of result) expect(s.installed).toBe(true)
|
|
169
|
+
|
|
170
|
+
rmSync(tmp, { recursive: true, force: true })
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('list returns empty for CLI with no commands', async () => {
|
|
174
|
+
const cli = Cli.create('empty')
|
|
175
|
+
const commands = Cli.toCommands.get(cli)!
|
|
176
|
+
const result = await SyncSkills.list('empty', commands)
|
|
177
|
+
expect(result).toHaveLength(0)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test('list results are sorted alphabetically', async () => {
|
|
181
|
+
const cli = Cli.create('test')
|
|
182
|
+
cli.command('zebra', { description: 'Z command', run: () => ({}) })
|
|
183
|
+
cli.command('alpha', { description: 'A command', run: () => ({}) })
|
|
184
|
+
cli.command('middle', { description: 'M command', run: () => ({}) })
|
|
185
|
+
|
|
186
|
+
const commands = Cli.toCommands.get(cli)!
|
|
187
|
+
const result = await SyncSkills.list('test', commands)
|
|
188
|
+
const names = result.map((s) => s.name)
|
|
189
|
+
expect(names).toEqual([...names].sort())
|
|
190
|
+
})
|
package/src/SyncSkills.ts
CHANGED
|
@@ -125,6 +125,84 @@ export declare namespace sync {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/** Lists skills derived from a CLI's command map with install status. */
|
|
129
|
+
export async function list(
|
|
130
|
+
name: string,
|
|
131
|
+
commands: Map<string, any>,
|
|
132
|
+
options: list.Options = {},
|
|
133
|
+
): Promise<list.Skill[]> {
|
|
134
|
+
const { depth = 1, description } = options
|
|
135
|
+
const cwd = options.cwd ?? process.cwd()
|
|
136
|
+
|
|
137
|
+
const groups = new Map<string, string>()
|
|
138
|
+
if (description) groups.set(name, description)
|
|
139
|
+
const entries = collectEntries(commands, [], groups)
|
|
140
|
+
const files = Skill.split(name, entries, depth, groups)
|
|
141
|
+
|
|
142
|
+
const skills: list.Skill[] = []
|
|
143
|
+
const meta = readMeta(name)
|
|
144
|
+
const installed = new Set(meta?.skills)
|
|
145
|
+
|
|
146
|
+
for (const file of files) {
|
|
147
|
+
const nameMatch = file.content.match(/^name:\s*(.+)$/m)
|
|
148
|
+
const descMatch = file.content.match(/^description:\s*(.+)$/m)
|
|
149
|
+
const skillName = nameMatch?.[1] ?? (file.dir || name)
|
|
150
|
+
skills.push({
|
|
151
|
+
name: skillName,
|
|
152
|
+
description: descMatch?.[1],
|
|
153
|
+
installed: installed.has(skillName),
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Include additional SKILL.md files matched by glob patterns
|
|
158
|
+
if (options.include) {
|
|
159
|
+
for (const pattern of options.include) {
|
|
160
|
+
const globPattern = pattern === '_root' ? 'SKILL.md' : path.join(pattern, 'SKILL.md')
|
|
161
|
+
for await (const match of fs.glob(globPattern, { cwd })) {
|
|
162
|
+
try {
|
|
163
|
+
const content = await fs.readFile(path.resolve(cwd, match), 'utf8')
|
|
164
|
+
const nameMatch = content.match(/^name:\s*(.+)$/m)
|
|
165
|
+
const skillName =
|
|
166
|
+
pattern === '_root' ? (nameMatch?.[1] ?? name) : path.basename(path.dirname(match))
|
|
167
|
+
if (!skills.some((s) => s.name === skillName)) {
|
|
168
|
+
const descMatch = content.match(/^description:\s*(.+)$/m)
|
|
169
|
+
skills.push({
|
|
170
|
+
name: skillName,
|
|
171
|
+
description: descMatch?.[1],
|
|
172
|
+
installed: installed.has(skillName),
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
} catch {}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return skills.sort((a, b) => a.name.localeCompare(b.name))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export declare namespace list {
|
|
184
|
+
/** Options for listing skills. */
|
|
185
|
+
type Options = {
|
|
186
|
+
/** Working directory for resolving `include` globs. Defaults to `process.cwd()`. */
|
|
187
|
+
cwd?: string | undefined
|
|
188
|
+
/** Grouping depth for skill files. Defaults to `1`. */
|
|
189
|
+
depth?: number | undefined
|
|
190
|
+
/** CLI description, used as the top-level group description. */
|
|
191
|
+
description?: string | undefined
|
|
192
|
+
/** Glob patterns for directories containing SKILL.md files to include. */
|
|
193
|
+
include?: string[] | undefined
|
|
194
|
+
}
|
|
195
|
+
/** A skill entry with install status. */
|
|
196
|
+
type Skill = {
|
|
197
|
+
/** Description extracted from the skill frontmatter. */
|
|
198
|
+
description?: string | undefined
|
|
199
|
+
/** Whether this skill is currently installed. */
|
|
200
|
+
installed: boolean
|
|
201
|
+
/** Skill name. */
|
|
202
|
+
name: string
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
128
206
|
/** Recursively collects leaf commands as `Skill.CommandInfo`. */
|
|
129
207
|
function collectEntries(
|
|
130
208
|
commands: Map<string, any>,
|
package/src/e2e.test.ts
CHANGED
|
@@ -967,7 +967,7 @@ describe('help', () => {
|
|
|
967
967
|
Integrations:
|
|
968
968
|
completions Generate shell completion script
|
|
969
969
|
mcp add Register as MCP server
|
|
970
|
-
skills
|
|
970
|
+
skills Sync skill files to agents (add, list)
|
|
971
971
|
|
|
972
972
|
Global Options:
|
|
973
973
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
|
@@ -1742,7 +1742,7 @@ describe('root command with subcommands', () => {
|
|
|
1742
1742
|
Integrations:
|
|
1743
1743
|
completions Generate shell completion script
|
|
1744
1744
|
mcp add Register as MCP server
|
|
1745
|
-
skills
|
|
1745
|
+
skills Sync skill files to agents (add, list)
|
|
1746
1746
|
|
|
1747
1747
|
Global Options:
|
|
1748
1748
|
--filter-output <keys> Filter output by key paths (e.g. foo,bar.baz,a[0,3])
|
package/src/internal/command.ts
CHANGED