pinokiod 7.3.0 → 7.3.3

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 (125) hide show
  1. package/kernel/api/github/index.js +444 -0
  2. package/kernel/api/index.js +199 -11
  3. package/kernel/api/process/index.js +124 -44
  4. package/kernel/api/shell_run_template.js +273 -0
  5. package/kernel/api/uri/index.js +51 -0
  6. package/kernel/bin/{conda-python.js → conda-pins.js} +23 -0
  7. package/kernel/bin/conda.js +15 -5
  8. package/kernel/bin/git.js +9 -10
  9. package/kernel/bin/huggingface.js +1 -1
  10. package/kernel/bin/index.js +5 -2
  11. package/kernel/bin/zip.js +9 -1
  12. package/kernel/connect/providers/github/README.md +5 -4
  13. package/kernel/environment.js +195 -92
  14. package/kernel/git.js +98 -19
  15. package/kernel/gitconfig_template +7 -0
  16. package/kernel/gpu/amd.js +72 -0
  17. package/kernel/gpu/apple.js +8 -0
  18. package/kernel/gpu/common.js +12 -0
  19. package/kernel/gpu/intel.js +47 -0
  20. package/kernel/gpu/nvidia.js +8 -0
  21. package/kernel/index.js +11 -1
  22. package/kernel/managed_skills.js +871 -0
  23. package/kernel/plugin.js +6 -58
  24. package/kernel/plugin_sources.js +316 -0
  25. package/kernel/resource_usage/gpu.js +349 -0
  26. package/kernel/resource_usage/index.js +322 -0
  27. package/kernel/resource_usage/macos_footprint.js +197 -0
  28. package/kernel/resource_usage/preferences.js +92 -0
  29. package/kernel/resource_usage/process_tree.js +303 -0
  30. package/kernel/scripts/git/create +4 -4
  31. package/kernel/scripts/git/fork +7 -8
  32. package/kernel/shell.js +23 -2
  33. package/kernel/shells.js +41 -0
  34. package/kernel/sysinfo.js +62 -9
  35. package/kernel/util.js +60 -0
  36. package/package.json +1 -1
  37. package/server/index.js +984 -156
  38. package/server/lib/app_log_report.js +543 -0
  39. package/server/lib/content_validation.js +55 -33
  40. package/server/lib/launcher_instruction_bootstrap.js +4 -96
  41. package/server/lib/terminal_session_helpers.js +0 -3
  42. package/server/public/common.js +77 -31
  43. package/server/public/create-launcher.js +4 -32
  44. package/server/public/logs.js +1428 -0
  45. package/server/public/nav.js +7 -0
  46. package/server/public/plugin-detail.js +93 -10
  47. package/server/public/privacy_filter_worker.js +391 -0
  48. package/server/public/style.css +1104 -154
  49. package/server/public/task-launcher.js +8 -29
  50. package/server/public/universal-launcher.css +8 -6
  51. package/server/public/universal-launcher.js +3 -27
  52. package/server/routes/apps.js +195 -1
  53. package/server/views/app.ejs +3041 -717
  54. package/server/views/autolaunch.ejs +917 -0
  55. package/server/views/bootstrap.ejs +7 -1
  56. package/server/views/d.ejs +408 -65
  57. package/server/views/editor.ejs +85 -19
  58. package/server/views/index.ejs +661 -111
  59. package/server/views/init/index.ejs +1 -1
  60. package/server/views/install.ejs +1 -1
  61. package/server/views/logs.ejs +164 -86
  62. package/server/views/net.ejs +7 -1
  63. package/server/views/partials/d_terminal_column.ejs +2 -2
  64. package/server/views/partials/d_terminal_options.ejs +0 -8
  65. package/server/views/partials/fs_status.ejs +47 -0
  66. package/server/views/partials/home_action_modal.ejs +86 -0
  67. package/server/views/partials/home_run_menu.ejs +87 -0
  68. package/server/views/partials/main_sidebar.ejs +2 -0
  69. package/server/views/partials/menu.ejs +1 -1
  70. package/server/views/plugin_detail.ejs +19 -4
  71. package/server/views/plugins.ejs +201 -3
  72. package/server/views/pre.ejs +1 -1
  73. package/server/views/pro.ejs +1 -1
  74. package/server/views/shell.ejs +40 -18
  75. package/server/views/skills.ejs +506 -0
  76. package/server/views/terminal.ejs +45 -19
  77. package/spec/INSTRUCTION_SYNC.md +20 -10
  78. package/system/plugin/antigravity-cli/antigravity.png +0 -0
  79. package/system/plugin/antigravity-cli/common.js +155 -0
  80. package/system/plugin/antigravity-cli/install.js +272 -0
  81. package/system/plugin/antigravity-cli/pinokio.js +13 -0
  82. package/system/plugin/antigravity-cli-auto/antigravity.png +0 -0
  83. package/system/plugin/antigravity-cli-auto/pinokio.js +13 -0
  84. package/system/plugin/claude/claude.png +0 -0
  85. package/system/plugin/claude/pinokio.js +47 -0
  86. package/system/plugin/claude-auto/claude.png +0 -0
  87. package/system/plugin/claude-auto/pinokio.js +58 -0
  88. package/system/plugin/claude-desktop/icon.jpeg +0 -0
  89. package/system/plugin/claude-desktop/pinokio.js +23 -0
  90. package/system/plugin/codex/openai.webp +0 -0
  91. package/system/plugin/codex/pinokio.js +42 -0
  92. package/system/plugin/codex-auto/openai.webp +0 -0
  93. package/system/plugin/codex-auto/pinokio.js +49 -0
  94. package/system/plugin/codex-desktop/icon.png +0 -0
  95. package/system/plugin/codex-desktop/pinokio.js +23 -0
  96. package/system/plugin/crush/crush.png +0 -0
  97. package/system/plugin/crush/pinokio.js +15 -0
  98. package/system/plugin/cursor/cursor.jpeg +0 -0
  99. package/system/plugin/cursor/pinokio.js +23 -0
  100. package/system/plugin/qwen/pinokio.js +34 -0
  101. package/system/plugin/qwen/qwen.png +0 -0
  102. package/system/plugin/vscode/pinokio.js +20 -0
  103. package/system/plugin/vscode/vscode.png +0 -0
  104. package/system/plugin/windsurf/pinokio.js +23 -0
  105. package/system/plugin/windsurf/windsurf.png +0 -0
  106. package/test/antigravity-cli-plugin.test.js +185 -0
  107. package/test/app-api.test.js +239 -0
  108. package/test/app-log-report.test.js +67 -0
  109. package/test/environment-cache-preflight.test.js +98 -0
  110. package/test/git-bin.test.js +59 -0
  111. package/test/git-defaults.test.js +97 -0
  112. package/test/github-api.test.js +158 -0
  113. package/test/github-connection.test.js +117 -0
  114. package/test/huggingface-bin.test.js +25 -0
  115. package/test/managed-skills.test.js +351 -0
  116. package/test/plugin-action-functions.test.js +337 -0
  117. package/test/plugin-dev-iframe.test.js +17 -0
  118. package/test/plugin-sources.test.js +203 -0
  119. package/test/privacy-filter-worker-heuristics.test.js +69 -0
  120. package/test/process-wait.test.js +169 -0
  121. package/test/script-api.test.js +97 -0
  122. package/test/shell-api.test.js +134 -0
  123. package/test/shell-run-template.test.js +209 -0
  124. package/test/storage-api.test.js +137 -0
  125. package/test/uri-api.test.js +100 -0
