@aragon-agent/cli 0.4.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 (156) hide show
  1. package/CHANGELOG.md +292 -0
  2. package/README.md +558 -0
  3. package/dist/agent/coalesce.d.ts +14 -0
  4. package/dist/agent/coalesce.js +41 -0
  5. package/dist/agent/controller.d.ts +132 -0
  6. package/dist/agent/controller.js +332 -0
  7. package/dist/agent/headless.d.ts +22 -0
  8. package/dist/agent/headless.js +74 -0
  9. package/dist/agent/reducer.d.ts +181 -0
  10. package/dist/agent/reducer.js +420 -0
  11. package/dist/agent/system-prompt.d.ts +30 -0
  12. package/dist/agent/system-prompt.js +58 -0
  13. package/dist/agent/usage.d.ts +13 -0
  14. package/dist/agent/usage.js +41 -0
  15. package/dist/cli.d.ts +11 -0
  16. package/dist/cli.js +482 -0
  17. package/dist/commands/builtins.d.ts +16 -0
  18. package/dist/commands/builtins.js +223 -0
  19. package/dist/commands/registry.d.ts +48 -0
  20. package/dist/commands/registry.js +45 -0
  21. package/dist/commands/skills.d.ts +22 -0
  22. package/dist/commands/skills.js +625 -0
  23. package/dist/config/app-paths.d.ts +11 -0
  24. package/dist/config/app-paths.js +16 -0
  25. package/dist/config/env.d.ts +28 -0
  26. package/dist/config/env.js +92 -0
  27. package/dist/config/load.d.ts +48 -0
  28. package/dist/config/load.js +158 -0
  29. package/dist/config/migrate-legacy-state.d.ts +78 -0
  30. package/dist/config/migrate-legacy-state.js +181 -0
  31. package/dist/config/schema.d.ts +249 -0
  32. package/dist/config/schema.js +247 -0
  33. package/dist/config/store.d.ts +44 -0
  34. package/dist/config/store.js +126 -0
  35. package/dist/input/keymap.d.ts +20 -0
  36. package/dist/input/keymap.js +85 -0
  37. package/dist/session/persist.d.ts +24 -0
  38. package/dist/session/persist.js +51 -0
  39. package/dist/skills/archive.d.ts +41 -0
  40. package/dist/skills/archive.js +152 -0
  41. package/dist/skills/cli-commands.d.ts +52 -0
  42. package/dist/skills/cli-commands.js +502 -0
  43. package/dist/skills/fetch-source.d.ts +104 -0
  44. package/dist/skills/fetch-source.js +448 -0
  45. package/dist/skills/installer.d.ts +117 -0
  46. package/dist/skills/installer.js +414 -0
  47. package/dist/skills/lock.d.ts +75 -0
  48. package/dist/skills/lock.js +223 -0
  49. package/dist/skills/manifest.d.ts +47 -0
  50. package/dist/skills/manifest.js +95 -0
  51. package/dist/skills/node-host.d.ts +23 -0
  52. package/dist/skills/node-host.js +59 -0
  53. package/dist/skills/paths.d.ts +61 -0
  54. package/dist/skills/paths.js +107 -0
  55. package/dist/skills/service.d.ts +256 -0
  56. package/dist/skills/service.js +730 -0
  57. package/dist/skills/tools.d.ts +35 -0
  58. package/dist/skills/tools.js +164 -0
  59. package/dist/skills/updater.d.ts +101 -0
  60. package/dist/skills/updater.js +330 -0
  61. package/dist/skills/usage.d.ts +72 -0
  62. package/dist/skills/usage.js +216 -0
  63. package/dist/tools/bash-tool.d.ts +12 -0
  64. package/dist/tools/bash-tool.js +131 -0
  65. package/dist/tools/diff.d.ts +15 -0
  66. package/dist/tools/diff.js +50 -0
  67. package/dist/tools/fs-tools.d.ts +16 -0
  68. package/dist/tools/fs-tools.js +215 -0
  69. package/dist/tools/index.d.ts +93 -0
  70. package/dist/tools/index.js +157 -0
  71. package/dist/tools/search-tools.d.ts +11 -0
  72. package/dist/tools/search-tools.js +225 -0
  73. package/dist/ui/App.d.ts +24 -0
  74. package/dist/ui/App.js +522 -0
  75. package/dist/ui/AutocompletePopup.d.ts +21 -0
  76. package/dist/ui/AutocompletePopup.js +19 -0
  77. package/dist/ui/Composer.d.ts +40 -0
  78. package/dist/ui/Composer.js +61 -0
  79. package/dist/ui/Header.d.ts +30 -0
  80. package/dist/ui/Header.js +25 -0
  81. package/dist/ui/Logo.d.ts +50 -0
  82. package/dist/ui/Logo.js +85 -0
  83. package/dist/ui/Markdown.d.ts +23 -0
  84. package/dist/ui/Markdown.js +122 -0
  85. package/dist/ui/PromptInput.d.ts +81 -0
  86. package/dist/ui/PromptInput.js +383 -0
  87. package/dist/ui/SessionOpener.d.ts +30 -0
  88. package/dist/ui/SessionOpener.js +12 -0
  89. package/dist/ui/StatusBar.d.ts +39 -0
  90. package/dist/ui/StatusBar.js +43 -0
  91. package/dist/ui/ToastStack.d.ts +24 -0
  92. package/dist/ui/ToastStack.js +38 -0
  93. package/dist/ui/Transcript.d.ts +62 -0
  94. package/dist/ui/Transcript.js +128 -0
  95. package/dist/ui/capabilities.d.ts +25 -0
  96. package/dist/ui/capabilities.js +59 -0
  97. package/dist/ui/console-bridge.d.ts +19 -0
  98. package/dist/ui/console-bridge.js +52 -0
  99. package/dist/ui/density.d.ts +26 -0
  100. package/dist/ui/density.js +34 -0
  101. package/dist/ui/entries/AssistantEntry.d.ts +23 -0
  102. package/dist/ui/entries/AssistantEntry.js +13 -0
  103. package/dist/ui/entries/EntryFrame.d.ts +23 -0
  104. package/dist/ui/entries/EntryFrame.js +6 -0
  105. package/dist/ui/entries/ToolCard.d.ts +38 -0
  106. package/dist/ui/entries/ToolCard.js +72 -0
  107. package/dist/ui/entries/ToolPreview.d.ts +57 -0
  108. package/dist/ui/entries/ToolPreview.js +144 -0
  109. package/dist/ui/entries/UserEntry.d.ts +14 -0
  110. package/dist/ui/entries/UserEntry.js +5 -0
  111. package/dist/ui/exit-snapshot.d.ts +27 -0
  112. package/dist/ui/exit-snapshot.js +26 -0
  113. package/dist/ui/gauge.d.ts +24 -0
  114. package/dist/ui/gauge.js +36 -0
  115. package/dist/ui/glyphs.d.ts +87 -0
  116. package/dist/ui/glyphs.js +127 -0
  117. package/dist/ui/gradient.d.ts +11 -0
  118. package/dist/ui/gradient.js +50 -0
  119. package/dist/ui/layout/AppShell.d.ts +24 -0
  120. package/dist/ui/layout/AppShell.js +9 -0
  121. package/dist/ui/layout/Gutter.d.ts +67 -0
  122. package/dist/ui/layout/Gutter.js +42 -0
  123. package/dist/ui/layout/OverlayFrame.d.ts +62 -0
  124. package/dist/ui/layout/OverlayFrame.js +92 -0
  125. package/dist/ui/layout/ScrollViewport.d.ts +33 -0
  126. package/dist/ui/layout/ScrollViewport.js +71 -0
  127. package/dist/ui/layout/budget.d.ts +42 -0
  128. package/dist/ui/layout/budget.js +47 -0
  129. package/dist/ui/layout/frame.d.ts +58 -0
  130. package/dist/ui/layout/frame.js +75 -0
  131. package/dist/ui/layout/overlay-window.d.ts +43 -0
  132. package/dist/ui/layout/overlay-window.js +51 -0
  133. package/dist/ui/layout/scroll.d.ts +28 -0
  134. package/dist/ui/layout/scroll.js +76 -0
  135. package/dist/ui/layout/useTerminalSize.d.ts +13 -0
  136. package/dist/ui/layout/useTerminalSize.js +50 -0
  137. package/dist/ui/markdown-blocks.d.ts +62 -0
  138. package/dist/ui/markdown-blocks.js +120 -0
  139. package/dist/ui/overlays/ConfirmDialog.d.ts +30 -0
  140. package/dist/ui/overlays/ConfirmDialog.js +25 -0
  141. package/dist/ui/overlays/HelpOverlay.d.ts +17 -0
  142. package/dist/ui/overlays/HelpOverlay.js +60 -0
  143. package/dist/ui/overlays/ModelPicker.d.ts +29 -0
  144. package/dist/ui/overlays/ModelPicker.js +29 -0
  145. package/dist/ui/overlays/SettingsScreen.d.ts +37 -0
  146. package/dist/ui/overlays/SettingsScreen.js +106 -0
  147. package/dist/ui/palettes.d.ts +79 -0
  148. package/dist/ui/palettes.js +115 -0
  149. package/dist/ui/screen.d.ts +27 -0
  150. package/dist/ui/screen.js +57 -0
  151. package/dist/ui/theme.d.ts +78 -0
  152. package/dist/ui/theme.js +127 -0
  153. package/dist/ui/transcript-text.d.ts +27 -0
  154. package/dist/ui/transcript-text.js +110 -0
  155. package/package.json +73 -0
  156. package/skills/skill-creator/SKILL.md +96 -0
