@cmodjs/core 0.2.3 → 1.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/docs/commands.md +58 -10
- package/docs/dependencies.md +12 -12
- package/docs/hooks.md +41 -28
- package/docs/index.md +29 -25
- package/docs/install-steps.md +9 -7
- package/docs/jobs.md +59 -111
- package/docs/mod.md +200 -174
- package/docs/options.md +2 -2
- package/docs/permissions.md +27 -16
- package/docs/session.md +248 -0
- package/docs/state.md +11 -7
- package/docs/testing.md +66 -23
- package/docs/ui.md +108 -76
- package/jobs/check.d.ts.map +1 -1
- package/jobs/check.js +5 -2
- package/jobs/context.d.ts +7 -8
- package/jobs/context.d.ts.map +1 -1
- package/jobs/context.js +23 -73
- package/jobs/permissions/decide-permission.d.ts +5 -3
- package/jobs/permissions/decide-permission.d.ts.map +1 -1
- package/jobs/permissions/decide-permission.js +6 -4
- package/jobs/permissions/find-project-scope.d.ts +1 -1
- package/jobs/permissions/find-project-scope.d.ts.map +1 -1
- package/jobs/permissions/find-project-scope.js +8 -11
- package/jobs/permissions/match-target.d.ts +2 -2
- package/jobs/permissions/match-target.d.ts.map +1 -1
- package/jobs/permissions.d.ts +1 -1
- package/jobs/permissions.d.ts.map +1 -1
- package/jobs/permissions.js +6 -4
- package/jobs/program.d.ts.map +1 -1
- package/jobs/program.js +8 -3
- package/jobs/prompt.d.ts +4 -2
- package/jobs/prompt.d.ts.map +1 -1
- package/jobs/prompt.js +16 -15
- package/jobs/schedule.d.ts +2 -2
- package/jobs/schedule.d.ts.map +1 -1
- package/jobs/schedule.js +9 -9
- package/jobs/slash-command.d.ts +4 -4
- package/jobs/slash-command.d.ts.map +1 -1
- package/jobs/slash-command.js +14 -9
- package/jobs/status-line.d.ts +1 -11
- package/jobs/status-line.d.ts.map +1 -1
- package/jobs/status-line.js +8 -12
- package/jobs/tool.d.ts +2 -2
- package/jobs/tool.d.ts.map +1 -1
- package/jobs/tool.js +15 -9
- package/mod.d.ts +154 -75
- package/mod.d.ts.map +1 -1
- package/mod.js +94 -3
- package/options.d.ts +2 -2
- package/options.js +19 -19
- package/package.json +3 -3
- package/records.d.ts +10 -8
- package/records.d.ts.map +1 -1
- package/records.js +28 -17
- package/register.d.ts.map +1 -1
- package/register.js +18 -6
- package/runtime/announce.d.ts +18 -0
- package/runtime/announce.d.ts.map +1 -0
- package/runtime/announce.js +66 -0
- package/runtime/bound.d.ts +4 -0
- package/runtime/bound.d.ts.map +1 -0
- package/runtime/bound.js +9 -0
- package/runtime/claude.d.ts +3 -3
- package/runtime/claude.d.ts.map +1 -1
- package/runtime/conversations.d.ts +16 -0
- package/runtime/conversations.d.ts.map +1 -0
- package/runtime/conversations.js +291 -0
- package/runtime/deadline.d.ts +0 -1
- package/runtime/deadline.d.ts.map +1 -1
- package/runtime/deadline.js +1 -1
- package/runtime/dependencies.d.ts +2 -0
- package/runtime/dependencies.d.ts.map +1 -1
- package/runtime/dependencies.js +9 -0
- package/runtime/engine.d.ts +17 -0
- package/runtime/engine.d.ts.map +1 -0
- package/runtime/engine.js +10 -0
- package/runtime/grants.d.ts +4 -0
- package/runtime/grants.d.ts.map +1 -1
- package/runtime/grants.js +63 -33
- package/runtime/hooks.d.ts +5 -4
- package/runtime/hooks.d.ts.map +1 -1
- package/runtime/hooks.js +10 -5
- package/runtime/installer.d.ts.map +1 -1
- package/runtime/installer.js +11 -9
- package/runtime/lifecycle.d.ts +3 -1
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +317 -186
- package/runtime/metadata.d.ts +3 -1
- package/runtime/metadata.d.ts.map +1 -1
- package/runtime/metadata.js +21 -11
- package/runtime/options.d.ts +3 -1
- package/runtime/options.d.ts.map +1 -1
- package/runtime/options.js +4 -4
- package/runtime/programs.d.ts +8 -0
- package/runtime/programs.d.ts.map +1 -1
- package/runtime/programs.js +39 -1
- package/runtime/router.d.ts +2 -1
- package/runtime/router.d.ts.map +1 -1
- package/runtime/router.js +11 -3
- package/runtime/sessions.d.ts +36 -0
- package/runtime/sessions.d.ts.map +1 -0
- package/runtime/sessions.js +235 -0
- package/runtime/state.d.ts +6 -2
- package/runtime/state.d.ts.map +1 -1
- package/runtime/state.js +46 -13
- package/runtime/toaster.d.ts +31 -0
- package/runtime/toaster.d.ts.map +1 -0
- package/runtime/toaster.js +176 -0
- package/runtime/ui.d.ts +6 -17
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +36 -112
- package/session/context/context.d.ts +10 -0
- package/session/context/context.d.ts.map +1 -0
- package/session/context/context.js +2 -0
- package/session/context/turn/message/document.d.ts +7 -0
- package/session/context/turn/message/document.d.ts.map +1 -0
- package/session/context/turn/message/document.js +1 -0
- package/session/context/turn/message/message.d.ts +54 -0
- package/session/context/turn/message/message.d.ts.map +1 -0
- package/session/context/turn/message/message.js +137 -0
- package/session/context/turn/message/note.d.ts +8 -0
- package/session/context/turn/message/note.d.ts.map +1 -0
- package/session/context/turn/message/note.js +1 -0
- package/session/context/turn/message/notice.d.ts +6 -0
- package/session/context/turn/message/notice.d.ts.map +1 -0
- package/session/context/turn/message/notice.js +1 -0
- package/session/context/turn/message/prompt.d.ts +8 -0
- package/session/context/turn/message/prompt.d.ts.map +1 -0
- package/session/context/turn/message/prompt.js +1 -0
- package/session/context/turn/message/reply.d.ts +6 -0
- package/session/context/turn/message/reply.d.ts.map +1 -0
- package/session/context/turn/message/reply.js +1 -0
- package/session/context/turn/message/sender.d.ts +14 -0
- package/session/context/turn/message/sender.d.ts.map +1 -0
- package/session/context/turn/message/sender.js +30 -0
- package/session/context/turn/message/skill.d.ts +7 -0
- package/session/context/turn/message/skill.d.ts.map +1 -0
- package/session/context/turn/message/skill.js +1 -0
- package/session/context/turn/message/tool-call.d.ts +9 -0
- package/session/context/turn/message/tool-call.d.ts.map +1 -0
- package/session/context/turn/message/tool-call.js +1 -0
- package/session/context/turn/message/tool-result.d.ts +8 -0
- package/session/context/turn/message/tool-result.d.ts.map +1 -0
- package/session/context/turn/message/tool-result.js +1 -0
- package/session/context/turn/turn.d.ts +12 -0
- package/session/context/turn/turn.d.ts.map +1 -0
- package/session/context/turn/turn.js +2 -0
- package/session/conversation.d.ts +64 -0
- package/session/conversation.d.ts.map +1 -0
- package/session/conversation.js +123 -0
- package/session/goal/goal.d.ts +10 -0
- package/session/goal/goal.d.ts.map +1 -0
- package/session/goal/goal.js +1 -0
- package/session/goal/task.d.ts +16 -0
- package/session/goal/task.d.ts.map +1 -0
- package/session/goal/task.js +3 -0
- package/session/session.d.ts +99 -0
- package/session/session.d.ts.map +1 -0
- package/session/session.js +1 -0
- package/src/jobs/check.ts +7 -4
- package/src/jobs/context.ts +24 -82
- package/src/jobs/permissions/decide-permission.ts +10 -8
- package/src/jobs/permissions/find-project-scope.ts +8 -14
- package/src/jobs/permissions/match-target.ts +3 -3
- package/src/jobs/permissions.ts +8 -6
- package/src/jobs/program.ts +7 -3
- package/src/jobs/prompt.ts +21 -24
- package/src/jobs/schedule.ts +14 -14
- package/src/jobs/slash-command.ts +20 -14
- package/src/jobs/status-line.ts +9 -21
- package/src/jobs/tool.ts +17 -11
- package/src/mod.ts +209 -97
- package/src/options.ts +22 -22
- package/src/records.ts +35 -23
- package/src/register.ts +18 -6
- package/src/runtime/announce.ts +74 -0
- package/src/runtime/bound.ts +12 -0
- package/src/runtime/claude.ts +5 -3
- package/src/runtime/conversations.ts +302 -0
- package/src/runtime/deadline.ts +2 -2
- package/src/runtime/dependencies.ts +12 -0
- package/src/runtime/engine.ts +22 -0
- package/src/runtime/grants.ts +66 -31
- package/src/runtime/hooks.ts +19 -10
- package/src/runtime/installer.ts +10 -9
- package/src/runtime/lifecycle.ts +313 -181
- package/src/runtime/metadata.ts +33 -11
- package/src/runtime/options.ts +19 -4
- package/src/runtime/programs.ts +40 -1
- package/src/runtime/router.ts +11 -4
- package/src/runtime/sessions.ts +242 -0
- package/src/runtime/state.ts +50 -14
- package/src/runtime/toaster.ts +218 -0
- package/src/runtime/ui.ts +42 -139
- package/src/session/context/context.ts +12 -0
- package/src/session/context/turn/message/document.ts +8 -0
- package/src/session/context/turn/message/message.ts +165 -0
- package/src/session/context/turn/message/note.ts +9 -0
- package/src/session/context/turn/message/notice.ts +7 -0
- package/src/session/context/turn/message/prompt.ts +9 -0
- package/src/session/context/turn/message/reply.ts +7 -0
- package/src/session/context/turn/message/sender.ts +38 -0
- package/src/session/context/turn/message/skill.ts +8 -0
- package/src/session/context/turn/message/tool-call.ts +10 -0
- package/src/session/context/turn/message/tool-result.ts +9 -0
- package/src/session/context/turn/turn.ts +14 -0
- package/src/session/conversation.ts +169 -0
- package/src/session/goal/goal.ts +9 -0
- package/src/session/goal/task.ts +19 -0
- package/src/session/session.ts +94 -0
- package/src/testing/fake-claude.ts +23 -18
- package/src/testing/fake-elements.ts +3 -2
- package/src/testing/fake-files.ts +1 -1
- package/src/testing/fake-sessions.ts +203 -0
- package/src/testing.ts +112 -33
- package/src/ui/components.ts +3 -3
- package/src/ui/elements.ts +22 -8
- package/src/ui/markdown.ts +22 -26
- package/src/ui/places.ts +49 -0
- package/src/utils/call-effects.ts +3 -3
- package/src/utils/changelog.ts +32 -0
- package/src/utils/matcher.ts +37 -0
- package/src/utils/metadata.ts +17 -15
- package/src/utils/parse-shell.ts +24 -16
- package/src/utils/paths.ts +15 -1
- package/src/utils/text.ts +4 -0
- package/testing/fake-claude.d.ts +4 -2
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +18 -12
- package/testing/fake-elements.js +4 -2
- package/testing/fake-files.js +1 -1
- package/testing/fake-sessions.d.ts +74 -0
- package/testing/fake-sessions.d.ts.map +1 -0
- package/testing/fake-sessions.js +170 -0
- package/testing.d.ts +33 -13
- package/testing.d.ts.map +1 -1
- package/testing.js +92 -21
- package/ui/components.d.ts +2 -3
- package/ui/components.d.ts.map +1 -1
- package/ui/components.js +3 -3
- package/ui/elements.d.ts.map +1 -1
- package/ui/elements.js +19 -9
- package/ui/markdown.d.ts +35 -33
- package/ui/markdown.d.ts.map +1 -1
- package/ui/markdown.js +14 -16
- package/ui/places.d.ts +43 -0
- package/ui/places.d.ts.map +1 -0
- package/ui/places.js +31 -0
- package/utils/call-effects.d.ts +1 -1
- package/utils/call-effects.d.ts.map +1 -1
- package/utils/call-effects.js +2 -2
- package/utils/changelog.d.ts +9 -0
- package/utils/changelog.d.ts.map +1 -0
- package/utils/changelog.js +29 -0
- package/utils/matcher.d.ts +13 -0
- package/utils/matcher.d.ts.map +1 -0
- package/utils/matcher.js +32 -0
- package/utils/metadata.d.ts +3 -3
- package/utils/metadata.js +18 -14
- package/utils/parse-shell.d.ts +1 -0
- package/utils/parse-shell.d.ts.map +1 -1
- package/utils/parse-shell.js +22 -13
- package/utils/paths.d.ts +2 -0
- package/utils/paths.d.ts.map +1 -1
- package/utils/paths.js +13 -1
- package/utils/text.d.ts +1 -0
- package/utils/text.d.ts.map +1 -1
- package/utils/text.js +3 -0
- package/src/ui/slots.ts +0 -36
- package/ui/slots.d.ts +0 -208
- package/ui/slots.d.ts.map +0 -1
- package/ui/slots.js +0 -19
package/src/jobs/schedule.ts
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
import type { Job } from '../mod.js'
|
|
2
2
|
import { onStateChange } from '../runtime/state.js'
|
|
3
|
-
import {
|
|
3
|
+
import { engineOf } from '../runtime/engine.js'
|
|
4
4
|
|
|
5
|
-
export type
|
|
5
|
+
export type ScheduledPrompt = { readonly cron: string; readonly prompt: string }
|
|
6
6
|
|
|
7
7
|
const scheduleCounts = new WeakMap<object, number>()
|
|
8
8
|
|
|
9
|
-
export function schedule<State extends object = Record<never, never>>(entry:
|
|
9
|
+
export function schedule<State extends object = Record<never, never>>(entry: ScheduledPrompt | ((state: State) => ScheduledPrompt | undefined)): Job<void, State> {
|
|
10
10
|
if (typeof entry !== 'function') checked(entry)
|
|
11
11
|
|
|
12
12
|
return (job) => {
|
|
13
13
|
const { mod } = job
|
|
14
|
-
const { claude } = mod
|
|
14
|
+
const { claude, report: reportFailure } = engineOf(mod)
|
|
15
15
|
const order = scheduleCounts.get(mod) ?? 0
|
|
16
16
|
scheduleCounts.set(mod, order + 1)
|
|
17
17
|
const storeKey = `cmod:schedule:${order}`
|
|
18
|
-
const
|
|
18
|
+
const report = (message: string, error: unknown) => reportFailure({ id: storeKey, message: `The schedule ${message}`, error })
|
|
19
19
|
let current: { readonly id: string; readonly wanted: string } | undefined
|
|
20
20
|
let isQueued = false
|
|
21
21
|
|
|
22
22
|
const remove = async (id: string) => {
|
|
23
|
-
await claude.tool.call({ tool: 'CronDelete', id }).catch(() =>
|
|
23
|
+
await claude.tool.call({ tool: 'CronDelete', id }).catch((error: unknown) => report(`could not delete its last cron ${id}`, error))
|
|
24
24
|
await claude.store.delete(storeKey)
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const update = async () => {
|
|
28
28
|
isQueued = false
|
|
29
|
-
let next:
|
|
29
|
+
let next: ScheduledPrompt | undefined
|
|
30
30
|
try {
|
|
31
|
-
next = typeof entry === 'function' ? entry(mod.state
|
|
31
|
+
next = typeof entry === 'function' ? entry(mod.state) : entry
|
|
32
32
|
if (next !== undefined) checked(next)
|
|
33
33
|
} catch (error) {
|
|
34
|
-
|
|
34
|
+
report('kept its last cron', error)
|
|
35
35
|
return
|
|
36
36
|
}
|
|
37
37
|
const wanted = next === undefined ? undefined : JSON.stringify([next.cron, next.prompt])
|
|
@@ -40,8 +40,8 @@ export function schedule<State extends object = Record<never, never>>(entry: Sch
|
|
|
40
40
|
current = undefined
|
|
41
41
|
if (next === undefined) return
|
|
42
42
|
const answer = await claude.tool.call({ tool: 'CronCreate', cron: next.cron, prompt: next.prompt, recurring: true })
|
|
43
|
-
if (answer.deny !== undefined) return
|
|
44
|
-
if (answer.isError === true) return
|
|
43
|
+
if (answer.deny !== undefined) return report('was refused', answer.deny)
|
|
44
|
+
if (answer.isError === true) return report('failed', answer.text ?? 'CronCreate answered an error')
|
|
45
45
|
current = { id: answer.result.id, wanted: wanted as string }
|
|
46
46
|
await claude.store.set(storeKey, current.id)
|
|
47
47
|
}
|
|
@@ -50,17 +50,17 @@ export function schedule<State extends object = Record<never, never>>(entry: Sch
|
|
|
50
50
|
.get(storeKey)
|
|
51
51
|
.then((left) => (typeof left === 'string' ? remove(left) : undefined))
|
|
52
52
|
.then(update)
|
|
53
|
-
.catch((error: unknown) =>
|
|
53
|
+
.catch((error: unknown) => report('failed', error))
|
|
54
54
|
onStateChange(mod.state, () => {
|
|
55
55
|
if (isQueued) return
|
|
56
56
|
isQueued = true
|
|
57
|
-
updates = updates.then(update).catch((error: unknown) =>
|
|
57
|
+
updates = updates.then(update).catch((error: unknown) => report('failed', error))
|
|
58
58
|
})
|
|
59
59
|
job.announce(typeof entry === 'function' ? 'a schedule from its state' : `a schedule at ${entry.cron}`)
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function checked(entry:
|
|
63
|
+
function checked(entry: ScheduledPrompt): void {
|
|
64
64
|
if (entry.cron.trim() === '') throw new Error('schedule: give it a cron, such as "*/10 * * * *" for every 10 minutes.')
|
|
65
65
|
if (entry.prompt.trim() === '') throw new Error('schedule: give it the prompt Claude gets at each fire.')
|
|
66
66
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { CommandRunResult } from 'claude-code'
|
|
2
|
-
import type { Job, Mod } from '../mod.js'
|
|
2
|
+
import type { Disposable, Job, Mod } from '../mod.js'
|
|
3
3
|
import { parse } from '../vendor.js'
|
|
4
|
-
import {
|
|
4
|
+
import type { Deadline } from '../runtime/deadline.js'
|
|
5
|
+
import { engineOf } from '../runtime/engine.js'
|
|
5
6
|
import { messageOf } from '../utils/text.js'
|
|
6
|
-
import {
|
|
7
|
+
import { withinDeadline } from './context.js'
|
|
7
8
|
|
|
8
|
-
export type
|
|
9
|
+
export type CommandReply = string | { text?: string; context?: string } | undefined
|
|
9
10
|
|
|
10
|
-
const deadline: Deadline = { ms: 30000,
|
|
11
|
+
const deadline: Deadline = { ms: 30000, job: 'slashCommand' }
|
|
11
12
|
|
|
12
13
|
const commandName = /^[A-Za-z0-9_-]{1,64}$/
|
|
13
14
|
|
|
@@ -16,14 +17,14 @@ export function slashCommand<State extends object = Record<never, never>>(option
|
|
|
16
17
|
readonly description: string
|
|
17
18
|
readonly argumentHint?: string
|
|
18
19
|
readonly immediate?: true
|
|
19
|
-
readonly reply: (input: { args: string; positionals: string[] }, mod: Mod<State>) =>
|
|
20
|
-
}): Job<
|
|
20
|
+
readonly reply: (input: { args: string; positionals: string[] }, mod: Mod<State>) => CommandReply | void | Promise<CommandReply | void>
|
|
21
|
+
}): Job<Disposable, State> {
|
|
21
22
|
const { name, description, argumentHint, immediate, reply } = options
|
|
22
23
|
return (job) => {
|
|
23
24
|
const { mod, announce, reserveName } = job
|
|
24
|
-
const { claude } = mod
|
|
25
|
+
const { claude, router, report } = engineOf(mod)
|
|
25
26
|
if (!commandName.test(name)) throw new Error(`${mod.name}: "${name}" is not a slash command name. Use 1 to 64 letters, digits, "_", or "-", without the slash.`)
|
|
26
|
-
reserveName('slashCommand', name, `${mod.name}: the slash command /${name} is already added. Give each slashCommand its own name.`)
|
|
27
|
+
const reserved = reserveName('slashCommand', name, `${mod.name}: the slash command /${name} is already added. Give each slashCommand its own name.`)
|
|
27
28
|
|
|
28
29
|
let registered: string | undefined
|
|
29
30
|
claude.command.register({ name, description, ...(argumentHint === undefined ? {} : { argumentHint }), ...(immediate === undefined ? {} : { immediate }) }).then(
|
|
@@ -31,24 +32,29 @@ export function slashCommand<State extends object = Record<never, never>>(option
|
|
|
31
32
|
registered = command
|
|
32
33
|
},
|
|
33
34
|
(error: unknown) => {
|
|
34
|
-
|
|
35
|
+
report({ id: `command:${name}`, message: `/${name} is not added`, error })
|
|
35
36
|
},
|
|
36
37
|
)
|
|
37
38
|
announce(`/${name}`)
|
|
38
39
|
|
|
39
|
-
const
|
|
40
|
-
claude.on('command.run', async (e, next) => {
|
|
40
|
+
const answering = router.add('command.run', async (e, next) => {
|
|
41
41
|
if (e.command !== registered) return next(e)
|
|
42
42
|
try {
|
|
43
|
-
return answerOf(await reply({ args: e.args, positionals: splitWords(e.args) },
|
|
43
|
+
return answerOf(await withinDeadline(mod, deadline, `/${name}`, () => reply({ args: e.args, positionals: splitWords(e.args) }, mod)))
|
|
44
44
|
} catch (error) {
|
|
45
45
|
return { text: `/${name} failed: ${oneLine(error)}` }
|
|
46
46
|
}
|
|
47
47
|
})
|
|
48
|
+
return {
|
|
49
|
+
dispose() {
|
|
50
|
+
answering.dispose()
|
|
51
|
+
reserved.dispose()
|
|
52
|
+
},
|
|
53
|
+
}
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
function answerOf(reply:
|
|
57
|
+
function answerOf(reply: CommandReply | void): CommandRunResult {
|
|
52
58
|
if (reply === undefined) return {}
|
|
53
59
|
if (typeof reply === 'string') return { text: reply }
|
|
54
60
|
return { ...(reply.text === undefined ? {} : { text: reply.text }), ...(reply.context === undefined ? {} : { context: [reply.context] }) }
|
package/src/jobs/status-line.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import type { Job, Mod } from '../mod.js'
|
|
2
2
|
import type { Deadline } from '../runtime/deadline.js'
|
|
3
|
+
import { engineOf } from '../runtime/engine.js'
|
|
4
|
+
import { usageOf } from '../runtime/sessions.js'
|
|
5
|
+
import type { Usage } from '../session/session.js'
|
|
3
6
|
import { messageOf } from '../utils/text.js'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
export type Usage = {
|
|
7
|
-
model: string
|
|
8
|
-
context: { tokens?: number; window: number; percent?: number }
|
|
9
|
-
cost?: { usd: number }
|
|
10
|
-
}
|
|
7
|
+
import { withinDeadline } from './context.js'
|
|
11
8
|
|
|
12
9
|
const deadline: Deadline = { ms: 2000, job: 'statusLine' }
|
|
13
10
|
|
|
@@ -18,30 +15,21 @@ export function statusLine<State extends object = Record<never, never>>(options:
|
|
|
18
15
|
const { text, interval = 10000 } = options
|
|
19
16
|
return (job) => {
|
|
20
17
|
const { mod, announce, reserveName } = job
|
|
21
|
-
const { claude } = mod
|
|
18
|
+
const { claude, router } = engineOf(mod)
|
|
22
19
|
if (!(interval >= 1)) throw new Error(`${mod.name}: the status line interval is ${interval}. Give it in milliseconds, 1 or more.`)
|
|
23
20
|
reserveName('statusLine', '', `${mod.name}: a status line is already added. A mod has one status line: join the texts in one statusLine.`)
|
|
24
21
|
|
|
25
|
-
const textMod = modWithin(job, deadline)
|
|
26
22
|
let shown: string | undefined
|
|
27
23
|
let queue = Promise.resolve()
|
|
28
24
|
const update = async () => {
|
|
29
25
|
try {
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const next = await text(
|
|
33
|
-
{
|
|
34
|
-
model,
|
|
35
|
-
context: { window, ...(tokens === undefined ? {} : { tokens }), ...(percent === undefined ? {} : { percent }) },
|
|
36
|
-
...(usage.cost === undefined ? {} : { cost: { usd: usage.cost.usd } }),
|
|
37
|
-
},
|
|
38
|
-
textMod,
|
|
39
|
-
)
|
|
26
|
+
const usage = await usageOf(claude)
|
|
27
|
+
const next = await withinDeadline(mod, deadline, 'the status line', () => text(usage, mod))
|
|
40
28
|
if (next === shown) return
|
|
41
29
|
shown = next
|
|
42
30
|
claude.ui.status(next)
|
|
43
31
|
} catch (error) {
|
|
44
|
-
|
|
32
|
+
mod.log(`the status line kept its last text: ${messageOf(error)}`)
|
|
45
33
|
}
|
|
46
34
|
}
|
|
47
35
|
const refresh = () => {
|
|
@@ -49,7 +37,7 @@ export function statusLine<State extends object = Record<never, never>>(options:
|
|
|
49
37
|
}
|
|
50
38
|
|
|
51
39
|
announce('a status line')
|
|
52
|
-
|
|
40
|
+
router.add('session.measure', (e, next) => {
|
|
53
41
|
refresh()
|
|
54
42
|
return next(e)
|
|
55
43
|
})
|
package/src/jobs/tool.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { FromSchema, JSONSchema } from 'json-schema-to-ts'
|
|
2
|
-
import type { Job, Mod } from '../mod.js'
|
|
2
|
+
import type { Disposable, Job, Mod } from '../mod.js'
|
|
3
3
|
import { Validator } from '../vendor.js'
|
|
4
4
|
import { toolDeadline } from '../runtime/deadline.js'
|
|
5
|
+
import { engineOf } from '../runtime/engine.js'
|
|
5
6
|
import { reservedKeys, toolInputOf } from '../runtime/tool-calls.js'
|
|
6
7
|
import { messageOf } from '../utils/text.js'
|
|
7
|
-
import {
|
|
8
|
+
import { withinDeadline } from './context.js'
|
|
8
9
|
|
|
9
10
|
const toolName = /^[A-Za-z0-9_-]{1,64}$/
|
|
10
11
|
|
|
@@ -13,42 +14,47 @@ export function tool<const Schema extends object = { type: 'object' }, State ext
|
|
|
13
14
|
readonly description: string
|
|
14
15
|
readonly inputSchema?: Schema
|
|
15
16
|
readonly execute: (input: Schema extends JSONSchema ? FromSchema<Schema> : never, mod: Mod<State>) => unknown
|
|
16
|
-
}): Job<{ readonly name: string }, State> {
|
|
17
|
+
}): Job<Disposable & { readonly name: string }, State> {
|
|
17
18
|
const { name, description, inputSchema, execute } = options
|
|
18
19
|
return (job) => {
|
|
19
20
|
const { mod, announce, reserveName } = job
|
|
20
|
-
const { claude } = mod
|
|
21
|
+
const { claude, router, report } = engineOf(mod)
|
|
21
22
|
if (!toolName.test(name)) throw new Error(`${mod.name}: "${name}" is not a tool name. Use 1 to 64 letters, digits, "_", or "-".`)
|
|
22
23
|
const properties = (inputSchema as { properties?: object } | undefined)?.properties ?? {}
|
|
23
24
|
const reserved = reservedKeys.filter((key) => Object.hasOwn(properties, key))
|
|
24
25
|
if (reserved.length > 0) throw new Error(`${mod.name}: the ${name} tool's inputSchema has the property ${reserved.join(', ')}, which Claude Code keeps for itself. Rename it.`)
|
|
25
|
-
reserveName('tool', name, `${mod.name}: the tool ${name} is already added. Give each tool its own name.`)
|
|
26
|
+
const named = reserveName('tool', name, `${mod.name}: the tool ${name} is already added. Give each tool its own name.`)
|
|
26
27
|
|
|
27
|
-
const validator = new Validator(
|
|
28
|
+
const validator = new Validator(inputSchema ?? { type: 'object' }, '2020-12', false)
|
|
28
29
|
let registered: string | undefined
|
|
29
30
|
claude.tool.register({ name, description, ...(inputSchema === undefined ? {} : { inputSchema: inputSchema as Record<string, unknown> }) }).then(
|
|
30
31
|
(answer) => {
|
|
31
32
|
registered = answer.tool
|
|
32
33
|
},
|
|
33
34
|
(error: unknown) => {
|
|
34
|
-
|
|
35
|
+
report({ id: `tool:${name}`, message: `The ${name} tool is not added`, error })
|
|
35
36
|
},
|
|
36
37
|
)
|
|
37
38
|
announce(`the ${name} tool`)
|
|
38
39
|
|
|
39
|
-
const
|
|
40
|
-
claude.on('tool.call', async (e, next) => {
|
|
40
|
+
const answering = router.add('tool.call', async (e, next) => {
|
|
41
41
|
if (e.tool !== registered) return next(e)
|
|
42
42
|
const input = toolInputOf(e)
|
|
43
43
|
const { valid, errors } = validator.validate(input)
|
|
44
44
|
if (!valid) return { deny: `The ${name} tool input is not valid:\n${errors.map((error) => `- ${error.instanceLocation}: ${error.error}`).join('\n')}` }
|
|
45
45
|
try {
|
|
46
|
-
return { result: resultOf(await execute(input as Parameters<typeof execute>[0],
|
|
46
|
+
return { result: resultOf(await withinDeadline(mod, toolDeadline, `the ${name} tool`, () => execute(input as Parameters<typeof execute>[0], mod))) }
|
|
47
47
|
} catch (error) {
|
|
48
48
|
return { deny: `The ${name} tool failed: ${messageOf(error)}` }
|
|
49
49
|
}
|
|
50
50
|
})
|
|
51
|
-
return {
|
|
51
|
+
return {
|
|
52
|
+
name: `mcp__${claude.plugin.name}__${name}`,
|
|
53
|
+
dispose() {
|
|
54
|
+
answering.dispose()
|
|
55
|
+
named.dispose()
|
|
56
|
+
},
|
|
57
|
+
}
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
60
|
|