pi-mono-all 1.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.
Files changed (161) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENCE.md +7 -0
  3. package/node_modules/pi-common/package.json +22 -0
  4. package/node_modules/pi-common/src/auth-config.ts +290 -0
  5. package/node_modules/pi-common/src/auth.ts +63 -0
  6. package/node_modules/pi-common/src/cache.ts +60 -0
  7. package/node_modules/pi-common/src/errors.ts +47 -0
  8. package/node_modules/pi-common/src/http-client.ts +118 -0
  9. package/node_modules/pi-common/src/index.ts +7 -0
  10. package/node_modules/pi-common/src/rate-limiter.ts +32 -0
  11. package/node_modules/pi-common/src/tool-result.ts +27 -0
  12. package/node_modules/pi-mono-ask-user-question/CHANGELOG.md +185 -0
  13. package/node_modules/pi-mono-ask-user-question/README.md +226 -0
  14. package/node_modules/pi-mono-ask-user-question/index.ts +923 -0
  15. package/node_modules/pi-mono-ask-user-question/package.json +29 -0
  16. package/node_modules/pi-mono-auto-fix/CHANGELOG.md +59 -0
  17. package/node_modules/pi-mono-auto-fix/README.md +77 -0
  18. package/node_modules/pi-mono-auto-fix/index.ts +488 -0
  19. package/node_modules/pi-mono-auto-fix/package.json +23 -0
  20. package/node_modules/pi-mono-btw/CHANGELOG.md +180 -0
  21. package/node_modules/pi-mono-btw/README.md +24 -0
  22. package/node_modules/pi-mono-btw/index.ts +499 -0
  23. package/node_modules/pi-mono-btw/package.json +29 -0
  24. package/node_modules/pi-mono-clear/CHANGELOG.md +180 -0
  25. package/node_modules/pi-mono-clear/README.md +40 -0
  26. package/node_modules/pi-mono-clear/index.ts +45 -0
  27. package/node_modules/pi-mono-clear/package.json +29 -0
  28. package/node_modules/pi-mono-context/CHANGELOG.md +12 -0
  29. package/node_modules/pi-mono-context/README.md +74 -0
  30. package/node_modules/pi-mono-context/index.ts +641 -0
  31. package/node_modules/pi-mono-context/package.json +29 -0
  32. package/node_modules/pi-mono-context-guard/CHANGELOG.md +195 -0
  33. package/node_modules/pi-mono-context-guard/README.md +81 -0
  34. package/node_modules/pi-mono-context-guard/index.ts +212 -0
  35. package/node_modules/pi-mono-context-guard/package.json +23 -0
  36. package/node_modules/pi-mono-figma/CHANGELOG.md +59 -0
  37. package/node_modules/pi-mono-figma/README.md +236 -0
  38. package/node_modules/pi-mono-figma/__tests__/code-connect.test.ts +32 -0
  39. package/node_modules/pi-mono-figma/__tests__/figma-assets.test.ts +38 -0
  40. package/node_modules/pi-mono-figma/__tests__/figma-component-hints.test.ts +23 -0
  41. package/node_modules/pi-mono-figma/__tests__/figma-implementation-layout.test.ts +47 -0
  42. package/node_modules/pi-mono-figma/__tests__/figma-search.test.ts +51 -0
  43. package/node_modules/pi-mono-figma/__tests__/figma-summarizer.test.ts +65 -0
  44. package/node_modules/pi-mono-figma/__tests__/fixtures/complex-auto-layout.json +115 -0
  45. package/node_modules/pi-mono-figma/__tests__/fixtures/component-instance.json +50 -0
  46. package/node_modules/pi-mono-figma/__tests__/fixtures/hidden-and-vectors.json +28 -0
  47. package/node_modules/pi-mono-figma/__tests__/fixtures/variables-and-styles.json +40 -0
  48. package/node_modules/pi-mono-figma/docs/live-selection-bridge.md +16 -0
  49. package/node_modules/pi-mono-figma/index.ts +6 -0
  50. package/node_modules/pi-mono-figma/package.json +33 -0
  51. package/node_modules/pi-mono-figma/skills/figma/SKILL.md +143 -0
  52. package/node_modules/pi-mono-figma/src/code-connect.ts +110 -0
  53. package/node_modules/pi-mono-figma/src/figma-assets.ts +146 -0
  54. package/node_modules/pi-mono-figma/src/figma-cache.ts +6 -0
  55. package/node_modules/pi-mono-figma/src/figma-client.ts +471 -0
  56. package/node_modules/pi-mono-figma/src/figma-component-hints.ts +87 -0
  57. package/node_modules/pi-mono-figma/src/figma-implementation.ts +264 -0
  58. package/node_modules/pi-mono-figma/src/figma-schemas.ts +139 -0
  59. package/node_modules/pi-mono-figma/src/figma-search.ts +195 -0
  60. package/node_modules/pi-mono-figma/src/figma-summarizer.ts +673 -0
  61. package/node_modules/pi-mono-figma/src/figma-tokens.ts +57 -0
  62. package/node_modules/pi-mono-figma/src/figma-tools.ts +352 -0
  63. package/node_modules/pi-mono-linear/CHANGELOG.md +44 -0
  64. package/node_modules/pi-mono-linear/README.md +159 -0
  65. package/node_modules/pi-mono-linear/index.ts +6 -0
  66. package/node_modules/pi-mono-linear/package.json +30 -0
  67. package/node_modules/pi-mono-linear/skills/linear/SKILL.md +107 -0
  68. package/node_modules/pi-mono-linear/src/linear-client.ts +339 -0
  69. package/node_modules/pi-mono-linear/src/linear-queries.ts +101 -0
  70. package/node_modules/pi-mono-linear/src/linear-schemas.ts +90 -0
  71. package/node_modules/pi-mono-linear/src/linear-tools.ts +362 -0
  72. package/node_modules/pi-mono-loop/CHANGELOG.md +163 -0
  73. package/node_modules/pi-mono-loop/README.md +54 -0
  74. package/node_modules/pi-mono-loop/index.ts +291 -0
  75. package/node_modules/pi-mono-loop/package.json +26 -0
  76. package/node_modules/pi-mono-multi-edit/CHANGELOG.md +232 -0
  77. package/node_modules/pi-mono-multi-edit/README.md +244 -0
  78. package/node_modules/pi-mono-multi-edit/__tests__/classic.test.ts +277 -0
  79. package/node_modules/pi-mono-multi-edit/__tests__/diff.test.ts +77 -0
  80. package/node_modules/pi-mono-multi-edit/__tests__/patch.test.ts +287 -0
  81. package/node_modules/pi-mono-multi-edit/benchmark-edits.ts +966 -0
  82. package/node_modules/pi-mono-multi-edit/classic.ts +435 -0
  83. package/node_modules/pi-mono-multi-edit/diff.ts +143 -0
  84. package/node_modules/pi-mono-multi-edit/index.ts +266 -0
  85. package/node_modules/pi-mono-multi-edit/package.json +37 -0
  86. package/node_modules/pi-mono-multi-edit/patch.ts +463 -0
  87. package/node_modules/pi-mono-multi-edit/types.ts +53 -0
  88. package/node_modules/pi-mono-multi-edit/workspace.ts +85 -0
  89. package/node_modules/pi-mono-review/CHANGELOG.md +190 -0
  90. package/node_modules/pi-mono-review/README.md +30 -0
  91. package/node_modules/pi-mono-review/common.ts +930 -0
  92. package/node_modules/pi-mono-review/index.ts +8 -0
  93. package/node_modules/pi-mono-review/package.json +29 -0
  94. package/node_modules/pi-mono-review/review-tui.ts +194 -0
  95. package/node_modules/pi-mono-review/review.ts +119 -0
  96. package/node_modules/pi-mono-review/reviewer.ts +339 -0
  97. package/node_modules/pi-mono-sentinel/CHANGELOG.md +158 -0
  98. package/node_modules/pi-mono-sentinel/README.md +87 -0
  99. package/node_modules/pi-mono-sentinel/__tests__/output-scanner.test.ts +109 -0
  100. package/node_modules/pi-mono-sentinel/__tests__/permissions.test.ts +202 -0
  101. package/node_modules/pi-mono-sentinel/__tests__/whitelist.test.ts +59 -0
  102. package/node_modules/pi-mono-sentinel/guards/execution-tracker.ts +281 -0
  103. package/node_modules/pi-mono-sentinel/guards/output-scanner.ts +232 -0
  104. package/node_modules/pi-mono-sentinel/guards/permission-gate.ts +170 -0
  105. package/node_modules/pi-mono-sentinel/index.ts +43 -0
  106. package/node_modules/pi-mono-sentinel/package.json +26 -0
  107. package/node_modules/pi-mono-sentinel/patterns/permissions.ts +175 -0
  108. package/node_modules/pi-mono-sentinel/patterns/read-targets.ts +104 -0
  109. package/node_modules/pi-mono-sentinel/patterns/secrets.ts +143 -0
  110. package/node_modules/pi-mono-sentinel/session.ts +95 -0
  111. package/node_modules/pi-mono-sentinel/specs/2026/04/sentinel/001-permission-gate.md +145 -0
  112. package/node_modules/pi-mono-sentinel/types.ts +39 -0
  113. package/node_modules/pi-mono-sentinel/whitelist.ts +86 -0
  114. package/node_modules/pi-mono-simplify/CHANGELOG.md +163 -0
  115. package/node_modules/pi-mono-simplify/README.md +56 -0
  116. package/node_modules/pi-mono-simplify/index.ts +78 -0
  117. package/node_modules/pi-mono-simplify/package.json +29 -0
  118. package/node_modules/pi-mono-status-line/CHANGELOG.md +180 -0
  119. package/node_modules/pi-mono-status-line/README.md +96 -0
  120. package/node_modules/pi-mono-status-line/basic.ts +89 -0
  121. package/node_modules/pi-mono-status-line/expert.ts +689 -0
  122. package/node_modules/pi-mono-status-line/index.ts +54 -0
  123. package/node_modules/pi-mono-status-line/package.json +29 -0
  124. package/node_modules/pi-mono-team-mode/CHANGELOG.md +278 -0
  125. package/node_modules/pi-mono-team-mode/README.md +246 -0
  126. package/node_modules/pi-mono-team-mode/__tests__/agent-manager-transient.test.ts +75 -0
  127. package/node_modules/pi-mono-team-mode/__tests__/delegation-manager.test.ts +118 -0
  128. package/node_modules/pi-mono-team-mode/__tests__/formatters.test.ts +104 -0
  129. package/node_modules/pi-mono-team-mode/__tests__/model-config.test.ts +272 -0
  130. package/node_modules/pi-mono-team-mode/__tests__/notification-box.test.ts +34 -0
  131. package/node_modules/pi-mono-team-mode/__tests__/parallel-utils.test.ts +32 -0
  132. package/node_modules/pi-mono-team-mode/__tests__/pi-stream-parser.test.ts +64 -0
  133. package/node_modules/pi-mono-team-mode/__tests__/prompts.test.ts +106 -0
  134. package/node_modules/pi-mono-team-mode/__tests__/store.test.ts +164 -0
  135. package/node_modules/pi-mono-team-mode/__tests__/tasks.test.ts +267 -0
  136. package/node_modules/pi-mono-team-mode/__tests__/teammate-specs.test.ts +114 -0
  137. package/node_modules/pi-mono-team-mode/__tests__/widget.test.ts +41 -0
  138. package/node_modules/pi-mono-team-mode/__tests__/worktree.test.ts +78 -0
  139. package/node_modules/pi-mono-team-mode/core/chain-utils.ts +90 -0
  140. package/node_modules/pi-mono-team-mode/core/fs-utils.ts +44 -0
  141. package/node_modules/pi-mono-team-mode/core/model-config.ts +432 -0
  142. package/node_modules/pi-mono-team-mode/core/parallel-utils.ts +48 -0
  143. package/node_modules/pi-mono-team-mode/core/prompts.ts +158 -0
  144. package/node_modules/pi-mono-team-mode/core/store.ts +156 -0
  145. package/node_modules/pi-mono-team-mode/core/tasks.ts +99 -0
  146. package/node_modules/pi-mono-team-mode/core/teammate-specs.ts +124 -0
  147. package/node_modules/pi-mono-team-mode/core/types.ts +160 -0
  148. package/node_modules/pi-mono-team-mode/index.ts +825 -0
  149. package/node_modules/pi-mono-team-mode/managers/agent-manager.ts +654 -0
  150. package/node_modules/pi-mono-team-mode/managers/delegation-manager.ts +211 -0
  151. package/node_modules/pi-mono-team-mode/managers/task-manager.ts +238 -0
  152. package/node_modules/pi-mono-team-mode/managers/team-manager.ts +59 -0
  153. package/node_modules/pi-mono-team-mode/package.json +33 -0
  154. package/node_modules/pi-mono-team-mode/runtime/pi-stream-parser.ts +194 -0
  155. package/node_modules/pi-mono-team-mode/runtime/subprocess.ts +183 -0
  156. package/node_modules/pi-mono-team-mode/runtime/transient-session.ts +196 -0
  157. package/node_modules/pi-mono-team-mode/runtime/worktree.ts +90 -0
  158. package/node_modules/pi-mono-team-mode/ui/formatters.ts +149 -0
  159. package/node_modules/pi-mono-team-mode/ui/notification-box.ts +55 -0
  160. package/node_modules/pi-mono-team-mode/ui/widget.ts +94 -0
  161. package/package.json +76 -0
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Multi-Edit Extension — replaces the built-in `edit` tool.
3
+ *
4
+ * Supports all original parameters (path, oldText, newText) plus:
5
+ * - `multi`: array of {path, oldText, newText} edits applied in sequence
6
+ * - `patch`: Codex-style apply_patch payload
7
+ *
8
+ * When both top-level params and `multi` are provided, the top-level edit
9
+ * is treated as an implicit first item prepended to the multi list.
10
+ *
11
+ * A preflight pass is performed before mutating files:
12
+ * - multi/top-level mode: preflight via virtualized built-in edit tool
13
+ * - patch mode: preflight by applying patch operations on a virtual filesystem
14
+ */
15
+
16
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
17
+ import { Type } from "@sinclair/typebox";
18
+
19
+ import { applyClassicEdits } from "./classic.ts";
20
+ import { applyPatchOperations, parsePatch } from "./patch.ts";
21
+ import type { EditItem } from "./types.ts";
22
+ import { createRealWorkspace, createVirtualWorkspace } from "./workspace.ts";
23
+
24
+ const editItemSchema = Type.Object({
25
+ path: Type.Optional(
26
+ Type.String({
27
+ description:
28
+ "Path to the file to edit (relative or absolute). Inherits from top-level path if omitted.",
29
+ }),
30
+ ),
31
+ oldText: Type.String({
32
+ description: "Exact text to find and replace (must match exactly)",
33
+ }),
34
+ newText: Type.String({
35
+ description: "New text to replace the old text with",
36
+ }),
37
+ });
38
+
39
+ const multiEditSchema = Type.Object({
40
+ path: Type.Optional(
41
+ Type.String({
42
+ description: "Path to the file to edit (relative or absolute)",
43
+ }),
44
+ ),
45
+ oldText: Type.Optional(
46
+ Type.String({
47
+ description: "Exact text to find and replace (must match exactly)",
48
+ }),
49
+ ),
50
+ newText: Type.Optional(
51
+ Type.String({ description: "New text to replace the old text with" }),
52
+ ),
53
+ multi: Type.Optional(
54
+ Type.Array(editItemSchema, {
55
+ description:
56
+ "Multiple edits to apply in sequence. Each item has path, oldText, and newText.",
57
+ }),
58
+ ),
59
+ patch: Type.Optional(
60
+ Type.String({
61
+ description:
62
+ "Codex-style apply_patch payload (*** Begin Patch ... *** End Patch). Mutually exclusive with path/oldText/newText/multi.",
63
+ }),
64
+ ),
65
+ });
66
+
67
+ export default function (pi: ExtensionAPI) {
68
+ pi.registerTool({
69
+ name: "edit",
70
+ label: "edit",
71
+ description:
72
+ "Edit a file by replacing exact text. The oldText must match exactly (including whitespace). Use this for precise, surgical edits. Supports a `multi` parameter for batch edits across one or more files, and a `patch` parameter for Codex-style patches.",
73
+ promptSnippet:
74
+ "Edit a file by replacing exact text. The oldText must match exactly (including whitespace). Use this for precise, surgical edits.",
75
+ promptGuidelines: [
76
+ "Use edit for precise changes (old text must match exactly)",
77
+ "Use the `multi` parameter to apply multiple edits in a single tool call",
78
+ "Use the `patch` parameter for Codex-style multi-file / hunk-based edits",
79
+ ],
80
+ parameters: multiEditSchema,
81
+
82
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
83
+ const { path, oldText, newText, multi, patch } = params;
84
+
85
+ const hasAnyClassicParam =
86
+ path !== undefined ||
87
+ oldText !== undefined ||
88
+ newText !== undefined ||
89
+ multi !== undefined;
90
+ if (patch !== undefined && hasAnyClassicParam) {
91
+ throw new Error(
92
+ "The `patch` parameter is mutually exclusive with path/oldText/newText/multi.",
93
+ );
94
+ }
95
+
96
+ if (patch !== undefined) {
97
+ const ops = parsePatch(patch);
98
+
99
+ // Preflight on virtual filesystem before mutating real files.
100
+ await applyPatchOperations(
101
+ ops,
102
+ createVirtualWorkspace(ctx.cwd),
103
+ ctx.cwd,
104
+ signal,
105
+ { collectDiff: false },
106
+ );
107
+
108
+ // Apply for real.
109
+ const applied = await applyPatchOperations(
110
+ ops,
111
+ createRealWorkspace(pi),
112
+ ctx.cwd,
113
+ signal,
114
+ { collectDiff: true },
115
+ );
116
+ const summary = applied
117
+ .map((r, i) => `${i + 1}. ${r.message}`)
118
+ .join("\n");
119
+ const combinedDiff = applied
120
+ .filter((r) => r.diff)
121
+ .map((r) => `File: ${r.path}\n${r.diff}`)
122
+ .join("\n\n");
123
+ const firstChangedLine = applied.find(
124
+ (r) => r.firstChangedLine !== undefined,
125
+ )?.firstChangedLine;
126
+ return {
127
+ content: [
128
+ {
129
+ type: "text" as const,
130
+ text: `Applied patch with ${applied.length} operation(s).\n${summary}`,
131
+ },
132
+ ],
133
+ details: {
134
+ diff: combinedDiff,
135
+ firstChangedLine,
136
+ },
137
+ };
138
+ }
139
+
140
+ // Build classic edit list.
141
+ const edits: EditItem[] = [];
142
+ const hasTopLevel =
143
+ path !== undefined && oldText !== undefined && newText !== undefined;
144
+
145
+ if (hasTopLevel) {
146
+ edits.push({ path: path!, oldText: oldText!, newText: newText! });
147
+ } else if (
148
+ path !== undefined ||
149
+ oldText !== undefined ||
150
+ newText !== undefined
151
+ ) {
152
+ // When multi is present, only a bare top-level `path` (for inheritance) is allowed.
153
+ // Any other partial combination (e.g. path+oldText, oldText+newText) is an error.
154
+ const hasOnlyPath =
155
+ path !== undefined && oldText === undefined && newText === undefined;
156
+ if (!hasOnlyPath || multi === undefined) {
157
+ const missing: string[] = [];
158
+ if (path === undefined) missing.push("path");
159
+ if (oldText === undefined) missing.push("oldText");
160
+ if (newText === undefined) missing.push("newText");
161
+ throw new Error(
162
+ `Incomplete top-level edit: missing ${missing.join(", ")}. Provide all three (path, oldText, newText) or use only the multi parameter.`,
163
+ );
164
+ }
165
+ // path-only top-level with multi is fine — path is inherited below.
166
+ }
167
+
168
+ if (multi) {
169
+ for (const item of multi) {
170
+ edits.push({
171
+ path: item.path ?? path ?? "",
172
+ oldText: item.oldText,
173
+ newText: item.newText,
174
+ });
175
+ }
176
+ }
177
+
178
+ if (edits.length === 0) {
179
+ throw new Error(
180
+ "No edits provided. Supply path/oldText/newText, a multi array, or a patch.",
181
+ );
182
+ }
183
+
184
+ // Validate that every edit has a path.
185
+ for (let i = 0; i < edits.length; i++) {
186
+ if (!edits[i].path) {
187
+ throw new Error(
188
+ `Edit ${i + 1} is missing a path. Provide a path on each multi item or set a top-level path to inherit.`,
189
+ );
190
+ }
191
+ }
192
+
193
+ // Preflight pass on virtual workspace before mutating real files.
194
+ // Uses sequential occurrence matching so same-file edits are resolved
195
+ // in file order (positional ordering).
196
+ try {
197
+ await applyClassicEdits(
198
+ edits,
199
+ createVirtualWorkspace(ctx.cwd),
200
+ ctx.cwd,
201
+ signal,
202
+ { collectDiff: false },
203
+ );
204
+ } catch (err: any) {
205
+ throw new Error(
206
+ `Preflight failed before mutating files.\n${err.message ?? String(err)}`,
207
+ );
208
+ }
209
+
210
+ // Apply for real. `continueOnError` lets successful edits land even
211
+ // when a sibling fails; `rollbackOnError` restores files if an
212
+ // unexpected error (I/O, abort) breaks the batch mid-write.
213
+ const isBatch = edits.length > 1;
214
+ const results = await applyClassicEdits(
215
+ edits,
216
+ createRealWorkspace(pi),
217
+ ctx.cwd,
218
+ signal,
219
+ {
220
+ collectDiff: true,
221
+ rollbackOnError: true,
222
+ continueOnError: isBatch,
223
+ },
224
+ );
225
+
226
+ const succeeded = results.filter((r) => r?.success);
227
+ const failed = results.filter((r) => r && !r.success);
228
+
229
+ if (results.length === 1) {
230
+ const r = results[0];
231
+ return {
232
+ content: [{ type: "text" as const, text: r.message }],
233
+ details: {
234
+ diff: r.diff ?? "",
235
+ firstChangedLine: r.firstChangedLine,
236
+ },
237
+ };
238
+ }
239
+
240
+ const combinedDiff = results
241
+ .filter((r) => r?.diff)
242
+ .map((r) => r.diff)
243
+ .join("\n");
244
+
245
+ const firstChanged = results.find(
246
+ (r) => r?.firstChangedLine !== undefined,
247
+ )?.firstChangedLine;
248
+ const summary = results
249
+ .map((r, i) => `${i + 1}. ${r.message}`)
250
+ .join("\n");
251
+
252
+ const statusLine =
253
+ failed.length > 0
254
+ ? `Applied ${succeeded.length}/${results.length} edit(s). ${failed.length} failed:\n${summary}`
255
+ : `Applied ${results.length} edit(s) successfully.\n${summary}`;
256
+
257
+ return {
258
+ content: [{ type: "text" as const, text: statusLine }],
259
+ details: {
260
+ diff: combinedDiff,
261
+ firstChangedLine: firstChanged,
262
+ },
263
+ };
264
+ },
265
+ });
266
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "pi-mono-multi-edit",
3
+ "version": "1.7.2",
4
+ "description": "Pi extension that replaces edit with batch edits and Codex-style patches",
5
+ "type": "module",
6
+ "keywords": [
7
+ "pi-package",
8
+ "pi-extension"
9
+ ],
10
+ "scripts": {
11
+ "test": "npx tsx --test '__tests__/**/*.test.ts'",
12
+ "bench": "npx tsx benchmark-edits.ts"
13
+ },
14
+ "peerDependencies": {
15
+ "@mariozechner/pi-ai": "*",
16
+ "@mariozechner/pi-coding-agent": "*",
17
+ "@mariozechner/pi-tui": "*",
18
+ "@sinclair/typebox": "*"
19
+ },
20
+ "dependencies": {
21
+ "diff": "^8.0.4"
22
+ },
23
+ "pi": {
24
+ "extensions": [
25
+ "./index.ts"
26
+ ]
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/emanuelcasco/pi-mono-extensions.git",
31
+ "directory": "extensions/multi-edit"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/emanuelcasco/pi-mono-extensions/issues"
35
+ },
36
+ "homepage": "https://github.com/emanuelcasco/pi-mono-extensions#readme"
37
+ }