@rmrdeveloper/sideroom-pi 6.0.1 → 7.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 CHANGED
@@ -7,9 +7,10 @@
7
7
  Sideroom Pi is a global [Pi package](https://pi.dev/docs/latest/packages). It
8
8
  registers two parent-agent tools: `sideroom_ask`, a TUI questionnaire for
9
9
  clarifying decisions, and `sideroom_todo`, a live work board shown above the
10
- editor. It also shows a session-scoped list of files successfully edited by Pi.
11
- It writes no `.pi` configuration, task graph, or other Sideroom files into the
12
- repository you are working in.
10
+ editor. It also shows a session-scoped list of files successfully edited by Pi,
11
+ and steers write/edit toward packaged coding guidelines. It writes no `.pi`
12
+ configuration, task graph, or other Sideroom files into the repository you are
13
+ working in.
13
14
 
14
15
  ## Quick path
15
16
 
@@ -48,12 +49,15 @@ pi update --extension npm:@rmrdeveloper/sideroom-pi
48
49
  | Headless | `sideroom_todo update` works in print, JSON, and RPC. `propose` and `sideroom_ask` return `Error: UI not available` there. |
49
50
  | Edited files | Successful `write` and `edit` results appear below an active work board; `F8` opens the full list. |
50
51
  | Session state | The work board and edited-file history live in the Pi session branch, not in the target repository. |
51
- | Seed artifact | `assets/artifacts/GUIDELINES_TEMPLATE.md` ships unwired. |
52
+ | Coding guidelines | A short system-prompt reminder points at the `sideroom-guidelines` skill. Language deltas load on demand. |
53
+ | Seed artifact | `assets/artifacts/GUIDELINES_TEMPLATE.md` is the canonical seed; it is not dumped into the prompt. |
52
54
 
53
55
  ## Tool
54
56
 
55
57
  `sideroom_ask` follows Pi's questionnaire UI: a simple list for one question,
56
- tabs plus Submit for several. English copy. Each question needs:
58
+ tabs plus Submit for several. Write prompts, tab labels, and option copy in
59
+ the language the user is speaking; ids, option values, and TUI chrome stay
60
+ English. Each question needs:
57
61
 
58
62
  - `id`
59
63
  - `prompt`
@@ -95,6 +99,14 @@ one is active and contains at most five recent paths.
95
99
  - A new session starts with a new list; the current session's history survives
96
100
  reload, tree navigation, and compaction.
97
101
 
102
+ ## Coding guidelines
103
+
104
+ Before `write` or `edit`, the parent agent is reminded to read the packaged
105
+ `sideroom-guidelines` skill. That skill holds the shared Do/Don't table. For
106
+ `.java`, `.php`, `.ts`/`.tsx`, `.py`, `.go`, and `.rs`, the agent also reads one
107
+ matching file under `skills/sideroom-guidelines/references/languages/`. Other
108
+ languages follow the shared table only.
109
+
98
110
  ## Development
99
111
 
100
112
  ```bash
@@ -141,7 +153,9 @@ extensions/
141
153
  todo/ sideroom_todo (index.ts, execute.ts, session.ts,
142
154
  guards.ts, model.ts, ui.ts)
143
155
  modified-files/ session-backed edited-file tracker and view
144
- assets/artifacts/ unwired GUIDELINES_TEMPLATE.md seed
156
+ guidelines/ short write/edit reminder
157
+ skills/sideroom-guidelines/ on-demand checklist and language deltas
158
+ assets/artifacts/ GUIDELINES_TEMPLATE.md seed
145
159
  ```
146
160
 
147
161
  Add another tool as `extensions/<name>/index.ts`. Pi discovers
@@ -4,14 +4,15 @@ import { executeAsk } from './execute.ts';
4
4
  import { AskParamsSchema, type AskResult, TOOL_NAME } from './model.ts';
5
5
 
6
6
  export const ASK_DESCRIPTION =
7
- 'Ask the user one or more questions. Use for clarifying requirements, getting preferences, or confirming decisions. Each question must include a recommended option. For a single question, shows a simple option list. For multiple questions, shows a tab-based interface.';
7
+ 'Ask the user one or more questions in the language they are speaking. Use for clarifying requirements, getting preferences, or confirming decisions. Each question must include a recommended option. For a single question, shows a simple option list. For multiple questions, shows a tab-based interface.';
8
8
 
9
9
  export const ASK_PROMPT_SNIPPET =
10
- 'Ask the user one or more questions with a recommended option.';
10
+ 'Ask the user one or more questions with a recommended option, in their language.';
11
11
 
12
12
  export const ASK_PROMPT_GUIDELINES = [
13
13
  'Use sideroom_ask to clarify requirements, preferences, or decisions with the user.',
14
14
  'Each sideroom_ask question needs an id, a prompt, at least two options, and a recommendationIndex pointing at the recommended option.',
15
+ 'Write every sideroom_ask prompt, tab label, and option label or description in the language the user is speaking. Keep ids, option values, and tool code in English.',
15
16
  'sideroom_ask always adds Out of scope and a custom answer; do not include those options yourself.',
16
17
  'One sideroom_ask call is one batch; call it again if another round of questions is needed.',
17
18
  'sideroom_ask only works in the interactive TUI; it returns an error in print, JSON, or RPC modes.',
@@ -0,0 +1,13 @@
1
+ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
+ import { appendGuidelinesReminder } from './prompt.ts';
3
+
4
+ // Pi loads extensions/*/index.ts through export default.
5
+ export default function registerGuidelines(pi: ExtensionAPI): void {
6
+ pi.on('before_agent_start', (event) => {
7
+ const systemPrompt = appendGuidelinesReminder(event.systemPrompt);
8
+ if (systemPrompt === event.systemPrompt) {
9
+ return;
10
+ }
11
+ return { systemPrompt };
12
+ });
13
+ }
@@ -0,0 +1,26 @@
1
+ export const GUIDELINES_REMINDER_HEADING =
2
+ 'Sideroom coding guidelines (always-on reminder):';
3
+
4
+ export const GUIDELINES_REMINDER = `${GUIDELINES_REMINDER_HEADING}
5
+ Before write or edit, read the sideroom-guidelines skill.
6
+ Apply its Do/Don't table. If the path is .java, .php, .ts/.tsx, .py, .go, or .rs, also read that language file under the skill's references/languages/.
7
+ Do not load other language files.`;
8
+
9
+ export const LANGUAGE_DELTA_FILES = [
10
+ 'go.md',
11
+ 'java.md',
12
+ 'php-laravel.md',
13
+ 'python.md',
14
+ 'rust.md',
15
+ 'typescript.md',
16
+ ] as const;
17
+
18
+ export function appendGuidelinesReminder(systemPrompt: string): string {
19
+ if (systemPrompt.includes(GUIDELINES_REMINDER_HEADING)) {
20
+ return systemPrompt;
21
+ }
22
+ if (systemPrompt.length === 0) {
23
+ return GUIDELINES_REMINDER;
24
+ }
25
+ return `${systemPrompt}\n\n${GUIDELINES_REMINDER}`;
26
+ }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@rmrdeveloper/sideroom-pi",
3
- "version": "6.0.1",
4
- "description": "A global Pi package with a questionnaire, work board, and edited-file view for parent agents.",
3
+ "version": "7.0.0",
4
+ "description": "A global Pi package with a questionnaire, work board, edited-file view, and coding guidelines for parent agents.",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "extensions",
8
8
  "assets",
9
+ "skills",
9
10
  "!extensions/**/*.test.ts"
10
11
  ],
11
12
  "scripts": {
@@ -35,6 +36,7 @@
35
36
  "edited-files",
36
37
  "sideroom_ask",
37
38
  "sideroom_todo",
39
+ "guidelines",
38
40
  "typescript"
39
41
  ],
40
42
  "license": "MIT",
@@ -48,6 +50,9 @@
48
50
  "pi": {
49
51
  "extensions": [
50
52
  "./extensions"
53
+ ],
54
+ "skills": [
55
+ "./skills"
51
56
  ]
52
57
  },
53
58
  "peerDependencies": {
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: sideroom-guidelines
3
+ description: "Trigger: write, edit, coding, Java, Laravel, TypeScript, Python, Go, Rust. Apply Sideroom coding principles before changing code."
4
+ license: MIT
5
+ metadata:
6
+ author: RMRdeveloper
7
+ version: "1.0"
8
+ ---
9
+
10
+ # Sideroom guidelines
11
+
12
+ ## Activation Contract
13
+
14
+ Load this skill before `write` or `edit`. Load at most one file from `references/languages/` for the path being changed.
15
+
16
+ ## Hard Rules
17
+
18
+ - Follow the Do/Don't table. Language files override syntax and framework idiom only.
19
+ - Do not load language files you are not editing this turn.
20
+ - Fail fast, keep control flow flat, and validate once at the boundary.
21
+
22
+ ## Decision Gates
23
+
24
+ | Path | Read |
25
+ |------|------|
26
+ | `.java` | `references/languages/java.md` |
27
+ | `.php` | `references/languages/php-laravel.md` |
28
+ | `.ts` `.tsx` | `references/languages/typescript.md` |
29
+ | `.py` | `references/languages/python.md` |
30
+ | `.go` | `references/languages/go.md` |
31
+ | `.rs` | `references/languages/rust.md` |
32
+ | other | shared table only |
33
+
34
+ ## Execution Steps
35
+
36
+ 1. Apply the table to the change.
37
+ 2. Read the matching language file, if any.
38
+ 3. Ship the smallest change that satisfies the current requirement.
39
+
40
+ ## Do / Don't
41
+
42
+ | Do | Don't |
43
+ | --- | --- |
44
+ | Braces around every `if` body (Python: a full indented suite) | Unbraced or one-line conditional bodies |
45
+ | Early return on bad input | Pyramid `if/else` nesting |
46
+ | Explicit error, fail now | Fallbacks that hide the real failure |
47
+ | One responsibility per unit | Validate + transform + persist + notify together |
48
+ | Extract when duplication repeats | Abstract before a second real use |
49
+ | Simplest solution for the current problem | Layers, hooks, or config "just in case" |
50
+ | Build only what is needed today | Fields or branches for a future case |
51
+ | Compose small focused units | Deep inheritance for unrelated behavior |
52
+ | Talk only to immediate collaborators | Reach through another object's internals |
53
+ | A function either does or returns, not both | Side effects inside a getter |
54
+ | Validate once at the edge | Re-check the same invariant inward |
55
+ | One validator per input | Two validators for the same body/query |
56
+ | Let errors surface with context | Empty `catch` or catch-and-continue |
57
+ | Return new values instead of mutating input | Mutate parameters |
58
+ | One meaning per null/undefined | Overload null for several business states |
59
+ | Inject dependencies | Hardcode infra that blocks tests |
60
+ | Inner layers depend on nothing outward | Domain imports of DB/HTTP/framework types |
61
+ | Names that reveal role or domain meaning | `data`, `info`, `temp`, `result`, `obj` |
62
+ | Named const/enum/contract for domain literals | Magic strings |
63
+ | Depend on ports where variation is real | Couple a use case to a concrete implementation |
64
+ | Comments only for non-obvious intent | Narration, noise, or stale TODOs |
65
+
66
+ ## Output Contract
67
+
68
+ Changed code follows the table and the loaded language delta. No extra files, layers, or comments unless the change requires them.
69
+
70
+ ## References
71
+
72
+ - `references/languages/java.md`
73
+ - `references/languages/php-laravel.md`
74
+ - `references/languages/typescript.md`
75
+ - `references/languages/python.md`
76
+ - `references/languages/go.md`
77
+ - `references/languages/rust.md`
@@ -0,0 +1,27 @@
1
+ # Go
2
+
3
+ Delta only. Keep the shared table.
4
+
5
+ - Braces on every `if`. Keep `err` checks as flat guard clauses; do not nest the happy path.
6
+ - Return errors with context. Do not `panic` for expected business failure, and do not ignore `err`.
7
+ - Do not mutate slices or maps passed in as input; return a new value.
8
+
9
+ ```go
10
+ // Bad
11
+ func loadUser(id string) (*User, error) {
12
+ user, err := repo.Get(id)
13
+ if err == nil {
14
+ return user, nil
15
+ }
16
+ return nil, nil
17
+ }
18
+
19
+ // Good
20
+ func loadUser(id string) (*User, error) {
21
+ user, err := repo.Get(id)
22
+ if err != nil {
23
+ return nil, fmt.Errorf("load user %s: %w", id, err)
24
+ }
25
+ return user, nil
26
+ }
27
+ ```
@@ -0,0 +1,24 @@
1
+ # Java
2
+
3
+ Delta only. Keep the shared table.
4
+
5
+ - Braces on every `if` / `else`, including one-line guards.
6
+ - Fail fast with a clear exception. Do not hide a missing value behind a silent default.
7
+ - Domain code depends on ports, not `new` of infrastructure types.
8
+
9
+ ```java
10
+ // Bad
11
+ public int shippingCost(Order order) {
12
+ Zone zone = zones.getOrDefault(order.zoneId(), Zone.DEFAULT);
13
+ return zone.baseCost();
14
+ }
15
+
16
+ // Good
17
+ public int shippingCost(Order order) {
18
+ Zone zone = zones.get(order.zoneId());
19
+ if (zone == null) {
20
+ throw new IllegalArgumentException("Unknown zoneId: " + order.zoneId());
21
+ }
22
+ return zone.baseCost();
23
+ }
24
+ ```
@@ -0,0 +1,26 @@
1
+ # PHP / Laravel
2
+
3
+ Delta only. Keep the shared table.
4
+
5
+ - Braces on every `if` / `else`.
6
+ - `FormRequest` (or one equivalent boundary) is the single validator. Services trust that contract and do not re-check the same rules.
7
+ - Keep Eloquent/HTTP types out of domain logic.
8
+
9
+ ```php
10
+ // Bad — Service repeats the FormRequest rule
11
+ class ProfileService {
12
+ public function update(array $data) {
13
+ if (empty($data['email'])) {
14
+ throw new \InvalidArgumentException('email required');
15
+ }
16
+ $this->user->update($data);
17
+ }
18
+ }
19
+
20
+ // Good — Service trusts validated input
21
+ class ProfileService {
22
+ public function update(array $validated) {
23
+ $this->user->update($validated);
24
+ }
25
+ }
26
+ ```
@@ -0,0 +1,21 @@
1
+ # Python
2
+
3
+ Delta only. Keep the shared table.
4
+
5
+ - Use a complete indented suite for every `if` / `else`. Never a one-line conditional body.
6
+ - Treat `None` as "not set" or "explicitly empty" — pick one meaning and keep it. Do not use `None` for a real business state that deserves its own value.
7
+ - Prefer returning new collections over mutating a caller's list or dict.
8
+
9
+ ```python
10
+ # Bad
11
+ def get_discount(user):
12
+ if user: return 0.2 if user.is_active else 0
13
+
14
+ # Good
15
+ def get_discount(user):
16
+ if user is None:
17
+ return 0
18
+ if not user.is_active:
19
+ return 0
20
+ return 0.2
21
+ ```
@@ -0,0 +1,19 @@
1
+ # Rust
2
+
3
+ Delta only. Keep the shared table.
4
+
5
+ - Prefer `Result` and `?`. Do not `.unwrap()` / `.expect()` in library or production paths.
6
+ - Let errors surface with context (`map_err`, `anyhow`, or an equivalent). Do not swallow with `let _ =`.
7
+ - Do not mutate the caller's value when returning a new one is enough.
8
+
9
+ ```rust
10
+ // Bad
11
+ fn load_user(id: &str) -> User {
12
+ repo.get(id).unwrap()
13
+ }
14
+
15
+ // Good
16
+ fn load_user(id: &str) -> Result<User, LoadUserError> {
17
+ repo.get(id).map_err(|err| LoadUserError::from_repo(id, err))
18
+ }
19
+ ```
@@ -0,0 +1,30 @@
1
+ # TypeScript
2
+
3
+ Delta only. Keep the shared table.
4
+
5
+ - Braces on every `if` / `else`, including one-line guards.
6
+ - Pick one meaning for `undefined` vs `null` and keep it. Do not use either as a stand-in for a named business state.
7
+ - Name domain literals once (`const` / enum / const object). Do not scatter magic strings.
8
+
9
+ ```ts
10
+ // Bad
11
+ function getDiscount(user?: { plan?: { discount: number } }) {
12
+ if (!user) {
13
+ return null;
14
+ }
15
+ if (!user.plan) {
16
+ return null;
17
+ }
18
+ if (user.plan.discount === 0) {
19
+ return null;
20
+ }
21
+ }
22
+
23
+ // Good
24
+ function getDiscount(user: { plan?: { discount: number } } | undefined): number {
25
+ if (user === undefined || user.plan === undefined) {
26
+ return 0;
27
+ }
28
+ return user.plan.discount;
29
+ }
30
+ ```