@phnx-labs/agents-cli 1.22.10 → 1.22.12

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 (43) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/dist/bin/agents +0 -0
  3. package/dist/commands/beta.js +21 -9
  4. package/dist/commands/cli.js +12 -12
  5. package/dist/commands/events.d.ts +20 -1
  6. package/dist/commands/events.js +71 -55
  7. package/dist/commands/logs.js +14 -138
  8. package/dist/commands/projects.d.ts +1 -2
  9. package/dist/commands/projects.js +39 -47
  10. package/dist/commands/repo.d.ts +1 -1
  11. package/dist/commands/repo.js +3 -3
  12. package/dist/commands/view.d.ts +1 -1
  13. package/dist/commands/view.js +7 -7
  14. package/dist/index.js +1 -1
  15. package/dist/lib/beta.d.ts +1 -1
  16. package/dist/lib/beta.js +1 -1
  17. package/dist/lib/cli-resources.js +2 -2
  18. package/dist/lib/event-stream.d.ts +1 -1
  19. package/dist/lib/event-stream.js +1 -1
  20. package/dist/lib/events.d.ts +21 -12
  21. package/dist/lib/events.js +311 -64
  22. package/dist/lib/exec.js +1 -2
  23. package/dist/lib/feed-broadcast.d.ts +19 -5
  24. package/dist/lib/feed-broadcast.js +41 -8
  25. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  26. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  27. package/dist/lib/migrate.d.ts +8 -0
  28. package/dist/lib/migrate.js +26 -1
  29. package/dist/lib/project-probe.d.ts +1 -1
  30. package/dist/lib/project-probe.js +1 -1
  31. package/dist/lib/projects.d.ts +1 -1
  32. package/dist/lib/resources.d.ts +1 -1
  33. package/dist/lib/runner.js +1 -2
  34. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  35. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  36. package/dist/lib/secrets/audit.js +1 -1
  37. package/dist/lib/startup/command-registry.js +1 -1
  38. package/dist/lib/state.d.ts +1 -1
  39. package/dist/lib/state.js +60 -2
  40. package/dist/lib/types.d.ts +1 -1
  41. package/dist/lib/watchdog/runner.d.ts +13 -0
  42. package/dist/lib/watchdog/runner.js +75 -6
  43. package/package.json +1 -1