@@ -0,0 +1,506 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Skills</title>
7
+ <link href="/css/fontawesome.min.css" rel="stylesheet">
8
+ <link href="/css/solid.min.css" rel="stylesheet">
9
+ <link href="/css/brands.min.css" rel="stylesheet">
10
+ <link href="/noty.css" rel="stylesheet">
11
+ <link href="/style.css" rel="stylesheet">
12
+ <link href="/task-launcher.css" rel="stylesheet">
13
+ <% if (agent === "electron") { %>
14
+ <link href="/electron.css" rel="stylesheet"/>
15
+ <% } %>
16
+ <style>
17
+ body.skills-page .skills-overview {
18
+ display: grid;
19
+ grid-template-columns: minmax(0, 1.3fr) minmax(280px, 0.7fr);
20
+ gap: 16px;
21
+ align-items: start;
22
+ }
23
+ body.skills-page .skills-explainer {
24
+ display: grid;
25
+ gap: 8px;
26
+ max-width: 82ch;
27
+ }
28
+ body.skills-page .skills-explainer-title {
29
+ margin: 0;
30
+ font-size: 14px;
31
+ line-height: 1.25;
32
+ font-weight: 650;
33
+ color: var(--task-text);
34
+ }
35
+ body.skills-page .skills-explainer-copy {
36
+ margin: 0;
37
+ font-size: 13px;
38
+ line-height: 1.5;
39
+ color: var(--task-muted);
40
+ }
41
+ body.skills-page .skills-explainer-list {
42
+ display: grid;
43
+ gap: 5px;
44
+ margin: 0;
45
+ padding: 0;
46
+ list-style: none;
47
+ font-size: 12px;
48
+ line-height: 1.45;
49
+ color: var(--task-muted);
50
+ }
51
+ body.skills-page .skills-explainer-list li {
52
+ position: relative;
53
+ padding-left: 14px;
54
+ }
55
+ body.skills-page .skills-explainer-list li::before {
56
+ content: "";
57
+ position: absolute;
58
+ top: 0.72em;
59
+ left: 0;
60
+ width: 4px;
61
+ height: 4px;
62
+ border-radius: 999px;
63
+ background: currentColor;
64
+ opacity: 0.56;
65
+ }
66
+ body.skills-page .skills-path-stack {
67
+ display: grid;
68
+ gap: 8px;
69
+ align-content: start;
70
+ font-size: 12px;
71
+ color: var(--task-muted);
72
+ }
73
+ body.skills-page .skills-path-stack code {
74
+ overflow-wrap: anywhere;
75
+ }
76
+ body.skills-page .skills-install-form {
77
+ display: grid;
78
+ grid-template-columns: minmax(240px, 1fr) auto;
79
+ gap: 10px;
80
+ align-items: end;
81
+ }
82
+ body.skills-page .skills-install-submit {
83
+ min-width: 132px;
84
+ min-height: 38px;
85
+ padding: 0 16px;
86
+ gap: 7px;
87
+ font-size: 13px;
88
+ font-weight: 650;
89
+ }
90
+ body.skills-page .skills-list {
91
+ display: grid;
92
+ gap: 10px;
93
+ }
94
+ body.skills-page .skill-row {
95
+ display: grid;
96
+ grid-template-columns: minmax(0, 1fr) auto;
97
+ gap: 14px;
98
+ align-items: start;
99
+ border: 1px solid rgba(0, 0, 0, 0.09);
100
+ border-radius: 8px;
101
+ padding: 14px;
102
+ background: rgba(0, 0, 0, 0.025);
103
+ }
104
+ body.dark.skills-page .skill-row {
105
+ border-color: rgba(255, 255, 255, 0.12);
106
+ background: rgba(255, 255, 255, 0.04);
107
+ }
108
+ body.skills-page .skill-row.has-conflict {
109
+ border-color: rgba(190, 72, 20, 0.55);
110
+ background: rgba(190, 72, 20, 0.06);
111
+ }
112
+ body.dark.skills-page .skill-row.has-conflict {
113
+ border-color: rgba(251, 146, 60, 0.5);
114
+ background: rgba(251, 146, 60, 0.1);
115
+ }
116
+ body.skills-page .skill-main {
117
+ display: grid;
118
+ gap: 10px;
119
+ min-width: 0;
120
+ }
121
+ body.skills-page .skill-title-line {
122
+ display: flex;
123
+ align-items: center;
124
+ gap: 8px;
125
+ min-width: 0;
126
+ flex-wrap: wrap;
127
+ }
128
+ body.skills-page .skill-title {
129
+ margin: 0;
130
+ font-size: 16px;
131
+ line-height: 1.2;
132
+ font-weight: 650;
133
+ }
134
+ body.skills-page .skill-description {
135
+ margin: 0;
136
+ max-width: 72ch;
137
+ font-size: 13px;
138
+ line-height: 1.45;
139
+ opacity: 0.72;
140
+ }
141
+ body.skills-page .skill-status {
142
+ display: inline-flex;
143
+ align-items: center;
144
+ font-size: 12px;
145
+ line-height: 1.2;
146
+ font-weight: 500;
147
+ color: var(--task-muted);
148
+ white-space: nowrap;
149
+ }
150
+ body.skills-page .skill-status.good {
151
+ color: #166534;
152
+ }
153
+ body.skills-page .skill-status.warn {
154
+ color: #9a3412;
155
+ }
156
+ body.dark.skills-page .skill-status.good {
157
+ color: #86efac;
158
+ }
159
+ body.dark.skills-page .skill-status.warn {
160
+ color: #fdba74;
161
+ }
162
+ body.skills-page .skill-details {
163
+ display: grid;
164
+ gap: 7px;
165
+ font-size: 12px;
166
+ }
167
+ body.skills-page .skill-detail-line {
168
+ display: flex;
169
+ gap: 8px;
170
+ align-items: baseline;
171
+ min-width: 0;
172
+ }
173
+ body.skills-page .skill-detail-label {
174
+ flex: 0 0 auto;
175
+ width: 88px;
176
+ opacity: 0.58;
177
+ }
178
+ body.skills-page .skill-detail-value {
179
+ min-width: 0;
180
+ overflow-wrap: anywhere;
181
+ }
182
+ body.skills-page .skill-targets {
183
+ display: grid;
184
+ gap: 4px;
185
+ margin: 0;
186
+ padding: 0;
187
+ list-style: none;
188
+ }
189
+ body.skills-page .skill-target {
190
+ display: flex;
191
+ align-items: center;
192
+ gap: 7px;
193
+ min-width: 0;
194
+ opacity: 0.72;
195
+ }
196
+ body.skills-page .skill-target i {
197
+ width: 13px;
198
+ text-align: center;
199
+ }
200
+ body.skills-page .skill-target.conflict {
201
+ color: rgb(154, 52, 18);
202
+ opacity: 1;
203
+ }
204
+ body.dark.skills-page .skill-target.conflict {
205
+ color: rgb(253, 186, 116);
206
+ }
207
+ body.skills-page .skill-actions {
208
+ display: grid;
209
+ gap: 8px;
210
+ justify-items: end;
211
+ min-width: 230px;
212
+ }
213
+ body.skills-page .skill-toggle-form {
214
+ margin: 0;
215
+ }
216
+ body.skills-page .skill-switch {
217
+ display: inline-flex;
218
+ align-items: center;
219
+ justify-content: center;
220
+ gap: 7px;
221
+ min-width: 78px;
222
+ min-height: 28px;
223
+ box-sizing: border-box;
224
+ padding: 0 8px;
225
+ border: 1px solid var(--task-border);
226
+ border-radius: 999px;
227
+ background: color-mix(in srgb, var(--task-panel) 96%, var(--task-soft));
228
+ color: var(--task-muted);
229
+ cursor: pointer;
230
+ font-size: 11px;
231
+ font-weight: 650;
232
+ line-height: 1;
233
+ transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
234
+ }
235
+ body.skills-page .skill-switch:hover {
236
+ background: color-mix(in srgb, var(--task-soft) 76%, var(--task-panel));
237
+ color: var(--task-text);
238
+ }
239
+ body.skills-page .skill-switch[aria-checked="true"] {
240
+ border-color: color-mix(in srgb, #22c55e 30%, transparent);
241
+ background: color-mix(in srgb, #22c55e 12%, var(--task-panel));
242
+ color: #166534;
243
+ }
244
+ body.dark.skills-page .skill-switch[aria-checked="true"] {
245
+ color: #86efac;
246
+ }
247
+ body.skills-page .skill-switch:disabled {
248
+ cursor: not-allowed;
249
+ opacity: 0.62;
250
+ }
251
+ body.skills-page .skill-switch-track {
252
+ position: relative;
253
+ width: 30px;
254
+ height: 16px;
255
+ border-radius: 999px;
256
+ background: color-mix(in srgb, var(--task-muted) 24%, transparent);
257
+ transition: background 140ms ease;
258
+ }
259
+ body.skills-page .skill-switch[aria-checked="true"] .skill-switch-track {
260
+ background: color-mix(in srgb, #22c55e 52%, transparent);
261
+ }
262
+ body.skills-page .skill-switch-thumb {
263
+ position: absolute;
264
+ top: 2px;
265
+ left: 2px;
266
+ width: 12px;
267
+ height: 12px;
268
+ border-radius: 999px;
269
+ background: var(--task-panel);
270
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.18);
271
+ transition: transform 140ms ease;
272
+ }
273
+ body.skills-page .skill-switch[aria-checked="true"] .skill-switch-thumb {
274
+ transform: translateX(14px);
275
+ }
276
+ body.skills-page .skill-switch-label {
277
+ min-width: 18px;
278
+ text-align: left;
279
+ }
280
+ body.skills-page .skill-publish-form {
281
+ display: grid;
282
+ grid-template-columns: minmax(0, 1fr) auto;
283
+ gap: 6px;
284
+ width: 230px;
285
+ }
286
+ body.skills-page .skill-remove-form {
287
+ margin: 0;
288
+ }
289
+ body.skills-page .skills-message {
290
+ margin-bottom: 12px;
291
+ }
292
+ @media (max-width: 980px) {
293
+ body.skills-page .skills-overview,
294
+ body.skills-page .skill-row {
295
+ grid-template-columns: 1fr;
296
+ }
297
+ body.skills-page .skill-actions {
298
+ justify-items: stretch;
299
+ min-width: 0;
300
+ }
301
+ body.skills-page .skill-publish-form,
302
+ body.skills-page .skill-switch {
303
+ width: 100%;
304
+ }
305
+ body.skills-page .skills-install-form {
306
+ grid-template-columns: 1fr;
307
+ }
308
+ body.skills-page .skills-install-submit {
309
+ width: 100%;
310
+ }
311
+ }
312
+ </style>
313
+ </head>
314
+ <%
315
+ const validCount = skills.filter((skill) => skill.valid).length;
316
+ const hasIndexError = Boolean(indexError);
317
+ const targetIcon = (status) => {
318
+ if (status === "published" || status === "legacy-managed") return "fa-solid fa-circle-check";
319
+ if (status === "conflict") return "fa-solid fa-triangle-exclamation";
320
+ if (status === "missing") return "fa-regular fa-circle";
321
+ return "fa-regular fa-circle";
322
+ };
323
+ const targetClass = (status) => status === "conflict" ? "conflict" : "";
324
+ %>
325
+ <body class="<%= theme %> task-launcher-page task-page skills-page" data-agent="<%= agent %>">
326
+ <%- include('partials/app_navheader', { agent }) %>
327
+ <main>
328
+ <div class="task-container">
329
+ <section class="task-shell">
330
+ <header class="task-shell-header">
331
+ <div class="task-shell-header-main">
332
+ <p class="task-eyebrow">Skills</p>
333
+ <h1 class="task-title">Skills</h1>
334
+ <p class="task-description">Stored in <code><%= skillsRootPath %></code>.</p>
335
+ </div>
336
+ </header>
337
+
338
+ <div class="task-shell-body">
339
+ <% if (notice) { %>
340
+ <div class="task-status skills-message"><%= notice %></div>
341
+ <% } %>
342
+ <% if (error) { %>
343
+ <div class="task-status error skills-message"><%= error %></div>
344
+ <% } %>
345
+ <% if (hasIndexError) { %>
346
+ <div class="task-status error skills-message">
347
+ Managed skills are not being synced because <code><%= indexPath %></code> could not be read. Fix the JSON file, then reload this page. <%= indexError %>
348
+ </div>
349
+ <% } %>
350
+
351
+ <section class="task-section skills-overview" aria-label="How managed skills work">
352
+ <div class="skills-explainer">
353
+ <h2 class="skills-explainer-title">How this works</h2>
354
+ <p class="skills-explainer-copy">
355
+ Pinokio keeps the source copy of each managed skill in <code><%= skillsRootPath %></code>. When a skill is ON, Pinokio syncs its <code>SKILL.md</code> into the target skill folders used by agent tools.
356
+ </p>
357
+ <ul class="skills-explainer-list">
358
+ <li>Sync runs when Pinokio starts and after changes made on this page.</li>
359
+ <li>Turning a skill OFF removes only Pinokio-managed synced copies from the target folders.</li>
360
+ <li>The source skill stays in the managed library, so it can be turned on again later.</li>
361
+ <li>If a target folder already contains a non-Pinokio skill, Pinokio leaves it untouched and marks a conflict.</li>
362
+ <li>Downloaded repositories must contain a root <code>SKILL.md</code> before they can be turned on.</li>
363
+ </ul>
364
+ </div>
365
+ <div class="skills-path-stack">
366
+ <div><strong>Index</strong> <code><%= indexPath %></code></div>
367
+ <div><strong>Sync targets</strong></div>
368
+ <% publishRoots.forEach((root) => { %>
369
+ <code><%= root %></code>
370
+ <% }) %>
371
+ </div>
372
+ </section>
373
+
374
+ <% if (!hasIndexError) { %>
375
+ <section class="task-section" aria-label="Download skill">
376
+ <form class="skills-install-form" method="post" action="/skills/install">
377
+ <label class="task-field">
378
+ <span class="task-label">Git URL</span>
379
+ <input class="task-input" type="url" name="ref" placeholder="https://github.com/org/skill.git" required>
380
+ </label>
381
+ <button class="task-button primary skills-install-submit" type="submit">
382
+ <i class="fa-solid fa-download" aria-hidden="true"></i>
383
+ <span>Download</span>
384
+ </button>
385
+ </form>
386
+ </section>
387
+ <% } %>
388
+
389
+ <section class="task-section" aria-label="Installed skills">
390
+ <div class="task-section-head">
391
+ <div>
392
+ <h2 class="task-section-title">Library</h2>
393
+ <p class="task-section-subcopy"><%= hasIndexError ? 'Unavailable until the index is readable.' : `${validCount} valid, ${skills.length - validCount} invalid.` %></p>
394
+ </div>
395
+ </div>
396
+ <% if (hasIndexError) { %>
397
+ <div class="task-empty-state">
398
+ <p class="task-empty-copy">No managed skill changes were applied.</p>
399
+ </div>
400
+ <% } else if (!skills.length) { %>
401
+ <div class="task-empty-state">
402
+ <p class="task-empty-copy">No managed skills installed.</p>
403
+ </div>
404
+ <% } else { %>
405
+ <div class="skills-list">
406
+ <% skills.forEach((skill) => { %>
407
+ <% const canRenamePublishName = !skill.builtin && skill.hasConflict; %>
408
+ <article class="skill-row <%= skill.hasConflict ? 'has-conflict' : '' %>">
409
+ <div class="skill-main">
410
+ <div>
411
+ <div class="skill-title-line">
412
+ <h2 class="skill-title"><%= skill.label || skill.id %></h2>
413
+ <% if (skill.builtin) { %><span class="skill-status">Built-in</span><% } %>
414
+ <% if (skill.enabled) { %><span class="skill-status good">On</span><% } else { %><span class="skill-status">Off</span><% } %>
415
+ <% if (skill.valid) { %><span class="skill-status good">Valid</span><% } else { %><span class="skill-status warn">Invalid</span><% } %>
416
+ <% if (skill.hasConflict) { %><span class="skill-status warn">Conflict</span><% } %>
417
+ </div>
418
+ <% if (skill.description) { %>
419
+ <p class="skill-description"><%= skill.description %></p>
420
+ <% } %>
421
+ </div>
422
+ <div class="skill-details">
423
+ <div class="skill-detail-line">
424
+ <span class="skill-detail-label">Skill ID</span>
425
+ <span class="skill-detail-value"><code><%= skill.id %></code></span>
426
+ </div>
427
+ <div class="skill-detail-line">
428
+ <span class="skill-detail-label">Synced as</span>
429
+ <span class="skill-detail-value"><code><%= skill.publishName %></code></span>
430
+ </div>
431
+ <div class="skill-detail-line">
432
+ <span class="skill-detail-label">Source</span>
433
+ <span class="skill-detail-value"><code><%= skill.path %></code></span>
434
+ </div>
435
+ <% if (skill.ref) { %>
436
+ <div class="skill-detail-line">
437
+ <span class="skill-detail-label">Remote</span>
438
+ <span class="skill-detail-value"><code><%= skill.ref %></code></span>
439
+ </div>
440
+ <% } %>
441
+ <% if (!skill.valid && skill.errors.length) { %>
442
+ <div class="skill-detail-line">
443
+ <span class="skill-detail-label">Error</span>
444
+ <span class="skill-detail-value"><%= skill.errors.join(" ") %></span>
445
+ </div>
446
+ <% } %>
447
+ <div class="skill-detail-line">
448
+ <span class="skill-detail-label">Synced to</span>
449
+ <ul class="skill-targets">
450
+ <% skill.targets.forEach((target) => { %>
451
+ <li class="skill-target <%= targetClass(target.status) %>">
452
+ <i class="<%= targetIcon(target.status) %>" aria-hidden="true"></i>
453
+ <span><code><%= target.path %></code><%= target.message ? ` - ${target.message}` : '' %></span>
454
+ </li>
455
+ <% }) %>
456
+ </ul>
457
+ </div>
458
+ </div>
459
+ </div>
460
+ <div class="skill-actions">
461
+ <form class="skill-toggle-form" method="post" action="/skills/<%= encodeURIComponent(skill.id) %>/toggle">
462
+ <input type="hidden" name="enabled" value="<%= skill.enabled ? '0' : '1' %>">
463
+ <button class="skill-switch" type="submit" role="switch" aria-checked="<%= skill.enabled ? 'true' : 'false' %>" aria-label="<%= skill.enabled ? 'Turn skill off' : 'Turn skill on' %>" title="<%= skill.enabled ? 'Turn skill off' : 'Turn skill on' %>" <%= !skill.enabled && !skill.valid ? 'disabled' : '' %>>
464
+ <span class="skill-switch-track" aria-hidden="true"><span class="skill-switch-thumb"></span></span>
465
+ <span class="skill-switch-label"><%= skill.enabled ? 'ON' : 'OFF' %></span>
466
+ </button>
467
+ </form>
468
+ <% if (canRenamePublishName) { %>
469
+ <form class="skill-publish-form" method="post" action="/skills/<%= encodeURIComponent(skill.id) %>/publish-name">
470
+ <input class="task-input" type="text" name="publishName" value="<%= skill.publishName %>" aria-label="Sync folder name">
471
+ <button class="task-link-button" type="submit" aria-label="Save sync folder name">
472
+ <i class="fa-solid fa-check" aria-hidden="true"></i>
473
+ </button>
474
+ </form>
475
+ <% } %>
476
+ <% if (skill.removable) { %>
477
+ <form class="skill-remove-form" method="post" action="/skills/<%= encodeURIComponent(skill.id) %>/remove" data-skill-remove>
478
+ <button class="task-link-button" type="submit">
479
+ <i class="fa-solid fa-trash-can" aria-hidden="true"></i>
480
+ <span>Remove</span>
481
+ </button>
482
+ </form>
483
+ <% } %>
484
+ </div>
485
+ </article>
486
+ <% }) %>
487
+ </div>
488
+ <% } %>
489
+ </section>
490
+ </div>
491
+ </section>
492
+ </div>
493
+ <%- include('partials/main_sidebar', { selected: 'skills' }) %>
494
+ </main>
495
+ <%- include('partials/app_common_scripts') %>
496
+ <script>
497
+ document.querySelectorAll("[data-skill-remove]").forEach((form) => {
498
+ form.addEventListener("submit", (event) => {
499
+ if (!window.confirm("Remove this downloaded skill?")) {
500
+ event.preventDefault()
501
+ }
502
+ })
503
+ })
504
+ </script>
505
+ </body>
506
+ </html>
@@ -713,6 +713,35 @@ function postMessageToAncestors(payload) {
713
713
  })
714
714
  return dispatched
715
715
  }
