@phnx-labs/agents-cli 1.21.2 → 1.21.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 (48) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/bin/agents +0 -0
  3. package/dist/commands/computer-actions.d.ts +4 -0
  4. package/dist/commands/computer-actions.js +34 -0
  5. package/dist/commands/computer.js +4 -2
  6. package/dist/commands/perf.d.ts +16 -0
  7. package/dist/commands/perf.js +11 -1
  8. package/dist/commands/projects.d.ts +11 -1
  9. package/dist/commands/projects.js +16 -2
  10. package/dist/commands/sessions-picker.js +17 -2
  11. package/dist/commands/sessions.d.ts +5 -0
  12. package/dist/commands/sessions.js +14 -0
  13. package/dist/index.js +8 -0
  14. package/dist/lib/browser/service.js +13 -0
  15. package/dist/lib/computer/dispatch.d.ts +3 -1
  16. package/dist/lib/computer/dispatch.js +10 -2
  17. package/dist/lib/event-stream.d.ts +2 -0
  18. package/dist/lib/event-stream.js +3 -0
  19. package/dist/lib/events.d.ts +3 -1
  20. package/dist/lib/events.js +4 -2
  21. package/dist/lib/git.d.ts +14 -0
  22. package/dist/lib/git.js +36 -0
  23. package/dist/lib/hooks/profile.js +1 -14
  24. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  25. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  26. package/dist/lib/percentile.d.ts +12 -0
  27. package/dist/lib/percentile.js +24 -0
  28. package/dist/lib/perf/db.d.ts +1 -2
  29. package/dist/lib/perf/db.js +2 -14
  30. package/dist/lib/plugins.js +12 -1
  31. package/dist/lib/resources.d.ts +16 -0
  32. package/dist/lib/resources.js +25 -14
  33. package/dist/lib/routines.js +1 -14
  34. package/dist/lib/runner.js +8 -1
  35. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  36. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  37. package/dist/lib/session/db.d.ts +16 -1
  38. package/dist/lib/session/db.js +227 -4
  39. package/dist/lib/session/discover.d.ts +28 -0
  40. package/dist/lib/session/discover.js +42 -1
  41. package/dist/lib/session/highlights.d.ts +24 -4
  42. package/dist/lib/session/highlights.js +52 -7
  43. package/dist/lib/session/parse.js +19 -3
  44. package/dist/lib/session/prompt.d.ts +19 -0
  45. package/dist/lib/session/prompt.js +43 -0
  46. package/dist/lib/session/types.d.ts +42 -0
  47. package/dist/lib/types.d.ts +14 -0
  48. package/package.json +1 -1
@@ -9,7 +9,7 @@ import * as fs from 'fs';
9
9
  import { truncate } from '../format.js';
10
10
  import * as path from 'path';
11
11
  import Database from '../sqlite.js';
12
- import { isSyntheticUserMessage } from './prompt.js';
12
+ import { isSyntheticUserMessage, extractSlashCommandName, extractSlashCommandFromToolInput } from './prompt.js';
13
13
  /**
14
14
  * Largest session file we will load into memory. Above this we throw a clean
15
15
  * error instead of OOMing or hitting V8's ERR_STRING_TOO_LONG. Aligns with
@@ -379,6 +379,14 @@ export function parseClaudeContent(content) {
379
379
  };
380
380
  if (isLocal)
381
381
  event._local = true;
382
+ // SlashCommand: the MODEL invoking a slash command programmatically
383
+ // (distinct from the <command-name> wrapper below, which is the
384
+ // USER typing one) — see prompt.ts's extractSlashCommandFromToolInput.
385
+ if (toolName === 'SlashCommand') {
386
+ const slashCommand = extractSlashCommandFromToolInput(toolInput);
387
+ if (slashCommand)
388
+ event.slashCommand = slashCommand;
389
+ }
382
390
  events.push(event);
383
391
  }
384
392
  }
@@ -403,13 +411,21 @@ export function parseClaudeContent(content) {
403
411
  // Simple user text
404
412
  const text = contentBlocks.trim();
405
413
  if (text) {
406
- events.push({
414
+ const event = {
407
415
  type: 'message',
408
416
  agent: 'claude',
409
417
  timestamp,
410
418
  role: 'user',
411
419
  content: text,
412
- });
420
+ };
421
+ // The USER typing a slash command — Claude injects a <command-name>
422
+ // wrapper as the message content (see prompt.ts's
423
+ // extractSlashCommandName; distinct from the SlashCommand tool-use
424
+ // above, which is the model invoking one programmatically).
425
+ const slashCommand = extractSlashCommandName(text);
426
+ if (slashCommand)
427
+ event.slashCommand = slashCommand;
428
+ events.push(event);
413
429
  }
414
430
  }
415
431
  else if (Array.isArray(contentBlocks)) {
@@ -12,6 +12,25 @@
12
12
  * own scaffolding at parse time simply never reach here.
13
13
  */
