@agentshouse/kit 0.2.1-cd646adba3f64 → 0.2.1-cde47fd72fe59
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/README.md +30 -28
- package/dist/acp.js +46 -103
- package/dist/agents.js +142 -41
- package/dist/api.js +9 -5
- package/dist/app-git.js +10 -26
- package/dist/bootstrap.json +4 -4
- package/dist/bridge.js +14 -43
- package/dist/chatgpt.js +32 -0
- package/dist/clis.js +124 -69
- package/dist/codex-main.js +58 -0
- package/dist/conversations.js +385 -277
- package/dist/copies.js +195 -119
- package/dist/end.js +19 -4
- package/dist/enrol-main.js +7 -1
- package/dist/events.js +525 -0
- package/dist/files.js +22 -2
- package/dist/git.js +10 -8
- package/dist/home.js +96 -7
- package/dist/house-cli.js +291 -0
- package/dist/house-main.js +7 -159
- package/dist/inputs.js +37 -0
- package/dist/instructions.js +10 -32
- package/dist/kit-cli.js +68 -0
- package/dist/kit-main.js +8 -22
- package/dist/login.js +117 -106
- package/dist/logout.js +14 -16
- package/dist/parts.js +82 -0
- package/dist/placement.js +30 -0
- package/dist/relay.js +12 -2
- package/dist/resident.js +91 -33
- package/dist/scope.js +203 -0
- package/dist/service-main.js +9 -0
- package/dist/service.js +55 -0
- package/dist/sign-in.js +34 -28
- package/dist/skills.js +81 -12
- package/dist/stream.js +68 -17
- package/dist/transcription.js +3 -3
- package/dist/translate.js +2 -8
- package/dist/update.js +48 -29
- package/dist/wire.js +6 -0
- package/dist/work.js +346 -0
- package/how-we-work.md +13 -3
- package/package.json +3 -2
- package/skills/ask-matt/SKILL.md +17 -11
- package/skills/ask-matt/agents/openai.yaml +1 -0
- package/skills/code-review/SKILL.md +5 -5
- package/skills/codebase-design/DESIGN-IT-TWICE.md +1 -1
- package/skills/codebase-design/SKILL.md +0 -2
- package/skills/diagnosing-bugs/SKILL.md +2 -4
- package/skills/domain-modeling/{CONTEXT-FORMAT.md → GLOSSARY-FORMAT.md} +10 -10
- package/skills/domain-modeling/SKILL.md +12 -14
- package/skills/grill-me/SKILL.md +1 -2
- package/skills/grill-me/agents/openai.yaml +1 -0
- package/skills/grill-with-docs/SKILL.md +1 -2
- package/skills/grill-with-docs/agents/openai.yaml +1 -0
- package/skills/grilling/SKILL.md +2 -2
- package/skills/handoff/SKILL.md +2 -3
- package/skills/handoff/agents/openai.yaml +1 -0
- package/skills/implement/SKILL.md +5 -4
- package/skills/implement/agents/openai.yaml +1 -0
- package/skills/implement-spec/SKILL.md +40 -0
- package/skills/implement-spec/agents/openai.yaml +5 -0
- package/skills/improve-codebase-architecture/SKILL.md +6 -7
- package/skills/improve-codebase-architecture/agents/openai.yaml +1 -0
- package/skills/pr/CREDITS.md +3 -0
- package/skills/pr/LICENSE +21 -0
- package/skills/pr/SKILL.md +170 -0
- package/skills/pr/agents/openai.yaml +3 -0
- package/skills/prototype/SKILL.md +0 -2
- package/skills/research/SKILL.md +0 -2
- package/skills/retro/LICENSE +21 -0
- package/skills/retro/SKILL.md +44 -0
- package/skills/retro/agents/openai.yaml +5 -0
- package/skills/tdd/SKILL.md +2 -4
- package/skills/teach/SKILL.md +3 -4
- package/skills/teach/agents/openai.yaml +1 -0
- package/skills/to-questionnaire/SKILL.md +1 -2
- package/skills/to-questionnaire/agents/openai.yaml +1 -0
- package/skills/to-spec/SKILL.md +1 -2
- package/skills/to-spec/agents/openai.yaml +1 -0
- package/skills/to-tickets/SKILL.md +3 -4
- package/skills/to-tickets/agents/openai.yaml +1 -0
- package/skills/triage/AGENT-BRIEF.md +207 -0
- package/skills/triage/LICENSE +21 -0
- package/skills/triage/OUT-OF-SCOPE.md +105 -0
- package/skills/triage/SKILL.md +112 -0
- package/skills/triage/agents/openai.yaml +5 -0
- package/skills/wait-what/SKILL.md +2 -3
- package/skills/wait-what/agents/openai.yaml +1 -0
- package/skills/wayfinder/SKILL.md +5 -6
- package/skills/wayfinder/agents/openai.yaml +1 -0
- package/skills/wizard/LICENSE +21 -0
- package/skills/wizard/SKILL.md +44 -0
- package/skills/wizard/agents/openai.yaml +3 -0
- package/skills/wizard/template.sh +223 -0
- package/skills/writing-for-agents/SKILL.md +0 -2
- package/dist/blocks.js +0 -51
- package/skills/resolving-merge-conflicts/SKILL.md +0 -16
- package/skills/resolving-merge-conflicts/agents/openai.yaml +0 -3
- /package/skills/{resolving-merge-conflicts → implement-spec}/LICENSE +0 -0
package/dist/work.js
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { once } from 'node:events';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { rm } from 'node:fs/promises';
|
|
5
|
+
import { createServer } from 'node:net';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { RUNNING_JOB } from './events.js';
|
|
8
|
+
import { kitHome, socketDirectory } from './home.js';
|
|
9
|
+
const ENDED_TASK = new Set(['completed', 'failed', 'killed']);
|
|
10
|
+
const SCHEDULING = new Set(['CronCreate', 'CronDelete', 'ScheduleWakeup']);
|
|
11
|
+
const QUEUE_LIST = 'house-kit-queue-';
|
|
12
|
+
const SYNC = 'house-kit-sync-';
|
|
13
|
+
const WAKEUP = /Next wakeup scheduled for (\d\d):(\d\d):(\d\d) \(in (\d+)s\)/;
|
|
14
|
+
const HALF_DAY = 12 * 60 * 60 * 1000;
|
|
15
|
+
export function codexLauncher() {
|
|
16
|
+
return join(kitHome(), 'launchers', 'codex');
|
|
17
|
+
}
|
|
18
|
+
export class Work {
|
|
19
|
+
changed;
|
|
20
|
+
jobs = new Set();
|
|
21
|
+
constructor(changed) {
|
|
22
|
+
this.changed = changed;
|
|
23
|
+
}
|
|
24
|
+
async open(_cli) {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
async settled() { }
|
|
28
|
+
read(message) {
|
|
29
|
+
this.take(message);
|
|
30
|
+
this.changed();
|
|
31
|
+
}
|
|
32
|
+
take(message) {
|
|
33
|
+
const update = message.params?.update;
|
|
34
|
+
if (message.method !== 'session/update' || update?.asyncTaskId === undefined)
|
|
35
|
+
return;
|
|
36
|
+
if (update.sessionUpdate === 'async_task_spawned')
|
|
37
|
+
this.jobs.add(update.asyncTaskId);
|
|
38
|
+
if (update.sessionUpdate === 'async_task_state_update' && !RUNNING_JOB.has(update.state))
|
|
39
|
+
this.jobs.delete(update.asyncTaskId);
|
|
40
|
+
}
|
|
41
|
+
busy() {
|
|
42
|
+
return this.jobs.size > 0;
|
|
43
|
+
}
|
|
44
|
+
close() { }
|
|
45
|
+
}
|
|
46
|
+
class ClaudeWork extends Work {
|
|
47
|
+
cwd;
|
|
48
|
+
level = new Set();
|
|
49
|
+
listed = new Set();
|
|
50
|
+
started = new Set();
|
|
51
|
+
running = false;
|
|
52
|
+
hooks = new Set();
|
|
53
|
+
calls = new Map();
|
|
54
|
+
crons = new Map();
|
|
55
|
+
wakeups = [];
|
|
56
|
+
goal = false;
|
|
57
|
+
constructor(cwd, changed) {
|
|
58
|
+
super(changed);
|
|
59
|
+
this.cwd = cwd;
|
|
60
|
+
}
|
|
61
|
+
take(message) {
|
|
62
|
+
super.take(message);
|
|
63
|
+
if (message.method === '_claude/sdkMessage')
|
|
64
|
+
this.sdk(message.params.message);
|
|
65
|
+
if (message.method === 'session/update')
|
|
66
|
+
this.update(message.params.update);
|
|
67
|
+
}
|
|
68
|
+
busy() {
|
|
69
|
+
return (super.busy() ||
|
|
70
|
+
this.level.size > 0 ||
|
|
71
|
+
this.started.size > 0 ||
|
|
72
|
+
this.running ||
|
|
73
|
+
this.hooks.size > 0 ||
|
|
74
|
+
this.crons.size > 0 ||
|
|
75
|
+
this.wakeups.length > 0 ||
|
|
76
|
+
this.goal ||
|
|
77
|
+
this.durable());
|
|
78
|
+
}
|
|
79
|
+
sdk(message) {
|
|
80
|
+
switch (message.subtype) {
|
|
81
|
+
case 'background_tasks_changed':
|
|
82
|
+
this.level = new Set(message.tasks.map((task) => task.task_id));
|
|
83
|
+
for (const id of this.level) {
|
|
84
|
+
this.listed.add(id);
|
|
85
|
+
this.started.delete(id);
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
case 'task_started':
|
|
89
|
+
if (!this.listed.has(message.task_id))
|
|
90
|
+
this.started.add(message.task_id);
|
|
91
|
+
break;
|
|
92
|
+
case 'task_updated':
|
|
93
|
+
if (ENDED_TASK.has(message.patch.status))
|
|
94
|
+
this.started.delete(message.task_id);
|
|
95
|
+
break;
|
|
96
|
+
case 'task_notification':
|
|
97
|
+
this.started.delete(message.task_id);
|
|
98
|
+
break;
|
|
99
|
+
case 'session_state_changed':
|
|
100
|
+
if (message.state === 'running' && !this.running)
|
|
101
|
+
this.wakeups = this.wakeups.filter((due) => due > Date.now());
|
|
102
|
+
this.running = message.state === 'running';
|
|
103
|
+
break;
|
|
104
|
+
case 'hook_started':
|
|
105
|
+
this.hooks.add(message.hook_id);
|
|
106
|
+
break;
|
|
107
|
+
case 'hook_response':
|
|
108
|
+
this.hooks.delete(message.hook_id);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
update(update) {
|
|
113
|
+
const air = update._meta?.jetbrains?.air;
|
|
114
|
+
if (update.sessionUpdate === 'session_info_update' && air !== undefined && 'goal' in air) {
|
|
115
|
+
this.goal = air.goal?.status === 'active';
|
|
116
|
+
}
|
|
117
|
+
if (update.sessionUpdate !== 'tool_call' && update.sessionUpdate !== 'tool_call_update')
|
|
118
|
+
return;
|
|
119
|
+
const tool = update._meta?.claudeCode?.toolName;
|
|
120
|
+
if (update.sessionUpdate === 'tool_call' && SCHEDULING.has(tool ?? ''))
|
|
121
|
+
this.calls.set(update.toolCallId, { tool });
|
|
122
|
+
const call = this.calls.get(update.toolCallId);
|
|
123
|
+
if (call === undefined)
|
|
124
|
+
return;
|
|
125
|
+
if (update.rawInput !== undefined)
|
|
126
|
+
call.input = update.rawInput;
|
|
127
|
+
if (update.content !== undefined)
|
|
128
|
+
call.content = update.content;
|
|
129
|
+
if (update.status === 'failed')
|
|
130
|
+
this.calls.delete(update.toolCallId);
|
|
131
|
+
if (update.status !== 'completed')
|
|
132
|
+
return;
|
|
133
|
+
this.calls.delete(update.toolCallId);
|
|
134
|
+
if (call.tool === 'CronCreate')
|
|
135
|
+
this.crons.set(update.toolCallId, JSON.stringify(call.content ?? []));
|
|
136
|
+
if (call.tool === 'ScheduleWakeup')
|
|
137
|
+
this.wakeup(call);
|
|
138
|
+
if (call.tool !== 'CronDelete')
|
|
139
|
+
return;
|
|
140
|
+
for (const [id, text] of this.crons)
|
|
141
|
+
if (text.includes(String(call.input?.id)))
|
|
142
|
+
this.crons.delete(id);
|
|
143
|
+
}
|
|
144
|
+
wakeup(call) {
|
|
145
|
+
if (call.input?.stop === true) {
|
|
146
|
+
this.wakeups = [];
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const time = WAKEUP.exec(JSON.stringify(call.content ?? []));
|
|
150
|
+
if (time === null) {
|
|
151
|
+
this.wakeups.push(Infinity);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const fires = Date.now() + Number(time[4]) * 1000;
|
|
155
|
+
const due = new Date(fires);
|
|
156
|
+
due.setHours(Number(time[1]), Number(time[2]), Number(time[3]), 0);
|
|
157
|
+
if (due.getTime() - fires > HALF_DAY)
|
|
158
|
+
due.setDate(due.getDate() - 1);
|
|
159
|
+
if (fires - due.getTime() > HALF_DAY)
|
|
160
|
+
due.setDate(due.getDate() + 1);
|
|
161
|
+
this.wakeups.push(due.getTime());
|
|
162
|
+
}
|
|
163
|
+
durable() {
|
|
164
|
+
let text;
|
|
165
|
+
try {
|
|
166
|
+
text = readFileSync(join(this.cwd, '.claude', 'scheduled_tasks.json'), 'utf8');
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
return error.code !== 'ENOENT';
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
return JSON.parse(text).tasks?.length !== 0;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
class CodexWork extends Work {
|
|
180
|
+
threads = new Map();
|
|
181
|
+
queues = new Map();
|
|
182
|
+
goals = new Set();
|
|
183
|
+
items = new Set();
|
|
184
|
+
hooks = new Set();
|
|
185
|
+
starting = new Set();
|
|
186
|
+
settling = [];
|
|
187
|
+
memories = null;
|
|
188
|
+
lost = false;
|
|
189
|
+
server = null;
|
|
190
|
+
path = null;
|
|
191
|
+
socket = null;
|
|
192
|
+
asked = 0;
|
|
193
|
+
heard = 0;
|
|
194
|
+
proving = -1;
|
|
195
|
+
proven = -1;
|
|
196
|
+
async open(cli) {
|
|
197
|
+
this.path = join(socketDirectory(), `${randomUUID()}.sock`);
|
|
198
|
+
this.server = createServer((socket) => this.connected(socket));
|
|
199
|
+
this.server.listen(this.path);
|
|
200
|
+
await once(this.server, 'listening');
|
|
201
|
+
return { CODEX_PATH: codexLauncher(), HOUSE_KIT_CODEX: cli, HOUSE_KIT_CODEX_STREAM: this.path };
|
|
202
|
+
}
|
|
203
|
+
settled() {
|
|
204
|
+
return new Promise((resolve) => {
|
|
205
|
+
this.settling.push(resolve);
|
|
206
|
+
this.settle();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
take(message) {
|
|
210
|
+
super.take(message);
|
|
211
|
+
this.heard++;
|
|
212
|
+
}
|
|
213
|
+
busy() {
|
|
214
|
+
if (super.busy() ||
|
|
215
|
+
this.lost ||
|
|
216
|
+
this.memories !== false ||
|
|
217
|
+
[...this.threads.values()].some(Boolean) ||
|
|
218
|
+
this.queues.size > 0 ||
|
|
219
|
+
this.goals.size > 0 ||
|
|
220
|
+
this.items.size > 0 ||
|
|
221
|
+
this.hooks.size > 0) {
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
if (this.proven === this.heard)
|
|
225
|
+
return false;
|
|
226
|
+
this.prove();
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
close() {
|
|
230
|
+
this.server?.close();
|
|
231
|
+
if (this.path !== null)
|
|
232
|
+
void rm(this.path, { force: true });
|
|
233
|
+
}
|
|
234
|
+
connected(socket) {
|
|
235
|
+
this.socket = socket;
|
|
236
|
+
let buffer = '';
|
|
237
|
+
socket.on('error', () => this.lose());
|
|
238
|
+
socket.on('close', () => this.lose());
|
|
239
|
+
socket.setEncoding('utf8').on('data', (chunk) => {
|
|
240
|
+
buffer += chunk;
|
|
241
|
+
for (let index = buffer.indexOf('\n'); index >= 0; index = buffer.indexOf('\n')) {
|
|
242
|
+
const message = this.parsed(buffer.slice(0, index));
|
|
243
|
+
buffer = buffer.slice(index + 1);
|
|
244
|
+
if (message !== null)
|
|
245
|
+
this.stream(message, socket);
|
|
246
|
+
}
|
|
247
|
+
this.settle();
|
|
248
|
+
this.changed();
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
parsed(line) {
|
|
252
|
+
try {
|
|
253
|
+
return JSON.parse(line);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
this.lost = true;
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
lose() {
|
|
261
|
+
this.lost = true;
|
|
262
|
+
this.changed();
|
|
263
|
+
}
|
|
264
|
+
prove() {
|
|
265
|
+
if (this.proving === this.heard)
|
|
266
|
+
return;
|
|
267
|
+
this.proving = this.heard;
|
|
268
|
+
this.socket.write(`${JSON.stringify({ jsonrpc: '2.0', id: `${SYNC}${this.heard}`, method: 'house-kit/sync' })}\n`);
|
|
269
|
+
}
|
|
270
|
+
settle() {
|
|
271
|
+
if (this.starting.size === 0)
|
|
272
|
+
for (const resolve of this.settling.splice(0))
|
|
273
|
+
resolve();
|
|
274
|
+
}
|
|
275
|
+
stream(message, socket) {
|
|
276
|
+
const params = message.params;
|
|
277
|
+
switch (message.method) {
|
|
278
|
+
case 'thread/status/changed':
|
|
279
|
+
this.threads.set(params.threadId, params.status.type === 'active' && params.status.activeFlags.length === 0);
|
|
280
|
+
break;
|
|
281
|
+
case 'thread/closed':
|
|
282
|
+
this.threads.delete(params.threadId);
|
|
283
|
+
break;
|
|
284
|
+
case 'thread/queue/changed':
|
|
285
|
+
this.queues.set(params.threadId, ++this.asked);
|
|
286
|
+
socket.write(`${JSON.stringify({ jsonrpc: '2.0', id: `${QUEUE_LIST}${this.asked}`, method: 'thread/queue/list', params: { threadId: params.threadId } })}\n`);
|
|
287
|
+
break;
|
|
288
|
+
case 'thread/goal/updated':
|
|
289
|
+
if (params.goal.status === 'active')
|
|
290
|
+
this.goals.add(params.threadId);
|
|
291
|
+
else
|
|
292
|
+
this.goals.delete(params.threadId);
|
|
293
|
+
break;
|
|
294
|
+
case 'thread/goal/cleared':
|
|
295
|
+
this.goals.delete(params.threadId);
|
|
296
|
+
break;
|
|
297
|
+
case 'item/started':
|
|
298
|
+
this.items.add(`${params.threadId} ${params.item.id}`);
|
|
299
|
+
break;
|
|
300
|
+
case 'item/completed':
|
|
301
|
+
this.items.delete(`${params.threadId} ${params.item.id}`);
|
|
302
|
+
break;
|
|
303
|
+
case 'hook/started':
|
|
304
|
+
this.hooks.add(params.run.id);
|
|
305
|
+
break;
|
|
306
|
+
case 'hook/completed':
|
|
307
|
+
this.hooks.delete(params.run.id);
|
|
308
|
+
break;
|
|
309
|
+
case 'mcpServer/startupStatus/updated':
|
|
310
|
+
if (params.status === 'starting')
|
|
311
|
+
this.starting.add(`${params.threadId} ${params.name}`);
|
|
312
|
+
else
|
|
313
|
+
this.starting.delete(`${params.threadId} ${params.name}`);
|
|
314
|
+
break;
|
|
315
|
+
case undefined:
|
|
316
|
+
this.answered(message);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
answered(message) {
|
|
320
|
+
if (typeof message.id === 'string' && message.id.startsWith(SYNC)) {
|
|
321
|
+
this.proven = Number(message.id.slice(SYNC.length));
|
|
322
|
+
}
|
|
323
|
+
else if (typeof message.id === 'string' && message.id.startsWith(QUEUE_LIST)) {
|
|
324
|
+
const asked = Number(message.id.slice(QUEUE_LIST.length));
|
|
325
|
+
for (const [thread, latest] of this.queues) {
|
|
326
|
+
if (latest === asked && message.result?.data?.length === 0)
|
|
327
|
+
this.queues.delete(thread);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
else if (message.result?.config?.features !== undefined) {
|
|
331
|
+
this.memories = message.result.config.features.memories !== false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
class GrokWork extends Work {
|
|
336
|
+
busy() {
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
export function workFor(kind, cwd, changed) {
|
|
341
|
+
if (kind === 'claude-agent-acp')
|
|
342
|
+
return new ClaudeWork(cwd, changed);
|
|
343
|
+
if (kind === 'codex-acp')
|
|
344
|
+
return new CodexWork(changed);
|
|
345
|
+
return new GrokWork(changed);
|
|
346
|
+
}
|
package/how-we-work.md
CHANGED
|
@@ -4,20 +4,30 @@ A Room is where information people share is kept; it is not the place for
|
|
|
4
4
|
private information. Work goes into the Room it belongs to, or the First Room
|
|
5
5
|
when none is named.
|
|
6
6
|
|
|
7
|
+
Every House answer carries the `url` of what it names; send the User that link.
|
|
8
|
+
|
|
7
9
|
The skills are already set up; never run `setup-matt-pocock-skills`.
|
|
8
10
|
|
|
9
11
|
## Where skill output goes
|
|
10
12
|
|
|
11
|
-
- Glossary (`
|
|
13
|
+
- Glossary (`GLOSSARY.md`): `glossary.md`
|
|
12
14
|
- Decisions (ADRs): `decisions/NNNN-<slug>.md`
|
|
13
15
|
- Plan (spec): `projects/<name>/plan.md`
|
|
14
16
|
- Tasks (tickets): `projects/<name>/tasks/NN-<slug>.md`, with a
|
|
15
17
|
`Status:` line (`needs-triage`, `needs-info`, `ready-for-agent`,
|
|
16
|
-
`ready-for-human`, `wontfix
|
|
17
|
-
|
|
18
|
+
`ready-for-human`, `wontfix`, or `done` once implemented), a
|
|
19
|
+
`Category:` line (`bug`, `enhancement`), a `Blocked by:` line and comments
|
|
20
|
+
under `## Comments`; triage takes no pull requests
|
|
21
|
+
- Rejected requests (triage): `out-of-scope/<concept>.md`
|
|
22
|
+
- Implementation notes (implement-spec): `projects/<name>/notes/`
|
|
23
|
+
- PR bodies (pr): `projects/<name>/prs/<branch>.md`
|
|
18
24
|
- Map (Wayfinding): `projects/<name>/map.md`, its tasks under
|
|
19
25
|
`tasks/` with `Type:`, `Status: claimed|resolved` and `Blocked by:` lines
|
|
20
26
|
- Research: `research/<slug>.md`
|
|
27
|
+
- Architecture reports (improve-codebase-architecture): `architecture/<slug>.html`
|
|
28
|
+
- Handoffs: `handoffs/<slug>.md`
|
|
21
29
|
- Questionnaires: `questionnaires/<slug>.md`
|
|
30
|
+
- Retrospectives (retro): `retros/<date>-<slug>.md`
|
|
31
|
+
- Wizards: `wizards/<slug>.sh`; the values they capture stay out of the Room
|
|
22
32
|
- Learning: `learning/<topic>/`
|
|
23
33
|
- Prototype: one HTML file with every variant on one page.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentshouse/kit",
|
|
3
|
-
"version": "0.2.1-
|
|
3
|
+
"version": "0.2.1-cde47fd72fe59",
|
|
4
4
|
"description": "House Kit connects an Environment to agents.house.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -23,11 +23,12 @@
|
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "tsc -p tsconfig.build.json && node scripts/bootstrap-manifest.mjs",
|
|
25
25
|
"engine": "mkdir -p tmp/engine && docker run --rm -v \"$PWD:/kit\" -w /kit alpine:3.24@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 sh scripts/engine.sh tmp/engine",
|
|
26
|
+
"sync-skills": "node scripts/sync-skills.mjs",
|
|
26
27
|
"test": "tsc -p tsconfig.json && vitest run"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
30
|
"@agentclientprotocol/sdk": "1.7.0",
|
|
30
|
-
"@agentshouse/mdmodel": "0.1.0-
|
|
31
|
+
"@agentshouse/mdmodel": "0.1.0-cabd61587647f",
|
|
31
32
|
"yaml": "2.9.1"
|
|
32
33
|
},
|
|
33
34
|
"peerDependencies": {
|
package/skills/ask-matt/SKILL.md
CHANGED
|
@@ -1,34 +1,41 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ask-matt
|
|
3
3
|
description: Ask which skill or flow fits your situation. A router over the skills in this repo.
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
<!-- Vendored from mattpocock/skills at commit c55ee46073ed923f86ce59a5eb3b6d895095d1b7 (skills/engineering/ask-matt). MIT licensed — see LICENSE in this directory. -->
|
|
7
|
-
|
|
8
7
|
# Ask Matt
|
|
9
8
|
|
|
10
9
|
You don't remember every skill, so ask.
|
|
11
10
|
|
|
11
|
+
Before stating what a skill does or recommending a step be skipped, read that skill's SKILL.md: the summaries here are for orientation only.
|
|
12
|
+
|
|
12
13
|
A **flow** is a path through the skills. Most paths run along one **main flow**, and two **on-ramps** merge onto it. Everything else is standalone, or a vocabulary layer that runs underneath.
|
|
13
14
|
|
|
14
15
|
## The main flow: idea → ship
|
|
15
16
|
|
|
16
17
|
The route most work travels. You have an idea and want it built.
|
|
17
18
|
|
|
18
|
-
1. **`/grill-with-docs`** sharpens the idea by interview. Start here whenever you are **working in a working directory**: it's stateful, retaining what it learns in `
|
|
19
|
+
1. **`/grill-with-docs`** sharpens the idea by interview. Start here whenever you are **working in a working directory**: it's stateful, retaining what it learns in `GLOSSARY.md` and ADRs. (No working directory? Use `/grill-me` instead, covered under Standalone. Both run the same `/grilling` primitive; `grill-with-docs` is the one that leaves a paper trail, which makes it the better of the two whenever a repo is there to leave it in.)
|
|
19
20
|
2. **Branch: can you settle every question in conversation?** If a question needs a runnable answer (state, business logic, a UI you have to see), detour through a prototype, bridged by **`/handoff`** in both directions (a prototype lives in its own directory, which is exactly what `/handoff` is for; see Phase boundaries):
|
|
20
21
|
- **`/handoff`** out, then open a fresh session against that file,
|
|
21
22
|
- **`/prototype`** to answer the question with throwaway code,
|
|
22
23
|
- **`/handoff`** back what you learned, and reference it from the original idea thread.
|
|
23
24
|
3. **Branch: is this a multi-session build?**
|
|
24
|
-
- **Yes** → **`/to-spec`** (turn the thread into a spec), then **`/to-tickets`** to split it into tracer-bullet tickets, each declaring its **blocking edges**.
|
|
25
|
+
- **Yes** → **`/to-spec`** (turn the thread into a spec), then **`/to-tickets`** to split it into tracer-bullet tickets, each declaring its **blocking edges**. Then work the tickets one of two ways:
|
|
26
|
+
- **`/implement`** per ticket, **`/clear`ing context between each one**. On a local tracker that's one file per ticket under `.scratch/<feature>/issues/`, worked blockers-first by hand; on a real tracker the edges become native blocking links, so any ticket whose blockers are done can be grabbed. Each ticket is self-contained, so the last one's context is disposable.
|
|
27
|
+
- **`/implement-spec`** for the whole spec in one run. It reads the tickets as a **task graph**, runs implementer subagents across the ready **frontier** in parallel, and lands everything on one **integration branch**. Reach for it when you'd rather orchestrate the build than drive each ticket yourself.
|
|
25
28
|
- **No** → **`/implement`** right here, in the same context window.
|
|
26
29
|
|
|
27
|
-
Either way,
|
|
30
|
+
Either way, the code gets built by driving **`/tdd`** (one red-green slice at a time) and closes out with **`/code-review`**, a two-axis review (Standards + Spec) of the diff. `/implement` runs both per ticket; `/implement-spec`'s implementers each drive `/tdd`, and it runs one `/code-review` over the integration branch. Reach for **`/tdd`** on its own when you just want to build a concrete behaviour test-first without a full spec, and **`/code-review`** on its own whenever you want to review a branch or PR against a fixed point.
|
|
31
|
+
|
|
32
|
+
When the work goes up as a pull request, **`/pr`** shapes the body: the smallest visual that shows the change, before/after evidence that it works, and a one-way or two-way door call. It's model-invoked, so the agent reaches for it whenever it writes a PR.
|
|
33
|
+
|
|
34
|
+
4. **`/retro`** closes the loop. After a build, and especially one that went sideways, it looks back over the session and suggests changes to the agent's **environment**, not the code: navigation pointers, automated checks, the coding standards `/code-review` enforces, steering files, tooling. Mechanical mistakes become deterministic checks; judgement calls become coding standards. The next build then starts from a better environment.
|
|
28
35
|
|
|
29
36
|
### Context hygiene
|
|
30
37
|
|
|
31
|
-
Keep steps 1–3 in **one unbroken context window** (don't compact or clear until after `/to-tickets`) so the grilling, spec, and tickets all build on the same thinking. Each `/implement` then starts fresh, working from the ticket.
|
|
38
|
+
Keep steps 1–3 in **one unbroken context window** (don't compact or clear until after `/to-tickets`) so the grilling, spec, and tickets all build on the same thinking. Each `/implement` then starts fresh, working from the ticket. Run `/retro` in the session it's looking back on, before you clear; after clearing, point it at that session's log instead.
|
|
32
39
|
|
|
33
40
|
The limit on this is the **[smart zone](https://www.aihero.dev/ai-coding-dictionary/smart-zone)**: the window (~150k tokens on state-of-the-art models) within which the model still reasons sharply. If a session approaches it before `/to-tickets`, don't push on degraded; `/compact` at the nearest phase boundary and carry on (see Phase boundaries).
|
|
34
41
|
|
|
@@ -40,7 +47,7 @@ A starting situation that generates work, then merges onto the main flow.
|
|
|
40
47
|
|
|
41
48
|
Triage is only for issues **you didn't create**: bug reports, incoming feature requests, anything that arrives raw. Tickets that `/to-tickets` produced are already agent-ready, so **don't triage them**.
|
|
42
49
|
|
|
43
|
-
- **Something's broken** → **`/diagnosing-bugs`**. For the hard ones: the bug that resists a first glance, the intermittent flake, the regression that crept in between two known-good states. It refuses to theorise until it has a **tight feedback loop** (one command that already goes red on *this* bug), then fixes with a regression test.
|
|
50
|
+
- **Something's broken** → **`/diagnosing-bugs`**. For the hard ones: the bug that resists a first glance, the intermittent flake, the regression that crept in between two known-good states. It refuses to theorise until it has a **tight feedback loop** (one command that already goes red on *this* bug), then fixes with a regression test. Once the fix is in, run **`/retro`** in the same session to ask what would have prevented the bug; where the real finding is that there's no good seam to lock it down, that's a job for **`/improve-codebase-architecture`**.
|
|
44
51
|
|
|
45
52
|
- **A huge, foggy effort: a greenfield project or a huge feature build, too big for one session** → **`/wayfinder`**, the most cognitively demanding flow here. When the way from here to the destination isn't visible yet, it charts a **shared map** of **decision tickets** on the issue tracker and resolves them one at a time, producing **decisions, not deliverables**, until the fog is pushed back and the way is clear. Where **`/grill-with-docs`** sharpens an idea you can hold in one session, wayfinder is for the idea you can't, and it's slower and denser, so save it for exactly that, never a well-scoped feature.
|
|
46
53
|
|
|
@@ -56,7 +63,7 @@ Not feature work, just upkeep.
|
|
|
56
63
|
|
|
57
64
|
Two model-invoked references that run *beneath* the other skills, each the single source of truth for its vocabulary. Reach for them directly when the **words**, not the process, are the problem; or let the skills above pull them in.
|
|
58
65
|
|
|
59
|
-
- **`/domain-modeling`**: sharpen the project's *domain* language: challenge a fuzzy term, resolve an overloaded word ("account" doing three jobs), record a hard-to-reverse decision as an ADR. It's the active discipline `/grill-with-docs` drives to keep `
|
|
66
|
+
- **`/domain-modeling`**: sharpen the project's *domain* language: challenge a fuzzy term, resolve an overloaded word ("account" doing three jobs), record a hard-to-reverse decision as an ADR. It's the active discipline `/grill-with-docs` drives to keep `GLOSSARY.md` a clean glossary.
|
|
60
67
|
- **`/codebase-design`** is the deep-module vocabulary (module, interface, depth, seam, adapter, leverage, locality) for designing a module's *shape*: a lot of behaviour behind a small interface at a clean seam. `/tdd` and `/improve-codebase-architecture` both speak it.
|
|
61
68
|
|
|
62
69
|
## Phase boundaries
|
|
@@ -75,14 +82,13 @@ Read [PHASE-BOUNDARIES.md](PHASE-BOUNDARIES.md) for the ordered tree: the five q
|
|
|
75
82
|
|
|
76
83
|
Off the main flow entirely.
|
|
77
84
|
|
|
78
|
-
- **`/grill-me`**: the same relentless interview as `/grill-with-docs`, but **stateless**: it saves nothing locally and builds no `
|
|
85
|
+
- **`/grill-me`**: the same relentless interview as `/grill-with-docs`, but **stateless**: it saves nothing locally and builds no `GLOSSARY.md`. Reach for it when you are **not working in a working directory** (sharpening a plan, a design, a piece of writing, anything with no repo under it). If you are in a working directory, use `/grill-with-docs` instead: it runs the same interview and leaves a paper trail, so it is strictly the better one.
|
|
79
86
|
- **`/grilling`** is the interview primitive itself: rounds, the frontier, facts are the agent's job and decisions are yours. `/grill-me` and `/grill-with-docs` are the two named ways in, and `/triage`, `/wayfinder` and `/improve-codebase-architecture` all run it internally. Reach for it directly only when you want the interview with no wrapper around it.
|
|
80
|
-
- **`/resolving-merge-conflicts`** works an in-progress merge or rebase conflict hunk by hunk, resolving by **intent** traced to each side's primary source rather than by picking lines, then finishes the operation. It never runs `--abort`. Standalone and off every flow: reach for it when you are already mid-conflict.
|
|
81
87
|
- **`/prototype`** is a small, throwaway program that answers one design question: does this state model feel right, or what should this UI look like. Throwaway is a constraint on how the code is written, not a promise to destroy it: the answer folds into the real code, and the prototype itself is kept as a **primary source** on a `prototype/<name>` branch out of main, pointed at from the implementation issue. It's the detour in step 2 of the main flow, but reach for it any time a design question is hard to settle on paper.
|
|
82
88
|
- **`/research`**: delegate reading legwork to a **background agent**: it investigates a question against **primary sources**, then leaves a cited Markdown file in the repo. Keep working while it reads. The file it produces is something to take *into* the main flow at `/grill-with-docs`, since research feeds the thinking rather than replacing it.
|
|
83
89
|
- **`/to-questionnaire`** comes in when the thing blocking you isn't in your head or the codebase but in **someone else's**, and it writes them a questionnaire to fill in. It's the inverse of `/grill-me`: instead of interviewing you about the subject, it interviews you about the **send** (who it's going to, what you need back) and aims the questions at the gap. What comes back is material for `/grill-with-docs` or `/to-spec`.
|
|
84
90
|
- **`/wizard`** is for the steps only a **human** can take: provisioning infrastructure, setting up credentials or CI secrets, clicking through an unfamiliar third-party dashboard, running a one-off migration or cutover. It generates an interactive bash script that opens each URL, captures each value, and writes it into `.env` and GitHub secrets, so the procedure stops being something you re-explain to an agent every time. Model-invoked, so the agent reaches for it the moment it hits a wall only you can pass. If the agent could just do it itself, it should; this is for where a human is genuinely in the loop.
|
|
85
|
-
- **`/wait-what`** is the corrective for a message that didn't land. Use it mid-conversation, inside any other skill, and the agent re-pitches what it just said with the context you were missing, in plain English, using the `
|
|
91
|
+
- **`/wait-what`** is the corrective for a message that didn't land. Use it mid-conversation, inside any other skill, and the agent re-pitches what it just said with the context you were missing, in plain English, using the `GLOSSARY.md` vocabulary. It works after the fact; `/grill-with-docs` is the upfront cure, because a shared language agreed early is what stops the jargon arriving at all.
|
|
86
92
|
- **`/teach`**: learn a concept over multiple sessions, using the current directory as a stateful workspace.
|
|
87
93
|
- **`/writing-for-agents`** is the reference for writing documents agents consume: skills, AGENTS.md, pointed-at docs.
|
|
88
94
|
|
|
@@ -3,8 +3,6 @@ name: code-review
|
|
|
3
3
|
description: "Review the changes since a fixed point (commit, branch, tag, or merge-base) along two axes: Standards (does the code follow this repo's documented coding standards?) and Spec (does the code match what the originating issue/spec asked for?). Runs both reviews in parallel sub-agents and reports them side by side. Use when the user wants to review a branch, a PR, work-in-progress changes, or asks to \"review since X\"."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- Vendored from mattpocock/skills at commit c55ee46073ed923f86ce59a5eb3b6d895095d1b7 (skills/engineering/code-review). MIT licensed — see LICENSE in this directory. -->
|
|
7
|
-
|
|
8
6
|
Two-axis review of the diff between `HEAD` and a fixed point the user supplies:
|
|
9
7
|
|
|
10
8
|
- **Standards**: does the code conform to this repo's documented coding standards?
|
|
@@ -12,7 +10,7 @@ Two-axis review of the diff between `HEAD` and a fixed point the user supplies:
|
|
|
12
10
|
|
|
13
11
|
Both axes run as **parallel sub-agents** so they don't pollute each other's context, then this skill aggregates their findings.
|
|
14
12
|
|
|
15
|
-
The issue tracker should have been provided to you. If
|
|
13
|
+
The issue tracker should have been provided to you. If not, tell the user to run `/setup-matt-pocock-skills`.
|
|
16
14
|
|
|
17
15
|
## Process
|
|
18
16
|
|
|
@@ -28,14 +26,14 @@ Before going further, confirm the fixed point resolves (`git rev-parse <fixed-po
|
|
|
28
26
|
|
|
29
27
|
Look for the originating spec, in this order:
|
|
30
28
|
|
|
31
|
-
1. Issue references in the commit messages (`#123`, `Closes #45`, GitLab `!67`, etc.), fetched via the workflow in
|
|
29
|
+
1. Issue references in the commit messages (`#123`, `Closes #45`, GitLab `!67`, etc.), fetched via the workflow in the tracker doc.
|
|
32
30
|
2. A path the user passed as an argument.
|
|
33
31
|
3. A spec file under `docs/`, `specs/`, or `.scratch/` matching the branch name or feature.
|
|
34
32
|
4. If nothing is found, ask the user where the spec is. If they say there isn't one, the **Spec** sub-agent will skip and report "no spec available".
|
|
35
33
|
|
|
36
34
|
### 3. Identify the standards sources
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
Search the repo for every file that documents how code should be written. When `CODING_STANDARDS.md` or `CONTRIBUTING.md` exists, it must be on the list.
|
|
39
37
|
|
|
40
38
|
On top of whatever the repo documents, the Standards axis always carries the **smell baseline** below: a fixed set of Fowler code smells (_Refactoring_, ch.3) that applies even when a repo documents nothing. Two rules bind it:
|
|
41
39
|
|
|
@@ -59,6 +57,8 @@ Each smell reads *what it is* → *how to fix*; match it against the diff:
|
|
|
59
57
|
|
|
60
58
|
### 4. Spawn both sub-agents in parallel
|
|
61
59
|
|
|
60
|
+
Issue both sub-agent calls together, in the foreground, and aggregate the reports they return.
|
|
61
|
+
|
|
62
62
|
**Standards sub-agent prompt** should include:
|
|
63
63
|
|
|
64
64
|
- The full diff command and commit list.
|
|
@@ -27,7 +27,7 @@ Prompt each sub-agent with a separate technical brief (file paths, coupling deta
|
|
|
27
27
|
- Agent 3: "Optimise for the most common caller: make the default case trivial."
|
|
28
28
|
- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies."
|
|
29
29
|
|
|
30
|
-
Include both [SKILL.md](SKILL.md) vocabulary and
|
|
30
|
+
Include both [SKILL.md](SKILL.md) vocabulary and GLOSSARY.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language.
|
|
31
31
|
|
|
32
32
|
Each sub-agent outputs:
|
|
33
33
|
|
|
@@ -3,8 +3,6 @@ name: codebase-design
|
|
|
3
3
|
description: Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- Vendored from mattpocock/skills at commit c55ee46073ed923f86ce59a5eb3b6d895095d1b7 (skills/engineering/codebase-design). MIT licensed — see LICENSE in this directory. -->
|
|
7
|
-
|
|
8
6
|
# Codebase Design
|
|
9
7
|
|
|
10
8
|
Design **deep modules**: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. Use this language and these principles wherever code is being designed or restructured. The aim is leverage for callers, locality for maintainers, and testability for everyone.
|
|
@@ -3,13 +3,11 @@ name: diagnosing-bugs
|
|
|
3
3
|
description: Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- Vendored from mattpocock/skills at commit c55ee46073ed923f86ce59a5eb3b6d895095d1b7 (skills/engineering/diagnosing-bugs). MIT licensed — see LICENSE in this directory. -->
|
|
7
|
-
|
|
8
6
|
# Diagnosing Bugs
|
|
9
7
|
|
|
10
8
|
A discipline for hard bugs. Skip phases only when explicitly justified.
|
|
11
9
|
|
|
12
|
-
When exploring the codebase, read `
|
|
10
|
+
When exploring the codebase, read `GLOSSARY.md` (if it exists) to get a clear mental model of the relevant modules, and check ADRs in the area you're touching.
|
|
13
11
|
|
|
14
12
|
## Redact
|
|
15
13
|
|
|
@@ -124,7 +122,7 @@ A correct seam is one where the test exercises the **real bug pattern** as it oc
|
|
|
124
122
|
If a correct seam exists:
|
|
125
123
|
|
|
126
124
|
1. Turn the minimised repro into a failing test at that seam.
|
|
127
|
-
2. Watch it fail.
|
|
125
|
+
2. Watch it fail. If you forced the red by mutating code or a fixture, `diff` against a pristine copy to prove the mutation landed before you trust it.
|
|
128
126
|
3. Apply the fix.
|
|
129
127
|
4. Watch it pass.
|
|
130
128
|
5. Re-run the Phase 1 feedback loop against the original (un-minimised) scenario.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# GLOSSARY.md Format
|
|
2
2
|
|
|
3
3
|
## Structure
|
|
4
4
|
|
|
@@ -31,18 +31,18 @@ _Avoid_: Client, buyer, account
|
|
|
31
31
|
|
|
32
32
|
## Single vs multi-context repos
|
|
33
33
|
|
|
34
|
-
**Single context (most repos):** One `
|
|
34
|
+
**Single context (most repos):** One `GLOSSARY.md` at the repo root.
|
|
35
35
|
|
|
36
|
-
**Multiple contexts:** A `
|
|
36
|
+
**Multiple contexts:** A `GLOSSARY-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other:
|
|
37
37
|
|
|
38
38
|
```md
|
|
39
|
-
#
|
|
39
|
+
# Glossary Map
|
|
40
40
|
|
|
41
41
|
## Contexts
|
|
42
42
|
|
|
43
|
-
- [Ordering](./src/ordering/
|
|
44
|
-
- [Billing](./src/billing/
|
|
45
|
-
- [Fulfillment](./src/fulfillment/
|
|
43
|
+
- [Ordering](./src/ordering/GLOSSARY.md): receives and tracks customer orders
|
|
44
|
+
- [Billing](./src/billing/GLOSSARY.md): generates invoices and processes payments
|
|
45
|
+
- [Fulfillment](./src/fulfillment/GLOSSARY.md): manages warehouse picking and shipping
|
|
46
46
|
|
|
47
47
|
## Relationships
|
|
48
48
|
|
|
@@ -53,8 +53,8 @@ _Avoid_: Client, buyer, account
|
|
|
53
53
|
|
|
54
54
|
The skill infers which structure applies:
|
|
55
55
|
|
|
56
|
-
- If `
|
|
57
|
-
- If only a root `
|
|
58
|
-
- If neither exists, create a root `
|
|
56
|
+
- If `GLOSSARY-MAP.md` exists, read it to find contexts
|
|
57
|
+
- If only a root `GLOSSARY.md` exists, single context
|
|
58
|
+
- If neither exists, create a root `GLOSSARY.md` lazily when the first term is resolved
|
|
59
59
|
|
|
60
60
|
When multiple contexts exist, infer which one the current topic relates to. If unclear, ask.
|