kimaki 1.0.0 → 1.2.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/dist/autostart.js +15 -3
- package/dist/autostart.js.map +1 -1
- package/dist/bot.d.ts +7 -5
- package/dist/bot.js.map +1 -1
- package/dist/builtin-skills.d.ts +18 -0
- package/dist/builtin-skills.js +51 -0
- package/dist/builtin-skills.js.map +1 -0
- package/dist/builtin-skills.test.d.ts +1 -0
- package/dist/builtin-skills.test.js +28 -0
- package/dist/builtin-skills.test.js.map +1 -0
- package/dist/cli/bot.js +10 -0
- package/dist/cli/bot.js.map +1 -1
- package/dist/cli/discord.js +1 -1
- package/dist/cli/discord.js.map +1 -1
- package/dist/cli/session.js +17 -53
- package/dist/cli/session.js.map +1 -1
- package/dist/cli-discord.e2e.test.js +19 -1
- package/dist/cli-discord.e2e.test.js.map +1 -1
- package/dist/cli-lock.e2e.test.js +7 -6
- package/dist/cli-lock.e2e.test.js.map +1 -1
- package/dist/cli-read.e2e.test.js +8 -3
- package/dist/cli-read.e2e.test.js.map +1 -1
- package/dist/cli-sessions.e2e.test.js +2 -4
- package/dist/cli-sessions.e2e.test.js.map +1 -1
- package/dist/commands/login-commands.d.ts +1 -1
- package/dist/commands/preference-commands.d.ts +1 -1
- package/dist/commands/preference-commands.js +17 -3
- package/dist/commands/preference-commands.js.map +1 -1
- package/dist/commands/worktree-commands.d.ts +1 -1
- package/dist/effects.js +30 -6
- package/dist/effects.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/event-loop.d.ts +2 -1
- package/dist/event-loop.js +5 -1
- package/dist/event-loop.js.map +1 -1
- package/dist/ingress.d.ts +1 -1
- package/dist/ingress.js +14 -11
- package/dist/ingress.js.map +1 -1
- package/dist/lock-routes.d.ts +12 -12
- package/dist/login.e2e.test.js +2 -2
- package/dist/main.d.ts +2 -0
- package/dist/main.js +223 -151
- package/dist/main.js.map +1 -1
- package/dist/opencode-server.js +15 -11
- package/dist/opencode-server.js.map +1 -1
- package/dist/opencode-server.test.js +1 -1
- package/dist/opencode-server.test.js.map +1 -1
- package/dist/plugin/index.js +19 -1
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/read-media.d.ts +23 -0
- package/dist/plugin/read-media.js +72 -0
- package/dist/plugin/read-media.js.map +1 -0
- package/dist/plugin.e2e.test.js +6 -1
- package/dist/plugin.e2e.test.js.map +1 -1
- package/dist/prompt.d.ts +6 -6
- package/dist/prompt.js +10 -11
- package/dist/prompt.js.map +1 -1
- package/dist/scheduler.d.ts +1 -1
- package/dist/session-events.d.ts +29 -1
- package/dist/session-events.js +70 -0
- package/dist/session-events.js.map +1 -1
- package/dist/sessions.d.ts +10 -10
- package/dist/sessions.js +3 -2
- package/dist/sessions.js.map +1 -1
- package/dist/slash-commands.js +4 -3
- package/dist/slash-commands.js.map +1 -1
- package/dist/system-prompt.d.ts +13 -2
- package/dist/system-prompt.js +41 -64
- package/dist/system-prompt.js.map +1 -1
- package/dist/voice-attachments.e2e.test.js +6 -3
- package/dist/voice-attachments.e2e.test.js.map +1 -1
- package/dist/voice-call.js +1 -1
- package/dist/voice-call.js.map +1 -1
- package/package.json +7 -6
- package/skills/kimaki-debug/SKILL.md +44 -0
- package/skills/kimaki-tunnel/SKILL.md +34 -0
- package/src/autostart.ts +15 -3
- package/src/bot.ts +4 -0
- package/src/builtin-skills.test.ts +29 -0
- package/src/builtin-skills.ts +59 -0
- package/src/cli/bot.ts +9 -0
- package/src/cli/discord.ts +1 -1
- package/src/cli/session.ts +14 -47
- package/src/cli-discord.e2e.test.ts +18 -1
- package/src/cli-lock.e2e.test.ts +7 -6
- package/src/cli-read.e2e.test.ts +8 -3
- package/src/cli-sessions.e2e.test.ts +2 -4
- package/src/commands/preference-commands.ts +19 -6
- package/src/effects.ts +40 -6
- package/src/errors.ts +1 -1
- package/src/event-loop.ts +9 -0
- package/src/ingress.ts +16 -11
- package/src/login.e2e.test.ts +2 -2
- package/src/main.ts +12 -2
- package/src/opencode-server.test.ts +1 -1
- package/src/opencode-server.ts +12 -9
- package/src/plugin/index.ts +20 -1
- package/src/plugin.e2e.test.ts +6 -1
- package/src/prompt.ts +10 -10
- package/src/session-events.ts +83 -0
- package/src/sessions.ts +3 -2
- package/src/slash-commands.ts +4 -3
- package/src/system-prompt.ts +43 -64
- package/src/voice-attachments.e2e.test.ts +6 -3
- package/src/voice-call.ts +1 -1
- package/src/plugin/session-id/index.ts +0 -19
package/src/system-prompt.ts
CHANGED
|
@@ -108,28 +108,7 @@ export function baseInstructions({
|
|
|
108
108
|
|
|
109
109
|
## debugging kimaki issues
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
If there are internal kimaki issues (sessions not responding, bot errors, unexpected behavior), read the logs of the running bot and of the OpenCode service it talks to. Read them before guessing.
|
|
113
|
-
|
|
114
|
-
**Kimaki bot log**: \`${dataDir}/kimaki.log\`, only the current bot run. The run before the last restart (including the crash that caused it) is in \`${dataDir}/kimaki.previous.log\`. Each line is \`<ISO time> <LEVEL> [<MODULE>] <message>\`, and errors include their cause chain. It logs incoming messages, run start/finish/failure per thread and session, Discord and OpenCode errors, routines and crashes.
|
|
115
|
-
|
|
116
|
-
\`\`\`bash
|
|
117
|
-
tail -n 200 ${dataDir}/kimaki.log
|
|
118
|
-
grep -E ' (WARN|ERROR) ' ${dataDir}/kimaki.log | tail -n 50
|
|
119
|
-
grep '<thread_id or ses_id>' ${dataDir}/kimaki.log
|
|
120
|
-
\`\`\`
|
|
121
|
-
|
|
122
|
-
**OpenCode service log**: the shared \`opencode serve --service\` daemon appends to \`~/.local/share/opencode/log/opencode.log\` (\`$XDG_DATA_HOME/opencode/log\`; \`opencode debug paths\` prints the folder). The file is never reset and can be very large: always use \`tail\` or \`grep\`, never read it whole. Lines are logfmt. \`run=<id>\` is one daemon process, so filter by the run of the last line. The daemon pid and URL are in \`~/.local/state/opencode/service.json\`.
|
|
123
|
-
|
|
124
|
-
\`\`\`bash
|
|
125
|
-
LOG=~/.local/share/opencode/log/opencode.log
|
|
126
|
-
RUN=$(tail -n 1 $LOG | grep -o 'run=[a-z0-9]*')
|
|
127
|
-
grep "$RUN " $LOG | grep -E '^timestamp=[^ ]+ level=(ERROR|WARN)' | tail -n 50
|
|
128
|
-
grep "$RUN " $LOG | grep '<ses_id>' | tail -n 100
|
|
129
|
-
\`\`\`
|
|
130
|
-
|
|
131
|
-
\`kimaki session events <id>\` prints the recorded OpenCode events of a thread as JSONL.
|
|
132
|
-
\`kimaki restart\` restarts the bot (only when the user asks). \`kimaki profile cpu --duration 30s\` and \`kimaki profile heap\` print the profile file path.
|
|
111
|
+
When sessions do not respond, the bot shows errors, or you need the Kimaki or OpenCode logs, load the \`kimaki-debug\` skill (logs, profiling, \`kimaki login\` for provider credentials) and read the logs before guessing. Do not restart the bot unless the user explicitly asks. ALWAYS read https://kimaki.dev/docs/guides/report-bugs before submitting any issue to Kimaki. Never open a pull request on remorses/kimaki unless remorses asked for one in a comment on the issue.
|
|
133
112
|
|
|
134
113
|
## uploading files to discord
|
|
135
114
|
|
|
@@ -137,6 +116,10 @@ export function baseInstructions({
|
|
|
137
116
|
|
|
138
117
|
kimaki upload-to-discord --session ${sessionId} <file1> [file2] ...
|
|
139
118
|
|
|
119
|
+
To send several images or files, pass them all to ONE command in the order you want them shown, not one command per file. Discord shows them in argument order with numbered labels (\`1. a.png\`, \`2. b.pdf\`), so you and the user can refer to them by number (for example "screenshot 2"). Name them in your text with the same numbers.
|
|
120
|
+
|
|
121
|
+
Files the user attaches arrive in an \`<attachments>\` block, numbered in the order the user attached them. When the user says "the second image", use that numbering.
|
|
122
|
+
|
|
140
123
|
NEVER show images with markdown like \`\` or \`\`. Discord does not render local markdown images. ALWAYS upload them with \`kimaki upload-to-discord\` so they appear as real Discord attachments. Do this for every screenshot, generated image, and visual step the user should see.
|
|
141
124
|
|
|
142
125
|
## generating audio from text
|
|
@@ -386,15 +369,25 @@ export function baseInstructions({
|
|
|
386
369
|
|
|
387
370
|
Each row shows the session ID, status (\`busy\`, \`waiting\` for input, or \`idle\`), the title, and \`tokens: N\`. Titles prefixed with \`btw:\` are side sessions that answer a related user question in parallel. They are not duplicate sessions of the main task.
|
|
388
371
|
|
|
389
|
-
To search past sessions (
|
|
372
|
+
To search past sessions, search the transcripts. Kimaki writes one markdown file per session to \`${dataDir}/transcripts/<session id>.md\` when a run ends (user messages, assistant text and short tool inputs; no tool outputs, no thinking). The first lines are \`# <title> (<session id>)\` and \`directory: <folder>\`; messages start with \`## user <time>\` or \`## assistant <time> (<agent>, <model>)\`. Use rg (or \`grep -r\` when rg is missing):
|
|
373
|
+
|
|
374
|
+
\`\`\`bash
|
|
375
|
+
rg -il 'auth timeout' ${dataDir}/transcripts # sessions that mention it; the file name is the session ID
|
|
376
|
+
rg -i -C3 'auth timeout' ${dataDir}/transcripts/<session id>.md # matches with context in one session
|
|
377
|
+
rg -i -m3 'rate limit' ${dataDir}/transcripts # first 3 matches per session
|
|
378
|
+
rg -i 'error\\s+42|panic' ${dataDir}/transcripts # regex
|
|
379
|
+
rg -i '^# .*billing' ${dataDir}/transcripts # titles
|
|
380
|
+
rg -l '^directory: /path/to/project' ${dataDir}/transcripts # sessions of one project
|
|
381
|
+
rg -il 'auth timeout' $(find ${dataDir}/transcripts -name '*.md' -mtime -3) # last 3 days
|
|
382
|
+
\`\`\`
|
|
383
|
+
|
|
384
|
+
Sessions that Kimaki did not run (OpenCode TUI) and old sessions are not there until you sync. The command skips current transcripts, so it is cheap to run again:
|
|
390
385
|
|
|
391
386
|
\`\`\`bash
|
|
392
|
-
kimaki session
|
|
393
|
-
kimaki session
|
|
394
|
-
kimaki session
|
|
395
|
-
kimaki session
|
|
396
|
-
kimaki session search "/panic|crash/i" --channel <channel_id>
|
|
397
|
-
kimaki session search "auth timeout" --all
|
|
387
|
+
kimaki session sync # sessions updated in the last 14 days
|
|
388
|
+
kimaki session sync --days 60
|
|
389
|
+
kimaki session sync --all # every session, any age (slow the first time)
|
|
390
|
+
kimaki session sync --rebuild # write all again, for example after the format changed
|
|
398
391
|
\`\`\`
|
|
399
392
|
|
|
400
393
|
To read a session as markdown, pipe to a file. Logs go to stderr:
|
|
@@ -469,10 +462,6 @@ export function baseInstructions({
|
|
|
469
462
|
|
|
470
463
|
\`session list --active\` exits 0 while it finds active sessions, 1 when none remain, and 64 on errors. The loop stops only on 1, so an error never looks like "no active sessions". \`--active\` never lists the calling session (\`OPENCODE_SESSION_ID\`), so the loop does not wait for itself. Sessions can start again after the loop ends, so run the check again right before each commit. \`session wait\` returns once the model finishes responding, or when the session pauses to show the user a question (it does not finish on its own until answered).
|
|
471
464
|
|
|
472
|
-
## provider credentials
|
|
473
|
-
|
|
474
|
-
Provider credentials belong to OpenCode. Use \`kimaki login PROVIDER\` to list login methods; \`--key\` connects an API key, \`--method\` starts OAuth, and \`--attempt\` checks or completes an attempt.
|
|
475
|
-
|
|
476
465
|
## submodules
|
|
477
466
|
|
|
478
467
|
When pulling submodules and they jump to a new commit, commit that submodule pointer update right away before doing other work. Otherwise critique diffs later will include the noisy submodule jump along with the real changes.
|
|
@@ -512,31 +501,7 @@ export function baseInstructions({
|
|
|
512
501
|
|
|
513
502
|
## running dev servers with tunnel access
|
|
514
503
|
|
|
515
|
-
Localhost URLs are useless from Discord. When the user should open a local dev server in a browser,
|
|
516
|
-
|
|
517
|
-
- Use random tunnel IDs by default. Pass \`-t <id>\` only for services that are safe to be publicly discoverable.
|
|
518
|
-
- \`kimaki tunnel\` detects the local port from the child output. Pass \`--port\` only when the server prints no detectable localhost URL or port line.
|
|
519
|
-
- \`kimaki tunnel\` injects \`TRAFORO_URL\` into the child process. Wire the app to it so OAuth callbacks, webhook URLs, and absolute links use the public URL.
|
|
520
|
-
|
|
521
|
-
\`\`\`bash
|
|
522
|
-
# start in a named background session, wait for output, then read the tunnel URL
|
|
523
|
-
bunx tuistory launch "kimaki tunnel -- pnpm dev" -s myapp-dev
|
|
524
|
-
bunx tuistory -s myapp-dev wait "/ready|local|tunnel/i" --timeout 30000
|
|
525
|
-
bunx tuistory read -s myapp-dev
|
|
526
|
-
|
|
527
|
-
# pass the public URL to the app (better-auth, Next.js, Vite; node can read process.env.TRAFORO_URL)
|
|
528
|
-
bunx tuistory launch "kimaki tunnel -- sh -c 'BETTER_AUTH_URL=$TRAFORO_URL exec pnpm dev'" -s myapp-dev
|
|
529
|
-
bunx tuistory launch "kimaki tunnel -- sh -c 'APP_URL=$TRAFORO_URL exec pnpm dev'" -s myapp-dev
|
|
530
|
-
bunx tuistory launch "kimaki tunnel -- sh -c 'VITE_BASE_URL=$TRAFORO_URL exec pnpm dev'" -s myapp-dev
|
|
531
|
-
|
|
532
|
-
# custom tunnel ID (only for intentionally public-safe services)
|
|
533
|
-
bunx tuistory launch "kimaki tunnel -t holocron -- pnpm dev" -s holocron-dev
|
|
534
|
-
|
|
535
|
-
# list sessions; stop with Ctrl+C, then close
|
|
536
|
-
bunx tuistory sessions
|
|
537
|
-
bunx tuistory -s myapp-dev press ctrl c
|
|
538
|
-
bunx tuistory -s myapp-dev close
|
|
539
|
-
\`\`\`
|
|
504
|
+
Localhost URLs are useless from Discord. When the user should open a local dev server in a browser, load the \`kimaki-tunnel\` skill: it wraps the server in \`kimaki tunnel\` to get a public URL.
|
|
540
505
|
|
|
541
506
|
## markdown formatting
|
|
542
507
|
|
|
@@ -641,8 +606,19 @@ export function stripTurnContext(text: string): string {
|
|
|
641
606
|
return text.replace(/\n\n(<local-files>\n[\s\S]*?<\/local-files>\n)?<discord-user [^\n]*\/>$/, '')
|
|
642
607
|
}
|
|
643
608
|
|
|
644
|
-
|
|
645
|
-
export
|
|
609
|
+
const PromptAttachmentSchema = z.object({ name: z.string(), path: z.string(), inline: z.boolean() })
|
|
610
|
+
export type PromptAttachment = z.infer<typeof PromptAttachmentSchema>
|
|
611
|
+
|
|
612
|
+
// Every attachment of the message, numbered in Discord order, so the user and the
|
|
613
|
+
// model can refer to "image 2". Inline ones are already in the message.
|
|
614
|
+
export function attachmentsBlock(attachments: readonly PromptAttachment[]): string {
|
|
615
|
+
if (attachments.length === 0) return ''
|
|
616
|
+
const lines = attachments.map((file, index) => `${index + 1}. ${file.name}${file.inline ? ' (inline)' : ''}: ${file.path}`)
|
|
617
|
+
return `<attachments>\nFiles of this message in the order the user attached them, saved on disk. Read the ones not marked inline with tools.\n${lines.join('\n')}\n</attachments>\n`
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Prompts before numbered attachments: only the files the model could not see inline.
|
|
621
|
+
function localFilesBlock(paths: readonly string[]): string {
|
|
646
622
|
if (paths.length === 0) return ''
|
|
647
623
|
return `<local-files>\nAttachments saved on disk. OpenCode cannot show these inline; use tools to read them.\n${paths.join('\n')}\n</local-files>\n`
|
|
648
624
|
}
|
|
@@ -654,7 +630,8 @@ const TurnContextSchema = z.object({
|
|
|
654
630
|
messageId: z.string(),
|
|
655
631
|
threadId: z.string(),
|
|
656
632
|
threadName: z.string(),
|
|
657
|
-
|
|
633
|
+
attachments: z.array(PromptAttachmentSchema).optional(),
|
|
634
|
+
localFiles: z.array(z.string()).optional(),
|
|
658
635
|
})
|
|
659
636
|
export type TurnContext = z.infer<typeof TurnContextSchema>
|
|
660
637
|
|
|
@@ -663,8 +640,9 @@ export type TurnContext = z.infer<typeof TurnContextSchema>
|
|
|
663
640
|
export function turnContext(metadata: Readonly<Record<string, unknown>> | undefined): string | null {
|
|
664
641
|
const parsed = TurnContextSchema.safeParse(metadata?.['discord'])
|
|
665
642
|
if (!parsed.success) return null
|
|
666
|
-
const { username, userId, messageId, threadId, threadName, localFiles } = parsed.data
|
|
667
|
-
|
|
643
|
+
const { username, userId, messageId, threadId, threadName, attachments, localFiles } = parsed.data
|
|
644
|
+
const files = attachments ? attachmentsBlock(attachments) : localFilesBlock(localFiles ?? [])
|
|
645
|
+
return `${files}<discord-user name="${escapeAttribute(username)}" user-id="${userId}" message-id="${messageId}" thread-id="${threadId}" thread-name="${escapeAttribute(threadName)}" />`
|
|
668
646
|
}
|
|
669
647
|
|
|
670
648
|
export type VoiceCallUser = { id: string; username: string }
|
|
@@ -913,7 +891,8 @@ function voiceCallReference({
|
|
|
913
891
|
kimaki project list --json # projects with channel ID, folder and guild ID
|
|
914
892
|
kimaki session list --project <folder> # threads of a project with status busy, waiting or idle
|
|
915
893
|
kimaki session list --all --active # threads that run now, in every project
|
|
916
|
-
|
|
894
|
+
rg -il 'text' ${dataDir}/transcripts # find threads by title or content; file name = session ID
|
|
895
|
+
kimaki session sync --days 60 # write transcripts of older or non-Kimaki sessions first
|
|
917
896
|
kimaki session read <thread ID or link> 2>/dev/null | tail -c 3000 # end of a thread transcript
|
|
918
897
|
kimaki session wait <thread ID> # wait until a thread finishes, then print it
|
|
919
898
|
kimaki session abort <thread ID> # stop the agent of a thread; the thread stays
|
|
@@ -39,8 +39,11 @@ const matchers: DeterministicMatcher[] = [
|
|
|
39
39
|
{
|
|
40
40
|
id: 'image',
|
|
41
41
|
priority: 500,
|
|
42
|
-
// The plugin adds <
|
|
43
|
-
when: {
|
|
42
|
+
// The plugin adds the numbered <attachments> and <discord-user> to the model request only.
|
|
43
|
+
when: {
|
|
44
|
+
latestUserTextRegex: 'image-marker[\\s\\S]*<attachments>[\\s\\S]*1\\. dot\\.png \\(inline\\): [^\\n]*\\n2\\. notes\\.txt: [^\\n]*\\n3\\. archive\\.zip: [^\\n]*\\n</attachments>\\n<discord-user ',
|
|
45
|
+
rawPromptIncludes: 'image/png',
|
|
46
|
+
},
|
|
44
47
|
then: { parts: textParts('I see an image') },
|
|
45
48
|
},
|
|
46
49
|
reply('voice-steer', 'voice steer ok'),
|
|
@@ -137,7 +140,7 @@ test('image and text attachments reach the model as files', async () => {
|
|
|
137
140
|
// OpenCode drops binary files: the model gets their local paths from metadata, the stored text stays clean.
|
|
138
141
|
expect(user?.type === 'user' && user.text).toBe('What is this image-marker')
|
|
139
142
|
const discord = user?.type === 'user' ? user.metadata?.['discord'] : null
|
|
140
|
-
expect(JSON.stringify(discord).replaceAll(dataDir, '<data>').replace(/\/\d+\//g, '/<message>/').replace(/\d{6,}/g, 'ID')).toMatchInlineSnapshot(`"{"userId":"ID","username":"tommy","messageId":"ID","threadId":"ID","threadName":"What is this image-marker","
|
|
143
|
+
expect(JSON.stringify(discord).replaceAll(dataDir, '<data>').replace(/\/\d+\//g, '/<message>/').replace(/\d{6,}/g, 'ID')).toMatchInlineSnapshot(`"{"userId":"ID","username":"tommy","messageId":"ID","threadId":"ID","threadName":"What is this image-marker","attachments":[{"name":"dot.png","path":"<data>/attachments/<message>/0-dot.png","inline":true},{"name":"notes.txt","path":"<data>/attachments/<message>/1-notes.txt","inline":false},{"name":"archive.zip","path":"<data>/attachments/<message>/2-archive.zip","inline":false}]}"`)
|
|
141
144
|
})
|
|
142
145
|
|
|
143
146
|
test('voice in a channel starts a session with the transcription and the spoken agent', async () => {
|
package/src/voice-call.ts
CHANGED
|
@@ -349,7 +349,7 @@ export function createVoiceCalls({ opencodeConfigDir }: { opencodeConfigDir: str
|
|
|
349
349
|
const states = guild.voiceStates.cache.filter((state) => state.channelId === channelId && state.id !== bot.discord.user?.id)
|
|
350
350
|
const users = new Map<string, Author>()
|
|
351
351
|
for (const state of states.values()) {
|
|
352
|
-
if (!(await canUseKimaki({ guild, userId: state.id }))) continue
|
|
352
|
+
if (!(await canUseKimaki(bot, { guild, userId: state.id }))) continue
|
|
353
353
|
const user = await bot.discord.users.fetch(state.id).catch(() => null)
|
|
354
354
|
users.set(state.id, { id: state.id, username: user?.username ?? state.id })
|
|
355
355
|
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Tells the model its own OpenCode session ID, in every session (TUI, Discord, subagents).
|
|
2
|
-
//
|
|
3
|
-
// The ID never changes in a session, so the system text is the same on every
|
|
4
|
-
// request and the provider prompt cache keeps hitting.
|
|
5
|
-
//
|
|
6
|
-
// Standalone in plain OpenCode without the bot: list this directory in
|
|
7
|
-
// opencode.json `plugins`. The bot then skips its shim, see installPluginShim
|
|
8
|
-
// in opencode-server.ts. It imports nothing from Kimaki outside this folder.
|
|
9
|
-
|
|
10
|
-
import { Plugin } from '@opencode/plugin'
|
|
11
|
-
|
|
12
|
-
export default Plugin.define({
|
|
13
|
-
id: 'kimaki-session-id',
|
|
14
|
-
async setup(ctx) {
|
|
15
|
-
await ctx.session.hook('context', (event) => {
|
|
16
|
-
event.system.push({ type: 'text', text: `OpenCode session ID: ${event.sessionID}` })
|
|
17
|
-
})
|
|
18
|
-
},
|
|
19
|
-
})
|