14
14
  export declare function isSyntheticUserMessage(raw: string | undefined): boolean;
15
+ /**
16
+ * Extract the invoked slash-command name from a raw `role=user` message, or
17
+ * undefined when the message carries no `<command-name>` wrapper. Used by
18
+ * `parseClaudeContent` to populate `SessionEvent.slashCommand`.
19
+ *
20
+ * Normalizes a leading `/` onto the result: current Claude Code wraps the
21
+ * name WITH the slash already (`/recap`), but the wrapper predates that and
22
+ * older transcripts carry the bare name (`recap`) — normalizing here means
23
+ * every consumer sees one consistent `/name` shape regardless of vintage.
24
+ */
25
+ export declare function extractSlashCommandName(raw: string | undefined): string | undefined;
26
+ /**
27
+ * Extract the invoked slash-command name from the `SlashCommand` tool's input
28
+ * — the MODEL invoking a command programmatically, not the user typing it.
29
+ * The tool's `command` field carries the full invocation text (name plus any
30
+ * args, e.g. `/code:commit fix the bug`); only the leading command token is
31
+ * the name, matching {@link extractSlashCommandName}'s shape.
32
+ */
33
+ export declare function extractSlashCommandFromToolInput(toolInput: Record<string, any> | undefined): string | undefined;
15
34
  export declare const HEADLESS_PLAN_MODE_PREFIX = "You are running in HEADLESS PLAN MODE.";
16
35
  /** Strip framework noise from a raw session prompt, returning only meaningful user text. */
17
36
  export declare function cleanSessionPrompt(raw: string): string;
@@ -63,6 +63,49 @@ export function isSyntheticUserMessage(raw) {
63
63
  return false;
64
64
  return SYNTHETIC_USER_MESSAGE_PATTERNS.some(pattern => pattern.test(raw));
65
65
  }
66
+ /**
67
+ * The `<command-name>` wrapper Claude injects into a `role=user` message when
68
+ * a typed slash command is invoked, e.g.:
69
+ * <command-message>recap</command-message>
70
+ * <command-name>/recap</command-name>
71
+ * Captures the name INCLUDING its leading slash (`/recap`), matching how the
72
+ * `SlashCommand` tool's own `command` input is formatted (see
73
+ * `extractSlashCommandFromToolInput`) so both sources land on the same shape.
74
+ */
75
+ const COMMAND_NAME_WRAPPER_RE = /<command-name>\s*([^<]*?)\s*<\/command-name>/i;
76
+ /**
77
+ * Extract the invoked slash-command name from a raw `role=user` message, or
78
+ * undefined when the message carries no `<command-name>` wrapper. Used by
79
+ * `parseClaudeContent` to populate `SessionEvent.slashCommand`.
80
+ *
81
+ * Normalizes a leading `/` onto the result: current Claude Code wraps the
82
+ * name WITH the slash already (`/recap`), but the wrapper predates that and
83
+ * older transcripts carry the bare name (`recap`) — normalizing here means
84
+ * every consumer sees one consistent `/name` shape regardless of vintage.
85
+ */
86
+ export function extractSlashCommandName(raw) {
87
+ if (!raw)
88
+ return undefined;
89
+ const m = raw.match(COMMAND_NAME_WRAPPER_RE);
90
+ const name = m?.[1]?.trim();
91
+ if (!name)
92
+ return undefined;
93
+ return name.startsWith('/') ? name : `/${name}`;
94
+ }
95
+ /**
96
+ * Extract the invoked slash-command name from the `SlashCommand` tool's input
97
+ * — the MODEL invoking a command programmatically, not the user typing it.
98
+ * The tool's `command` field carries the full invocation text (name plus any
99
+ * args, e.g. `/code:commit fix the bug`); only the leading command token is
100
+ * the name, matching {@link extractSlashCommandName}'s shape.
101
+ */
102
+ export function extractSlashCommandFromToolInput(toolInput) {
103
+ const command = toolInput?.command;
104
+ if (typeof command !== 'string')
105
+ return undefined;
106
+ const name = command.trim().split(/\s+/)[0];
107
+ return name || undefined;
108
+ }
66
109
  // Prefix prepended to every Claude-in-plan-mode team spawn prompt.
67
110
  // Ends at a blank line before the real user task.