@@ -0,0 +1,256 @@
1
+ /**
2
+ * SkillService — discovery, lazy loading, prompt-block assembly, and the
3
+ * lifecycle surface the UI and the agent tools call into (spec §12.2).
4
+ *
5
+ * Everything mutating (install / create / remove) delegates to `installer.ts`;
6
+ * this file owns the read side and the registry.
7
+ */
8
+ import { SkillRegistry, type SkillFileRef, type SkillHost, type SkillPlatform, type SkillPolicyView, type SkillRecord, type SkillUsageMap, type ToolPolicyDecision, type ToolPolicyVerdict } from '@aragon-agent/core';
9
+ import type { NoticeLevel } from '../agent/reducer.js';
10
+ import type { SkillsConfig, SkillsRuntimeOptions, SkillsToolPolicyMode } from '../config/schema.js';
11
+ import { type ConfirmRequest } from '../tools/index.js';
12
+ /**
13
+ * Capability-probing approval gate (D17 / §8.3.1).
14
+ *
15
+ * NOT `confirm?: (req) => Promise<boolean>`. That shape cannot express "there is
16
+ * no human here", and the CLI's existing confirm callback resolves `true` when
17
+ * unattached (`cli.tsx`'s `confirmBridge.handler ? … : Promise.resolve(true)`).
18
+ * Inheriting that fallback would make `requireApproval: true` a no-op under
19
+ * `-p`, where the App never mounts and the handler is permanently null.
20
+ * Probing FIRST makes fail-closed the default path rather than a discipline.
21
+ */
22
+ export interface ApprovalGate {
23
+ /** Is there a human channel RIGHT NOW? Must be read live, never cached (Q7). */
24
+ canPrompt(): boolean;
25
+ /** Only called when `canPrompt()` returned true. */
26
+ request(req: ConfirmRequest): Promise<boolean>;
27
+ }
28
+ export interface DiscoverResult {
29
+ records: SkillRecord[];
30
+ errors: string[];
31
+ /** Project roots skipped because the directory is not trusted yet (D13). */
32
+ pendingTrust: string[];
33
+ }
34
+ export interface SkillServiceDeps {
35
+ host: SkillHost;
36
+ getCwd: () => string;
37
+ config: SkillsConfig;
38
+ runtime: SkillsRuntimeOptions;
39
+ approval: ApprovalGate;
40
+ /** → `controller.refreshSkills()`. */
41
+ onChange?: () => void;
42
+ notify?: (level: NoticeLevel, text: string) => void;
43
+ /** Persist a skills patch. MUST deep-merge (§10.3.2). */
44
+ persist?: (patch: Partial<SkillsConfig>) => void;
45
+ }
46
+ export declare class SkillService {
47
+ private readonly deps;
48
+ private readonly registry;
49
+ private config;
50
+ private lastErrors;
51
+ private pendingTrust;
52
+ /** Empty when `skills.usageTracking` is off — which is exactly invariant I-A1. */
53
+ private usage;
54
+ /** The integrity warning is worth saying once a session, not once a rescan. */
55
+ private integrityWarned;
56
+ /**
57
+ * `/skills policy <mode>` writes here. `null` = this session has no opinion.
58
+ * NEVER persisted — the config file is written separately by the same command.
59
+ */
60
+ private sessionToolPolicy;
61
+ /**
62
+ * The previous decision, used to notice when the ceiling WIDENS (§5.6e).
63
+ *
64
+ * A skill can tell the model to load a second skill, and because the ceiling
65
+ * is a union with `skill` permanently in the floor, that works (D-G17). The
66
+ * path is not blocked — every way of blocking it costs more than it saves —
67
+ * so instead it leaves a trace. This pair of fields is that trace's memory.
68
+ */
69
+ private lastDecisionAllowed;
70
+ private lastDecisionSources;
71
+ constructor(deps: SkillServiceDeps);
72
+ /**
73
+ * Scan all four roots in ascending precedence and rebuild the registry.
74
+ *
75
+ * One unreadable directory must never sink the whole pass: every root and
76
+ * every candidate is wrapped, and partial results are returned alongside the
77
+ * errors so `/skills` can show both what loaded and what did not.
78
+ */
79
+ discover(): DiscoverResult;
80
+ /**
81
+ * Enforce the usage-file cap (§9.2) — and ONLY when it is actually exceeded.
82
+ *
83
+ * `pruneUsage()` drops every counter whose skill is absent from the registry,
84
+ * and the registry is CWD-DEPENDENT: it holds the `project` skills of
85
+ * whichever directory this process happens to be running in. Calling it on
86
+ * every scan would therefore make a session in repo B silently erase the
87
+ * usage history of repo A — and a root that was momentarily unreadable would
88
+ * erase all of it. Counters for uninstalled skills are worth a few hundred
89
+ * bytes; the ranking signal they would take with them is not.
90
+ */
91
+ private pruneUsageIfOverCap;
92
+ /**
93
+ * Tell the user once per session which skills no longer match what they
94
+ * approved (`warn` mode only — `strict` already refuses them outright, and
95
+ * repeating the notice on every rescan is how a warning becomes wallpaper).
96
+ */
97
+ private reportIntegrity;
98
+ /** Scan one root. Returns the records it yielded; appends to `errors` in place. */
99
+ private scanRoot;
100
+ /** Build a record from a candidate directory, or `null` when it is not a skill. */
101
+ private readSkillDir;
102
+ /**
103
+ * Compare SKILL.md against the hash recorded at install time (§8.1).
104
+ *
105
+ * Costs no extra read on the happy path: the text is already in memory from
106
+ * the scan, and the manifest was already parsed.
107
+ *
108
+ * D-A12 — THE SECOND HASH IS NOT REDUNDANT. `readTextFile()` returns a
109
+ * DECODED string. Re-encoding it round-trips byte-for-byte only for
110
+ * well-formed UTF-8; a file containing an invalid sequence comes back with
111
+ * U+FFFD substituted, so the re-encoded bytes differ from what is on disk and
112
+ * an untouched file would be reported as tampered. A false alarm here is
113
+ * worse than no alarm at all — it teaches the user that this warning is noise,
114
+ * and it is the same warning that has to be believed on the day it is real.
115
+ */
116
+ private computeIntegrity;
117
+ /** Reload from disk and notify the controller so the prompt is rebuilt. */
118
+ reload(): DiscoverResult;
119
+ list(): SkillRecord[];
120
+ get(name: string): SkillRecord | undefined;
121
+ getRegistry(): SkillRegistry;
122
+ errors(): string[];
123
+ /** Project roots the trust gate is still holding back (§9.3). */
124
+ untrustedDirs(): string[];
125
+ /**
126
+ * Level 2 load: SKILL.md body + the bundled-file listing.
127
+ * MAY THROW — see the `SkillHost` error contract; `skill`'s `execute()` and
128
+ * every UI caller wrap this.
129
+ *
130
+ * THE SINGLE USAGE MEASUREMENT POINT (F8). All three consumption paths — the
131
+ * `skill` tool, a `/<skill-name>` command, and `alwaysBlock()` — funnel
132
+ * through here, so one call site covers everything.
133
+ *
134
+ * `countUse: false` is MANDATORY for `alwaysBlock()`. An always-on skill is
135
+ * injected automatically on every single turn; counting that would pin it to
136
+ * the top of the ranking forever and drown out the signal the ranking exists
137
+ * to capture, which is what a human or a model actually CHOSE to reach for.
138
+ */
139
+ loadBody(name: string, opts?: {
140
+ countUse?: boolean;
141
+ }): {
142
+ body: string;
143
+ files: SkillFileRef[];
144
+ };
145
+ /** Walk a skill directory for bundled resources (depth ≤ 3, dotfiles skipped). */
146
+ private listSkillFiles;
147
+ /** Level 1 catalog for the system prompt, or `''` when there is nothing to say. */
148
+ catalogBlock(): string;
149
+ /**
150
+ * `activation: always` bodies plus anything forced with `--skill`.
151
+ * Returns `''` when empty so the caller can concatenate unconditionally
152
+ * without disturbing invariant I-S1.
153
+ */
154
+ alwaysBlock(): string;
155
+ /** Host shell family, forwarded into every Level 2 / Level 3 render (D-G14). */
156
+ platform(): SkillPlatform;
157
+ /**
158
+ * The mode actually in force, and where it came from.
159
+ *
160
+ * THREE LAYERS, not two (D-G19). `SkillsRuntimeOptions` is read-only for the
161
+ * life of the session, so with only `flag > config` a user who started with
162
+ * `--skill-tool-policy enforce` could run the very command the refusal text
163
+ * recommends — `/skills policy off` — and have precisely nothing happen, with
164
+ * no message either. An iteration about silent failures cannot ship one in its
165
+ * own escape hatch.
166
+ */
167
+ effectiveToolPolicy(): {
168
+ mode: SkillsToolPolicyMode;
169
+ from: 'session' | 'flag' | 'config';
170
+ };
171
+ setSessionToolPolicy(mode: SkillsToolPolicyMode): void;
172
+ /** `/skills policy <mode>` — session slot and config file, together. */
173
+ persistToolPolicy(mode: SkillsToolPolicyMode): void;
174
+ /**
175
+ * Compute the ceiling for right now. Constant-time when the frame is empty or
176
+ * the mode is `off`, which is the overwhelmingly common case on the hot path.
177
+ */
178
+ toolPolicyDecision(getRegistered: () => readonly string[]): ToolPolicyDecision;
179
+ /** The renderer-facing slice of the current decision, or `undefined` for "say nothing". */
180
+ toolPolicyView(getRegistered: () => readonly string[]): SkillPolicyView | undefined;
181
+ /**
182
+ * The ceiling a QUEUED skill is about to impose — for the `/<skill-name>` path,
183
+ * whose message has not been submitted yet and whose frame is therefore still
184
+ * pending (§13.2).
185
+ *
186
+ * Computed from the pending set alone, which is exactly right for the common
187
+ * case (`controller.prompt()` replaces the frame with the pending one). When
188
+ * the agent happens to be running, `steer()` unions instead, so this can
189
+ * understate — it will never claim a tool is forbidden that is in fact allowed
190
+ * only because some other skill is also in the frame. Overstating would be the
191
+ * dangerous direction; a refusal, if one ever happens, prints the true set.
192
+ *
193
+ * Emits no announcements: nothing has been decided yet.
194
+ */
195
+ pendingToolPolicyView(getRegistered: () => readonly string[]): SkillPolicyView | undefined;
196
+ /**
197
+ * One notice per skill per session when a frame member stops constraining
198
+ * because it was uninstalled, disabled, or turned invalid (D-G20 / AC-G21).
199
+ *
200
+ * Without this, `/skills disable`, a `/skills reload` after an uninstall, and a
201
+ * flip to `integrity: strict` each end with the ceiling gone and nothing said
202
+ * — a silent lift, which is the same failure class as the silent no-op this
203
+ * whole iteration set out to remove.
204
+ */
205
+ private reportCeilingLifted;
206
+ /** One notice per skill per session when D-G4 waives it (§5.7 / RG3). */
207
+ private reportFailOpen;
208
+ /** Print the union-escalation trace (§5.6e / D-G17) and remember the new state. */
209
+ private reportCeilingWidened;
210
+ /**
211
+ * The single place the three announce-once rules live (§5.4c / P1-2).
212
+ *
213
+ * `verdict.message` is MUTATED in the escalate branch rather than being
214
+ * followed by a second message: the model is already reading this text, and a
215
+ * separate message is one more thing for it to skip.
216
+ */
217
+ reportToolPolicyEvent(e: {
218
+ tool: string;
219
+ verdict: ToolPolicyVerdict;
220
+ sourceNames: string[];
221
+ }): void;
222
+ beginUserTurn(): void;
223
+ absorbPendingFrames(): void;
224
+ queueFrame(name: string): void;
225
+ clearFrames(): void;
226
+ frameNames(): string[];
227
+ /** Level 2 body budget — shared by the tool path and the slash path (GG3). */
228
+ bodyMaxBytes(): number;
229
+ /** Warn once about `--skill <name>` values that match nothing (§7.5). */
230
+ reportUnknownForcedSkills(): void;
231
+ setDisabled(name: string, disabled: boolean): void;
232
+ getConfig(): SkillsConfig;
233
+ /** Local use counters backing `--sort=recent`; empty when tracking is off. */
234
+ getUsage(): SkillUsageMap;
235
+ /** Working directory an install should resolve `--scope project` against. */
236
+ getCwdForInstall(): string;
237
+ getRuntime(): SkillsRuntimeOptions;
238
+ getApproval(): ApprovalGate;
239
+ notify(level: NoticeLevel, text: string): void;
240
+ /** Keep the in-memory copy and the config file in step (deep-merged, §10.3.2). */
241
+ private updateConfig;
242
+ isTrusted(dir: string): boolean;
243
+ trustDir(dir: string): boolean;
244
+ untrustDir(dir: string): boolean;
245
+ /**
246
+ * Load a project root for THIS SESSION ONLY, without persisting trust.
247
+ * Used when the user answers the confirm dialog with "just this once".
248
+ */
249
+ trustForSession(dir: string): void;
250
+ /**
251
+ * Drop staging directories left behind by a crashed install (P2-10).
252
+ * Same discipline as the host repo's workspace cleanup: age-based, best
253
+ * effort, and never allowed to interrupt discovery.
254
+ */
255
+ private reclaimStaleStaging;
256
+ }