716
+ function openAppLogsForError(packet) {
717
+ return postMessageToAncestors({
718
+ e: "pinokio:logs-new",
719
+ type: "pinokio:logs-new",
720
+ reason: "error",
721
+ frame: window.name || null,
722
+ source: "terminal"
723
+ })
724
+ }
725
+ function showErrorScreen(packet) {
726
+ const errorScreen = document.querySelector("#error-screen")
727
+ const errorOutput = errorScreen ? errorScreen.querySelector("pre") : null
728
+ if (!errorScreen || !errorOutput) {
729
+ return
730
+ }
731
+ errorScreen.classList.remove("hidden")
732
+ errorOutput.textContent = packet && packet.data ? packet.data : ""
733
+ let instance = new Mark(errorOutput, {
734
+ separateWordSearch: false,
735
+ accuracy: "exactly"
736
+ })
737
+ if (packet && packet.event) {
738
+ instance.mark(packet.event)
739
+ let element = errorScreen.querySelector("mark")
740
+ if (element) {
741
+ element.scrollIntoView({ behavior: "smooth", block: "center" })
742
+ }
743
+ }
744
+ }
716
745
 
717
746
  function buildBinaryRpcPayload(rpc, fileEntries) {
718
747
  const metadata = {
@@ -895,6 +924,7 @@ const createPluginTerminalDiscoveryRefresher = (context = {}) => {
895
924
  const enabled = (() => {
896
925
  try {
897
926
  return window.location.pathname.startsWith("/run/plugin/")
927
+ || window.location.pathname.startsWith("/pinokio/run/plugin/")
898
928
  || (window.location.pathname.startsWith("/run/api/") && /\/pinokio\.js$/i.test(window.location.pathname))
899
929
  } catch (_) {
900
930
  return false
@@ -1232,6 +1262,10 @@ document.addEventListener("DOMContentLoaded", async () => {
1232
1262
  const scriptAction = <% if (typeof action !== 'undefined') { %><%- JSON.stringify(action) %><% } else { %>null<% } %>
1233
1263
  const protectionAppId = <% if (typeof protection_app_id !== 'undefined' && protection_app_id) { %><%- JSON.stringify(protection_app_id) %><% } else { %>""<% } %>
1234
1264
  const initialProtectionEnabled = <% if (typeof protection_enabled !== 'undefined') { %><%- JSON.stringify(protection_enabled === true) %><% } else { %>false<% } %>
1265
+ const activeProcessWait = <% if (typeof active_process_wait !== 'undefined' && active_process_wait) { %><%- JSON.stringify(active_process_wait) %><% } else { %>null<% } %>
1266
+ if (activeProcessWait && (activeProcessWait.title || activeProcessWait.description || activeProcessWait.message) && window.PinokioWaitFooterStatus) {
1267
+ window.PinokioWaitFooterStatus.show(activeProcessWait)
1268
+ }
1235
1269
  const shouldBypassAiConsent = () => {
1236
1270
  const normalize = (value) => (typeof value === "string" ? value.replace(/\\/g, "/") : "")
1237
1271
  const uri = normalize(scriptUri || "")
@@ -1678,6 +1712,14 @@ document.addEventListener("DOMContentLoaded", async () => {
1678
1712
  // uri: "~" + location.pathname,
1679
1713
  uri: packet.id
1680
1714
  })
1715
+ } else if (packet.type === "process.wait.start") {
1716
+ if (window.PinokioWaitFooterStatus) {
1717
+ window.PinokioWaitFooterStatus.show(packet.data)
1718
+ }
1719
+ } else if (packet.type === "process.wait.end") {
1720
+ if (window.PinokioWaitFooterStatus) {
1721
+ window.PinokioWaitFooterStatus.hide()
1722
+ }
1681
1723
  } else if (packet.type === 'wait') {
1682
1724
  await WaitModal(packet.data)
1683
1725
  } else if (packet.type === "htmlmodal") {
@@ -1890,24 +1932,8 @@ document.addEventListener("DOMContentLoaded", async () => {
1890
1932
  } else if (packet.type === "error") {
1891
1933
  pluginTerminalDiscoveryRefresher.clear()
1892
1934
 
1893
-
1894
- document.querySelector("#error-screen").classList.remove("hidden")
1895
- document.querySelector("#error-screen pre").textContent = packet.data
1896
- let instance = new Mark(document.querySelector("#error-screen pre"), {
1897
- separateWordSearch: false,
1898
- accuracy: "exactly"
1899
- })
1900
- /*
1901
- document.querySelector("#error-screen").addEventListener("click", (e) => {
1902
- document.querySelector("#error-screen").classList.add("hidden")
1903
- })
1904
- */
1905
- if (packet.event) {
1906
- instance.mark(packet.event)
1907
- let element = document.querySelector("#error-screen mark")
1908
- if (element) {
1909
- element.scrollIntoView({ behavior: "smooth", block: "center" })
1910
- }
1935
+ if (!openAppLogsForError(packet)) {
1936
+ showErrorScreen(packet)
1911
1937
  }
1912
1938
 
1913
1939
  this.error = true
@@ -2750,7 +2776,7 @@ document.addEventListener("DOMContentLoaded", async () => {
2750
2776
  term.loadAddon(fitAddon);
2751
2777
  <% if (agent === "electron") { %>
2752
2778
  term.loadAddon(new WebLinksAddon.WebLinksAddon((event, uri) => {
2753
- window.open(uri, "_blank")
2779
+ window.open(uri, "_blank", "browser")
2754
2780
  }))
2755
2781
  <% } else { %>
2756
2782
  term.loadAddon(new WebLinksAddon.WebLinksAddon());
@@ -83,10 +83,13 @@ Policy:
83
83
  - write each generated output only if its content differs from the desired content
84
84
  - `.geminiignore` remains a write-if-changed generated file.
85
85
 
86
- ### 4. Mirrored instruction outputs
86
+ ### 4. Managed skills and mirrored instruction outputs
87
87
 
88
88
  Paths:
89
89
 
90
+ - `PINOKIO_HOME/skills/index.json`
91
+ - `PINOKIO_HOME/skills/gepeto/SKILL.md`
92
+ - `PINOKIO_HOME/skills/pinokio/SKILL.md`
90
93
  - `~/.agents/skills/gepeto/SKILL.md`
91
94
  - `~/.claude/skills/gepeto/SKILL.md`
92
95
  - `~/.hermes/skills/gepeto/SKILL.md`
@@ -96,14 +99,20 @@ Paths:
96
99
 
97
100
  Population:
98
101
 
99
- - synced from Pinokio-owned sources during home environment initialization
102
+ - `PINOKIO_HOME/skills/gepeto/SKILL.md` is generated from the current effective `PINOKIO_HOME/AGENTS.md`
103
+ - `PINOKIO_HOME/skills/pinokio/SKILL.md` is generated from the source-controlled file in this repo at `prototype/system/SKILL_PINOKIO.md`
104
+ - enabled managed skills are published into external agent roots during managed skill sync
100
105
 
101
106
  Policy:
102
107
 
103
- - Every time home environment initialization runs:
104
- - `gepeto` mirrors the current effective `PINOKIO_HOME/AGENTS.md`
105
- - `pinokio` mirrors the source-controlled file in this repo at `prototype/system/SKILL_PINOKIO.md`
106
- - Mirror files are rewritten only if the desired mirrored content changed.
108
+ - `PINOKIO_HOME/skills/index.json` is the managed skill source of truth.
109
+ - Built-in skills `pinokio` and `gepeto` are present by default and may be disabled but not removed.
110
+ - Downloaded skills are cloned into `PINOKIO_HOME/skills/<id>` and publish as `pinokio-<id>` by default.
111
+ - Enabled managed skills are published to `~/.agents/skills`, `~/.claude/skills`, and `~/.hermes/skills`.
112
+ - Published copies include a `.pinokio-managed.json` marker.
113
+ - Pinokio may update or remove published copies only when they are marked Pinokio-owned, or when migrating a legacy copy whose `SKILL.md` content matches the generated desired content.
114
+ - If a publish target already contains a non-Pinokio skill, sync must report a conflict and not overwrite it.
115
+ - If a published copy is manually deleted while the skill remains enabled, sync treats that as drift and recreates it.
107
116
 
108
117
  ### 5. Out-of-scope user installs
109
118
 
@@ -347,7 +356,7 @@ Changes:
347
356
  - do not apply `SOUL.md` logic to app-root generated files in v1
348
357
  - leave app-root `AGENTS.md` / `CLAUDE.md` / `GEMINI.md` / `QWEN.md` / rule generation on current behavior unless required by a concrete bug
349
358
 
350
- ### 5. Keep mirror sync behavior, but with updated home outputs
359
+ ### 5. Move mirror sync behind managed skills
351
360
 
352
361
  File:
353
362
 
@@ -355,9 +364,10 @@ File:
355
364
 
356
365
  Changes:
357
366
 
358
- - keep `gepeto` mirroring the effective home `AGENTS.md`
359
- - keep `pinokio` mirroring the source-controlled file `prototype/system/SKILL_PINOKIO.md`
360
- - keep mirror writes on "write if changed"
367
+ - generate built-in managed skill sources under `PINOKIO_HOME/skills`
368
+ - publish only enabled managed skills to external agent roots
369
+ - remove disabled Pinokio-owned published copies
370
+ - preserve user-owned publish target conflicts without overwriting
361
371
 
362
372
  ### 6. Add targeted missing-path Git repair helpers
363
373