68
111
  export const HEADLESS_PLAN_MODE_PREFIX = 'You are running in HEADLESS PLAN MODE.';
@@ -52,6 +52,15 @@ export interface SessionEvent {
52
52
  hookName?: string;
53
53
  /** Lifecycle event the hook fired on (SessionStart, PreToolUse, …). */
54
54
  hookEvent?: string;
55
+ /**
56
+ * Slash-command invocation name (e.g. `/recap`, `/code:commit`), captured
57
+ * two ways in a Claude transcript: a `role=user` message whose content is
58
+ * the `<command-name>` wrapper Claude injects for a typed slash command
59
+ * (`parseClaudeContent`, see `prompt.ts`'s `extractSlashCommandName`), or a
60
+ * `tool_use` event for the `SlashCommand` tool (a command the MODEL invoked
61
+ * programmatically, not the user). Undefined for every other event.
62
+ */
63
+ slashCommand?: string;
55
64
  }
56
65
  /** A displayable file attachment discovered in a session transcript. */
57
66
  export interface SessionAttachment {
@@ -186,6 +195,39 @@ export interface SessionMeta {
186
195
  todos?: TodoProgress;
187
196
  /** Most-recent unique directories changed or used as a shell working directory. */
188
197
  recentDirectoriesTouched?: string[];
198
+ /**
199
+ * Skills invoked during the session (structurally identical to
200
+ * session/highlights.ts's SkillUse — declared inline here rather than
201
+ * imported, to avoid a circular import: highlights.ts imports SessionEvent
202
+ * from this file). Populated by discover.ts's incremental Claude
203
+ * accumulator (ClaudeParseState.skillEvents, run through extractSkills at
204
+ * finalize) so session/db.ts's upsertSessionsBatch can write
205
+ * session_resource_usage rows WITHOUT re-parsing the whole transcript —
206
+ * the same reason meta.todos/recentDirectoriesTouched are pre-computed by
207
+ * the caller for claude/codex instead of left to db.ts's re-parse path.
208
+ */
209
+ skillsUsed?: Array<{
210
+ name: string;
211
+ count: number;
212
+ }>;
213
+ /** Sibling of {@link skillsUsed} for slash-command invocations (SessionEvent.slashCommand). */
214
+ slashCommandsUsed?: Array<{
215
+ name: string;
216
+ count: number;
217
+ }>;
218
+ /**
219
+ * Whether this session emitted at least one `browser.navigate` /
220
+ * `browser.screenshot` event, computed at scan time from a sessionId-scoped
221
+ * read of the events log (events.ts `query({ sessionId })`) rather than a
222
+ * transcript re-scan — see `detectToolUsage` in session/db.ts. `undefined`
223
+ * means a legacy row this scanner hasn't computed the field for yet (never
224
+ * collapsed to `false`, so a consumer — e.g. sessions-picker.ts's
225
+ * `classifySessionTool` — knows to fall back to a transcript-derived guess
226
+ * instead of trusting a false negative).
227
+ */
228
+ usedBrowser?: boolean;
229
+ /** Sibling of {@link usedBrowser} for `computer.action` events. */
230
+ usedComputer?: boolean;
189
231
  /** Linear project containing ticketId, resolved lazily and cached in SQLite. */
190
232
  linearProject?: string;
191
233
  /** Browser URL for linearProject. */
@@ -605,6 +605,20 @@ export interface DiscoveredPlugin {
605
605
  * (e.g. workflow-scoped) — those default to the user marketplace on sync.
606
606
  */
607
607
  marketplace?: string;
608
+ /**
609
+ * Absolute path to the DotAgents repo root containing this plugin — the
610
+ * grandparent of `root` (`<repo>/plugins/<name>` → `<repo>`), true for every
611
+ * marketplace kind (user/system/extra/project). DotAgents repos are
612
+ * git-tracked (plugins.ts), so this pairs with {@link snapshotSha}.
613
+ */
614
+ repoRoot: string;
615
+ /**
616
+ * Short HEAD sha of `repoRoot`'s git checkout, lazily resolved (a getter,
617
+ * not computed at discovery time) and memoized per repoRoot
618
+ * (`git.ts` `resolveSnapshotSha`) — see `ResolvedResource.snapshotSha` for
619
+ * the identical rationale. `undefined` when `repoRoot` isn't a git repo.
620
+ */
621
+ readonly snapshotSha: string | undefined;
608
622
  }
609
623
  /**
610
624
  * Identifies one DotAgents repo that contributes a plugin marketplace. Each
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.21.2",
3
+ "version": "1.21.3",
4
4
  "description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",