@crouton-kit/crouter 0.3.11 → 0.3.13

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 (182) hide show
  1. package/bin/crtrd +2 -0
  2. package/dist/builtin-personas/design/base.md +9 -0
  3. package/dist/builtin-personas/design/orchestrator.md +10 -0
  4. package/dist/builtin-personas/developer/base.md +9 -0
  5. package/dist/builtin-personas/developer/orchestrator.md +12 -0
  6. package/dist/builtin-personas/explore/base.md +9 -0
  7. package/dist/builtin-personas/explore/orchestrator.md +9 -0
  8. package/dist/builtin-personas/general/base.md +5 -0
  9. package/dist/builtin-personas/general/orchestrator.md +7 -0
  10. package/dist/builtin-personas/orchestration-kernel.md +71 -0
  11. package/dist/builtin-personas/plan/base.md +7 -0
  12. package/dist/builtin-personas/plan/orchestrator.md +12 -0
  13. package/dist/builtin-personas/review/base.md +7 -0
  14. package/dist/builtin-personas/review/orchestrator.md +9 -0
  15. package/dist/builtin-personas/runtime-base.md +39 -0
  16. package/dist/builtin-personas/spec/base.md +7 -0
  17. package/dist/builtin-personas/spec/orchestrator.md +10 -0
  18. package/dist/builtin-skills/skills/design/SKILL.md +51 -0
  19. package/dist/builtin-skills/skills/development/SKILL.md +109 -0
  20. package/dist/builtin-skills/skills/planning/SKILL.md +59 -0
  21. package/dist/builtin-skills/skills/spec/SKILL.md +83 -0
  22. package/dist/cli.js +14 -6
  23. package/dist/commands/{mode.d.ts → attention.d.ts} +1 -1
  24. package/dist/commands/attention.js +152 -0
  25. package/dist/commands/canvas.d.ts +2 -0
  26. package/dist/commands/canvas.js +35 -0
  27. package/dist/commands/daemon.d.ts +2 -0
  28. package/dist/commands/daemon.js +111 -0
  29. package/dist/commands/dashboard.d.ts +2 -0
  30. package/dist/commands/dashboard.js +65 -0
  31. package/dist/commands/human/prompts.d.ts +5 -0
  32. package/dist/commands/human/prompts.js +269 -0
  33. package/dist/commands/human/queue.d.ts +3 -0
  34. package/dist/commands/human/queue.js +133 -0
  35. package/dist/commands/human/shared.d.ts +43 -0
  36. package/dist/commands/human/shared.js +107 -0
  37. package/dist/commands/human.js +10 -454
  38. package/dist/commands/node.d.ts +2 -0
  39. package/dist/commands/node.js +407 -0
  40. package/dist/commands/pkg/market-inspect.d.ts +1 -0
  41. package/dist/commands/pkg/market-inspect.js +157 -0
  42. package/dist/commands/pkg/market-manage.d.ts +1 -0
  43. package/dist/commands/pkg/market-manage.js +316 -0
  44. package/dist/commands/pkg/market.d.ts +1 -0
  45. package/dist/commands/pkg/market.js +16 -0
  46. package/dist/commands/pkg/plugin-inspect.d.ts +1 -0
  47. package/dist/commands/pkg/plugin-inspect.js +142 -0
  48. package/dist/commands/pkg/plugin-manage.d.ts +1 -0
  49. package/dist/commands/pkg/plugin-manage.js +294 -0
  50. package/dist/commands/pkg/plugin.d.ts +1 -0
  51. package/dist/commands/pkg/plugin.js +16 -0
  52. package/dist/commands/pkg/shared.d.ts +5 -0
  53. package/dist/commands/pkg/shared.js +61 -0
  54. package/dist/commands/pkg.js +3 -1004
  55. package/dist/commands/push.d.ts +3 -0
  56. package/dist/commands/push.js +159 -0
  57. package/dist/commands/revive.d.ts +2 -0
  58. package/dist/commands/revive.js +64 -0
  59. package/dist/commands/skill/author.d.ts +3 -0
  60. package/dist/commands/skill/author.js +147 -0
  61. package/dist/commands/skill/find.d.ts +4 -0
  62. package/dist/commands/skill/find.js +254 -0
  63. package/dist/commands/skill/read.d.ts +1 -0
  64. package/dist/commands/skill/read.js +89 -0
  65. package/dist/commands/skill/shared.d.ts +19 -0
  66. package/dist/commands/skill/shared.js +207 -0
  67. package/dist/commands/skill/state.d.ts +3 -0
  68. package/dist/commands/skill/state.js +69 -0
  69. package/dist/commands/skill.js +6 -691
  70. package/dist/commands/sys/config.d.ts +1 -0
  71. package/dist/commands/sys/config.js +186 -0
  72. package/dist/commands/sys/doctor.d.ts +1 -0
  73. package/dist/commands/sys/doctor.js +369 -0
  74. package/dist/commands/sys/shared.d.ts +3 -0
  75. package/dist/commands/sys/shared.js +24 -0
  76. package/dist/commands/sys/update.d.ts +2 -0
  77. package/dist/commands/sys/update.js +114 -0
  78. package/dist/commands/sys.js +4 -694
  79. package/dist/core/__tests__/argv-parser.test.js +19 -1
  80. package/dist/core/__tests__/canvas-inbox-watcher.test.js +100 -0
  81. package/dist/core/__tests__/canvas.test.js +154 -0
  82. package/dist/core/__tests__/reset.test.js +105 -0
  83. package/dist/core/canvas/attention.d.ts +24 -0
  84. package/dist/core/canvas/attention.js +94 -0
  85. package/dist/core/canvas/canvas.d.ts +40 -0
  86. package/dist/core/canvas/canvas.js +210 -0
  87. package/dist/core/canvas/db.d.ts +7 -0
  88. package/dist/core/canvas/db.js +61 -0
  89. package/dist/core/canvas/index.d.ts +4 -0
  90. package/dist/core/canvas/index.js +6 -0
  91. package/dist/core/canvas/paths.d.ts +16 -0
  92. package/dist/core/canvas/paths.js +62 -0
  93. package/dist/core/canvas/render.d.ts +30 -0
  94. package/dist/core/canvas/render.js +186 -0
  95. package/dist/core/canvas/types.d.ts +87 -0
  96. package/dist/core/canvas/types.js +8 -0
  97. package/dist/core/command.d.ts +5 -0
  98. package/dist/core/command.js +35 -10
  99. package/dist/core/feed/feed.d.ts +43 -0
  100. package/dist/core/feed/feed.js +116 -0
  101. package/dist/core/feed/inbox.d.ts +50 -0
  102. package/dist/core/feed/inbox.js +124 -0
  103. package/dist/core/help.js +5 -3
  104. package/dist/core/io.d.ts +15 -1
  105. package/dist/core/io.js +56 -6
  106. package/dist/core/personas/index.d.ts +12 -0
  107. package/dist/core/personas/index.js +10 -0
  108. package/dist/core/personas/loader.d.ts +44 -0
  109. package/dist/core/personas/loader.js +157 -0
  110. package/dist/core/personas/resolve.d.ts +36 -0
  111. package/dist/core/personas/resolve.js +110 -0
  112. package/dist/core/render.d.ts +11 -0
  113. package/dist/core/render.js +126 -0
  114. package/dist/core/resolver.d.ts +10 -0
  115. package/dist/core/resolver.js +109 -1
  116. package/dist/core/runtime/front-door.d.ts +10 -0
  117. package/dist/core/runtime/front-door.js +97 -0
  118. package/dist/core/runtime/kickoff.d.ts +23 -0
  119. package/dist/core/runtime/kickoff.js +134 -0
  120. package/dist/core/runtime/launch.d.ts +34 -0
  121. package/dist/core/runtime/launch.js +85 -0
  122. package/dist/core/runtime/nodes.d.ts +38 -0
  123. package/dist/core/runtime/nodes.js +95 -0
  124. package/dist/core/runtime/presence.d.ts +55 -0
  125. package/dist/core/runtime/presence.js +198 -0
  126. package/dist/core/runtime/promote.d.ts +30 -0
  127. package/dist/core/runtime/promote.js +105 -0
  128. package/dist/core/runtime/reset.d.ts +13 -0
  129. package/dist/core/runtime/reset.js +97 -0
  130. package/dist/core/runtime/revive.d.ts +26 -0
  131. package/dist/core/runtime/revive.js +87 -0
  132. package/dist/core/runtime/roadmap.d.ts +12 -0
  133. package/dist/core/runtime/roadmap.js +52 -0
  134. package/dist/core/runtime/spawn.d.ts +31 -0
  135. package/dist/core/runtime/spawn.js +123 -0
  136. package/dist/core/runtime/stop-guard.d.ts +18 -0
  137. package/dist/core/runtime/stop-guard.js +33 -0
  138. package/dist/core/runtime/tmux.d.ts +107 -0
  139. package/dist/core/runtime/tmux.js +244 -0
  140. package/dist/core/spawn.d.ts +17 -197
  141. package/dist/core/spawn.js +16 -539
  142. package/dist/daemon/crtrd-cli.js +4 -0
  143. package/dist/daemon/crtrd.d.ts +20 -0
  144. package/dist/daemon/crtrd.js +200 -0
  145. package/dist/daemon/manage.d.ts +17 -0
  146. package/dist/daemon/manage.js +57 -0
  147. package/dist/pi-extensions/canvas-inbox-watcher.d.ts +16 -0
  148. package/dist/pi-extensions/canvas-inbox-watcher.js +229 -0
  149. package/dist/pi-extensions/canvas-nav.d.ts +32 -0
  150. package/dist/pi-extensions/canvas-nav.js +536 -0
  151. package/dist/pi-extensions/canvas-stophook.d.ts +17 -0
  152. package/dist/pi-extensions/canvas-stophook.js +396 -0
  153. package/package.json +6 -5
  154. package/dist/commands/agent.d.ts +0 -6
  155. package/dist/commands/agent.js +0 -585
  156. package/dist/commands/debug.d.ts +0 -3
  157. package/dist/commands/debug.js +0 -192
  158. package/dist/commands/job.d.ts +0 -11
  159. package/dist/commands/job.js +0 -384
  160. package/dist/commands/mode.js +0 -231
  161. package/dist/commands/plan.d.ts +0 -4
  162. package/dist/commands/plan.js +0 -322
  163. package/dist/commands/spec.d.ts +0 -3
  164. package/dist/commands/spec.js +0 -299
  165. package/dist/core/__tests__/flow-leaves.test.js +0 -248
  166. package/dist/core/__tests__/job.test.js +0 -310
  167. package/dist/core/__tests__/jobs.test.js +0 -98
  168. package/dist/core/__tests__/spawn.test.js +0 -138
  169. package/dist/core/__tests__/subagents.test.d.ts +0 -1
  170. package/dist/core/__tests__/subagents.test.js +0 -75
  171. package/dist/core/jobs.d.ts +0 -107
  172. package/dist/core/jobs.js +0 -565
  173. package/dist/core/subagents.d.ts +0 -18
  174. package/dist/core/subagents.js +0 -163
  175. package/dist/prompts/agent.d.ts +0 -27
  176. package/dist/prompts/agent.js +0 -184
  177. package/dist/prompts/debug.d.ts +0 -8
  178. package/dist/prompts/debug.js +0 -44
  179. /package/dist/core/__tests__/{flow-leaves.test.d.ts → canvas-inbox-watcher.test.d.ts} +0 -0
  180. /package/dist/core/__tests__/{job.test.d.ts → canvas.test.d.ts} +0 -0
  181. /package/dist/core/__tests__/{jobs.test.d.ts → reset.test.d.ts} +0 -0
  182. /package/dist/{core/__tests__/spawn.test.d.ts → daemon/crtrd-cli.d.ts} +0 -0
