@rmrdeveloper/sideroom-pi 8.0.1 → 8.0.2

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.
@@ -0,0 +1,47 @@
1
+ import { dirname, join, resolve } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
5
+ const LANGUAGE_GUIDE_DIRECTORY = join(
6
+ PACKAGE_ROOT,
7
+ 'skills/sideroom-guidelines/references/languages',
8
+ );
9
+
10
+ export const GUIDELINE_SKILL_PATH = join(
11
+ PACKAGE_ROOT,
12
+ 'skills/sideroom-guidelines/SKILL.md',
13
+ );
14
+
15
+ export interface LanguageGuide {
16
+ readonly extensions: readonly string[];
17
+ readonly fileName: string;
18
+ readonly path: string;
19
+ }
20
+
21
+ export const LANGUAGE_GUIDES: readonly LanguageGuide[] = [
22
+ languageGuide('go.md', ['.go']),
23
+ languageGuide('java.md', ['.java']),
24
+ languageGuide('php-laravel.md', ['.php']),
25
+ languageGuide('python.md', ['.py']),
26
+ languageGuide('rust.md', ['.rs']),
27
+ languageGuide('typescript.md', ['.ts', '.tsx']),
28
+ ];
29
+
30
+ export const LANGUAGE_GUIDE_FILES: readonly string[] = LANGUAGE_GUIDES.map(
31
+ ({ fileName }) => fileName,
32
+ );
33
+
34
+ export const LANGUAGE_TARGET_LABELS: readonly string[] = LANGUAGE_GUIDES.map(
35
+ ({ extensions }) => extensions.join('/'),
36
+ );
37
+
38
+ function languageGuide(
39
+ fileName: string,
40
+ extensions: readonly string[],
41
+ ): LanguageGuide {
42
+ return {
43
+ extensions,
44
+ fileName,
45
+ path: join(LANGUAGE_GUIDE_DIRECTORY, fileName),
46
+ };
47
+ }
@@ -1,29 +1,11 @@
1
- import { dirname, join, resolve } from 'node:path';
2
- import { fileURLToPath } from 'node:url';
3
1
  import {
4
2
  type ExtensionAPI,
5
3
  isReadToolResult,
6
4
  isToolCallEventType,
7
5
  } from '@earendil-works/pi-coding-agent';
6
+ import { GUIDELINE_SKILL_PATH, LANGUAGE_GUIDES } from './catalog.ts';
8
7
 
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
- };
8
+ export { GUIDELINE_SKILL_PATH } from './catalog.ts';
27
9
 
28
10
  interface ReadInput {
29
11
  readonly path: string;
@@ -56,7 +38,11 @@ export function registerGuidelineGuard(
56
38
 
57
39
  // SAFETY: Pi validates built-in read arguments before emitting tool_result.
58
40
  const input = event.input as unknown as ReadInput;
59
- if (input.offset !== undefined || input.limit !== undefined) {
41
+ if (
42
+ input.offset !== undefined ||
43
+ input.limit !== undefined ||
44
+ event.details?.truncation?.truncated === true
45
+ ) {
60
46
  return;
61
47
  }
62
48
  recordGuidelineRead(state, input.path);
@@ -70,7 +56,11 @@ export function registerGuidelineGuard(
70
56
  return;
71
57
  }
72
58
 
73
- const reason = mutationBlockReason(state, event.input.path);
59
+ const reason = mutationBlockReason(
60
+ state,
61
+ event.input.path,
62
+ pi.getActiveTools().includes('read'),
63
+ );
74
64
  if (reason === undefined) {
75
65
  return;
76
66
  }
@@ -92,9 +82,9 @@ export function recordGuidelineRead(
92
82
  return;
93
83
  }
94
84
 
95
- for (const guidePath of new Set(Object.values(LANGUAGE_GUIDE_BY_EXTENSION))) {
96
- if (normalizedPath === normalizePath(guidePath)) {
97
- state.languageGuidesRead.add(guidePath);
85
+ for (const guide of LANGUAGE_GUIDES) {
86
+ if (normalizedPath === normalizePath(guide.path)) {
87
+ state.languageGuidesRead.add(guide.path);
98
88
  return;
99
89
  }
100
90
  }
@@ -103,6 +93,7 @@ export function recordGuidelineRead(
103
93
  export function mutationBlockReason(
104
94
  state: GuidelineReadState,
105
95
  targetPath: string,
96
+ readToolAvailable = true,
106
97
  ): string | undefined {
107
98
  const requiredPaths: string[] = [];
108
99
  if (!state.skillRead) {
@@ -117,18 +108,18 @@ export function mutationBlockReason(
117
108
  if (requiredPaths.length === 0) {
118
109
  return undefined;
119
110
  }
111
+ if (!readToolAvailable) {
112
+ return `Blocked ${targetPath}: the read tool is inactive, so the guidelines gate cannot be satisfied. Enable read, then read ${requiredPaths.join(' and ')} before retrying the mutation.`;
113
+ }
120
114
 
121
- return `Blocked ${targetPath}: read ${requiredPaths.join(' and ')} with the read tool in this agent run, then retry the edit.`;
115
+ return `Blocked ${targetPath}: read ${requiredPaths.join(' and ')} with the read tool in this agent run, then retry the mutation.`;
122
116
  }
123
117
 
124
118
  function languageGuideFor(path: string): string | undefined {
125
119
  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];
120
+ return LANGUAGE_GUIDES.find(({ extensions }) =>
121
+ extensions.some((extension) => normalizedPath.endsWith(extension)),
122
+ )?.path;
132
123
  }
133
124
 
134
125
  function normalizePath(path: string): string {
@@ -1,20 +1,20 @@
1
+ import { GUIDELINE_SKILL_PATH, LANGUAGE_GUIDES } from './catalog.ts';
2
+
3
+ export { LANGUAGE_GUIDE_FILES } from './catalog.ts';
4
+
1
5
  export const GUIDELINES_REMINDER_HEADING =
2
6
  'Sideroom coding guidelines (always-on reminder):';
3
7
 
4
- export const GUIDELINES_REMINDER = `${GUIDELINES_REMINDER_HEADING}
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
+ const LANGUAGE_GUIDE_REMINDERS = LANGUAGE_GUIDES.map(
9
+ ({ extensions, path }) =>
10
+ `If a file you will write or edit ends in ${extensions.join(' or ')}, use the read tool without offset or limit to load the exact matching guide at ${path} in full.`,
11
+ ).join('\n');
9
12
 
10
- export const LANGUAGE_GUIDE_FILES = [
11
- 'go.md',
12
- 'java.md',
13
- 'php-laravel.md',
14
- 'python.md',
15
- 'rust.md',
16
- 'typescript.md',
17
- ] as const;
13
+ export const GUIDELINES_REMINDER = `${GUIDELINES_REMINDER_HEADING}
14
+ Before the first write or edit in each agent run, use the read tool without offset or limit to load ${GUIDELINE_SKILL_PATH} in full.
15
+ ${LANGUAGE_GUIDE_REMINDERS}
16
+ A required read counts only when it succeeds without truncation. Until all required reads succeed, do not call write or edit and do not mutate files through bash or another tool; the extension blocks those mutations.
17
+ After changing files, review the code against the loaded guide and run the relevant formatter, linter, type checks, and tests.`;
18
18
 
19
19
  export function appendGuidelinesReminder(systemPrompt: string): string {
20
20
  if (systemPrompt.includes(GUIDELINES_REMINDER_HEADING)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmrdeveloper/sideroom-pi",
3
- "version": "8.0.1",
3
+ "version": "8.0.2",
4
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": [