@@ -1633,7 +1633,7 @@ function containsOnlyDsStore(dir) {
1633
1633
  function warnSystemOrphans() {
1634
1634
  const SHIPPED_ALLOWLIST = new Set([
1635
1635
  // resource directories shipped by the npm package
1636
- 'commands', 'hooks', 'skills', 'rules', 'mcp', 'cli', 'permissions', 'subagents', 'profiles', 'agents', 'routines',
1636
+ 'commands', 'hooks', 'skills', 'rules', 'mcp', 'clis', 'permissions', 'subagents', 'profiles', 'agents', 'routines',
1637
1637
  // top-level metadata files
1638
1638
  'agents.yaml', 'hooks.yaml', 'README.md', 'CHANGELOG.md',
1639
1639
  // git + repo metadata
@@ -2013,10 +2013,35 @@ export function migrateWatchdogSentinelToRoutine(sentinelPath = path.join(CACHE_
2013
2013
  catch { /* already gone */ }
2014
2014
  console.error('Migrated watchdog: legacy enable sentinel → watchdog routine (kept enabled)');
2015
2015
  }
2016
+ /**
2017
+ * Rename cli/ → clis/ in each of the given `.agents/` directories.
2018
+ *
2019
+ * One-way, idempotent: no-op when src is absent. Throws when both
2020
+ * `<dir>/cli` and `<dir>/clis` exist — the user must resolve the conflict
2021
+ * manually before proceeding. Exported for unit-testing with temp dirs.
2022
+ */
2023
+ export function migrateCliDirToClis(agentsDirs) {
2024
+ for (const agentsDir of agentsDirs) {
2025
+ const src = path.join(agentsDir, 'cli');
2026
+ const dest = path.join(agentsDir, 'clis');
2027
+ if (!fs.existsSync(src))
2028
+ continue;
2029
+ if (fs.existsSync(dest)) {
2030
+ throw new Error(`Migration conflict: both ${src} and ${dest} exist. ` +
2031
+ `Remove or merge the old cli/ directory into clis/ manually.`);
2032
+ }
2033
+ fs.renameSync(src, dest);
2034
+ }
2035
+ }
2016
2036
  /** Run all idempotent migrations. Safe to call multiple times. */
2017
2037
  export async function runMigration() {
2018
2038
  // MUST run first: every other migrator reads SYSTEM_DIR (the new path).
2019
2039
  foldLegacySystemRepo();
2040
+ const cliMigrateDirs = [USER_DIR, SYSTEM_DIR];
2041
+ const projectDotAgents = path.join(process.cwd(), '.agents');
2042
+ if (fs.existsSync(projectDotAgents))
2043
+ cliMigrateDirs.push(projectDotAgents);
2044
+ migrateCliDirToClis(cliMigrateDirs);
2020
2045
  migrateAgentsYaml();
2021
2046
  deleteSystemPromptsJson();
2022
2047
  migrateSystemConfigJson();
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Project workspace probing — the drift signal behind `projects status --fleet`.
2
+ * Project workspace probing — the drift signal behind `projects status`.
3
3
  *
4
4
  * Projects are natively multi-device: the same definition (home-relative paths)
5
5
  * re-roots on every fleet machine, and the question is whether the project's
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Project workspace probing — the drift signal behind `projects status --fleet`.
2
+ * Project workspace probing — the drift signal behind `projects status`.
3
3
  *
4
4
  * Projects are natively multi-device: the same definition (home-relative paths)
5
5
  * re-roots on every fleet machine, and the question is whether the project's
@@ -36,7 +36,7 @@ export interface ProjectRepo {
36
36
  /**
37
37
  * Optional home-relative local checkout of this repo. The def's `root` only
38
38
  * knows the primary repo on disk; `path` opts an additional repo into
39
- * workspace probing (`projects status --fleet`).
39
+ * workspace probing (`projects status`).
40
40
  */
41
41
  path?: string;
42
42
  }
@@ -5,7 +5,7 @@
5
5
  import type { AgentId } from './types.js';
6
6
  import { type SkillParseError } from './skills.js';
7
7
  /** Resource kind — matches the subdirectory name under each repo root. */
8
- export type ResourceKind = 'commands' | 'skills' | 'hooks' | 'rules' | 'mcp' | 'cli' | 'permissions' | 'subagents' | 'workflows' | 'profiles' | 'secrets';
8
+ export type ResourceKind = 'commands' | 'skills' | 'hooks' | 'rules' | 'mcp' | 'clis' | 'permissions' | 'subagents' | 'workflows' | 'profiles' | 'secrets';
9
9
  /** A resource resolved with its origin. */
10
10
  export interface ResolvedResource {
11
11
  name: string;
@@ -21,7 +21,7 @@ import { resolveJobPrompt, parseTimeout, writeRunMeta, listRuns, getJobRunsDir,
21
21
  import { getRunsDir } from './state.js';
22
22
  import { prepareJobHome, buildSpawnEnv, getJobHomePath } from './sandbox.js';
23
23
  import { resolveModel, buildReasoningFlags } from './models.js';
24
- import { createTimer, maybeRotate, redactPrompt } from './events.js';
24
+ import { createTimer, redactPrompt } from './events.js';
25
25
  import { normalizeMode, resolveHeadlessMode, buildExecEnv, detectRateLimit, detectAuthFailure, isAuthFailureFromLog, authFailureReason, } from './exec.js';
26
26
  import { resolveActor } from './actor.js';
27
27
  import { loadTask as loadHostTask } from './hosts/tasks.js';
@@ -691,7 +691,6 @@ export async function executeJob(config, deps) {
691
691
  if (config.command) {
692
692
  return executeCommandJobForeground(config);
693
693
  }
694
- maybeRotate();
695
694
  const launch = await resolveRoutineLaunch(config);
696
695
  const primaryVersion = launch.chain[0]?.version ?? config.version;
697
696
  const timer = createTimer('agent.run', {
@@ -4,7 +4,7 @@
4
4
  * This is the ONE write path for secret events. Every path that creates,
5
5
  * imports, exports, views, reads a VALUE from, or unlocks a bundle funnels its
6
6
  * audit through here, so the operational event stream — `agents events`, backed
7
- * by the append-only `~/.agents/events.jsonl` audit log — carries a uniform,
7
+ * by the dated append-only `~/.agents/.history/events/` audit log — carries a uniform,
8
8
  * value-free provenance record: bundle, key NAMES, the resolving agent/harness
9
9
  * identity, operation, source, status. The ts / host / session / caller fields
10
10
  * are filled in by `emit()` itself. The secret VALUE is never part of the
@@ -124,7 +124,7 @@ export const COMMAND_LOADERS = {
124
124
  memory: [loadMemory],
125
125
  permissions: [loadPermissions],
126
126
  mcp: [loadMcp],
127
- cli: [loadCli],
127
+ clis: [loadCli],
128
128
  subagents: [loadSubagents],
129
129
  plugins: [loadPlugins],
130
130
  workflows: [loadWorkflows],
@@ -113,7 +113,7 @@ export declare function getUserSecretsDir(): string;
113
113
  * value-free usage telemetry (which bundle was created/imported/exported/viewed/
114
114
  * accessed/unlocked, when, by whom), never a secret value. It is a derived index
115
115
  * fed FROM the emitSecretAudit chokepoint alongside the append-only
116
- * ~/.agents/events.jsonl audit log — the same way sessions.db indexes session
116
+ * ~/.agents/.history/events/YYYY-MM-DD audit log — the same way sessions.db indexes session
117
117
  * metadata off the real session flow — not a second write path.
118
118
  */
119
119
  export declare function getSecretsDbPath(): string;
package/dist/lib/state.js CHANGED
@@ -372,7 +372,7 @@ export function getUserSecretsDir() { return USER_SECRETS_DIR; }
372
372
  * value-free usage telemetry (which bundle was created/imported/exported/viewed/
373
373
  * accessed/unlocked, when, by whom), never a secret value. It is a derived index
374
374
  * fed FROM the emitSecretAudit chokepoint alongside the append-only
375
- * ~/.agents/events.jsonl audit log — the same way sessions.db indexes session
375
+ * ~/.agents/.history/events/YYYY-MM-DD audit log — the same way sessions.db indexes session
376
376
  * metadata off the real session flow — not a second write path.
377
377
  */
378
378
  export function getSecretsDbPath() {
@@ -794,6 +794,59 @@ function writeIfChanged(filePath, content) {
794
794
  * All callers funnel through writeMeta → here, so nothing else changes. Empty
795
795
  * `agents:` / `versions:` are not written (no empty committed files).
796
796
  */
797
+ /**
798
+ * Every `Meta` key, classified as `central` (synced via agents.yaml) or `device`
799
+ * (routed to the per-machine device file by {@link writeMetaUnlocked}). The
800
+ * `Record<keyof Meta, …>` type makes this EXHAUSTIVE: adding a field to `Meta`
801
+ * without classifying it here is a compile error. That guarantee is what lets
802
+ * `serializeCentral` delete only keys THIS version models — a key a newer CLI
803
+ * version added (absent from this map) is preserved verbatim instead of being
804
+ * dropped and synced fleet-wide as data loss. This is the fix for the recurring
805
+ * agents.yaml key-loss (beta flags, `notify.owner`, and `feed:` all vanished
806
+ * this way when an older-versioned CLI on the fleet rewrote the file).
807
+ */
808
+ const META_KEY_SCOPE = {
809
+ // Device-local — writeMetaUnlocked destructures these out; never in central.
810
+ agents: 'device',
811
+ isolatedAgents: 'device',
812
+ versions: 'device',
813
+ defaultBrowserProfile: 'device',
814
+ deviceConfig: 'device',
815
+ // Central — synced via agents.yaml.
816
+ run: 'central',
817
+ model: 'central',
818
+ watchdog: 'central',
819
+ lease: 'central',
820
+ secrets: 'central',
821
+ budget: 'central',
822
+ feed: 'central',
823
+ beta: 'central',
824
+ registries: 'central',
825
+ profiles: 'central',
826
+ source: 'central',
827
+ projectRoot: 'central',
828
+ extraRepos: 'central',
829
+ brands: 'central',
830
+ actors: 'central',
831
+ seededPresets: 'central',
832
+ hooks: 'central',
833
+ browser: 'central',
834
+ config: 'central',
835
+ hosts: 'central',
836
+ fleet: 'central',
837
+ share: 'central',
838
+ notify: 'central',
839
+ routines: 'central',
840
+ };
841
+ /**
842
+ * Every key this version models (central + device). serializeCentral deletes an
843
+ * on-disk key only when it is KNOWN and absent from the write's in-memory object:
844
+ * a central key the caller cleared, OR a device key that is legacy cruft in the
845
+ * synced file (device keys are routed to the per-machine file, so one lingering
846
+ * in central is stale and must be migrated out). A key NOT listed here — e.g. one
847
+ * a newer CLI version added — is preserved verbatim, never dropped + synced away.
848
+ */
849
+ const KNOWN_META_KEYS = new Set(Object.keys(META_KEY_SCOPE));
797
850
  /**
798
851
  * Serialize the central (synced) meta to `agents.yaml` WITHOUT destroying the
799
852
  * hand-written comments in the committed file.
@@ -834,7 +887,12 @@ function serializeCentral(central) {
834
887
  }
835
888
  }
836
889
  for (const k of Object.keys(current)) {
837
- if (!(k in central)) {
890
+ // Only delete a key THIS version knows about. A key not in KNOWN_META_KEYS
891
+ // (e.g. one a newer CLI version added) is preserved verbatim — deleting it
892
+ // here would drop it and sync the deletion fleet-wide (the agents.yaml
893
+ // config data-loss bug). A known device key lingering in the synced file is
894
+ // still removed — it belongs in the per-machine file, not here.
895
+ if (!(k in central) && KNOWN_META_KEYS.has(k)) {
838
896
  doc.delete(k);
839
897
  changed = true;
840
898
  }
@@ -79,7 +79,7 @@ export interface BudgetConfig {
79
79
  require_confirm_over?: number;
80
80
  }
81
81
  /** Preview features that users can opt into via `agents beta`. */
82
- export type BetaFeatureName = 'factory' | 'projects';
82
+ export type BetaFeatureName = 'factory';
83
83
  /** Subset of chalk color names used for agent-specific terminal output. */
84
84
  export type ChalkColor = 'magenta' | 'green' | 'blue' | 'cyan' | 'yellowBright' | 'redBright' | 'whiteBright' | 'blueBright' | 'greenBright' | 'magentaBright' | 'cyanBright';
85
85
  /** Static configuration for a single agent -- paths, capabilities, and format conventions. */
@@ -97,6 +97,11 @@ export interface WatchdogTickOptions {
97
97
  dryRun?: boolean;
98
98
  enter?: boolean;
99
99
  }) => Promise<InjectResult>;
100
+ /**
101
+ * Publish a declared block on the owner's feed. Default publishBlock() — tests
102
+ * inject a collector so no real feed dir is touched.
103
+ */
104
+ publishBlockFn?: (block: OpenBlock) => void;
100
105
  /** Override the canonical watchdog.log path (tests point at a tmp file). */
101
106
  logPath?: string;
102
107
  /**
@@ -195,6 +200,14 @@ export interface NudgeDecision {
195
200
  nudge: boolean;
196
201
  reason: string;
197
202
  text?: string;
203
+ /**
204
+ * Set to `true` when the brain (smart decider) explicitly concluded the session
205
+ * needs the human — as opposed to a cheap deterministic skip (session is done,
206
+ * not stalled, etc.). Only `true` when the brain escalation path ran and returned
207
+ * `nudge: false`. The watchdog uses this to gate the self-file reminder: a
208
+ * "session is done" skip does NOT trigger a reminder.
209
+ */
210
+ needsHuman?: boolean;
198
211
  }
199
212
  /**
200
213
  * The smart brain. Resolves a `watchdog` workflow for the session's cwd
@@ -44,7 +44,7 @@ import { withFileLock, atomicWriteFileSync, ensureLockTarget } from '../fs-atomi
44
44
  import { resolveAnswerRoute, isOpenQuestionBlock } from '../answer-router.js';
45
45
  import { enqueue, mailboxDir } from '../mailbox.js';
46
46
  import { mailboxIdForActiveSession } from '../mailbox-target.js';
47
- import { readBlock, blockIdForSession } from '../feed.js';
47
+ import { readBlock, blockIdForSession, buildDeclaredBlock, publishBlock } from '../feed.js';
48
48
  import { summarizeWatchdogTail } from './watchdogTail.js';
49
49
  import { appendWatchdogEvents } from './log.js';
50
50
  import { buildRotateLaunchCommand, buildRotateReplayText, classifyTailForRotate, defaultRotateGate, defaultTuiLiveFor, exitSequenceFor, isInflightPhase, isWatchdogRotateEnabled, listInflightRotates, readRotateState, recordRotateSkip, shouldLogRotateSkip, writeRotateState, DEFAULT_ROTATE_READINESS_MS, DEFAULT_ROTATE_SKIP_COOLDOWN_MS, DEFAULT_ROTATE_FAILED_COOLDOWN_MS, } from './rotate.js';
@@ -353,6 +353,7 @@ export async function runWatchdogTick(opts = {}) {
353
353
  const smartDecider = opts.smartDecider ?? makeDefaultSmartDecider(opts.smartAgent ?? 'claude');
354
354
  const openBlockFor = opts.openBlockFor ?? defaultOpenBlockFor;
355
355
  const injectFn = opts.injectFn ?? injectIntoTerminal;
356
+ const publishBlockFn = opts.publishBlockFn ?? publishBlock;
356
357
  // Rotate seams (watchdog/rotate.ts). The config is read fresh per tick
357
358
  // (readMeta is mtime-cached), so a `watchdog.rotate` flip is honored on the
358
359
  // next pass. The default readiness probe (defaultTuiLiveFor) treats the
@@ -645,13 +646,21 @@ export async function runWatchdogTick(opts = {}) {
645
646
  // forces every stalled candidate through the brain.
646
647
  let decision;
647
648
  if (opts.smart) {
648
- decision = await smartDecider(session, candidate);
649
+ const d = await smartDecider(session, candidate);
650
+ // Mark needsHuman when the brain explicitly concluded "leave for human".
651
+ decision = d.nudge ? d : { ...d, needsHuman: true };
649
652
  }
650
653
  else {
651
654
  const det = deterministicDecision(session, candidate);
652
- decision = det.kind === 'escalate'
653
- ? await smartDecider(session, candidate)
654
- : { nudge: det.kind === 'nudge', reason: det.reason };
655
+ if (det.kind === 'escalate') {
656
+ const d = await smartDecider(session, candidate);
657
+ // Mark needsHuman when the brain escalation path concluded "leave for human".
658
+ decision = d.nudge ? d : { ...d, needsHuman: true };
659
+ }
660
+ else {
661
+ // Cheap deterministic path — completion or clear stall. Never "needs human".
662
+ decision = { nudge: det.kind === 'nudge', reason: det.reason };
663
+ }
655
664
  }
656
665
  const chosenText = decision.text ?? nudgeText;
657
666
  // Log the decision for the Factory watchdog card.
@@ -670,6 +679,53 @@ export async function runWatchdogTick(opts = {}) {
670
679
  lastAssistantMessage: summary.lastAssistantMessage,
671
680
  });
672
681
  if (!decision.nudge) {
682
+ // Brain explicitly concluded "leave for human" (needsHuman === true). Inject a
683
+ // self-file reminder into the agent's terminal so it knows to post a feed block.
684
+ // Cheap deterministic skips (session done, no stall) are NOT reminder-worthy —
685
+ // guard on needsHuman so a finished session is never poked.
686
+ // Gated by the same cooldown as a nudge to prevent re-firing every 2-minute tick.
687
+ if (decision.needsHuman) {
688
+ const lastNudgeMs = ledger[session.sessionId ?? ''] ?? 0;
689
+ const cooldownMs = thresholds.cooldownMs;
690
+ const withinCooldown = nowMs - lastNudgeMs < cooldownMs;
691
+ if (opts.nudge && session.sessionId && !withinCooldown) {
692
+ const existingBlock = openBlockFor(session);
693
+ if (existingBlock === null) {
694
+ // Determine addressability without planning the full nudge (no text needed).
695
+ const resolution = resolveInjectTargetForSession(session, { allowGhosttyFocus: opts.allowGhosttyFocus });
696
+ if (resolution.addressable) {
697
+ // Addressable → inject a reminder asking the agent to self-file a feed block.
698
+ const reminderText = 'You appear stuck. If you genuinely need Muqsit, file it: ' +
699
+ 'agents feed post "<one-line ask>" --blocked --default "<safe default>". ' +
700
+ 'Otherwise keep going.';
701
+ try {
702
+ await injectFn(resolution.target, reminderText, { dryRun: opts.injectDryRun });
703
+ }
704
+ catch {
705
+ // Swallow inject errors — reminder is best-effort; flag set below.
706
+ }
707
+ ledgerUpdates[session.sessionId] = nowMs;
708
+ }
709
+ else {
710
+ // Un-addressable → we can't reach the terminal to remind it, so the ONLY
711
+ // way to reach Muqsit is to file a declared block on the agent's behalf.
712
+ // This is the most important case: the session genuinely needs the human
713
+ // AND the watchdog can't even nudge it. Never let it silently vanish.
714
+ const mailboxId = mailboxIdForActiveSession(session) ?? session.sessionId;
715
+ const machineHost = session.provenance?.host ?? 'unknown';
716
+ const runtime = session.kind;
717
+ try {
718
+ const declaredBlock = buildDeclaredBlock({ sessionId: session.sessionId, mailboxId, host: machineHost, runtime, cwd: session.cwd }, { text: `Session genuinely needs Muqsit and is un-addressable — ${decision.reason}. Needs attention.` });
719
+ publishBlockFn(declaredBlock);
720
+ ledgerUpdates[session.sessionId] = nowMs;
721
+ }
722
+ catch {
723
+ // publishBlock failure is non-fatal — best-effort owner page.
724
+ }
725
+ }
726
+ }
727
+ }
728
+ }
673
729
  outcomes.push({ ...base, decision: 'skip', reason: decision.reason });
674
730
  continue;
675
731
  }
@@ -682,6 +738,12 @@ export async function runWatchdogTick(opts = {}) {
682
738
  const addressable = plan.via === 'inject' ? true : undefined;
683
739
  if (plan.via === 'refuse') {
684
740
  // No addressable rail and not headless-resumable — flag, NEVER guess.
741
+ // This branch is reached ONLY for a nudge-worthy (decision.nudge === true)
742
+ // drive-forward poke, which is NEVER needsHuman (needsHuman is set only when
743
+ // decision.nudge === false). So we do NOT page the owner here — a short "just
744
+ // needs a poke" stall must not text Muqsit's phone. Owner-paging for an
745
+ // un-addressable session happens only on the confirmed needsHuman skip path
746
+ // above. Here we only flag it for the tray.
685
747
  flags[session.sessionId] = { reason: plan.reason, host: session.host, atMs: nowMs };
686
748
  outcomes.push({
687
749
  ...base, decision: 'skip', addressable: false,
@@ -690,7 +752,14 @@ export async function runWatchdogTick(opts = {}) {
690
752
  });
691
753
  continue;
692
754
  }
693
- // handsoff = detect + flag, but never deliver.
755
+ // handsoff = detect + flag, but never deliver via inject/mailbox.
756
+ // This branch is reached ONLY for a nudge-worthy (decision.nudge === true)
757
+ // drive-forward poke, which is NEVER needsHuman. A hands-off policy means "don't
758
+ // nudge it forward" — it must NOT translate into paging Muqsit for a poke. So we
759
+ // only flag it for the tray, no owner page. A genuinely needs-human session (even
760
+ // under hands-off) is paged by the confirmed-needsHuman skip path above, which
761
+ // does not consult policy — hands-off silences the forward nudge, not the
762
+ // "it's actually stuck" signal.
694
763
  if (policy === 'handsoff') {
695
764
  flags[session.sessionId] = {
696
765
  reason: `handsoff: would nudge via ${viaLabel(plan)} but policy is hands-off`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.22.10",
3
+ "version": "1.22.12",
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",