kimaki 0.9.0 → 0.9.1
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-commands/user.js +9 -9
- package/dist/discord-bot.js +15 -4
- package/dist/onboarding-tutorial.js +1 -1
- package/dist/system-message.js +7 -7
- package/dist/system-message.test.js +27 -24
- package/dist/voice-message.e2e.test.js +7 -0
- package/dist/worktree-lifecycle.e2e.test.js +80 -4
- package/dist/worktrees.js +7 -0
- package/package.json +4 -4
- package/skills/parallel-security-review/SKILL.md +332 -0
- package/src/cli-commands/user.ts +9 -10
- package/src/discord-bot.ts +20 -4
- package/src/onboarding-tutorial.ts +1 -1
- package/src/system-message.test.ts +27 -24
- package/src/system-message.ts +7 -7
- package/src/voice-message.e2e.test.ts +8 -0
- package/src/worktree-lifecycle.e2e.test.ts +94 -3
- package/src/worktrees.ts +8 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: parallel-security-review
|
|
3
|
+
repo: https://github.com/remorses/kimaki
|
|
4
|
+
description: >
|
|
5
|
+
DeepSec-inspired security review workflow for branch or PR changes. Use when
|
|
6
|
+
the user asks for a thorough, high-signal security review that should derive
|
|
7
|
+
repository context, split candidate discovery across focused agents, and run
|
|
8
|
+
per-finding false-positive validation before reporting.
|
|
9
|
+
allowed-tools:
|
|
10
|
+
- Bash(git status:*)
|
|
11
|
+
- Bash(git diff:*)
|
|
12
|
+
- Bash(git log:*)
|
|
13
|
+
- Bash(git show:*)
|
|
14
|
+
- Read
|
|
15
|
+
- Glob
|
|
16
|
+
- Grep
|
|
17
|
+
- Task
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Parallel security review
|
|
21
|
+
|
|
22
|
+
Use this workflow for **high-confidence security reviews** of branch or PR
|
|
23
|
+
changes. It borrows the useful parts of DeepSec's prompt architecture: small
|
|
24
|
+
core instructions, repo context, tech-specific reminders, finding-specific
|
|
25
|
+
false-positive rules, concrete target files, strict output, and independent
|
|
26
|
+
validation.
|
|
27
|
+
|
|
28
|
+
## Goal
|
|
29
|
+
|
|
30
|
+
Report only **real vulnerabilities introduced by the change under review**.
|
|
31
|
+
Do not report hardening gaps, existing issues, test-only problems, style, or
|
|
32
|
+
generic best-practice concerns.
|
|
33
|
+
|
|
34
|
+
Keep the final report short. If there are no high-confidence findings, say so.
|
|
35
|
+
|
|
36
|
+
## Review architecture
|
|
37
|
+
|
|
38
|
+
Split the work into discovery and validation:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
main reviewer
|
|
42
|
+
│
|
|
43
|
+
├─► context scout
|
|
44
|
+
│ auth model, trust boundaries, safe patterns, tech stack
|
|
45
|
+
│
|
|
46
|
+
├─► candidate discovery agents
|
|
47
|
+
│ focused by vulnerability family, not by random files
|
|
48
|
+
│
|
|
49
|
+
├─► one validator per candidate
|
|
50
|
+
│ tries to disprove exploitability and assign confidence
|
|
51
|
+
│
|
|
52
|
+
└─► final report
|
|
53
|
+
only validated findings with confidence >= 8/10
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Discovery agents can be broad. Validator agents must be skeptical.
|
|
57
|
+
|
|
58
|
+
## Step 1: collect branch context
|
|
59
|
+
|
|
60
|
+
Use git to understand the review scope. Prefer the repository's base branch if
|
|
61
|
+
known; otherwise use the merge base with the upstream/default branch when
|
|
62
|
+
available.
|
|
63
|
+
|
|
64
|
+
Read:
|
|
65
|
+
|
|
66
|
+
- `git status -s -u`
|
|
67
|
+
- changed file names
|
|
68
|
+
- commit messages on the branch
|
|
69
|
+
- complete diff for the review range
|
|
70
|
+
|
|
71
|
+
Do not modify files. Do not commit.
|
|
72
|
+
|
|
73
|
+
## Step 2: derive a compact repo security context
|
|
74
|
+
|
|
75
|
+
Launch a **context scout** subagent before vulnerability discovery. Ask it to
|
|
76
|
+
read relevant unchanged files too, not just the diff.
|
|
77
|
+
|
|
78
|
+
The context scout returns at most 40 lines:
|
|
79
|
+
|
|
80
|
+
```md
|
|
81
|
+
## Security context
|
|
82
|
+
|
|
83
|
+
**Auth primitives**
|
|
84
|
+
- `requireUser()` protects server routes
|
|
85
|
+
- `canManageTeam()` gates team admin actions
|
|
86
|
+
|
|
87
|
+
**Trust boundaries**
|
|
88
|
+
- HTTP route params and request bodies are attacker-controlled
|
|
89
|
+
- CLI flags and environment variables are trusted for this review
|
|
90
|
+
|
|
91
|
+
**Sensitive sinks**
|
|
92
|
+
- database writes through `db.*`
|
|
93
|
+
- filesystem writes under workspace root
|
|
94
|
+
- subprocess execution through `execAsync()`
|
|
95
|
+
|
|
96
|
+
**Known safe patterns**
|
|
97
|
+
- Prisma object filters are parameterized
|
|
98
|
+
- React text interpolation is escaped by default
|
|
99
|
+
|
|
100
|
+
**Tech notes**
|
|
101
|
+
- Next.js Server Actions are public POST endpoints unless guarded server-side
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Use this context in every later subagent prompt.
|
|
105
|
+
|
|
106
|
+
## Step 3: inject tech-specific reminders
|
|
107
|
+
|
|
108
|
+
Add only the reminders that match the repository. Keep them short.
|
|
109
|
+
|
|
110
|
+
**React / Angular**
|
|
111
|
+
- Do not report XSS for normal text interpolation.
|
|
112
|
+
- Only investigate raw HTML sinks like `dangerouslySetInnerHTML`,
|
|
113
|
+
`bypassSecurityTrustHtml`, DOM writes, or template compilation.
|
|
114
|
+
|
|
115
|
+
**Next.js / React Server Components**
|
|
116
|
+
- Server Actions are public POST endpoints. Check backend auth inside the
|
|
117
|
+
action, not just UI visibility.
|
|
118
|
+
- Middleware is useful but not sufficient if routes can be reached through
|
|
119
|
+
alternate paths or internal handlers.
|
|
120
|
+
- `params`, `searchParams`, headers, cookies, and request bodies are untrusted.
|
|
121
|
+
|
|
122
|
+
**Express / Node HTTP servers**
|
|
123
|
+
- Middleware order matters. Verify auth/validation runs before handlers.
|
|
124
|
+
- For command execution, prove untrusted input reaches a shell or argv sink.
|
|
125
|
+
- For path traversal, require a containment check like resolved path starts
|
|
126
|
+
with the intended root.
|
|
127
|
+
|
|
128
|
+
**Prisma / SQL ORMs**
|
|
129
|
+
- Prisma object filters are safe from SQL injection by default.
|
|
130
|
+
- Investigate raw SQL APIs like `$queryRawUnsafe`, string-built SQL, or manual
|
|
131
|
+
driver calls.
|
|
132
|
+
|
|
133
|
+
**Cloudflare Workers / edge apps**
|
|
134
|
+
- Request headers, cookies, URL, and body are untrusted.
|
|
135
|
+
- Bindings and environment variables are trusted.
|
|
136
|
+
- Verify tenant/account identifiers are checked server-side before data access.
|
|
137
|
+
|
|
138
|
+
**Rust / Go / memory-safe services**
|
|
139
|
+
- Do not report memory safety issues unless unsafe/native code is directly in
|
|
140
|
+
scope and the exploit path is concrete.
|
|
141
|
+
- Focus on auth, deserialization, filesystem, command execution, and SSRF.
|
|
142
|
+
|
|
143
|
+
## Step 4: candidate discovery agents
|
|
144
|
+
|
|
145
|
+
Launch focused agents in parallel when the diff is non-trivial. Use fewer
|
|
146
|
+
agents for tiny diffs.
|
|
147
|
+
|
|
148
|
+
Good split:
|
|
149
|
+
|
|
150
|
+
1. **Auth and authorization**
|
|
151
|
+
- auth bypass
|
|
152
|
+
- privilege escalation
|
|
153
|
+
- cross-tenant access
|
|
154
|
+
- missing server-side checks
|
|
155
|
+
|
|
156
|
+
2. **Injection and code execution**
|
|
157
|
+
- SQL/NoSQL/template injection
|
|
158
|
+
- command execution
|
|
159
|
+
- unsafe deserialization
|
|
160
|
+
- XSS through raw HTML sinks
|
|
161
|
+
|
|
162
|
+
3. **Filesystem, network, and data exposure**
|
|
163
|
+
- path traversal
|
|
164
|
+
- SSRF with attacker-controlled host/protocol
|
|
165
|
+
- sensitive data leakage
|
|
166
|
+
- unsafe file upload/download paths
|
|
167
|
+
|
|
168
|
+
4. **Business logic and state transitions**
|
|
169
|
+
- payment/subscription bypass
|
|
170
|
+
- permission state confusion
|
|
171
|
+
- irreversible actions missing authorization
|
|
172
|
+
- trust boundary changes introduced by the PR
|
|
173
|
+
|
|
174
|
+
Each discovery agent must output **candidates**, not final findings:
|
|
175
|
+
|
|
176
|
+
```md
|
|
177
|
+
## Candidate
|
|
178
|
+
|
|
179
|
+
- File: `path/to/file.ts:42`
|
|
180
|
+
- Category: `auth_bypass`
|
|
181
|
+
- Changed code: what changed
|
|
182
|
+
- Input source: where attacker-controlled data enters
|
|
183
|
+
- Sensitive sink or boundary: what is reached
|
|
184
|
+
- Possible exploit path: concrete steps if real
|
|
185
|
+
- Why it might be false positive: known guard, framework protection, trusted input, etc.
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Discard candidates that do not name both an **attacker-controlled source** and a
|
|
189
|
+
**security-sensitive sink or privilege boundary**.
|
|
190
|
+
|
|
191
|
+
## Step 5: finding-specific false-positive rules
|
|
192
|
+
|
|
193
|
+
Before validation, attach the relevant rules to each candidate.
|
|
194
|
+
|
|
195
|
+
**SSRF**
|
|
196
|
+
- Only report if attacker controls host, protocol, or can route to internal
|
|
197
|
+
services.
|
|
198
|
+
- Do not report path-only control as SSRF.
|
|
199
|
+
|
|
200
|
+
**SQL injection**
|
|
201
|
+
- Only report if untrusted input reaches string-built SQL or explicitly unsafe
|
|
202
|
+
raw SQL APIs.
|
|
203
|
+
- Do not report parameterized queries or normal ORM object filters.
|
|
204
|
+
|
|
205
|
+
**Command injection**
|
|
206
|
+
- Report shell injection when untrusted input reaches a shell string.
|
|
207
|
+
- For argv-array execution, prove the called program interprets the argument as
|
|
208
|
+
code, flags, paths with dangerous semantics, or another command.
|
|
209
|
+
- Shell scripts are usually trusted tooling. Only report if there is a concrete
|
|
210
|
+
untrusted input path.
|
|
211
|
+
|
|
212
|
+
**Path traversal**
|
|
213
|
+
- Report only when untrusted path segments can escape the intended root and
|
|
214
|
+
read/write/delete sensitive files.
|
|
215
|
+
- A correct `resolve()` plus `startsWith(root)` or equivalent containment check
|
|
216
|
+
is usually enough to reject the finding.
|
|
217
|
+
|
|
218
|
+
**XSS**
|
|
219
|
+
- In React/Angular, ignore normal interpolation.
|
|
220
|
+
- Look for raw HTML sinks, DOM APIs, markdown-to-HTML without sanitization, or
|
|
221
|
+
user-controlled script/style URLs.
|
|
222
|
+
|
|
223
|
+
**Auth bypass**
|
|
224
|
+
- Client-side missing checks are not vulnerabilities by themselves.
|
|
225
|
+
- Prove the backend route/action/job lacks the required authorization or trusts
|
|
226
|
+
client-provided roles, tenant IDs, user IDs, or ownership claims.
|
|
227
|
+
|
|
228
|
+
**Data exposure**
|
|
229
|
+
- Logging URLs or non-PII data is not enough.
|
|
230
|
+
- Report plaintext secrets, passwords, tokens, or concrete PII exposure.
|
|
231
|
+
|
|
232
|
+
**AI prompt injection**
|
|
233
|
+
- User-controlled content in an AI prompt is not automatically a vulnerability.
|
|
234
|
+
- Report only if the prompt output controls a privileged tool, data access,
|
|
235
|
+
command execution, or authorization decision without a guard.
|
|
236
|
+
|
|
237
|
+
## Step 6: one validator per candidate
|
|
238
|
+
|
|
239
|
+
Launch one validator subagent per candidate, in parallel. The validator's job is
|
|
240
|
+
to **disprove** the candidate.
|
|
241
|
+
|
|
242
|
+
Validator prompt shape:
|
|
243
|
+
|
|
244
|
+
````md
|
|
245
|
+
You are validating one security-review candidate. Try to prove this is a false
|
|
246
|
+
positive before accepting it.
|
|
247
|
+
|
|
248
|
+
Repository security context:
|
|
249
|
+
<paste compact context>
|
|
250
|
+
|
|
251
|
+
Candidate:
|
|
252
|
+
<paste one candidate>
|
|
253
|
+
|
|
254
|
+
Rules:
|
|
255
|
+
- Only consider vulnerabilities introduced by the reviewed diff.
|
|
256
|
+
- Require a concrete attacker-controlled source.
|
|
257
|
+
- Require a concrete sensitive sink or authorization boundary.
|
|
258
|
+
- Apply the finding-specific false-positive rules.
|
|
259
|
+
- Ignore tests, docs, generated files, hardening gaps, DoS, rate limiting,
|
|
260
|
+
dependency age, regex injection, regex DoS, and log spoofing.
|
|
261
|
+
|
|
262
|
+
Return exactly:
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"verdict": "true_positive",
|
|
267
|
+
"confidence": 8,
|
|
268
|
+
"reasoning": "short explanation",
|
|
269
|
+
"exploitPath": "concrete exploit steps",
|
|
270
|
+
"fix": "specific fix"
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Use `"verdict": "false_positive"` or `"verdict": "uncertain"` when the exploit
|
|
275
|
+
path is not concrete enough. Set `exploitPath` and `fix` to `null` in those
|
|
276
|
+
cases.
|
|
277
|
+
````
|
|
278
|
+
|
|
279
|
+
Only keep candidates with `verdict: "true_positive"` and confidence `>= 8`.
|
|
280
|
+
|
|
281
|
+
## Step 7: final report format
|
|
282
|
+
|
|
283
|
+
If there are no findings:
|
|
284
|
+
|
|
285
|
+
```md
|
|
286
|
+
# Security review
|
|
287
|
+
|
|
288
|
+
No high-confidence security findings in the reviewed changes.
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
If there are findings:
|
|
292
|
+
|
|
293
|
+
```md
|
|
294
|
+
# Security review
|
|
295
|
+
|
|
296
|
+
## Finding 1: <category> in `<file>:<line>`
|
|
297
|
+
|
|
298
|
+
- **Severity:** High | Medium
|
|
299
|
+
- **Confidence:** 8/10
|
|
300
|
+
- **Category:** `auth_bypass`
|
|
301
|
+
- **Introduced by:** short description of the changed code
|
|
302
|
+
- **Exploit path:** concrete attacker steps
|
|
303
|
+
- **Impact:** what the attacker gains
|
|
304
|
+
- **Recommendation:** specific fix
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Never include rejected candidates in the final report unless the user asks for
|
|
308
|
+
review notes or methodology.
|
|
309
|
+
|
|
310
|
+
## Severity rules
|
|
311
|
+
|
|
312
|
+
- **High:** direct path to authentication bypass, privilege escalation, RCE,
|
|
313
|
+
sensitive data breach, cross-tenant access, or account takeover.
|
|
314
|
+
- **Medium:** concrete exploit path with meaningful impact but extra
|
|
315
|
+
preconditions or limited blast radius.
|
|
316
|
+
- **Low:** do not report by default. Mention only if the user explicitly asks
|
|
317
|
+
for defense-in-depth findings.
|
|
318
|
+
|
|
319
|
+
## Hard exclusions
|
|
320
|
+
|
|
321
|
+
Never report these as findings:
|
|
322
|
+
|
|
323
|
+
- Denial of service, resource exhaustion, memory/CPU/file descriptor leaks
|
|
324
|
+
- Rate limiting gaps
|
|
325
|
+
- Dependency age or vulnerable dependency advisories
|
|
326
|
+
- Documentation-only issues
|
|
327
|
+
- Test-only issues
|
|
328
|
+
- Log spoofing
|
|
329
|
+
- Regex injection or regex DoS
|
|
330
|
+
- Generic lack of hardening, audit logs, or best practices
|
|
331
|
+
- Environment variable or CLI flag attacks, unless the repo explicitly treats
|
|
332
|
+
them as untrusted input
|
package/src/cli-commands/user.ts
CHANGED
|
@@ -119,7 +119,7 @@ cli
|
|
|
119
119
|
|
|
120
120
|
cli
|
|
121
121
|
.command('tunnel', 'Expose a local port via tunnel')
|
|
122
|
-
.option('-p, --port <port>', 'Local port to expose (
|
|
122
|
+
.option('-p, --port <port>', 'Local port to expose (optional when command output reveals one)')
|
|
123
123
|
.option(
|
|
124
124
|
'-t, --tunnel-id [id]',
|
|
125
125
|
'Custom tunnel ID (only for services safe to expose publicly; prefer random default)',
|
|
@@ -128,25 +128,24 @@ cli
|
|
|
128
128
|
.option('-s, --server [url]', 'Tunnel server URL')
|
|
129
129
|
.option('-k, --kill', 'Kill any existing process on the port before starting')
|
|
130
130
|
.action(async (options) => {
|
|
131
|
-
const { runTunnel, parseCommandFromArgv
|
|
131
|
+
const { runTunnel, parseCommandFromArgv } = await import(
|
|
132
132
|
'traforo/run-tunnel'
|
|
133
133
|
)
|
|
134
|
+
const { command } = parseCommandFromArgv(process.argv)
|
|
134
135
|
|
|
135
|
-
if (!options.port) {
|
|
136
|
-
cliLogger.error('Error: --port is required')
|
|
137
|
-
cliLogger.error(`\nUsage: kimaki tunnel
|
|
136
|
+
if (!options.port && command.length === 0) {
|
|
137
|
+
cliLogger.error('Error: --port is required unless a command is provided after --')
|
|
138
|
+
cliLogger.error(`\nUsage: kimaki tunnel [-- command]`)
|
|
139
|
+
cliLogger.error(` or: kimaki tunnel --port <port>`)
|
|
138
140
|
process.exit(EXIT_NO_RESTART)
|
|
139
141
|
}
|
|
140
142
|
|
|
141
|
-
const port = parseInt(options.port, 10)
|
|
142
|
-
if (
|
|
143
|
+
const port = options.port ? parseInt(options.port, 10) : undefined
|
|
144
|
+
if (options.port && (!port || port < 1 || port > 65535)) {
|
|
143
145
|
cliLogger.error(`Error: Invalid port number: ${options.port}`)
|
|
144
146
|
process.exit(EXIT_NO_RESTART)
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
// Parse command after -- from argv
|
|
148
|
-
const { command } = parseCommandFromArgv(process.argv)
|
|
149
|
-
|
|
150
149
|
await runTunnel({
|
|
151
150
|
port,
|
|
152
151
|
tunnelId: options.tunnelId || undefined,
|
package/src/discord-bot.ts
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
stopOpencodeServer,
|
|
21
21
|
} from './opencode.js'
|
|
22
22
|
import { formatAutoWorktreeName, createWorktreeInBackground, worktreeCreatingMessage } from './commands/new-worktree.js'
|
|
23
|
-
import { validateWorktreeDirectory, git } from './worktrees.js'
|
|
23
|
+
import { validateWorktreeDirectory, git, isGitRepositoryRoot } from './worktrees.js'
|
|
24
24
|
import { WORKTREE_PREFIX } from './commands/merge-worktree.js'
|
|
25
25
|
import {
|
|
26
26
|
escapeBackticksInCodeBlocks,
|
|
@@ -857,9 +857,21 @@ export async function startDiscordBot({
|
|
|
857
857
|
.replace(/\s+/g, ' ')
|
|
858
858
|
.trim() || 'kimaki thread'
|
|
859
859
|
|
|
860
|
-
// Check if worktrees should be enabled (CLI flag OR channel setting)
|
|
861
|
-
|
|
860
|
+
// Check if worktrees should be enabled (CLI flag OR channel setting).
|
|
861
|
+
// Only create worktrees from the configured project directory when that
|
|
862
|
+
// directory is itself the git root. If the user registered a non-git
|
|
863
|
+
// workspace folder under a larger repo, git would create the worktree
|
|
864
|
+
// from the parent repo and strand follow-up messages on failure.
|
|
865
|
+
const wantsWorktrees =
|
|
862
866
|
useWorktrees || (await getChannelWorktreesEnabled(channel.id))
|
|
867
|
+
const shouldUseWorktrees =
|
|
868
|
+
wantsWorktrees && (await isGitRepositoryRoot(projectDirectory))
|
|
869
|
+
|
|
870
|
+
if (wantsWorktrees && !shouldUseWorktrees) {
|
|
871
|
+
discordLogger.warn(
|
|
872
|
+
`[WORKTREE] Skipping automatic worktree for non-git project directory: ${projectDirectory}`,
|
|
873
|
+
)
|
|
874
|
+
}
|
|
863
875
|
|
|
864
876
|
// Add worktree prefix if worktrees are enabled
|
|
865
877
|
const threadName = shouldUseWorktrees
|
|
@@ -1062,7 +1074,7 @@ export async function startDiscordBot({
|
|
|
1062
1074
|
// The runtime is created immediately so follow-up messages queue
|
|
1063
1075
|
// naturally; the worktree promise is awaited inside enqueueIncoming.
|
|
1064
1076
|
let worktreePromise: Promise<string | Error> | undefined
|
|
1065
|
-
if (marker.worktree) {
|
|
1077
|
+
if (marker.worktree && (await isGitRepositoryRoot(projectDirectory))) {
|
|
1066
1078
|
discordLogger.log(`[BOT_SESSION] Creating worktree: ${marker.worktree}`)
|
|
1067
1079
|
|
|
1068
1080
|
const worktreeStatusMessage = await thread
|
|
@@ -1079,6 +1091,10 @@ export async function startDiscordBot({
|
|
|
1079
1091
|
projectDirectory,
|
|
1080
1092
|
rest: discordClient.rest,
|
|
1081
1093
|
})
|
|
1094
|
+
} else if (marker.worktree) {
|
|
1095
|
+
discordLogger.warn(
|
|
1096
|
+
`[BOT_SESSION] Skipping requested worktree for non-git project directory: ${projectDirectory}`,
|
|
1097
|
+
)
|
|
1082
1098
|
}
|
|
1083
1099
|
|
|
1084
1100
|
// --cwd: reuse an existing worktree directory. Revalidate at bot-time
|
|
@@ -136,7 +136,7 @@ Pick a random port between 3000-9000 to avoid conflicts:
|
|
|
136
136
|
|
|
137
137
|
${backticks}bash
|
|
138
138
|
PORT=$((RANDOM % 6000 + 3000))
|
|
139
|
-
bunx tuistory launch "PORT=$PORT kimaki tunnel
|
|
139
|
+
bunx tuistory launch "PORT=$PORT kimaki tunnel -- bun run server.ts" -s game-dev --cwd "$PWD"
|
|
140
140
|
${backticks}
|
|
141
141
|
|
|
142
142
|
Wait a moment, then get the tunnel URL:
|
|
@@ -97,23 +97,26 @@ describe('system-message', () => {
|
|
|
97
97
|
|
|
98
98
|
Only do this when the user explicitly asks to close or archive the thread, and only after your final message.
|
|
99
99
|
|
|
100
|
-
##
|
|
100
|
+
## discord user mentions
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
Prefer Discord user IDs for mentions. Discord bots cannot ping by @name; use \`<@userId>\` in message text or pass the ID to \`--user\`.
|
|
103
|
+
The current user's ID is available in the per-turn \`<discord-user ... user-id="..." />\` metadata.
|
|
104
|
+
|
|
105
|
+
To search for Discord users in a guild as a best-effort fallback, run:
|
|
103
106
|
|
|
104
107
|
kimaki user list --guild guild_123 --query "username"
|
|
105
108
|
|
|
106
|
-
This returns user IDs you can use for Discord mentions.
|
|
109
|
+
This returns user IDs you can use for Discord mentions. It can fail when Server Members Intent is disabled, so prefer IDs from existing Discord metadata or raw mentions when possible.
|
|
107
110
|
|
|
108
111
|
## starting new sessions from CLI
|
|
109
112
|
|
|
110
113
|
To start a new thread/session in this channel pro-grammatically, run:
|
|
111
114
|
|
|
112
|
-
kimaki send --channel chan_123 --prompt 'your prompt here' --agent <current_agent> --user '
|
|
115
|
+
kimaki send --channel chan_123 --prompt 'your prompt here' --agent <current_agent> --user '<discord-user-id>'
|
|
113
116
|
|
|
114
117
|
You can use this to "spawn" parallel helper sessions like teammates: start new threads with focused prompts, then come back and collect the results.
|
|
115
118
|
Prefer passing the current agent with \`--agent <current_agent>\` so spawned or scheduled sessions keep the same agent unless you are intentionally switching. Replace \`<current_agent>\` with the value from the per-turn \`Current agent\` reminder.
|
|
116
|
-
When writing \`kimaki send\` shell commands, use single quotes around \`--prompt\`, \`--user\`, \`--send-at\`, and other literal arguments so backticks inside prompts are not interpreted by the shell.
|
|
119
|
+
When writing \`kimaki send\` shell commands, use single quotes around \`--prompt\`, \`--user\`, \`--send-at\`, and other literal arguments so backticks inside prompts are not interpreted by the shell. Prefer \`--user '<discord-user-id>'\` over \`--user 'name'\` because name lookup depends on optional Server Members Intent.
|
|
117
120
|
|
|
118
121
|
IMPORTANT: NEVER use \`--worktree\` unless the user explicitly asks for a worktree. Default to creating normal threads without worktrees.
|
|
119
122
|
|
|
@@ -131,19 +134,19 @@ describe('system-message', () => {
|
|
|
131
134
|
|
|
132
135
|
Use --notify-only to create a notification thread without starting an AI session:
|
|
133
136
|
|
|
134
|
-
kimaki send --channel chan_123 --prompt 'User cancelled subscription' --notify-only --agent <current_agent> --user '
|
|
137
|
+
kimaki send --channel chan_123 --prompt 'User cancelled subscription' --notify-only --agent <current_agent> --user '<discord-user-id>'
|
|
135
138
|
|
|
136
|
-
Use --user to add a specific Discord user to the new thread:
|
|
139
|
+
Use --user with a Discord user ID or raw mention to add a specific Discord user to the new thread:
|
|
137
140
|
|
|
138
|
-
kimaki send --channel chan_123 --prompt 'Review the latest CI failure' --agent <current_agent> --user '
|
|
141
|
+
kimaki send --channel chan_123 --prompt 'Review the latest CI failure' --agent <current_agent> --user '<discord-user-id>'
|
|
139
142
|
|
|
140
143
|
Use --worktree to create a git worktree for the session (ONLY when the user explicitly asks for a worktree):
|
|
141
144
|
|
|
142
|
-
kimaki send --channel chan_123 --prompt 'Add dark mode support' --worktree dark-mode --agent <current_agent> --user '
|
|
145
|
+
kimaki send --channel chan_123 --prompt 'Add dark mode support' --worktree dark-mode --agent <current_agent> --user '<discord-user-id>'
|
|
143
146
|
|
|
144
147
|
Use --cwd to start a session in an existing git worktree directory (must be a worktree of the project):
|
|
145
148
|
|
|
146
|
-
kimaki send --channel chan_123 --prompt 'Continue work on feature' --cwd /path/to/existing-worktree --agent <current_agent> --user '
|
|
149
|
+
kimaki send --channel chan_123 --prompt 'Continue work on feature' --cwd /path/to/existing-worktree --agent <current_agent> --user '<discord-user-id>'
|
|
147
150
|
|
|
148
151
|
Important:
|
|
149
152
|
- NEVER use \`--worktree\` unless the user explicitly requests a worktree. Most tasks should use normal threads without worktrees.
|
|
@@ -154,7 +157,7 @@ describe('system-message', () => {
|
|
|
154
157
|
|
|
155
158
|
Use --agent to specify which agent to use for the session:
|
|
156
159
|
|
|
157
|
-
kimaki send --channel chan_123 --prompt 'Plan the refactor of the auth module' --agent plan --user '
|
|
160
|
+
kimaki send --channel chan_123 --prompt 'Plan the refactor of the auth module' --agent plan --user '<discord-user-id>'
|
|
158
161
|
|
|
159
162
|
|
|
160
163
|
Available agents:
|
|
@@ -166,7 +169,7 @@ describe('system-message', () => {
|
|
|
166
169
|
You can trigger registered opencode commands (slash commands, skills, MCP prompts) by starting the \`--prompt\` with \`/commandname\`:
|
|
167
170
|
|
|
168
171
|
kimaki send --thread <thread_id> --prompt '/review fix the auth module' --agent <current_agent>
|
|
169
|
-
kimaki send --channel chan_123 --prompt '/build-cmd update dependencies' --agent <current_agent> --user '
|
|
172
|
+
kimaki send --channel chan_123 --prompt '/build-cmd update dependencies' --agent <current_agent> --user '<discord-user-id>'
|
|
170
173
|
|
|
171
174
|
The command name must match a registered opencode command. If the command is not recognized, the prompt is sent as plain text to the model. This works for both new threads (\`--channel\`) and existing threads (\`--thread\`/\`--session\`).
|
|
172
175
|
|
|
@@ -182,8 +185,8 @@ describe('system-message', () => {
|
|
|
182
185
|
|
|
183
186
|
Use \`--send-at\` to schedule a one-time or recurring task:
|
|
184
187
|
|
|
185
|
-
kimaki send --channel chan_123 --prompt 'Reminder: review open PRs' --send-at '2026-03-01T09:00:00Z' --agent <current_agent> --user '
|
|
186
|
-
kimaki send --channel chan_123 --prompt 'Run weekly test suite and summarize failures' --send-at '0 9 * * 1' --agent <current_agent> --user '
|
|
188
|
+
kimaki send --channel chan_123 --prompt 'Reminder: review open PRs' --send-at '2026-03-01T09:00:00Z' --agent <current_agent> --user '<discord-user-id>'
|
|
189
|
+
kimaki send --channel chan_123 --prompt 'Run weekly test suite and summarize failures' --send-at '0 9 * * 1' --agent <current_agent> --user '<discord-user-id>'
|
|
187
190
|
|
|
188
191
|
ALL scheduling is in UTC. Dates must be UTC ISO format ending with \`Z\`. Cron expressions also fire in UTC (e.g. \`0 9 * * 1\` means 9:00 UTC every Monday).
|
|
189
192
|
When the user specifies a time without a timezone, ask them to confirm their timezone or the UTC equivalent. Never guess the user's timezone.
|
|
@@ -241,7 +244,7 @@ describe('system-message', () => {
|
|
|
241
244
|
When the user asks to "create a worktree" or "make a worktree", they mean you should use the kimaki CLI to create it. Do NOT use raw \`git worktree add\` commands. Instead use:
|
|
242
245
|
|
|
243
246
|
\`\`\`bash
|
|
244
|
-
kimaki send --channel chan_123 --prompt 'your task description' --worktree worktree-name --agent <current_agent> --user '
|
|
247
|
+
kimaki send --channel chan_123 --prompt 'your task description' --worktree worktree-name --agent <current_agent> --user '<discord-user-id>'
|
|
245
248
|
\`\`\`
|
|
246
249
|
|
|
247
250
|
This creates a new Discord thread with an isolated git worktree and starts a session in it. The worktree name should be kebab-case and descriptive of the task.
|
|
@@ -257,7 +260,7 @@ describe('system-message', () => {
|
|
|
257
260
|
Use \`--cwd\` to start a session in an existing git worktree directory instead of creating a new one:
|
|
258
261
|
|
|
259
262
|
\`\`\`bash
|
|
260
|
-
kimaki send --channel chan_123 --prompt 'Continue work on feature X' --cwd /path/to/existing-worktree --agent <current_agent> --user '
|
|
263
|
+
kimaki send --channel chan_123 --prompt 'Continue work on feature X' --cwd /path/to/existing-worktree --agent <current_agent> --user '<discord-user-id>'
|
|
261
264
|
\`\`\`
|
|
262
265
|
|
|
263
266
|
The path must be a git worktree of the project (validated via \`git worktree list\`). The session resolves to the correct project channel but uses the worktree as its working directory. Use \`--worktree\` to create a new worktree, \`--cwd\` to reuse an existing one.
|
|
@@ -271,7 +274,7 @@ describe('system-message', () => {
|
|
|
271
274
|
When you are approaching the **context window limit** or the user explicitly asks to **handoff to a new thread**, use the \`kimaki send\` command to start a fresh session with context:
|
|
272
275
|
|
|
273
276
|
\`\`\`bash
|
|
274
|
-
kimaki send --channel chan_123 --prompt 'Continuing from previous session: <summary of current task and state>' --agent <current_agent> --user '
|
|
277
|
+
kimaki send --channel chan_123 --prompt 'Continuing from previous session: <summary of current task and state>' --agent <current_agent> --user '<discord-user-id>'
|
|
275
278
|
\`\`\`
|
|
276
279
|
|
|
277
280
|
The command automatically handles long prompts (over 2000 chars) by sending them as file attachments.
|
|
@@ -504,11 +507,11 @@ describe('system-message', () => {
|
|
|
504
507
|
|
|
505
508
|
Use random tunnel IDs by default. Only pass \`-t\` when exposing a service that is safe to be publicly discoverable.
|
|
506
509
|
|
|
507
|
-
\`kimaki tunnel\` injects \`TRAFORO_URL\` into the child process. Prefer wiring your app to that URL so OAuth callbacks, webhook URLs, and absolute links use the public tunnel instead of localhost.
|
|
510
|
+
\`kimaki tunnel\` injects \`TRAFORO_URL\` into the child process. Prefer wiring your app to that URL so OAuth callbacks, webhook URLs, and absolute links use the public tunnel instead of localhost. The local port is detected from the child process output, so do not pass \`-p\` when launching a dev server command unless detection fails.
|
|
508
511
|
|
|
509
512
|
\`\`\`bash
|
|
510
513
|
# Start the dev server in a named background session
|
|
511
|
-
bunx tuistory launch "kimaki tunnel
|
|
514
|
+
bunx tuistory launch "kimaki tunnel -- pnpm dev" -s myapp-dev
|
|
512
515
|
|
|
513
516
|
# Wait until the dev server prints something useful, then inspect it
|
|
514
517
|
bunx tuistory -s myapp-dev wait "/ready|local|tunnel/i" --timeout 30000
|
|
@@ -517,7 +520,7 @@ describe('system-message', () => {
|
|
|
517
520
|
|
|
518
521
|
### passing the public URL to your app
|
|
519
522
|
|
|
520
|
-
If you launch the server command through \`kimaki tunnel -- ...\`, the local port is auto-detected from the child process logs in many common dev-server setups
|
|
523
|
+
If you launch the server command through \`kimaki tunnel -- ...\`, the local port is auto-detected from the child process logs in many common dev-server setups. Use \`--port\` only when the dev server does not print a detectable localhost URL or port line.
|
|
521
524
|
|
|
522
525
|
\`\`\`bash
|
|
523
526
|
# Your app can read process.env.TRAFORO_URL directly
|
|
@@ -544,13 +547,13 @@ describe('system-message', () => {
|
|
|
544
547
|
|
|
545
548
|
\`\`\`bash
|
|
546
549
|
# Next.js project
|
|
547
|
-
bunx tuistory launch "kimaki tunnel
|
|
550
|
+
bunx tuistory launch "kimaki tunnel -- pnpm dev" -s projectname-nextjs-dev
|
|
548
551
|
|
|
549
|
-
# Vite project
|
|
550
|
-
bunx tuistory launch "kimaki tunnel
|
|
552
|
+
# Vite project
|
|
553
|
+
bunx tuistory launch "kimaki tunnel -- pnpm dev" -s vite-dev
|
|
551
554
|
|
|
552
555
|
# Custom tunnel ID (only for intentionally public-safe services)
|
|
553
|
-
bunx tuistory launch "kimaki tunnel -
|
|
556
|
+
bunx tuistory launch "kimaki tunnel -t holocron -- pnpm dev" -s holocron-dev
|
|
554
557
|
\`\`\`
|
|
555
558
|
|
|
556
559
|
### stopping the dev server
|
package/src/system-message.ts
CHANGED
|
@@ -139,11 +139,11 @@ Use a tuistory session with a descriptive name like \`projectname-dev\` so you c
|
|
|
139
139
|
|
|
140
140
|
Use random tunnel IDs by default. Only pass \`-t\` when exposing a service that is safe to be publicly discoverable.
|
|
141
141
|
|
|
142
|
-
\`kimaki tunnel\` injects \`TRAFORO_URL\` into the child process. Prefer wiring your app to that URL so OAuth callbacks, webhook URLs, and absolute links use the public tunnel instead of localhost.
|
|
142
|
+
\`kimaki tunnel\` injects \`TRAFORO_URL\` into the child process. Prefer wiring your app to that URL so OAuth callbacks, webhook URLs, and absolute links use the public tunnel instead of localhost. The local port is detected from the child process output, so do not pass \`-p\` when launching a dev server command unless detection fails.
|
|
143
143
|
|
|
144
144
|
\`\`\`bash
|
|
145
145
|
# Start the dev server in a named background session
|
|
146
|
-
bunx tuistory launch "kimaki tunnel
|
|
146
|
+
bunx tuistory launch "kimaki tunnel -- pnpm dev" -s myapp-dev
|
|
147
147
|
|
|
148
148
|
# Wait until the dev server prints something useful, then inspect it
|
|
149
149
|
bunx tuistory -s myapp-dev wait "/ready|local|tunnel/i" --timeout 30000
|
|
@@ -152,7 +152,7 @@ bunx tuistory read -s myapp-dev
|
|
|
152
152
|
|
|
153
153
|
### passing the public URL to your app
|
|
154
154
|
|
|
155
|
-
If you launch the server command through \`kimaki tunnel -- ...\`, the local port is auto-detected from the child process logs in many common dev-server setups
|
|
155
|
+
If you launch the server command through \`kimaki tunnel -- ...\`, the local port is auto-detected from the child process logs in many common dev-server setups. Use \`--port\` only when the dev server does not print a detectable localhost URL or port line.
|
|
156
156
|
|
|
157
157
|
\`\`\`bash
|
|
158
158
|
# Your app can read process.env.TRAFORO_URL directly
|
|
@@ -179,13 +179,13 @@ bunx tuistory read -s myapp-dev
|
|
|
179
179
|
|
|
180
180
|
\`\`\`bash
|
|
181
181
|
# Next.js project
|
|
182
|
-
bunx tuistory launch "kimaki tunnel
|
|
182
|
+
bunx tuistory launch "kimaki tunnel -- pnpm dev" -s projectname-nextjs-dev
|
|
183
183
|
|
|
184
|
-
# Vite project
|
|
185
|
-
bunx tuistory launch "kimaki tunnel
|
|
184
|
+
# Vite project
|
|
185
|
+
bunx tuistory launch "kimaki tunnel -- pnpm dev" -s vite-dev
|
|
186
186
|
|
|
187
187
|
# Custom tunnel ID (only for intentionally public-safe services)
|
|
188
|
-
bunx tuistory launch "kimaki tunnel -
|
|
188
|
+
bunx tuistory launch "kimaki tunnel -t holocron -- pnpm dev" -s holocron-dev
|
|
189
189
|
\`\`\`
|
|
190
190
|
|
|
191
191
|
### stopping the dev server
|
|
@@ -865,6 +865,14 @@ e2eTest('voice message handling', () => {
|
|
|
865
865
|
queueMessage: true,
|
|
866
866
|
})
|
|
867
867
|
|
|
868
|
+
await waitForBotMessageContaining({
|
|
869
|
+
discord,
|
|
870
|
+
threadId: thread.id,
|
|
871
|
+
userId: TEST_USER_ID,
|
|
872
|
+
text: 'using deterministic-provider/deterministic-v2',
|
|
873
|
+
timeout: 4_000,
|
|
874
|
+
})
|
|
875
|
+
|
|
868
876
|
await th.user(TEST_USER_ID).sendVoiceMessage()
|
|
869
877
|
|
|
870
878
|
// 3. Transcription should appear, followed by queue notification
|