@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 +20 -6
- package/extensions/ask/index.ts +3 -2
- package/extensions/guidelines/index.ts +13 -0
- package/extensions/guidelines/prompt.ts +26 -0
- package/package.json +7 -2
- package/skills/sideroom-guidelines/SKILL.md +77 -0
- package/skills/sideroom-guidelines/references/languages/go.md +27 -0
- package/skills/sideroom-guidelines/references/languages/java.md +24 -0
- package/skills/sideroom-guidelines/references/languages/php-laravel.md +26 -0
- package/skills/sideroom-guidelines/references/languages/python.md +21 -0
- package/skills/sideroom-guidelines/references/languages/rust.md +19 -0
- package/skills/sideroom-guidelines/references/languages/typescript.md +30 -0
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
|
-
|
|
12
|
-
repository you are
|
|
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
|
-
|
|
|
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.
|
|
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
|
-
|
|
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
|
package/extensions/ask/index.ts
CHANGED
|
@@ -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": "
|
|
4
|
-
"description": "A global Pi package with a questionnaire, work board,
|
|
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
|
+
```
|