@rmrdeveloper/sideroom-pi 7.0.1 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +162 -125
- package/assets/artifacts/GUIDELINES_TEMPLATE.md +193 -413
- package/extensions/ask/model.ts +60 -15
- package/extensions/guidelines/guard.ts +136 -0
- package/extensions/guidelines/index.ts +9 -0
- package/extensions/guidelines/prompt.ts +5 -4
- package/package.json +1 -1
- package/skills/sideroom-domain-modeling/SKILL.md +81 -0
- package/skills/sideroom-domain-scaffold/SKILL.md +70 -0
- package/skills/sideroom-grill/SKILL.md +61 -0
- package/skills/sideroom-guidelines/SKILL.md +12 -8
- package/skills/sideroom-guidelines/references/languages/go.md +369 -19
- package/skills/sideroom-guidelines/references/languages/java.md +249 -16
- package/skills/sideroom-guidelines/references/languages/php-laravel.md +242 -20
- package/skills/sideroom-guidelines/references/languages/python.md +312 -12
- package/skills/sideroom-guidelines/references/languages/rust.md +244 -12
- package/skills/sideroom-guidelines/references/languages/typescript.md +248 -29
package/extensions/ask/model.ts
CHANGED
|
@@ -9,9 +9,17 @@ export const CUSTOM_LABEL = 'Write a custom answer';
|
|
|
9
9
|
export const UI_UNAVAILABLE =
|
|
10
10
|
'Error: UI not available (running in non-interactive mode)';
|
|
11
11
|
|
|
12
|
+
const MAX_QUESTIONS = 4;
|
|
13
|
+
const MAX_OPTIONS = 4;
|
|
14
|
+
const MAX_TAB_LABEL_LENGTH = 16;
|
|
15
|
+
const MAX_OPTION_LABEL_LENGTH = 60;
|
|
16
|
+
|
|
12
17
|
const QuestionOptionSchema = Type.Object({
|
|
13
18
|
value: Type.String({ description: 'The value returned when selected' }),
|
|
14
|
-
label: Type.String({
|
|
19
|
+
label: Type.String({
|
|
20
|
+
maxLength: MAX_OPTION_LABEL_LENGTH,
|
|
21
|
+
description: `Display label for the option, with at most ${String(MAX_OPTION_LABEL_LENGTH)} characters`,
|
|
22
|
+
}),
|
|
15
23
|
description: Type.Optional(
|
|
16
24
|
Type.String({ description: 'Optional description shown below the label' }),
|
|
17
25
|
),
|
|
@@ -21,14 +29,15 @@ const QuestionSchema = Type.Object({
|
|
|
21
29
|
id: Type.String({ description: 'Unique identifier for this question' }),
|
|
22
30
|
label: Type.Optional(
|
|
23
31
|
Type.String({
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
maxLength: MAX_TAB_LABEL_LENGTH,
|
|
33
|
+
description: `Short contextual label of at most ${String(MAX_TAB_LABEL_LENGTH)} characters for the tab bar, e.g. 'Scope' (defaults to Q1, Q2)`,
|
|
26
34
|
}),
|
|
27
35
|
),
|
|
28
36
|
prompt: Type.String({ description: 'The full question text to display' }),
|
|
29
37
|
options: Type.Array(QuestionOptionSchema, {
|
|
30
38
|
minItems: 2,
|
|
31
|
-
|
|
39
|
+
maxItems: MAX_OPTIONS,
|
|
40
|
+
description: `Two to ${String(MAX_OPTIONS)} mutually exclusive options`,
|
|
32
41
|
}),
|
|
33
42
|
recommendationIndex: Type.Integer({
|
|
34
43
|
minimum: 0,
|
|
@@ -39,7 +48,8 @@ const QuestionSchema = Type.Object({
|
|
|
39
48
|
export const AskParamsSchema = Type.Object({
|
|
40
49
|
questions: Type.Array(QuestionSchema, {
|
|
41
50
|
minItems: 1,
|
|
42
|
-
|
|
51
|
+
maxItems: MAX_QUESTIONS,
|
|
52
|
+
description: `One to ${String(MAX_QUESTIONS)} questions to ask the user in this batch`,
|
|
43
53
|
}),
|
|
44
54
|
});
|
|
45
55
|
|
|
@@ -270,23 +280,58 @@ function schemaErrorMessage(value: unknown): string {
|
|
|
270
280
|
if (error === undefined) {
|
|
271
281
|
return 'Error: Invalid sideroom_ask parameters';
|
|
272
282
|
}
|
|
273
|
-
if (error.
|
|
274
|
-
|
|
283
|
+
if (error.instancePath === '/questions') {
|
|
284
|
+
if (error.keyword === 'minItems') {
|
|
285
|
+
return 'Error: No questions provided';
|
|
286
|
+
}
|
|
287
|
+
if (error.keyword === 'maxItems') {
|
|
288
|
+
return `Error: A questionnaire batch may include at most ${String(MAX_QUESTIONS)} questions`;
|
|
289
|
+
}
|
|
275
290
|
}
|
|
291
|
+
|
|
292
|
+
const questions = asQuestions(value);
|
|
276
293
|
const optionsMatch = /\/questions\/(\d+)\/options$/.exec(error.instancePath);
|
|
277
|
-
if (
|
|
294
|
+
if (optionsMatch !== null) {
|
|
278
295
|
const questionIndex = Number(optionsMatch[1]);
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
296
|
+
const id = questionId(questions, questionIndex);
|
|
297
|
+
if (error.keyword === 'minItems') {
|
|
298
|
+
return `Error: Question '${id}' must include at least two options`;
|
|
299
|
+
}
|
|
300
|
+
if (error.keyword === 'maxItems') {
|
|
301
|
+
return `Error: Question '${id}' may include at most ${String(MAX_OPTIONS)} options`;
|
|
302
|
+
}
|
|
286
303
|
}
|
|
304
|
+
|
|
305
|
+
const tabLabelMatch = /\/questions\/(\d+)\/label$/.exec(error.instancePath);
|
|
306
|
+
if (error.keyword === 'maxLength' && tabLabelMatch !== null) {
|
|
307
|
+
const questionIndex = Number(tabLabelMatch[1]);
|
|
308
|
+
const id = questionId(questions, questionIndex);
|
|
309
|
+
return `Error: Question '${id}' label may contain at most ${String(MAX_TAB_LABEL_LENGTH)} characters`;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const optionLabelMatch = /\/questions\/(\d+)\/options\/(\d+)\/label$/.exec(
|
|
313
|
+
error.instancePath,
|
|
314
|
+
);
|
|
315
|
+
if (error.keyword === 'maxLength' && optionLabelMatch !== null) {
|
|
316
|
+
const questionIndex = Number(optionLabelMatch[1]);
|
|
317
|
+
const optionIndex = Number(optionLabelMatch[2]);
|
|
318
|
+
const id = questionId(questions, questionIndex);
|
|
319
|
+
return `Error: Option ${String(optionIndex + 1)} for question '${id}' may contain at most ${String(MAX_OPTION_LABEL_LENGTH)} characters`;
|
|
320
|
+
}
|
|
321
|
+
|
|
287
322
|
return `Error: Invalid sideroom_ask parameters`;
|
|
288
323
|
}
|
|
289
324
|
|
|
325
|
+
function questionId(
|
|
326
|
+
questions: readonly Record<string, unknown>[],
|
|
327
|
+
index: number,
|
|
328
|
+
): string {
|
|
329
|
+
const question = questions[index];
|
|
330
|
+
return question !== undefined && typeof question.id === 'string'
|
|
331
|
+
? question.id
|
|
332
|
+
: String(index);
|
|
333
|
+
}
|
|
334
|
+
|
|
290
335
|
function asQuestions(value: unknown): readonly Record<string, unknown>[] {
|
|
291
336
|
if (typeof value !== 'object' || value === null) {
|
|
292
337
|
return [];
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { dirname, join, resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import {
|
|
4
|
+
type ExtensionAPI,
|
|
5
|
+
isReadToolResult,
|
|
6
|
+
isToolCallEventType,
|
|
7
|
+
} from '@earendil-works/pi-coding-agent';
|
|
8
|
+
|
|
9
|
+
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
|
|
10
|
+
export const GUIDELINE_SKILL_PATH = join(
|
|
11
|
+
PACKAGE_ROOT,
|
|
12
|
+
'skills/sideroom-guidelines/SKILL.md',
|
|
13
|
+
);
|
|
14
|
+
const LANGUAGE_GUIDE_DIRECTORY = join(
|
|
15
|
+
PACKAGE_ROOT,
|
|
16
|
+
'skills/sideroom-guidelines/references/languages',
|
|
17
|
+
);
|
|
18
|
+
const LANGUAGE_GUIDE_BY_EXTENSION: Readonly<Record<string, string>> = {
|
|
19
|
+
'.go': join(LANGUAGE_GUIDE_DIRECTORY, 'go.md'),
|
|
20
|
+
'.java': join(LANGUAGE_GUIDE_DIRECTORY, 'java.md'),
|
|
21
|
+
'.php': join(LANGUAGE_GUIDE_DIRECTORY, 'php-laravel.md'),
|
|
22
|
+
'.py': join(LANGUAGE_GUIDE_DIRECTORY, 'python.md'),
|
|
23
|
+
'.rs': join(LANGUAGE_GUIDE_DIRECTORY, 'rust.md'),
|
|
24
|
+
'.ts': join(LANGUAGE_GUIDE_DIRECTORY, 'typescript.md'),
|
|
25
|
+
'.tsx': join(LANGUAGE_GUIDE_DIRECTORY, 'typescript.md'),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
interface ReadInput {
|
|
29
|
+
readonly path: string;
|
|
30
|
+
readonly offset?: number;
|
|
31
|
+
readonly limit?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface GuidelineReadState {
|
|
35
|
+
skillRead: boolean;
|
|
36
|
+
readonly languageGuidesRead: Set<string>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createGuidelineReadState(): GuidelineReadState {
|
|
40
|
+
return { skillRead: false, languageGuidesRead: new Set() };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function resetGuidelineReadState(state: GuidelineReadState): void {
|
|
44
|
+
state.skillRead = false;
|
|
45
|
+
state.languageGuidesRead.clear();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function registerGuidelineGuard(
|
|
49
|
+
pi: ExtensionAPI,
|
|
50
|
+
state: GuidelineReadState,
|
|
51
|
+
): void {
|
|
52
|
+
pi.on('tool_result', (event) => {
|
|
53
|
+
if (!isReadToolResult(event) || event.isError) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// SAFETY: Pi validates built-in read arguments before emitting tool_result.
|
|
58
|
+
const input = event.input as unknown as ReadInput;
|
|
59
|
+
if (input.offset !== undefined || input.limit !== undefined) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
recordGuidelineRead(state, input.path);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
pi.on('tool_call', (event) => {
|
|
66
|
+
if (
|
|
67
|
+
!isToolCallEventType('edit', event) &&
|
|
68
|
+
!isToolCallEventType('write', event)
|
|
69
|
+
) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const reason = mutationBlockReason(state, event.input.path);
|
|
74
|
+
if (reason === undefined) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
return { block: true, reason };
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function recordGuidelineRead(
|
|
82
|
+
state: GuidelineReadState,
|
|
83
|
+
path: string | undefined,
|
|
84
|
+
): void {
|
|
85
|
+
if (path === undefined) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const normalizedPath = normalizePath(path);
|
|
90
|
+
if (normalizedPath === normalizePath(GUIDELINE_SKILL_PATH)) {
|
|
91
|
+
state.skillRead = true;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const guidePath of new Set(Object.values(LANGUAGE_GUIDE_BY_EXTENSION))) {
|
|
96
|
+
if (normalizedPath === normalizePath(guidePath)) {
|
|
97
|
+
state.languageGuidesRead.add(guidePath);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function mutationBlockReason(
|
|
104
|
+
state: GuidelineReadState,
|
|
105
|
+
targetPath: string,
|
|
106
|
+
): string | undefined {
|
|
107
|
+
const requiredPaths: string[] = [];
|
|
108
|
+
if (!state.skillRead) {
|
|
109
|
+
requiredPaths.push(GUIDELINE_SKILL_PATH);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const guidePath = languageGuideFor(targetPath);
|
|
113
|
+
if (guidePath !== undefined && !state.languageGuidesRead.has(guidePath)) {
|
|
114
|
+
requiredPaths.push(guidePath);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (requiredPaths.length === 0) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return `Blocked ${targetPath}: read ${requiredPaths.join(' and ')} with the read tool in this agent run, then retry the edit.`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function languageGuideFor(path: string): string | undefined {
|
|
125
|
+
const normalizedPath = normalizePath(path).toLowerCase();
|
|
126
|
+
const extension = Object.keys(LANGUAGE_GUIDE_BY_EXTENSION).find((candidate) =>
|
|
127
|
+
normalizedPath.endsWith(candidate),
|
|
128
|
+
);
|
|
129
|
+
return extension === undefined
|
|
130
|
+
? undefined
|
|
131
|
+
: LANGUAGE_GUIDE_BY_EXTENSION[extension];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function normalizePath(path: string): string {
|
|
135
|
+
return path.replace(/^@/, '').replaceAll('\\', '/');
|
|
136
|
+
}
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import {
|
|
3
|
+
createGuidelineReadState,
|
|
4
|
+
registerGuidelineGuard,
|
|
5
|
+
resetGuidelineReadState,
|
|
6
|
+
} from './guard.ts';
|
|
2
7
|
import { appendGuidelinesReminder } from './prompt.ts';
|
|
3
8
|
|
|
4
9
|
// Pi loads extensions/*/index.ts through export default.
|
|
5
10
|
export default function registerGuidelines(pi: ExtensionAPI): void {
|
|
11
|
+
const readState = createGuidelineReadState();
|
|
12
|
+
registerGuidelineGuard(pi, readState);
|
|
13
|
+
|
|
6
14
|
pi.on('before_agent_start', (event) => {
|
|
15
|
+
resetGuidelineReadState(readState);
|
|
7
16
|
const systemPrompt = appendGuidelinesReminder(event.systemPrompt);
|
|
8
17
|
if (systemPrompt === event.systemPrompt) {
|
|
9
18
|
return;
|
|
@@ -2,11 +2,12 @@ export const GUIDELINES_REMINDER_HEADING =
|
|
|
2
2
|
'Sideroom coding guidelines (always-on reminder):';
|
|
3
3
|
|
|
4
4
|
export const GUIDELINES_REMINDER = `${GUIDELINES_REMINDER_HEADING}
|
|
5
|
-
Before write or edit, read the sideroom-guidelines
|
|
6
|
-
|
|
7
|
-
Do not
|
|
5
|
+
Before the first write or edit in each agent run, use the read tool without offset or limit on the absolute SKILL.md path shown for sideroom-guidelines in Available Skills.
|
|
6
|
+
For .java, .php, .ts/.tsx, .py, .go, or .rs targets, also fully read exactly the matching guide under that skill's references/languages/ directory.
|
|
7
|
+
The extension blocks write/edit until those exact full-file reads succeed. Do not bypass the gate with bash or another mutation tool.
|
|
8
|
+
Before finishing, review changed code against the loaded guide and run the relevant formatter, linter, type checks, and tests.`;
|
|
8
9
|
|
|
9
|
-
export const
|
|
10
|
+
export const LANGUAGE_GUIDE_FILES = [
|
|
10
11
|
'go.md',
|
|
11
12
|
'java.md',
|
|
12
13
|
'php-laravel.md',
|
package/package.json
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sideroom-domain-modeling
|
|
3
|
+
description: "Trigger: ubiquitous language, domain term, CONTEXT.md, ADR, vague word, naming conflict. Sharpen the project's domain language while designing: challenge vague terms, split overloaded ones, and write the settled vocabulary into the repo."
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: RMRdeveloper
|
|
7
|
+
version: "1.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Sideroom domain modeling
|
|
11
|
+
|
|
12
|
+
## Activation Contract
|
|
13
|
+
|
|
14
|
+
Load this skill when the _words_ are the problem: two people mean different
|
|
15
|
+
things by one term, one term is doing several jobs, or a statement about the
|
|
16
|
+
domain needs to survive longer than the conversation. This is the active
|
|
17
|
+
discipline — changing the model — not the passive habit of reading
|
|
18
|
+
`CONTEXT.md` to borrow a word. That read needs no skill; it is one line.
|
|
19
|
+
|
|
20
|
+
It runs underneath `sideroom-grill` through a grilling session, and it also
|
|
21
|
+
runs on its own when you want the discipline without the interview.
|
|
22
|
+
|
|
23
|
+
## Hard Rules
|
|
24
|
+
|
|
25
|
+
- Interrupt. When a term conflicts with the glossary, stop and ask which of
|
|
26
|
+
the two things is meant instead of picking one and moving on. When a vague
|
|
27
|
+
word appears where a precise one exists, force the precise word.
|
|
28
|
+
- Stress-test relationships with a concrete scenario until the boundaries
|
|
29
|
+
are exact.
|
|
30
|
+
- Cross-check statements against the code. When the code and the sentence
|
|
31
|
+
disagree, quote the code back and settle which one is right out loud,
|
|
32
|
+
before changing either.
|
|
33
|
+
- A resolved term lands in `CONTEXT.md` the moment it resolves, inline, in
|
|
34
|
+
the middle of the conversation. One or two sentences saying what the thing
|
|
35
|
+
**is**, plus rejected synonyms under `_Avoid_`.
|
|
36
|
+
- `CONTEXT.md` is a glossary and nothing else. Never persist implementation
|
|
37
|
+
details, spec prose, scratch notes, or general programming concepts there.
|
|
38
|
+
Refuse the write and say why.
|
|
39
|
+
- An ADR under `docs/adr/` is offered, never assumed, and only when all
|
|
40
|
+
three gates pass at once: hard to reverse, surprising without context, and
|
|
41
|
+
the result of a real trade-off. When refusing, say which test failed.
|
|
42
|
+
- Create files lazily. Nothing exists until the first term or decision
|
|
43
|
+
crystallises. In a repo with a `CONTEXT-MAP.md` at the root, terms go into
|
|
44
|
+
the per-context `CONTEXT.md` the map points at instead.
|
|
45
|
+
- Keep the glossary lean. It gets shorter as often as it gets longer: split
|
|
46
|
+
an overloaded term, drop a dead one, and when the file has absorbed detail
|
|
47
|
+
that was never glossary material, offer to condense it.
|
|
48
|
+
|
|
49
|
+
## Decision Gates
|
|
50
|
+
|
|
51
|
+
| Situation | Move |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| Two meanings for one term | Pick the canonical term, list the other under `_Avoid_` |
|
|
54
|
+
| One term doing several jobs | Split it (e.g. `Account` into `Customer` and `User`) |
|
|
55
|
+
| A vague term became canonical | Write it to `CONTEXT.md` now |
|
|
56
|
+
| A hard-to-reverse choice clearing all three ADR gates | Offer an ADR under `docs/adr/` |
|
|
57
|
+
| The module's shape is the problem, not its words | This skill does not apply; that is module design |
|
|
58
|
+
| The whole plan needs interrogating before building | `sideroom-grill`, which drives this skill underneath |
|
|
59
|
+
| A term only needs looking up, not changing | Read `CONTEXT.md`. It is a file |
|
|
60
|
+
|
|
61
|
+
## Execution Steps
|
|
62
|
+
|
|
63
|
+
1. Read `CONTEXT.md` (and the ADRs, if any) before touching the model.
|
|
64
|
+
2. As terms settle, write each one inline with its `_Avoid_` list.
|
|
65
|
+
3. As hard choices surface, offer an ADR or refuse one with the failed
|
|
66
|
+
test named.
|
|
67
|
+
4. Before finishing, re-read the touched entries: each defines what a thing
|
|
68
|
+
is, in one or two sentences, with no implementation detail.
|
|
69
|
+
|
|
70
|
+
## Output Contract
|
|
71
|
+
|
|
72
|
+
The repo holds the shared vocabulary and the hard decisions, each entry
|
|
73
|
+
short enough to re-read. The glossary never becomes a running spec.
|
|
74
|
+
|
|
75
|
+
## References
|
|
76
|
+
|
|
77
|
+
- Driven by `sideroom-grill` during grilling sessions.
|
|
78
|
+
- Entry rules reused by `sideroom-domain-scaffold` for code-first
|
|
79
|
+
scaffolding without interviews.
|
|
80
|
+
- Based on the `domain-modeling` discipline described at
|
|
81
|
+
`https://www.aihero.dev/skills-domain-modeling`.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sideroom-domain-scaffold
|
|
3
|
+
description: "Trigger: document my repo, scaffold CONTEXT.md, brownfield glossary, survey domain. Read the central domain code as source of truth and build or complete CONTEXT.md without interviewing anyone; conflicting terms are collected and grilled once, at the end."
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: RMRdeveloper
|
|
7
|
+
version: "1.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Sideroom domain scaffold
|
|
11
|
+
|
|
12
|
+
## Activation Contract
|
|
13
|
+
|
|
14
|
+
Load this skill on explicit request ("document my repo", "scaffold the
|
|
15
|
+
glossary") over a codebase whose domain language is missing or thin. The
|
|
16
|
+
code is the source of truth. Nobody is interviewed while the modeling runs.
|
|
17
|
+
|
|
18
|
+
## Hard Rules
|
|
19
|
+
|
|
20
|
+
- Zero interviews during the scan. Every irreconcilable conflict goes onto
|
|
21
|
+
a conflict list; modeling continues. The happy path is never interrupted.
|
|
22
|
+
- Scope is the central business domain: its modules, types, enums, and
|
|
23
|
+
statuses. Ignore infrastructure, tooling, and utilities. Domain tests are
|
|
24
|
+
witnesses of the language, never sources of new terms.
|
|
25
|
+
- Reconcile by prevalence and centrality: the dominant, core-module usage
|
|
26
|
+
wins. Record each such call as an assumption for later review.
|
|
27
|
+
- Merge, never rebuild. An existing `CONTEXT.md` is curated truth: add and
|
|
28
|
+
sharpen only. A contradiction between the code and an existing entry is
|
|
29
|
+
not overwritten; it joins the conflict list.
|
|
30
|
+
- Entry format, glossary purity, lazy file creation, and multi-context
|
|
31
|
+
routing all follow `sideroom-domain-modeling`. This skill owns the scan;
|
|
32
|
+
that one owns the writing.
|
|
33
|
+
- No ADRs. Trade-offs cannot be validated without humans; hard choices found
|
|
34
|
+
in the code are reported, not recorded.
|
|
35
|
+
- Grill once, at the end. When modeling is complete, launch
|
|
36
|
+
`sideroom-grill` scoped exactly to the conflict list so a human bounds
|
|
37
|
+
each confused term correctly. With an empty list there is no grill: close
|
|
38
|
+
with the glossary and the assumptions.
|
|
39
|
+
|
|
40
|
+
## Decision Gates
|
|
41
|
+
|
|
42
|
+
| Situation | Move |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| Same term, two meanings, no dominant use | Conflict list; grilled at the end |
|
|
45
|
+
| Code contradicts an existing glossary entry | Conflict list; the entry stands until grilled |
|
|
46
|
+
| Term belongs to infra, tooling, or utils | Skip it; out of scope |
|
|
47
|
+
| Whole plan needs interrogating, not just terms | `sideroom-grill` instead of this skill |
|
|
48
|
+
|
|
49
|
+
## Execution Steps
|
|
50
|
+
|
|
51
|
+
1. Read `CONTEXT.md` (and `CONTEXT-MAP.md`, if any) before touching the
|
|
52
|
+
model.
|
|
53
|
+
2. Survey the central domain code and collect candidate terms.
|
|
54
|
+
3. Group synonyms, split overloaded terms, reconcile by prevalence.
|
|
55
|
+
4. Write each settled term inline, merged with what exists.
|
|
56
|
+
5. Collect conflicts and assumptions without stopping.
|
|
57
|
+
6. Close: report the glossary and the assumptions, then either launch the
|
|
58
|
+
end-of-run `sideroom-grill` on the conflict list or state that the model
|
|
59
|
+
came out clean.
|
|
60
|
+
|
|
61
|
+
## Output Contract
|
|
62
|
+
|
|
63
|
+
A merged `CONTEXT.md` the code already agrees with, a reviewable list of
|
|
64
|
+
assumptions, and — only when the code disagreed with itself — one bounded
|
|
65
|
+
grilling at the end. No interview ever interrupts the scan.
|
|
66
|
+
|
|
67
|
+
## References
|
|
68
|
+
|
|
69
|
+
- Entry rules come from `sideroom-domain-modeling`.
|
|
70
|
+
- Conflicts are handed to `sideroom-grill`, once, at the end of the run.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sideroom-grill
|
|
3
|
+
description: "Trigger: fuzzy plan, design review, grilling, CONTEXT.md, ADR, domain language. Interview the user in rounds until you share one understanding, then write the vocabulary and the hard decisions into the repo."
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: RMRdeveloper
|
|
7
|
+
version: "1.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Sideroom grill
|
|
11
|
+
|
|
12
|
+
## Activation Contract
|
|
13
|
+
|
|
14
|
+
Load this skill at the start of a change, in a repo, when the plan is still
|
|
15
|
+
fuzzy and the words for the thing are not settled yet. Single-session scope:
|
|
16
|
+
if the effort is too big to hold in one session, say so and stop.
|
|
17
|
+
|
|
18
|
+
## Hard Rules
|
|
19
|
+
|
|
20
|
+
- Interview in rounds. One `sideroom_ask` call is one round; wait for its
|
|
21
|
+
answers before the next round.
|
|
22
|
+
- Every question carries a recommendation (`recommendationIndex`). Write
|
|
23
|
+
prompts and option copy in the language the user is speaking; keep ids and
|
|
24
|
+
option values in English.
|
|
25
|
+
- Never ask the user what the codebase can answer. Read the code first; ask
|
|
26
|
+
only what the code cannot settle.
|
|
27
|
+
- An *Out of scope* answer closes that thread. Do not re-ask it.
|
|
28
|
+
- Everything a round settles is recorded through `sideroom-domain-modeling`:
|
|
29
|
+
inline glossary entries, the ADR three-gate offer, lazy file creation.
|
|
30
|
+
This skill owns the interview; that one owns the writing.
|
|
31
|
+
- Everything that is neither glossary nor ADR lives in the conversation
|
|
32
|
+
only. Do not clear the session after grilling; hand it to a spec or
|
|
33
|
+
straight to implementation instead.
|
|
34
|
+
|
|
35
|
+
## Execution Steps
|
|
36
|
+
|
|
37
|
+
1. Read the code around the change. Settle from the codebase everything the
|
|
38
|
+
codebase settles.
|
|
39
|
+
2. Run one `sideroom_ask` round on what remains genuinely open: terms first,
|
|
40
|
+
then decisions.
|
|
41
|
+
3. After each round, write resolved terms to `CONTEXT.md` and qualifying
|
|
42
|
+
decisions to `docs/adr/`.
|
|
43
|
+
4. Repeat until no open term or hard decision remains, or the user cancels.
|
|
44
|
+
5. Close by stating the settled glossary, the ADRs written (if any), and the
|
|
45
|
+
suggested next step: a spec for anything non-trivial, implementation for
|
|
46
|
+
what is small enough to build now.
|
|
47
|
+
|
|
48
|
+
## Output Contract
|
|
49
|
+
|
|
50
|
+
The repo holds the shared vocabulary and the hard decisions; the
|
|
51
|
+
conversation holds everything else. The user can re-read `CONTEXT.md` and
|
|
52
|
+
the ADRs and find exactly what was agreed, nothing softened.
|
|
53
|
+
|
|
54
|
+
## References
|
|
55
|
+
|
|
56
|
+
- Drives `sideroom-domain-modeling` underneath: vocabulary sharpening
|
|
57
|
+
during the rounds follows that skill.
|
|
58
|
+
- Closes `sideroom-domain-scaffold` runs: terms a scan could not reconcile
|
|
59
|
+
are grilled here, once, at the end.
|
|
60
|
+
- Based on the `grill-with-docs` discipline described at
|
|
61
|
+
`https://www.aihero.dev/skills-grill-with-docs`.
|
|
@@ -11,18 +11,20 @@ metadata:
|
|
|
11
11
|
|
|
12
12
|
## Activation Contract
|
|
13
13
|
|
|
14
|
-
Load this skill before `write` or `edit
|
|
14
|
+
Load this whole skill with `read`, without `offset` or `limit`, before the first `write` or `edit` in each agent run. Use the absolute skill path shown in Available Skills. For each supported target language, also fully load its exact guide from `references/languages/`.
|
|
15
15
|
|
|
16
16
|
## Hard Rules
|
|
17
17
|
|
|
18
|
-
-
|
|
19
|
-
- Do not
|
|
18
|
+
- Treat every rule in the loaded language guide as mandatory for changed code.
|
|
19
|
+
- Do not write or edit until exact full-file reads of the required packaged paths succeed; partial reads and same-named files do not count.
|
|
20
|
+
- Do not bypass the gate with Bash, a subprocess, or another mutation tool.
|
|
21
|
+
- Do not load language guides unrelated to files being changed.
|
|
20
22
|
- Fail fast, keep control flow flat, and validate once at the boundary.
|
|
21
23
|
|
|
22
24
|
## Decision Gates
|
|
23
25
|
|
|
24
26
|
| Path | Read |
|
|
25
|
-
|
|
27
|
+
| ------ | ------ |
|
|
26
28
|
| `.java` | `references/languages/java.md` |
|
|
27
29
|
| `.php` | `references/languages/php-laravel.md` |
|
|
28
30
|
| `.ts` `.tsx` | `references/languages/typescript.md` |
|
|
@@ -33,9 +35,11 @@ Load this skill before `write` or `edit`. Load at most one file from `references
|
|
|
33
35
|
|
|
34
36
|
## Execution Steps
|
|
35
37
|
|
|
36
|
-
1.
|
|
37
|
-
2.
|
|
38
|
-
3.
|
|
38
|
+
1. Read this complete file from its packaged path before any mutation.
|
|
39
|
+
2. Fully read the matching packaged language guide, if the target has a supported extension.
|
|
40
|
+
3. Implement the smallest change that satisfies the current requirement.
|
|
41
|
+
4. Review every changed unit against the loaded guide.
|
|
42
|
+
5. Run the relevant formatter, linter, type checks, and tests before finishing.
|
|
39
43
|
|
|
40
44
|
## Do / Don't
|
|
41
45
|
|
|
@@ -65,7 +69,7 @@ Load this skill before `write` or `edit`. Load at most one file from `references
|
|
|
65
69
|
|
|
66
70
|
## Output Contract
|
|
67
71
|
|
|
68
|
-
Changed code follows
|
|
72
|
+
Changed code follows every applicable rule in the loaded complete guide. The final response names the validation run and any check that could not run. No extra files, layers, comments, or bypass mutations unless the requirement needs them.
|
|
69
73
|
|
|
70
74
|
## References
|
|
71
75
|
|