@@ -0,0 +1,294 @@
1
+ import { join } from 'node:path';
2
+ import { renameSync } from 'node:fs';
3
+ import { defineBranch, defineLeaf } from '../../core/command.js';
4
+ import { notFound, usage, general } from '../../core/errors.js';
5
+ import { findPluginByName, listAllPlugins } from '../../core/resolver.js';
6
+ import { pluginsDir, ensureProjectScopeRoot, userScopeRoot, resolveScopeArg, projectScopeRoot, } from '../../core/scope.js';
7
+ import { updateConfig, updateState, ensureScopeInitialized } from '../../core/config.js';
8
+ import { pathExists, ensureDir, removePath, nowIso } from '../../core/fs-utils.js';
9
+ import { clone, pull, deriveNameFromUrl, currentSha } from '../../core/git.js';
10
+ import { readPluginManifest } from '../../core/manifest.js';
11
+ import { isGitUrl, setPluginEnabled, resolveInstallScope } from './shared.js';
12
+ // ---------------------------------------------------------------------------
13
+ // plugin.manage.install
14
+ // ---------------------------------------------------------------------------
15
+ const pluginInstall = defineLeaf({
16
+ name: 'install',
17
+ help: {
18
+ name: 'pkg plugin manage install',
19
+ summary: 'install a plugin from a git URL into the given scope',
20
+ params: [
21
+ { kind: 'positional', name: 'source', type: 'string', required: true, constraint: 'Git URL or relative path to the plugin directory.' },
22
+ { kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'One of: user, project. Default: project if available, else user.' },
23
+ { kind: 'flag', name: 'ref', type: 'string', required: false, constraint: 'Git ref (branch/tag) to clone. Default: default branch.' },
24
+ ],
25
+ output: [
26
+ { name: 'name', type: 'string', required: true, constraint: 'Plugin name as declared in plugin.json.' },
27
+ { name: 'scope', type: 'string', required: true, constraint: 'Scope the plugin was installed into.' },
28
+ { name: 'path', type: 'string', required: true, constraint: 'Absolute path to the installed plugin directory.' },
29
+ ],
30
+ outputKind: 'object',
31
+ effects: ['Clones or copies the plugin into the scope plugins directory. Registers the plugin in config.json.'],
32
+ },
33
+ run: async (input) => {
34
+ const source = input['source'];
35
+ const scopeInput = input['scope'];
36
+ const ref = input['ref'];
37
+ if (!isGitUrl(source)) {
38
+ throw usage(`"${source}" is not a git URL. Use \`pkg market manage install\` to install from a marketplace.`);
39
+ }
40
+ const scope = resolveInstallScope(scopeInput);
41
+ let scopeRootPath;
42
+ if (scope === 'project') {
43
+ scopeRootPath = ensureProjectScopeRoot();
44
+ ensureScopeInitialized(scope, scopeRootPath);
45
+ }
46
+ else {
47
+ scopeRootPath = userScopeRoot();
48
+ ensureScopeInitialized(scope, scopeRootPath);
49
+ }
50
+ const pDir = join(scopeRootPath, 'plugins');
51
+ ensureDir(pDir);
52
+ const tempName = deriveNameFromUrl(source);
53
+ const tempDir = join(pDir, tempName);
54
+ if (pathExists(tempDir)) {
55
+ throw general(`plugin directory already exists: ${tempDir}\n` +
56
+ `Uninstall the existing plugin first with \`pkg plugin manage remove ${tempName}\``);
57
+ }
58
+ clone(source, tempDir, { ref: ref !== undefined ? ref : undefined, depth: 1 });
59
+ const manifest = readPluginManifest(tempDir);
60
+ if (manifest === null) {
61
+ removePath(tempDir);
62
+ throw general(`cloned repo does not contain a valid .crouter-plugin/plugin.json: ${source}`);
63
+ }
64
+ const finalName = manifest.name;
65
+ let finalDir = tempDir;
66
+ if (finalName !== tempName) {
67
+ const candidateDir = join(pDir, finalName);
68
+ if (pathExists(candidateDir)) {
69
+ removePath(tempDir);
70
+ throw general(`plugin "${finalName}" is already installed at ${candidateDir}`);
71
+ }
72
+ renameSync(tempDir, candidateDir);
73
+ finalDir = candidateDir;
74
+ }
75
+ updateConfig(scope, (cfg) => {
76
+ cfg.plugins[finalName] = {
77
+ enabled: true,
78
+ version: manifest.version,
79
+ };
80
+ });
81
+ return { name: finalName, scope, path: finalDir };
82
+ },
83
+ });
84
+ // ---------------------------------------------------------------------------
85
+ // plugin.manage.remove
86
+ // ---------------------------------------------------------------------------
87
+ const pluginRemove = defineLeaf({
88
+ name: 'remove',
89
+ help: {
90
+ name: 'pkg plugin manage remove',
91
+ summary: 'remove a plugin and its directory from the given scope',
92
+ params: [
93
+ { kind: 'positional', name: 'name', type: 'string', required: true, constraint: 'Plugin name to remove.' },
94
+ { kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'One of: user, project. Default: searches all scopes.' },
95
+ ],
96
+ output: [
97
+ { name: 'removed', type: 'boolean', required: true, constraint: 'True if removed from at least one scope.' },
98
+ { name: 'scopes', type: 'string[]', required: true, constraint: 'Scopes the plugin was removed from.' },
99
+ ],
100
+ outputKind: 'object',
101
+ effects: ['Deletes the plugin directory. Removes the plugin entry from config.json.'],
102
+ },
103
+ run: async (input) => {
104
+ const name = input['name'];
105
+ const scopeInput = input['scope'];
106
+ if (name === 'crtr') {
107
+ throw usage('cannot remove builtin plugin "crtr" — it ships with the binary');
108
+ }
109
+ let scopes;
110
+ if (scopeInput !== undefined) {
111
+ const resolved = resolveScopeArg(scopeInput);
112
+ if (resolved === 'builtin')
113
+ throw usage('cannot remove plugins from builtin scope');
114
+ scopes = resolved === 'all' ? ['project', 'user'].filter((s) => s !== 'project' || projectScopeRoot() !== null) : [resolved];
115
+ }
116
+ else {
117
+ scopes = ['project', 'user'].filter((s) => s !== 'project' || projectScopeRoot() !== null);
118
+ }
119
+ const removedFrom = [];
120
+ for (const scope of scopes) {
121
+ const pDir = pluginsDir(scope);
122
+ if (pDir === null)
123
+ continue;
124
+ const pluginDir = join(pDir, name);
125
+ if (!pathExists(pluginDir))
126
+ continue;
127
+ removePath(pluginDir);
128
+ updateConfig(scope, (cfg) => {
129
+ delete cfg.plugins[name];
130
+ });
131
+ removedFrom.push(scope);
132
+ }
133
+ if (removedFrom.length === 0) {
134
+ throw notFound(`plugin not found: ${name}`);
135
+ }
136
+ return { removed: true, scopes: removedFrom };
137
+ },
138
+ });
139
+ // ---------------------------------------------------------------------------
140
+ // plugin.manage.enable / disable
141
+ // ---------------------------------------------------------------------------
142
+ const pluginEnable = defineLeaf({
143
+ name: 'enable',
144
+ help: {
145
+ name: 'pkg plugin manage enable',
146
+ summary: 'enable a plugin in the given scope',
147
+ params: [
148
+ { kind: 'positional', name: 'name', type: 'string', required: true, constraint: 'Plugin name to enable.' },
149
+ { kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'One of: user, project. Default: scope where the plugin is installed.' },
150
+ ],
151
+ output: [
152
+ { name: 'name', type: 'string', required: true, constraint: 'Plugin name.' },
153
+ { name: 'scope', type: 'string', required: true, constraint: 'Scope the change was applied in.' },
154
+ { name: 'enabled', type: 'boolean', required: true, constraint: 'Always true.' },
155
+ ],
156
+ outputKind: 'object',
157
+ effects: ['Sets plugin enabled=true in config.json.'],
158
+ },
159
+ run: async (input) => setPluginEnabled(input, true),
160
+ });
161
+ const pluginDisable = defineLeaf({
162
+ name: 'disable',
163
+ help: {
164
+ name: 'pkg plugin manage disable',
165
+ summary: 'disable a plugin (keeps files, hides from resolution)',
166
+ params: [
167
+ { kind: 'positional', name: 'name', type: 'string', required: true, constraint: 'Plugin name to disable.' },
168
+ { kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'One of: user, project. Default: scope where the plugin is installed.' },
169
+ ],
170
+ output: [
171
+ { name: 'name', type: 'string', required: true, constraint: 'Plugin name.' },
172
+ { name: 'scope', type: 'string', required: true, constraint: 'Scope the change was applied in.' },
173
+ { name: 'enabled', type: 'boolean', required: true, constraint: 'Always false.' },
174
+ ],
175
+ outputKind: 'object',
176
+ effects: ['Sets plugin enabled=false in config.json.'],
177
+ },
178
+ run: async (input) => setPluginEnabled(input, false),
179
+ });
180
+ // ---------------------------------------------------------------------------
181
+ // plugin.manage.update
182
+ // Single plugin: blocking (bounded). All plugins: job handle (unbounded network).
183
+ // ---------------------------------------------------------------------------
184
+ const pluginUpdate = defineLeaf({
185
+ name: 'update',
186
+ help: {
187
+ name: 'pkg plugin manage update',
188
+ summary: 'git pull updates for one or all installed plugins',
189
+ params: [
190
+ { kind: 'flag', name: 'name', type: 'string', required: false, constraint: 'Plugin name to update. Omit to update all (returns a job handle).' },
191
+ ],
192
+ output: [
193
+ { name: 'updated', type: 'object[]', required: false, constraint: 'Present for single-plugin (blocking) path: [{name, updated, sha}].' },
194
+ { name: 'updated', type: 'object[]', required: true, constraint: 'One entry per plugin processed: {name, updated, sha}.' },
195
+ ],
196
+ outputKind: 'object',
197
+ effects: ['Runs git pull in plugin directories. Updates version in config.json.'],
198
+ },
199
+ run: async (input) => {
200
+ const name = input['name'];
201
+ if (name !== undefined) {
202
+ // Single plugin — blocking (bounded network op, one repo)
203
+ const found = findPluginByName(name);
204
+ if (found === null) {
205
+ throw notFound(`plugin not found: ${name}`);
206
+ }
207
+ const shaBefore = currentSha(found.root);
208
+ const res = pull(found.root);
209
+ if (res.status !== 0) {
210
+ throw general(`git pull failed for "${name}": ${res.stderr.trim()}`);
211
+ }
212
+ const shaAfter = currentSha(found.root);
213
+ const updated = shaBefore !== shaAfter;
214
+ const manifest = readPluginManifest(found.root);
215
+ if (manifest !== null) {
216
+ updateConfig(found.scope, (cfg) => {
217
+ const entry = cfg.plugins[found.name];
218
+ if (entry !== undefined) {
219
+ entry.version = manifest.version;
220
+ }
221
+ else {
222
+ cfg.plugins[found.name] = { enabled: true, version: manifest.version };
223
+ }
224
+ });
225
+ updateState(found.scope, (s) => {
226
+ if (s.plugins[found.name] === undefined) {
227
+ s.plugins[found.name] = {};
228
+ }
229
+ s.plugins[found.name].last_updated = nowIso();
230
+ });
231
+ }
232
+ return {
233
+ updated: [
234
+ {
235
+ name: found.name,
236
+ updated,
237
+ sha: shaAfter !== null ? shaAfter : '',
238
+ },
239
+ ],
240
+ };
241
+ }
242
+ // All plugins — run synchronously (each git pull is a sync spawn).
243
+ const all = listAllPlugins();
244
+ const targets = all
245
+ .filter((p) => p.enabled && !p.sourceMarketplace && p.scope !== 'builtin')
246
+ .map((p) => ({ name: p.name, scope: p.scope, root: p.root }));
247
+ const results = [];
248
+ for (const target of targets) {
249
+ const shaBefore = currentSha(target.root);
250
+ const res = pull(target.root);
251
+ if (res.status !== 0) {
252
+ results.push({ name: target.name, updated: false, sha: shaBefore !== null ? shaBefore : '' });
253
+ continue;
254
+ }
255
+ const shaAfter = currentSha(target.root);
256
+ const updated = shaBefore !== shaAfter;
257
+ const manifest = readPluginManifest(target.root);
258
+ if (manifest !== null) {
259
+ updateConfig(target.scope, (cfg) => {
260
+ const entry = cfg.plugins[target.name];
261
+ if (entry !== undefined) {
262
+ entry.version = manifest.version;
263
+ }
264
+ else {
265
+ cfg.plugins[target.name] = { enabled: true, version: manifest.version };
266
+ }
267
+ });
268
+ updateState(target.scope, (s) => {
269
+ if (s.plugins[target.name] === undefined) {
270
+ s.plugins[target.name] = {};
271
+ }
272
+ s.plugins[target.name].last_updated = nowIso();
273
+ });
274
+ }
275
+ results.push({ name: target.name, updated, sha: shaAfter !== null ? shaAfter : '' });
276
+ }
277
+ return { updated: results };
278
+ },
279
+ });
280
+ export const pluginManageBranch = defineBranch({
281
+ name: 'manage',
282
+ help: {
283
+ name: 'pkg plugin manage',
284
+ summary: 'install, remove, enable, disable, or update plugins',
285
+ children: [
286
+ { name: 'install', desc: 'install from a git URL', useWhen: 'adding a new plugin' },
287
+ { name: 'remove', desc: 'remove plugin and directory', useWhen: 'uninstalling a plugin' },
288
+ { name: 'enable', desc: 'enable a plugin', useWhen: 'activating a disabled plugin' },
289
+ { name: 'disable', desc: 'disable without removing', useWhen: 'temporarily hiding a plugin' },
290
+ { name: 'update', desc: 'pull latest from git', useWhen: 'updating a plugin to its latest version' },
291
+ ],
292
+ },
293
+ children: [pluginInstall, pluginRemove, pluginEnable, pluginDisable, pluginUpdate],
294
+ });
@@ -0,0 +1 @@
1
+ export declare const pluginBranch: import("../../core/command.js").BranchDef;
@@ -0,0 +1,16 @@
1
+ import { defineBranch } from '../../core/command.js';
2
+ import { pluginManageBranch } from './plugin-manage.js';
3
+ import { pluginInspectBranch } from './plugin-inspect.js';
4
+ export const pluginBranch = defineBranch({
5
+ name: 'plugin',
6
+ help: {
7
+ name: 'pkg plugin',
8
+ summary: 'install and manage plugins that extend crtr with skills',
9
+ model: 'Plugins are git repos or local directories containing a .crouter-plugin/plugin.json manifest and a skills/ directory.',
10
+ children: [
11
+ { name: 'manage', desc: 'install, remove, enable, disable, update', useWhen: 'changing plugin state' },
12
+ { name: 'inspect', desc: 'list or show installed plugins', useWhen: 'reading plugin metadata' },
13
+ ],
14
+ },
15
+ children: [pluginManageBranch, pluginInspectBranch],
16
+ });
@@ -0,0 +1,5 @@
1
+ import type { Scope } from '../../types.js';
2
+ export declare const GIT_URL_RE: RegExp;
3
+ export declare function isGitUrl(arg: string): boolean;
4
+ export declare function resolveInstallScope(scopeInput: string | undefined): Scope;
5
+ export declare function setPluginEnabled(input: Record<string, unknown>, enabled: boolean): Promise<Record<string, unknown>>;
@@ -0,0 +1,61 @@
1
+ import { join } from 'node:path';
2
+ import { notFound, usage } from '../../core/errors.js';
3
+ import { resolveScopeArg, pluginsDir, projectScopeRoot } from '../../core/scope.js';
4
+ import { updateConfig } from '../../core/config.js';
5
+ import { pathExists } from '../../core/fs-utils.js';
6
+ // ---------------------------------------------------------------------------
7
+ // Helpers
8
+ // ---------------------------------------------------------------------------
9
+ export const GIT_URL_RE = /^(https?:\/\/|git@|ssh:\/\/|file:\/\/)/;
10
+ export function isGitUrl(arg) {
11
+ return GIT_URL_RE.test(arg) || arg.endsWith('.git');
12
+ }
13
+ export function resolveInstallScope(scopeInput) {
14
+ if (scopeInput !== undefined) {
15
+ const resolved = resolveScopeArg(scopeInput);
16
+ if (resolved === 'all' || resolved === 'builtin') {
17
+ throw usage('scope must be "user" or "project"');
18
+ }
19
+ return resolved;
20
+ }
21
+ // Default: project if available, else user
22
+ return projectScopeRoot() !== null ? 'project' : 'user';
23
+ }
24
+ export async function setPluginEnabled(input, enabled) {
25
+ const name = input['name'];
26
+ const scopeInput = input['scope'];
27
+ let scopes;
28
+ if (scopeInput !== undefined) {
29
+ const resolved = resolveScopeArg(scopeInput);
30
+ if (resolved === 'builtin')
31
+ throw usage('cannot enable/disable plugins in builtin scope');
32
+ scopes = resolved === 'all' ? ['project', 'user'].filter((s) => s !== 'project' || projectScopeRoot() !== null) : [resolved];
33
+ }
34
+ else {
35
+ scopes = ['project', 'user'].filter((s) => s !== 'project' || projectScopeRoot() !== null);
36
+ }
37
+ let actedScope;
38
+ for (const scope of scopes) {
39
+ const pDir = pluginsDir(scope);
40
+ if (pDir === null)
41
+ continue;
42
+ const pluginDir = join(pDir, name);
43
+ if (!pathExists(pluginDir))
44
+ continue;
45
+ updateConfig(scope, (cfg) => {
46
+ const entry = cfg.plugins[name];
47
+ if (entry !== undefined) {
48
+ entry.enabled = enabled;
49
+ }
50
+ else {
51
+ cfg.plugins[name] = { enabled };
52
+ }
53
+ });
54
+ actedScope = scope;
55
+ break; // only act on first found scope
56
+ }
57
+ if (actedScope === undefined) {
58
+ throw notFound(`plugin not found: ${name}`);
59
+ }
60
+ return { name, scope: actedScope, enabled };
61
+ }