kimaki 0.4.74 → 0.4.76
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/agent-model.e2e.test.js +4 -4
- package/dist/channel-management.js +130 -1
- package/dist/cli.js +765 -415
- package/dist/commands/agent.js +6 -2
- package/dist/commands/ask-question.js +44 -7
- package/dist/commands/create-new-project.js +9 -3
- package/dist/commands/login.js +6 -1
- package/dist/commands/mcp.js +239 -0
- package/dist/commands/merge-worktree.js +60 -3
- package/dist/commands/model-variant.js +364 -0
- package/dist/commands/model.js +16 -7
- package/dist/commands/new-worktree.js +340 -0
- package/dist/commands/permissions.js +50 -5
- package/dist/commands/restart-opencode-server.js +7 -5
- package/dist/commands/stop-opencode-server.js +19 -18
- package/dist/commands/user-command.js +1 -1
- package/dist/commands/worktrees.js +253 -36
- package/dist/debounce-timeout.js +28 -0
- package/dist/discord-bot.js +27 -20
- package/dist/errors.js +1 -6
- package/dist/event-stream-real-capture.e2e.test.js +3 -2
- package/dist/eventsource-parser.test.js +327 -0
- package/dist/format-tables.js +207 -27
- package/dist/format-tables.test.js +113 -4
- package/dist/gateway-proxy.e2e.test.js +3 -2
- package/dist/heap-monitor.js +38 -11
- package/dist/hrana-server.js +1 -2
- package/dist/html-actions.js +123 -0
- package/dist/html-actions.test.js +70 -0
- package/dist/html-components.js +117 -0
- package/dist/html-components.test.js +34 -0
- package/dist/interaction-handler.js +36 -11
- package/dist/kimaki-digital-twin.e2e.test.js +3 -2
- package/dist/logger.js +2 -0
- package/dist/markdown.test.js +9 -5
- package/dist/message-preprocessing.js +24 -9
- package/dist/onboarding-tutorial-plugin.js +73 -0
- package/dist/onboarding-tutorial.js +172 -0
- package/dist/onboarding-welcome.js +37 -0
- package/dist/opencode-plugin.js +39 -33
- package/dist/opencode.js +224 -162
- package/dist/queue-advanced-e2e-setup.js +62 -3
- package/dist/queue-advanced-permissions-typing.e2e.test.js +2 -0
- package/dist/queue-advanced-question.e2e.test.js +120 -0
- package/dist/queue-advanced-typing-interrupt.e2e.test.js +109 -0
- package/dist/queue-advanced-typing.e2e.test.js +54 -51
- package/dist/runtime-idle-sweeper.js +3 -12
- package/dist/runtime-lifecycle.e2e.test.js +61 -3
- package/dist/session-handler/event-stream-state.js +103 -163
- package/dist/session-handler/event-stream-state.test.js +320 -194
- package/dist/session-handler/thread-session-runtime.js +266 -208
- package/dist/system-message.js +9 -6
- package/dist/test-utils.js +1 -14
- package/dist/thread-message-queue.e2e.test.js +42 -8
- package/dist/utils.js +4 -1
- package/dist/voice-message.e2e.test.js +114 -4
- package/dist/voice.js +2 -2
- package/dist/worktrees.js +97 -10
- package/package.json +4 -2
- package/skills/errore/SKILL.md +18 -0
- package/skills/goke/SKILL.md +179 -8
- package/skills/npm-package/SKILL.md +92 -2
- package/skills/playwriter/SKILL.md +5 -1
- package/skills/zele/SKILL.md +103 -0
- package/src/agent-model.e2e.test.ts +3 -4
- package/src/channel-management.ts +169 -1
- package/src/cli.ts +1013 -532
- package/src/commands/agent.ts +6 -2
- package/src/commands/ask-question.ts +50 -8
- package/src/commands/create-new-project.ts +10 -3
- package/src/commands/login.ts +6 -1
- package/src/commands/mcp.ts +307 -0
- package/src/commands/merge-worktree.ts +83 -5
- package/src/commands/model-variant.ts +483 -0
- package/src/commands/model.ts +33 -23
- package/src/commands/{worktree.ts → new-worktree.ts} +84 -2
- package/src/commands/permissions.ts +55 -4
- package/src/commands/restart-opencode-server.ts +7 -9
- package/src/commands/user-command.ts +1 -1
- package/src/commands/worktrees.ts +355 -58
- package/src/debounce-timeout.ts +43 -0
- package/src/discord-bot.ts +28 -21
- package/src/errors.ts +1 -7
- package/src/event-stream-real-capture.e2e.test.ts +3 -2
- package/src/eventsource-parser.test.ts +351 -0
- package/src/format-tables.test.ts +116 -4
- package/src/format-tables.ts +327 -27
- package/src/gateway-proxy.e2e.test.ts +2 -2
- package/src/heap-monitor.ts +42 -11
- package/src/hrana-server.ts +1 -2
- package/src/html-actions.test.ts +87 -0
- package/src/html-actions.ts +174 -0
- package/src/html-components.test.ts +38 -0
- package/src/html-components.ts +181 -0
- package/src/interaction-handler.ts +55 -12
- package/src/kimaki-digital-twin.e2e.test.ts +3 -2
- package/src/logger.ts +2 -0
- package/src/markdown.test.ts +9 -5
- package/src/message-preprocessing.ts +26 -9
- package/src/onboarding-tutorial-plugin.ts +93 -0
- package/src/onboarding-tutorial.ts +176 -0
- package/src/onboarding-welcome.ts +49 -0
- package/src/opencode-plugin.ts +42 -35
- package/src/opencode.ts +288 -209
- package/src/queue-advanced-e2e-setup.ts +63 -3
- package/src/queue-advanced-permissions-typing.e2e.test.ts +2 -0
- package/src/queue-advanced-question.e2e.test.ts +158 -0
- package/src/queue-advanced-typing-interrupt.e2e.test.ts +138 -0
- package/src/queue-advanced-typing.e2e.test.ts +60 -54
- package/src/runtime-idle-sweeper.ts +2 -14
- package/src/runtime-lifecycle.e2e.test.ts +79 -3
- package/src/session-handler/event-stream-fixtures/session-abort-after-idle-race.jsonl +21 -0
- package/src/session-handler/event-stream-state.test.ts +350 -218
- package/src/session-handler/event-stream-state.ts +158 -215
- package/src/session-handler/thread-session-runtime.ts +321 -255
- package/src/system-message.ts +11 -5
- package/src/test-utils.ts +0 -15
- package/src/thread-message-queue.e2e.test.ts +43 -8
- package/src/utils.ts +7 -0
- package/src/voice-message.e2e.test.ts +133 -4
- package/src/voice.ts +2 -2
- package/src/worktrees.ts +150 -8
- package/skills/goke/.prettierrc +0 -5
- package/skills/goke/CHANGELOG.md +0 -40
- package/skills/goke/LICENSE +0 -21
- package/skills/goke/README.md +0 -666
- package/skills/goke/package.json +0 -43
- package/skills/goke/src/__test__/coerce.test.ts +0 -411
- package/skills/goke/src/__test__/index.test.ts +0 -1805
- package/skills/goke/src/__test__/types.test-d.ts +0 -111
- package/skills/goke/src/coerce.ts +0 -547
- package/skills/goke/src/goke.ts +0 -1362
- package/skills/goke/src/index.ts +0 -16
- package/skills/goke/src/mri.ts +0 -164
- package/skills/goke/tsconfig.json +0 -15
- package/src/commands/stop-opencode-server.ts +0 -113
|
@@ -23,8 +23,8 @@ import { startDiscordBot } from './discord-bot.js';
|
|
|
23
23
|
import { setBotToken, initDatabase, closeDatabase, setChannelDirectory, setChannelVerbosity, setChannelAgent, setChannelModel, } from './database.js';
|
|
24
24
|
import { getPrisma } from './db.js';
|
|
25
25
|
import { startHranaServer, stopHranaServer } from './hrana-server.js';
|
|
26
|
-
import { initializeOpencodeForDirectory } from './opencode.js';
|
|
27
|
-
import {
|
|
26
|
+
import { initializeOpencodeForDirectory, stopOpencodeServer } from './opencode.js';
|
|
27
|
+
import { cleanupTestSessions, waitForBotMessageContaining, waitForFooterMessage, } from './test-utils.js';
|
|
28
28
|
import { buildQuickAgentCommandDescription } from './commands/agent.js';
|
|
29
29
|
const TEST_USER_ID = '200000000000000920';
|
|
30
30
|
const TEXT_CHANNEL_ID = '200000000000000921';
|
|
@@ -266,7 +266,7 @@ describe('agent model resolution', () => {
|
|
|
266
266
|
if (botClient) {
|
|
267
267
|
botClient.destroy();
|
|
268
268
|
}
|
|
269
|
-
await
|
|
269
|
+
await stopOpencodeServer();
|
|
270
270
|
await Promise.all([
|
|
271
271
|
closeDatabase().catch(() => {
|
|
272
272
|
return;
|
|
@@ -674,7 +674,7 @@ describe('agent model resolution', () => {
|
|
|
674
674
|
--- from: assistant (TestBot)
|
|
675
675
|
⬥ ok
|
|
676
676
|
*project ⋅ main ⋅ Ns ⋅ N% ⋅ agent-model-v2 ⋅ **test-agent***
|
|
677
|
-
Switched to **plan** agent for this session (was **test-agent**)
|
|
677
|
+
Switched to **plan** agent for this session next messages (was **test-agent**)
|
|
678
678
|
--- from: user (agent-model-tester)
|
|
679
679
|
Reply with exactly: after-switch-msg
|
|
680
680
|
--- from: assistant (TestBot)
|
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
// Creates and manages Kimaki project channels (text + voice pairs),
|
|
3
3
|
// extracts channel metadata from topic tags, and ensures category structure.
|
|
4
4
|
import { ChannelType, } from 'discord.js';
|
|
5
|
+
import fs from 'node:fs';
|
|
5
6
|
import path from 'node:path';
|
|
6
|
-
import { getChannelDirectory, setChannelDirectory } from './database.js';
|
|
7
|
+
import { getChannelDirectory, setChannelDirectory, findChannelsByDirectory, } from './database.js';
|
|
8
|
+
import { getProjectsDir } from './config.js';
|
|
9
|
+
import { execAsync } from './worktrees.js';
|
|
10
|
+
import { createLogger, LogPrefix } from './logger.js';
|
|
11
|
+
const logger = createLogger(LogPrefix.CHANNEL);
|
|
7
12
|
export async function ensureKimakiCategory(guild, botName) {
|
|
8
13
|
// Skip appending bot name if it's already "kimaki" to avoid "Kimaki kimaki"
|
|
9
14
|
const isKimakiBot = botName?.toLowerCase() === 'kimaki';
|
|
@@ -96,3 +101,127 @@ export async function getChannelsWithDescriptions(guild) {
|
|
|
96
101
|
}
|
|
97
102
|
return channels;
|
|
98
103
|
}
|
|
104
|
+
const DEFAULT_GITIGNORE = `node_modules/
|
|
105
|
+
dist/
|
|
106
|
+
.env
|
|
107
|
+
.env.*
|
|
108
|
+
!.env.example
|
|
109
|
+
.DS_Store
|
|
110
|
+
tmp/
|
|
111
|
+
*.log
|
|
112
|
+
__pycache__/
|
|
113
|
+
*.pyc
|
|
114
|
+
.venv/
|
|
115
|
+
*.egg-info/
|
|
116
|
+
`;
|
|
117
|
+
const DEFAULT_CHANNEL_TOPIC = 'General channel for misc tasks with Kimaki. Not connected to a specific OpenCode project or repository.';
|
|
118
|
+
/**
|
|
119
|
+
* Create (or find) the default "kimaki" channel for general-purpose tasks.
|
|
120
|
+
* Channel name is "kimaki-{botName}" for self-hosted bots, "kimaki" for gateway.
|
|
121
|
+
* Directory is ~/.kimaki/projects/kimaki, git-initialized with a .gitignore.
|
|
122
|
+
*
|
|
123
|
+
* Idempotency: checks the database for an existing channel mapped to the
|
|
124
|
+
* kimaki projects directory. Also scans guild channels by name+category
|
|
125
|
+
* as a fallback for channels created before DB mapping existed.
|
|
126
|
+
*/
|
|
127
|
+
export async function createDefaultKimakiChannel({ guild, botName, appId, isGatewayMode, }) {
|
|
128
|
+
const projectDirectory = path.join(getProjectsDir(), 'kimaki');
|
|
129
|
+
// Ensure the default kimaki project directory exists before any DB mapping
|
|
130
|
+
// restoration or git setup. Custom data dirs may not have <dataDir>/projects
|
|
131
|
+
// created yet, and later writes assume the full path is present.
|
|
132
|
+
if (!fs.existsSync(projectDirectory)) {
|
|
133
|
+
fs.mkdirSync(projectDirectory, { recursive: true });
|
|
134
|
+
logger.log(`Created default kimaki directory: ${projectDirectory}`);
|
|
135
|
+
}
|
|
136
|
+
// Hydrate guild channels from API so the cache scan is complete
|
|
137
|
+
try {
|
|
138
|
+
await guild.channels.fetch();
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
logger.warn(`Could not fetch guild channels for ${guild.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
142
|
+
}
|
|
143
|
+
// 1. Check database for existing channel mapped to this directory.
|
|
144
|
+
// Check ALL mappings (not just the first) since the same directory could
|
|
145
|
+
// have stale rows from deleted channels or other guilds.
|
|
146
|
+
const existingMappings = await findChannelsByDirectory({
|
|
147
|
+
directory: projectDirectory,
|
|
148
|
+
channelType: 'text',
|
|
149
|
+
});
|
|
150
|
+
const mappedChannelInGuild = existingMappings
|
|
151
|
+
.map((row) => guild.channels.cache.get(row.channel_id))
|
|
152
|
+
.find((ch) => ch?.type === ChannelType.GuildText);
|
|
153
|
+
if (mappedChannelInGuild) {
|
|
154
|
+
logger.log(`Default kimaki channel already exists: ${mappedChannelInGuild.id}`);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
// 2. Fallback: detect existing channel by name+category
|
|
158
|
+
const kimakiCategory = await ensureKimakiCategory(guild, botName);
|
|
159
|
+
const existingByName = guild.channels.cache.find((ch) => {
|
|
160
|
+
if (ch.type !== ChannelType.GuildText) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
if (ch.parentId !== kimakiCategory.id) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
return ch.name === 'kimaki' || ch.name.startsWith('kimaki-');
|
|
167
|
+
});
|
|
168
|
+
if (existingByName) {
|
|
169
|
+
logger.log(`Found existing default kimaki channel by name: ${existingByName.id}, restoring DB mapping`);
|
|
170
|
+
await setChannelDirectory({
|
|
171
|
+
channelId: existingByName.id,
|
|
172
|
+
directory: projectDirectory,
|
|
173
|
+
channelType: 'text',
|
|
174
|
+
skipIfExists: true,
|
|
175
|
+
});
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
// Git init — gracefully skip if git is not installed
|
|
179
|
+
const gitDir = path.join(projectDirectory, '.git');
|
|
180
|
+
if (!fs.existsSync(gitDir)) {
|
|
181
|
+
try {
|
|
182
|
+
await execAsync('git init', { cwd: projectDirectory, timeout: 10_000 });
|
|
183
|
+
logger.log(`Initialized git in: ${projectDirectory}`);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
logger.warn(`Could not initialize git in ${projectDirectory}: ${error instanceof Error ? error.message : String(error)}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Write .gitignore if it doesn't exist
|
|
190
|
+
const gitignorePath = path.join(projectDirectory, '.gitignore');
|
|
191
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
192
|
+
fs.writeFileSync(gitignorePath, DEFAULT_GITIGNORE);
|
|
193
|
+
}
|
|
194
|
+
// Channel name: "kimaki-{botName}" for self-hosted, "kimaki" for gateway
|
|
195
|
+
const channelName = (() => {
|
|
196
|
+
if (isGatewayMode || !botName) {
|
|
197
|
+
return 'kimaki';
|
|
198
|
+
}
|
|
199
|
+
const sanitized = botName
|
|
200
|
+
.toLowerCase()
|
|
201
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
202
|
+
.replace(/-+/g, '-')
|
|
203
|
+
.replace(/^-|-$/g, '');
|
|
204
|
+
if (!sanitized || sanitized === 'kimaki') {
|
|
205
|
+
return 'kimaki';
|
|
206
|
+
}
|
|
207
|
+
return `kimaki-${sanitized}`.slice(0, 100);
|
|
208
|
+
})();
|
|
209
|
+
const textChannel = await guild.channels.create({
|
|
210
|
+
name: channelName,
|
|
211
|
+
type: ChannelType.GuildText,
|
|
212
|
+
parent: kimakiCategory,
|
|
213
|
+
topic: DEFAULT_CHANNEL_TOPIC,
|
|
214
|
+
});
|
|
215
|
+
await setChannelDirectory({
|
|
216
|
+
channelId: textChannel.id,
|
|
217
|
+
directory: projectDirectory,
|
|
218
|
+
channelType: 'text',
|
|
219
|
+
});
|
|
220
|
+
logger.log(`Created default kimaki channel: #${channelName} (${textChannel.id})`);
|
|
221
|
+
return {
|
|
222
|
+
textChannel,
|
|
223
|
+
textChannelId: textChannel.id,
|
|
224
|
+
channelName,
|
|
225
|
+
projectDirectory,
|
|
226
|
+
};
|
|
227
|
+
}
|