@phnx-labs/agents-cli 1.15.0 → 1.17.0

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 (111) hide show
  1. package/CHANGELOG.md +143 -39
  2. package/README.md +6 -6
  3. package/dist/commands/alias.js +2 -2
  4. package/dist/commands/browser-picker.d.ts +21 -0
  5. package/dist/commands/browser-picker.js +114 -0
  6. package/dist/commands/browser.js +793 -83
  7. package/dist/commands/cloud.js +8 -0
  8. package/dist/commands/commands.js +72 -22
  9. package/dist/commands/daemon.js +2 -2
  10. package/dist/commands/exec.js +70 -1
  11. package/dist/commands/hooks.js +71 -26
  12. package/dist/commands/mcp.js +81 -39
  13. package/dist/commands/plugins.js +224 -17
  14. package/dist/commands/prune.js +29 -1
  15. package/dist/commands/pull.js +3 -3
  16. package/dist/commands/repo.js +1 -1
  17. package/dist/commands/routines.js +2 -2
  18. package/dist/commands/secrets.js +154 -20
  19. package/dist/commands/sessions.js +62 -19
  20. package/dist/commands/{init.d.ts → setup.d.ts} +7 -6
  21. package/dist/commands/{init.js → setup.js} +22 -21
  22. package/dist/commands/skills.js +60 -19
  23. package/dist/commands/subagents.js +41 -13
  24. package/dist/commands/utils.d.ts +16 -0
  25. package/dist/commands/utils.js +32 -0
  26. package/dist/commands/view.js +78 -20
  27. package/dist/commands/workflows.d.ts +10 -0
  28. package/dist/commands/workflows.js +457 -0
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +48 -36
  31. package/dist/lib/agents.js +2 -2
  32. package/dist/lib/auto-pull-worker.js +2 -3
  33. package/dist/lib/auto-pull.js +2 -2
  34. package/dist/lib/browser/cdp.d.ts +7 -1
  35. package/dist/lib/browser/cdp.js +32 -1
  36. package/dist/lib/browser/chrome.d.ts +10 -0
  37. package/dist/lib/browser/chrome.js +41 -3
  38. package/dist/lib/browser/devices.d.ts +4 -0
  39. package/dist/lib/browser/devices.js +27 -0
  40. package/dist/lib/browser/drivers/local.js +22 -6
  41. package/dist/lib/browser/drivers/ssh.js +9 -2
  42. package/dist/lib/browser/input.d.ts +1 -0
  43. package/dist/lib/browser/input.js +3 -0
  44. package/dist/lib/browser/ipc.js +158 -23
  45. package/dist/lib/browser/profiles.d.ts +10 -2
  46. package/dist/lib/browser/profiles.js +122 -37
  47. package/dist/lib/browser/service.d.ts +91 -13
  48. package/dist/lib/browser/service.js +767 -132
  49. package/dist/lib/browser/types.d.ts +91 -3
  50. package/dist/lib/browser/types.js +16 -0
  51. package/dist/lib/cloud/rush.d.ts +28 -1
  52. package/dist/lib/cloud/rush.js +69 -14
  53. package/dist/lib/cloud/store.js +2 -2
  54. package/dist/lib/commands.d.ts +1 -15
  55. package/dist/lib/commands.js +11 -7
  56. package/dist/lib/daemon.js +2 -3
  57. package/dist/lib/doctor-diff.js +4 -4
  58. package/dist/lib/events.js +2 -2
  59. package/dist/lib/hooks.d.ts +11 -7
  60. package/dist/lib/hooks.js +138 -49
  61. package/dist/lib/migrate.d.ts +1 -1
  62. package/dist/lib/migrate.js +1237 -22
  63. package/dist/lib/models.js +2 -2
  64. package/dist/lib/permissions.d.ts +8 -66
  65. package/dist/lib/permissions.js +18 -18
  66. package/dist/lib/plugins.d.ts +94 -24
  67. package/dist/lib/plugins.js +702 -123
  68. package/dist/lib/pty-server.js +9 -10
  69. package/dist/lib/resource-patterns.d.ts +41 -0
  70. package/dist/lib/resource-patterns.js +82 -0
  71. package/dist/lib/resources/hooks.d.ts +5 -1
  72. package/dist/lib/resources/hooks.js +21 -4
  73. package/dist/lib/resources/index.d.ts +17 -0
  74. package/dist/lib/resources/index.js +7 -0
  75. package/dist/lib/resources/types.d.ts +1 -1
  76. package/dist/lib/resources/workflows.d.ts +24 -0
  77. package/dist/lib/resources/workflows.js +110 -0
  78. package/dist/lib/resources.d.ts +6 -1
  79. package/dist/lib/resources.js +12 -2
  80. package/dist/lib/rotate.js +3 -4
  81. package/dist/lib/session/active.d.ts +3 -0
  82. package/dist/lib/session/active.js +92 -6
  83. package/dist/lib/session/cloud.js +2 -2
  84. package/dist/lib/session/db.d.ts +18 -0
  85. package/dist/lib/session/db.js +109 -5
  86. package/dist/lib/session/discover.d.ts +6 -0
  87. package/dist/lib/session/discover.js +55 -29
  88. package/dist/lib/session/team-filter.js +2 -2
  89. package/dist/lib/shims.d.ts +4 -52
  90. package/dist/lib/shims.js +23 -15
  91. package/dist/lib/skills.js +6 -2
  92. package/dist/lib/sqlite.js +10 -4
  93. package/dist/lib/state.d.ts +101 -16
  94. package/dist/lib/state.js +179 -31
  95. package/dist/lib/subagents.d.ts +28 -0
  96. package/dist/lib/subagents.js +98 -1
  97. package/dist/lib/sync-manifest.d.ts +1 -1
  98. package/dist/lib/sync-manifest.js +3 -3
  99. package/dist/lib/teams/persistence.js +15 -5
  100. package/dist/lib/teams/registry.js +2 -2
  101. package/dist/lib/types.d.ts +75 -17
  102. package/dist/lib/types.js +3 -3
  103. package/dist/lib/usage.js +2 -2
  104. package/dist/lib/versions.d.ts +3 -0
  105. package/dist/lib/versions.js +158 -47
  106. package/dist/lib/workflows.d.ts +79 -0
  107. package/dist/lib/workflows.js +233 -0
  108. package/package.json +1 -5
  109. package/scripts/postinstall.js +60 -59
  110. package/dist/commands/fork.d.ts +0 -10
  111. package/dist/commands/fork.js +0 -146
package/dist/lib/shims.js CHANGED
@@ -44,7 +44,7 @@ function shouldIgnore(name) {
44
44
  * Detect conflicting files between source and destination directories.
45
45
  * Returns list of filenames that exist in both locations (excluding symlinks in dest).
46
46
  */
47
- export function detectConflicts(src, dest, prefix = '') {
47
+ function detectConflicts(src, dest, prefix = '') {
48
48
  const conflicts = [];
49
49
  if (!fs.existsSync(src) || !fs.existsSync(dest)) {
50
50
  return conflicts;
@@ -93,7 +93,7 @@ export function detectConflicts(src, dest, prefix = '') {
93
93
  /**
94
94
  * Prompt user for conflict resolution strategy.
95
95
  */
96
- export async function promptConflictStrategy(conflictInfos) {
96
+ async function promptConflictStrategy(conflictInfos) {
97
97
  const totalConflicts = conflictInfos.reduce((sum, info) => sum + info.conflicts.length, 0);
98
98
  if (totalConflicts === 0) {
99
99
  return null; // No conflicts, no prompt needed
@@ -171,8 +171,10 @@ export async function promptConflictStrategy(conflictInfos) {
171
171
  * and capability flag `memoryImports` → `rulesImports`.
172
172
  * v8 — versions moved from ~/.agents-system/versions to ~/.agents/versions
173
173
  * (two-repo split: system = shipped defaults, user = operational state).
174
+ * v9 — claude shim exports CLAUDE_CODE_OAUTH_TOKEN from per-version
175
+ * .oauth_token file on Linux (keychain-less sandbox fallback).
174
176
  */
175
- export const SHIM_SCHEMA_VERSION = 8;
177
+ export const SHIM_SCHEMA_VERSION = 10;
176
178
  /** Internal marker string used to embed the schema version in shim scripts. */
177
179
  const SHIM_VERSION_MARKER = 'agents-shim-version:';
178
180
  /**
@@ -189,6 +191,12 @@ export function generateShimScript(agent) {
189
191
  # selected version's config directory so switching versions also switches the
190
192
  # live Claude account.
191
193
  export CLAUDE_CONFIG_DIR="$VERSION_DIR/home/${configDirName}"
194
+ # On Linux sandboxes (no keychain), fall back to a per-version token file.
195
+ # The env var always wins if already set; no-op on macOS.
196
+ if [ "\$(uname -s)" = "Linux" ] && [ -z "\${CLAUDE_CODE_OAUTH_TOKEN:-}" ] && [ -f "\$CLAUDE_CONFIG_DIR/.oauth_token" ]; then
197
+ CLAUDE_CODE_OAUTH_TOKEN=\$(cat "\$CLAUDE_CONFIG_DIR/.oauth_token")
198
+ export CLAUDE_CODE_OAUTH_TOKEN
199
+ fi
192
200
  `
193
201
  : agent === 'codex'
194
202
  ? `
@@ -287,7 +295,7 @@ if [ -z "$VERSION" ]; then
287
295
  exit 1
288
296
  fi
289
297
 
290
- VERSION_DIR="$AGENTS_USER_DIR/versions/$AGENT/$VERSION"
298
+ VERSION_DIR="$AGENTS_USER_DIR/.history/versions/$AGENT/$VERSION"
291
299
  BINARY="$VERSION_DIR/node_modules/.bin/$CLI_COMMAND"
292
300
 
293
301
  # Auto-install if not present
@@ -380,7 +388,7 @@ export function removeShim(agent) {
380
388
  * v6 — versions moved from ~/.agents-system/versions to ~/.agents/versions
381
389
  * (two-repo split: system = shipped defaults, user = operational state).
382
390
  */
383
- export const VERSIONED_ALIAS_SCHEMA_VERSION = 6;
391
+ export const VERSIONED_ALIAS_SCHEMA_VERSION = 7;
384
392
  /** Internal marker string used to embed the schema version in versioned alias scripts. */
385
393
  const VERSIONED_ALIAS_VERSION_MARKER = 'agents-versioned-alias-version:';
386
394
  // The version string is interpolated into a generated bash script and into
@@ -405,14 +413,14 @@ export function generateVersionedAliasScript(agent, version) {
405
413
  ? `
406
414
  # Claude stores OAuth credentials in the macOS keychain. Scope them to this
407
415
  # version's config directory so direct aliases also switch the live account.
408
- export CLAUDE_CONFIG_DIR="$HOME/.agents/versions/${agent}/${version}/home/${configDirName}"
416
+ export CLAUDE_CONFIG_DIR="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
409
417
  `
410
418
  : agent === 'codex'
411
419
  ? `
412
420
  # Codex reads its config (approval_policy, sandbox_mode, MCP servers, rules)
413
421
  # from CODEX_HOME. Point direct aliases at the versioned home so permissions
414
422
  # and rules written by agents-cli actually take effect.
415
- export CODEX_HOME="$HOME/.agents/versions/${agent}/${version}/home/${configDirName}"
423
+ export CODEX_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
416
424
  `
417
425
  : '';
418
426
  const launchArgs = agent === 'codex' ? ' -c check_for_update_on_startup=false' : '';
@@ -421,7 +429,7 @@ export CODEX_HOME="$HOME/.agents/versions/${agent}/${version}/home/${configDirNa
421
429
  # ${VERSIONED_ALIAS_VERSION_MARKER} ${VERSIONED_ALIAS_SCHEMA_VERSION}
422
430
  # Direct alias for ${agentConfig.name}@${version}
423
431
 
424
- BINARY="$HOME/.agents/versions/${agent}/${version}/node_modules/.bin/${agentConfig.cliCommand}"
432
+ BINARY="$HOME/.agents/.history/versions/${agent}/${version}/node_modules/.bin/${agentConfig.cliCommand}"
425
433
 
426
434
  if [ ! -x "$BINARY" ]; then
427
435
  echo "agents: ${agent}@${version} not installed" >&2
@@ -543,7 +551,7 @@ function getVersionConfigPath(agent, version) {
543
551
  * Returns null if no migration is needed (already symlink or doesn't exist),
544
552
  * or ConflictInfo with the list of conflicting files.
545
553
  */
546
- export function detectMigrationConflicts(agent, version) {
554
+ function detectMigrationConflicts(agent, version) {
547
555
  const configPath = getAgentConfigPath(agent);
548
556
  const versionConfigPath = getVersionConfigPath(agent, version);
549
557
  try {
@@ -837,7 +845,7 @@ export function ensureClaudeInsideSymlink(version) {
837
845
  * Apply `ensureClaudeInsideSymlink` to every installed Claude version.
838
846
  * Safe to call repeatedly; per-version calls are idempotent.
839
847
  */
840
- export function ensureAllClaudeInsideSymlinks() {
848
+ function ensureAllClaudeInsideSymlinks() {
841
849
  const versionsDir = getVersionsDir();
842
850
  const claudeVersionsDir = path.join(versionsDir, 'claude');
843
851
  const migrated = [];
@@ -975,7 +983,7 @@ export function shimExists(agent) {
975
983
  * Read the schema version embedded in an existing on-disk shim. Returns
976
984
  * `null` if the shim doesn't exist or has no version marker (pre-v2 shim).
977
985
  */
978
- export function readShimSchemaVersion(agent) {
986
+ function readShimSchemaVersion(agent) {
979
987
  if (!shimExists(agent))
980
988
  return null;
981
989
  try {
@@ -996,7 +1004,7 @@ export function readShimSchemaVersion(agent) {
996
1004
  * False means either the shim is missing, is pre-v2 (no marker), or is an
997
1005
  * older version that needs regeneration.
998
1006
  */
999
- export function isShimCurrent(agent) {
1007
+ function isShimCurrent(agent) {
1000
1008
  const version = readShimSchemaVersion(agent);
1001
1009
  return version === SHIM_SCHEMA_VERSION;
1002
1010
  }
@@ -1309,7 +1317,7 @@ export function listAgentsWithInstalledVersions() {
1309
1317
  /**
1310
1318
  * Create shims for all installed agents.
1311
1319
  */
1312
- export function ensureAllShims() {
1320
+ function ensureAllShims() {
1313
1321
  const versionsDir = getVersionsDir();
1314
1322
  if (!fs.existsSync(versionsDir)) {
1315
1323
  return;
@@ -1331,7 +1339,7 @@ export function ensureAllShims() {
1331
1339
  * Compare resources between two versions.
1332
1340
  * Returns resources that exist in currentVersion but not in targetVersion.
1333
1341
  */
1334
- export function compareVersionResources(agent, currentVersion, targetVersion) {
1342
+ function compareVersionResources(agent, currentVersion, targetVersion) {
1335
1343
  const agentConfig = AGENTS[agent];
1336
1344
  const currentPath = getVersionConfigPath(agent, currentVersion);
1337
1345
  const targetPath = getVersionConfigPath(agent, targetVersion);
@@ -1420,7 +1428,7 @@ export function hasResourceDiff(diff) {
1420
1428
  * Copy resources from one version to another.
1421
1429
  * Only copies resources listed in the diff (i.e., ones missing in target).
1422
1430
  */
1423
- export function copyResourcesToVersion(agent, fromVersion, toVersion, diff) {
1431
+ function copyResourcesToVersion(agent, fromVersion, toVersion, diff) {
1424
1432
  const agentConfig = AGENTS[agent];
1425
1433
  const fromPath = getVersionConfigPath(agent, fromVersion);
1426
1434
  const toPath = getVersionConfigPath(agent, toVersion);
@@ -11,7 +11,7 @@ import * as path from 'path';
11
11
  import * as os from 'os';
12
12
  import * as yaml from 'yaml';
13
13
  import { SKILLS_CAPABLE_AGENTS, ensureSkillsDir } from './agents.js';
14
- import { getUserSkillsDir, getSkillsDir as getSystemSkillsDir, getProjectAgentsDir, getEnabledExtraRepos } from './state.js';
14
+ import { getUserSkillsDir, getSkillsDir as getSystemSkillsDir, getProjectAgentsDir, getEnabledExtraRepos, getTrashSkillsDir } from './state.js';
15
15
  import { getEffectiveHome, getVersionHomePath, listInstalledVersions } from './versions.js';
16
16
  import { emit } from './events.js';
17
17
  const HOME = os.homedir();
@@ -569,7 +569,11 @@ export function removeSkillFromVersion(agent, version, skillName) {
569
569
  return { success: true };
570
570
  }
571
571
  try {
572
- fs.rmSync(target, { recursive: true, force: true });
572
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
573
+ const trashDir = path.join(getTrashSkillsDir(), agent, version, skillName);
574
+ const trashDest = path.join(trashDir, stamp);
575
+ fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
576
+ fs.renameSync(target, trashDest);
573
577
  }
574
578
  catch (err) {
575
579
  return { success: false, error: err.message };
@@ -66,12 +66,18 @@ class Database {
66
66
  pragma(stmt) {
67
67
  this.inner.exec(`PRAGMA ${stmt}`);
68
68
  }
69
- // Wrap fn in BEGIN/COMMIT, ROLLBACK on throw. Manual on both runtimes
70
- // because node:sqlite has no `db.transaction(fn)` and the manual form is
71
- // identical in shape to what better-sqlite3 / bun:sqlite produce.
69
+ // Wrap fn in BEGIN IMMEDIATE/COMMIT, ROLLBACK on throw. Manual on both
70
+ // runtimes because node:sqlite has no `db.transaction(fn)`.
71
+ //
72
+ // BEGIN IMMEDIATE (not BEGIN DEFERRED) is required for write transactions in
73
+ // WAL mode. BEGIN DEFERRED upgrades the lock lazily on the first write; if
74
+ // another writer already committed since the transaction started, SQLite
75
+ // returns SQLITE_BUSY_SNAPSHOT (a sub-code of SQLITE_BUSY that the busy
76
+ // handler does NOT retry). BEGIN IMMEDIATE claims the write lock upfront so
77
+ // the busy handler fires correctly and respects busy_timeout.
72
78
  transaction(fn) {
73
79
  return (...args) => {
74
- this.inner.exec('BEGIN');
80
+ this.inner.exec('BEGIN IMMEDIATE');
75
81
  try {
76
82
  const result = fn(...args);
77
83
  this.inner.exec('COMMIT');
@@ -2,11 +2,18 @@
2
2
  * Filesystem layout and persistent state for agents-cli.
3
3
  *
4
4
  * Two roots:
5
- * - ~/.agents-system/ — system repo (npm-shipped resources and canonical read-side defaults)
6
- * - ~/.agents/ — user repo (user-authored commands, skills, hooks, rules, mcp,
7
- * permissions, subagents, profiles, secrets, agents.yaml,
8
- * packages, routines, runs, versions, shims, backups, plugins,
9
- * drive, trash)
5
+ * - ~/.agents-system/ — system repo (npm-shipped resources, read-only defaults)
6
+ * - ~/.agents/ — user repo (resources + agents.yaml + operational state)
7
+ *
8
+ * Inside ~/.agents/, top-level paths hold ONLY resources + agents.yaml.
9
+ * Operational state lives in two sibling buckets:
10
+ *
11
+ * - ~/.agents/.history/ — durable runtime data (sessions, versions, runs,
12
+ * teams/agents, trash, backups). Backed up by
13
+ * `agents repo push`.
14
+ * - ~/.agents/.cache/ — regenerable runtime data (shims, packages, helpers
15
+ * for daemon/pty, terminals, cloud, drive, browser
16
+ * chrome-data, logs, swarmify). Gitignored.
10
17
  *
11
18
  * Resolution precedence for resources: project > user > system.
12
19
  * Every module that needs a path or reads/writes agents.yaml goes through here.
@@ -48,8 +55,21 @@ export declare function getMcpDir(): string;
48
55
  export declare function getPermissionsDir(): string;
49
56
  /** Path to subagent definition directories — system repo. */
50
57
  export declare function getSubagentsDir(): string;
51
- /** Path to ~/.agents-system/promptcuts.yaml. */
58
+ /** Path to ~/.agents-system/hooks/promptcuts.yaml (system defaults). */
52
59
  export declare function getPromptcutsPath(): string;
60
+ /**
61
+ * Resolve the effective promptcuts file: user file if it exists, otherwise
62
+ * the system file. Use this for callers that need a single path (doctor
63
+ * diff, displaying which file is in play). Callers that need the merged
64
+ * shortcut set should use readMergedPromptcuts() instead.
65
+ */
66
+ export declare function getEffectivePromptcutsPath(): string;
67
+ /**
68
+ * Read promptcuts from system + user with user precedence. Returns the
69
+ * merged `shortcuts` map. Same layering model as parseHookManifest().
70
+ * Returns an empty object when neither file exists or both fail to parse.
71
+ */
72
+ export declare function readMergedPromptcuts(): Record<string, unknown>;
53
73
  /** Path to the legacy MCP config JSON. */
54
74
  export declare function getMcpConfigPath(): string;
55
75
  /** Path to the global instructions file. */
@@ -69,28 +89,83 @@ export declare function getUserRulesDir(): string;
69
89
  export declare function getUserMcpDir(): string;
70
90
  export declare function getUserPermissionsDir(): string;
71
91
  export declare function getUserSubagentsDir(): string;
92
+ export declare function getSystemWorkflowsDir(): string;
93
+ export declare function getUserWorkflowsDir(): string;
72
94
  export declare function getUserSecretsDir(): string;
73
95
  export declare function getUserPromptcutsPath(): string;
74
- /** Path to cloned packages (~/.agents/packages/). */
96
+ /** Bucket root for durable runtime data (~/.agents/.history/). */
97
+ export declare function getHistoryDir(): string;
98
+ /** Bucket root for regenerable runtime data (~/.agents/.cache/). */
99
+ export declare function getCacheDir(): string;
100
+ /** Path to cloned packages (~/.agents/.cache/packages/). */
75
101
  export declare function getPackagesDir(): string;
76
102
  /** Path to routine YAML definitions (~/.agents/routines/). */
77
103
  export declare function getRoutinesDir(): string;
78
- /** Path to routine execution logs (~/.agents/runs/). */
104
+ /** Path to routine execution logs (~/.agents/.history/runs/). */
79
105
  export declare function getRunsDir(): string;
80
- /** Path to installed agent CLI binaries (~/.agents/versions/). */
106
+ /** Path to installed agent CLI binaries (~/.agents/.history/versions/). */
81
107
  export declare function getVersionsDir(): string;
82
- /** Path to version-switching shim scripts (~/.agents/shims/). */
108
+ /** Path to version-switching shim scripts (~/.agents/.cache/shims/). */
83
109
  export declare function getShimsDir(): string;
84
- /** Path to config backups (~/.agents/backups/). */
110
+ /** Path to per-agent installed CLI binaries (~/.agents/.cache/bin/). */
111
+ export declare function getBinDir(): string;
112
+ /** Path to config backups (~/.agents/.history/backups/). */
85
113
  export declare function getBackupsDir(): string;
86
- /** Path to plugin bundles (~/.agents/plugins/). */
114
+ /** Path to plugin bundles (~/.agents/.cache/plugins/). */
87
115
  export declare function getPluginsDir(): string;
88
- /** Path to synced remote session data (~/.agents/drive/). */
116
+ /** Path to synced remote session data (~/.agents/.cache/drive/). */
89
117
  export declare function getDriveDir(): string;
90
- /** Path to soft-deleted resources (~/.agents/trash/). */
118
+ /** Path to soft-deleted resources (~/.agents/.history/trash/). */
91
119
  export declare function getTrashDir(): string;
120
+ /** Path to local session indexer storage (~/.agents/.history/sessions/). */
121
+ export declare function getSessionsDir(): string;
122
+ /** Path to the session index database (~/.agents/.history/sessions.db). */
123
+ export declare function getSessionsDbPath(): string;
124
+ /** Path to teams config + registry (~/.agents/teams/). */
125
+ export declare function getTeamsDir(): string;
126
+ /** Path to teams execution history (~/.agents/.history/teams/agents/). */
127
+ export declare function getTeamsAgentsDir(): string;
128
+ /** Path to cloud dispatch cache (~/.agents/.cache/cloud/). */
129
+ export declare function getCloudDir(): string;
130
+ /** Path to terminal session metadata (~/.agents/.cache/terminals/). */
131
+ export declare function getTerminalsDir(): string;
132
+ /** Path to runtime logs (~/.agents/.cache/logs/). */
133
+ export declare function getLogsDir(): string;
134
+ /** Path to per-process runtime state (~/.agents/.cache/state/). */
135
+ export declare function getRuntimeStateDir(): string;
136
+ /** Path to swarmify-extension scratch (~/.agents/.cache/swarmify/). */
137
+ export declare function getSwarmifyDir(): string;
138
+ /** Path to browser runtime data — chrome-data, pids (~/.agents/.cache/browser/). */
139
+ export declare function getBrowserRuntimeDir(): string;
140
+ /** Path to helper subprocess scratch (~/.agents/.cache/helpers/). */
141
+ export declare function getHelpersDir(): string;
142
+ /** Path to scheduler daemon scratch (~/.agents/.cache/helpers/daemon/). */
143
+ export declare function getDaemonDir(): string;
144
+ /** Path to PTY server scratch (~/.agents/.cache/helpers/pty/). */
145
+ export declare function getPtyDir(): string;
146
+ /** Path to remote-resource auto-pull cache (~/.agents/.cache/.fetch/). */
147
+ export declare function getFetchCacheDir(): string;
148
+ /** Path to the CLI version cache file (~/.agents/.cache/.cli-version-cache.json). */
149
+ export declare function getCliVersionCachePath(): string;
150
+ /** Path to the models cache file (~/.agents/.cache/.models-cache.json). */
151
+ export declare function getModelsCachePath(): string;
152
+ /** Path to the daily update-check sentinel (~/.agents/.cache/.update-check). */
153
+ export declare function getUpdateCheckPath(): string;
154
+ /** Path to the migration sentinel (~/.agents/.cache/.migrated). */
155
+ export declare function getMigratedSentinelPath(): string;
92
156
  /** Path to soft-deleted version dirs (~/.agents/trash/versions/). */
93
157
  export declare function getTrashVersionsDir(): string;
158
+ /** Path to soft-deleted skills (~/.agents/trash/skills/). */
159
+ export declare function getTrashSkillsDir(): string;
160
+ /** Path to soft-deleted commands (~/.agents/trash/commands/). */
161
+ export declare function getTrashCommandsDir(): string;
162
+ /** Path to soft-deleted hooks (~/.agents/trash/hooks/). */
163
+ export declare function getTrashHooksDir(): string;
164
+ /** Path to soft-deleted plugins (~/.agents/trash/plugins/). */
165
+ export declare function getTrashPluginsDir(): string;
166
+ /** Path to soft-deleted subagents (~/.agents/trash/subagents/). */
167
+ export declare function getTrashSubagentsDir(): string;
168
+ export declare function getTrashWorkflowsDir(): string;
94
169
  /**
95
170
  * Path to a single user-level extra DotAgent repo clone (~/.agents-<alias>/).
96
171
  *
@@ -122,8 +197,18 @@ export declare function writeMeta(meta: Meta): void;
122
197
  export declare function updateMeta(updates: Partial<Meta>): Meta;
123
198
  /** Derive a filesystem-safe local clone path for a package source URL. */
124
199
  export declare function getPackageLocalPath(source: string): string;
125
- import type { AgentId, ResourceType, VersionResources } from './types.js';
126
- export declare function recordVersionResources(agent: AgentId, version: string, resourceType: ResourceType, resources: string[]): void;
200
+ import type { AgentId, ResourceType, VersionResources, ResourcePattern } from './types.js';
201
+ /**
202
+ * @deprecated No-op. Use ensureVersionResourcePatterns instead.
203
+ * Kept for backward compat with command files that still call it.
204
+ */
205
+ export declare function recordVersionResources(_agent: AgentId, _version: string, _resourceType: ResourceType, _resources: string[]): void;
206
+ /**
207
+ * Write default resource selection patterns for an agent@version.
208
+ * Only writes each field when it is not already set, preserving user customization.
209
+ * Pass all resource types you want to initialize in one call to batch the write.
210
+ */
211
+ export declare function ensureVersionResourcePatterns(agent: AgentId, version: string, updates: Partial<Record<Exclude<keyof VersionResources, 'rulesPreset'>, ResourcePattern[]>>): void;
127
212
  export declare function getVersionResources(agent: AgentId, version: string): VersionResources | null;
128
213
  export declare function clearVersionResources(agent: AgentId, version: string): void;
129
214
  /** Active rules preset for an agent@version. Defaults to "default" when unset. */
package/dist/lib/state.js CHANGED
@@ -2,11 +2,18 @@
2
2
  * Filesystem layout and persistent state for agents-cli.
3
3
  *
4
4
  * Two roots:
5
- * - ~/.agents-system/ — system repo (npm-shipped resources and canonical read-side defaults)
6
- * - ~/.agents/ — user repo (user-authored commands, skills, hooks, rules, mcp,
7
- * permissions, subagents, profiles, secrets, agents.yaml,
8
- * packages, routines, runs, versions, shims, backups, plugins,
9
- * drive, trash)
5
+ * - ~/.agents-system/ — system repo (npm-shipped resources, read-only defaults)
6
+ * - ~/.agents/ — user repo (resources + agents.yaml + operational state)
7
+ *
8
+ * Inside ~/.agents/, top-level paths hold ONLY resources + agents.yaml.
9
+ * Operational state lives in two sibling buckets:
10
+ *
11
+ * - ~/.agents/.history/ — durable runtime data (sessions, versions, runs,
12
+ * teams/agents, trash, backups). Backed up by
13
+ * `agents repo push`.
14
+ * - ~/.agents/.cache/ — regenerable runtime data (shims, packages, helpers
15
+ * for daemon/pty, terminals, cloud, drive, browser
16
+ * chrome-data, logs, swarmify). Gitignored.
10
17
  *
11
18
  * Resolution precedence for resources: project > user > system.
12
19
  * Every module that needs a path or reads/writes agents.yaml goes through here.
@@ -16,7 +23,7 @@ import * as path from 'path';
16
23
  import * as os from 'os';
17
24
  import * as yaml from 'yaml';
18
25
  import { SEEDED_REGISTRIES } from './types.js';
19
- const HOME = os.homedir();
26
+ const HOME = process.env.HOME ?? os.homedir();
20
27
  // ─── Root directories ─────────────────────────────────────────────────────────
21
28
  /** System repo — npm-shipped, read-only from user commands. */
22
29
  const SYSTEM_AGENTS_DIR = path.join(HOME, '.agents-system');
@@ -34,19 +41,46 @@ const SYSTEM_RULES_DIR = path.join(SYSTEM_AGENTS_DIR, 'rules');
34
41
  const SYSTEM_MCP_DIR = path.join(SYSTEM_AGENTS_DIR, 'mcp');
35
42
  const SYSTEM_PERMISSIONS_DIR = path.join(SYSTEM_AGENTS_DIR, 'permissions');
36
43
  const SYSTEM_SUBAGENTS_DIR = path.join(SYSTEM_AGENTS_DIR, 'subagents');
44
+ const SYSTEM_WORKFLOWS_DIR = path.join(SYSTEM_AGENTS_DIR, 'workflows');
37
45
  const SYSTEM_PROMPTCUTS_FILE = path.join(SYSTEM_AGENTS_DIR, 'hooks', 'promptcuts.yaml');
38
46
  const SYSTEM_MCP_CONFIG_FILE = path.join(SYSTEM_AGENTS_DIR, 'mcp.json');
39
47
  const SYSTEM_INSTRUCTIONS_FILE = path.join(SYSTEM_AGENTS_DIR, 'instructions.md');
40
- // User-level operational state
41
- const PACKAGES_DIR = path.join(USER_AGENTS_DIR, 'packages');
48
+ // ─── User repo operational buckets ────────────────────────────────────────────
49
+ /** Durable runtime data (sessions, versions, runs, teams history, trash, backups). */
50
+ const HISTORY_DIR = path.join(USER_AGENTS_DIR, '.history');
51
+ /** Regenerable runtime data (shims, packages, helpers, terminals, cloud, drive, logs, browser). */
52
+ const CACHE_DIR = path.join(USER_AGENTS_DIR, '.cache');
53
+ // Top-level user dirs (config/definitions only — runtime moves into .history/.cache).
42
54
  const ROUTINES_DIR = path.join(USER_AGENTS_DIR, 'routines');
43
- const RUNS_DIR = path.join(ROUTINES_DIR, 'runs');
44
- const VERSIONS_DIR = path.join(USER_AGENTS_DIR, 'versions');
45
- const SHIMS_DIR = path.join(USER_AGENTS_DIR, 'shims');
46
- const BACKUPS_DIR = path.join(USER_AGENTS_DIR, '.backups');
47
- const PLUGINS_DIR = path.join(USER_AGENTS_DIR, 'plugins');
48
- const DRIVE_DIR = path.join(USER_AGENTS_DIR, 'drive');
49
- const TRASH_DIR = path.join(USER_AGENTS_DIR, '.trash');
55
+ const TEAMS_DIR = path.join(USER_AGENTS_DIR, 'teams');
56
+ // History bucket (durable).
57
+ const SESSIONS_DIR = path.join(HISTORY_DIR, 'sessions');
58
+ const SESSIONS_DB_PATH = path.join(SESSIONS_DIR, 'sessions.db');
59
+ const VERSIONS_DIR = path.join(HISTORY_DIR, 'versions');
60
+ const RUNS_DIR = path.join(HISTORY_DIR, 'runs');
61
+ const TEAMS_AGENTS_DIR = path.join(HISTORY_DIR, 'teams', 'agents');
62
+ const BACKUPS_DIR = path.join(HISTORY_DIR, 'backups');
63
+ const TRASH_DIR = path.join(HISTORY_DIR, 'trash');
64
+ // Cache bucket (regenerable).
65
+ const SHIMS_DIR = path.join(CACHE_DIR, 'shims');
66
+ const BIN_DIR = path.join(CACHE_DIR, 'bin');
67
+ const PACKAGES_DIR = path.join(CACHE_DIR, 'packages');
68
+ const PLUGINS_DIR = path.join(CACHE_DIR, 'plugins');
69
+ const CLOUD_DIR = path.join(CACHE_DIR, 'cloud');
70
+ const DRIVE_DIR = path.join(CACHE_DIR, 'drive');
71
+ const TERMINALS_DIR = path.join(CACHE_DIR, 'terminals');
72
+ const LOGS_DIR = path.join(CACHE_DIR, 'logs');
73
+ const RUNTIME_STATE_DIR = path.join(CACHE_DIR, 'state');
74
+ const SWARMIFY_DIR = path.join(CACHE_DIR, 'swarmify');
75
+ const BROWSER_RUNTIME_DIR = path.join(CACHE_DIR, 'browser');
76
+ const HELPERS_DIR = path.join(CACHE_DIR, 'helpers');
77
+ const DAEMON_DIR = path.join(HELPERS_DIR, 'daemon');
78
+ const PTY_DIR = path.join(HELPERS_DIR, 'pty');
79
+ const FETCH_CACHE_DIR = path.join(CACHE_DIR, '.fetch');
80
+ const CLI_VERSION_CACHE_FILE = path.join(CACHE_DIR, '.cli-version-cache.json');
81
+ const MODELS_CACHE_FILE = path.join(CACHE_DIR, '.models-cache.json');
82
+ const UPDATE_CHECK_FILE = path.join(CACHE_DIR, '.update-check');
83
+ const MIGRATED_SENTINEL_FILE = path.join(CACHE_DIR, '.migrated');
50
84
  // ─── User resource dirs ───────────────────────────────────────────────────────
51
85
  const USER_COMMANDS_DIR = path.join(USER_AGENTS_DIR, 'commands');
52
86
  const USER_HOOKS_DIR = path.join(USER_AGENTS_DIR, 'hooks');
@@ -55,6 +89,7 @@ const USER_RULES_DIR = path.join(USER_AGENTS_DIR, 'rules');
55
89
  const USER_MCP_DIR = path.join(USER_AGENTS_DIR, 'mcp');
56
90
  const USER_PERMISSIONS_DIR = path.join(USER_AGENTS_DIR, 'permissions');
57
91
  const USER_SUBAGENTS_DIR = path.join(USER_AGENTS_DIR, 'subagents');
92
+ const USER_WORKFLOWS_DIR = path.join(USER_AGENTS_DIR, 'workflows');
58
93
  const USER_SECRETS_DIR = path.join(USER_AGENTS_DIR, 'secrets');
59
94
  const USER_PROMPTCUTS_FILE = path.join(USER_AGENTS_DIR, 'hooks', 'promptcuts.yaml');
60
95
  const META_HEADER = `# agents-cli metadata
@@ -147,8 +182,43 @@ export function getMcpDir() { return SYSTEM_MCP_DIR; }
147
182
  export function getPermissionsDir() { return SYSTEM_PERMISSIONS_DIR; }
148
183
  /** Path to subagent definition directories — system repo. */
149
184
  export function getSubagentsDir() { return SYSTEM_SUBAGENTS_DIR; }
150
- /** Path to ~/.agents-system/promptcuts.yaml. */
185
+ /** Path to ~/.agents-system/hooks/promptcuts.yaml (system defaults). */
151
186
  export function getPromptcutsPath() { return SYSTEM_PROMPTCUTS_FILE; }
187
+ /**
188
+ * Resolve the effective promptcuts file: user file if it exists, otherwise
189
+ * the system file. Use this for callers that need a single path (doctor
190
+ * diff, displaying which file is in play). Callers that need the merged
191
+ * shortcut set should use readMergedPromptcuts() instead.
192
+ */
193
+ export function getEffectivePromptcutsPath() {
194
+ if (fs.existsSync(USER_PROMPTCUTS_FILE))
195
+ return USER_PROMPTCUTS_FILE;
196
+ return SYSTEM_PROMPTCUTS_FILE;
197
+ }
198
+ /**
199
+ * Read promptcuts from system + user with user precedence. Returns the
200
+ * merged `shortcuts` map. Same layering model as parseHookManifest().
201
+ * Returns an empty object when neither file exists or both fail to parse.
202
+ */
203
+ export function readMergedPromptcuts() {
204
+ const merged = {};
205
+ for (const filePath of [SYSTEM_PROMPTCUTS_FILE, USER_PROMPTCUTS_FILE]) {
206
+ if (!fs.existsSync(filePath))
207
+ continue;
208
+ try {
209
+ const parsed = yaml.parse(fs.readFileSync(filePath, 'utf-8'));
210
+ if (!parsed?.shortcuts)
211
+ continue;
212
+ for (const [key, value] of Object.entries(parsed.shortcuts)) {
213
+ merged[key] = value;
214
+ }
215
+ }
216
+ catch {
217
+ // Skip unreadable file, keep going
218
+ }
219
+ }
220
+ return merged;
221
+ }
152
222
  /** Path to the legacy MCP config JSON. */
153
223
  export function getMcpConfigPath() { return SYSTEM_MCP_CONFIG_FILE; }
154
224
  /** Path to the global instructions file. */
@@ -170,29 +240,87 @@ export function getUserRulesDir() { return USER_RULES_DIR; }
170
240
  export function getUserMcpDir() { return USER_MCP_DIR; }
171
241
  export function getUserPermissionsDir() { return USER_PERMISSIONS_DIR; }
172
242
  export function getUserSubagentsDir() { return USER_SUBAGENTS_DIR; }
243
+ export function getSystemWorkflowsDir() { return SYSTEM_WORKFLOWS_DIR; }
244
+ export function getUserWorkflowsDir() { return USER_WORKFLOWS_DIR; }
173
245
  export function getUserSecretsDir() { return USER_SECRETS_DIR; }
174
246
  export function getUserPromptcutsPath() { return USER_PROMPTCUTS_FILE; }
175
247
  // ─── User operational path getters ────────────────────────────────────────────
176
- /** Path to cloned packages (~/.agents/packages/). */
248
+ //
249
+ // Top-level dirs hold definitions/configs only; runtime data lives under
250
+ // .history/ (durable) or .cache/ (regenerable). See file header.
251
+ /** Bucket root for durable runtime data (~/.agents/.history/). */
252
+ export function getHistoryDir() { return HISTORY_DIR; }
253
+ /** Bucket root for regenerable runtime data (~/.agents/.cache/). */
254
+ export function getCacheDir() { return CACHE_DIR; }
255
+ /** Path to cloned packages (~/.agents/.cache/packages/). */
177
256
  export function getPackagesDir() { return PACKAGES_DIR; }
178
257
  /** Path to routine YAML definitions (~/.agents/routines/). */
179
258
  export function getRoutinesDir() { return ROUTINES_DIR; }
180
- /** Path to routine execution logs (~/.agents/runs/). */
259
+ /** Path to routine execution logs (~/.agents/.history/runs/). */
181
260
  export function getRunsDir() { return RUNS_DIR; }
182
- /** Path to installed agent CLI binaries (~/.agents/versions/). */
261
+ /** Path to installed agent CLI binaries (~/.agents/.history/versions/). */
183
262
  export function getVersionsDir() { return VERSIONS_DIR; }
184
- /** Path to version-switching shim scripts (~/.agents/shims/). */
263
+ /** Path to version-switching shim scripts (~/.agents/.cache/shims/). */
185
264
  export function getShimsDir() { return SHIMS_DIR; }
186
- /** Path to config backups (~/.agents/backups/). */
265
+ /** Path to per-agent installed CLI binaries (~/.agents/.cache/bin/). */
266
+ export function getBinDir() { return BIN_DIR; }
267
+ /** Path to config backups (~/.agents/.history/backups/). */
187
268
  export function getBackupsDir() { return BACKUPS_DIR; }
188
- /** Path to plugin bundles (~/.agents/plugins/). */
269
+ /** Path to plugin bundles (~/.agents/.cache/plugins/). */
189
270
  export function getPluginsDir() { return PLUGINS_DIR; }
190
- /** Path to synced remote session data (~/.agents/drive/). */
271
+ /** Path to synced remote session data (~/.agents/.cache/drive/). */
191
272
  export function getDriveDir() { return DRIVE_DIR; }
192
- /** Path to soft-deleted resources (~/.agents/trash/). */
273
+ /** Path to soft-deleted resources (~/.agents/.history/trash/). */
193
274
  export function getTrashDir() { return TRASH_DIR; }
275
+ /** Path to local session indexer storage (~/.agents/.history/sessions/). */
276
+ export function getSessionsDir() { return SESSIONS_DIR; }
277
+ /** Path to the session index database (~/.agents/.history/sessions.db). */
278
+ export function getSessionsDbPath() { return SESSIONS_DB_PATH; }
279
+ /** Path to teams config + registry (~/.agents/teams/). */
280
+ export function getTeamsDir() { return TEAMS_DIR; }
281
+ /** Path to teams execution history (~/.agents/.history/teams/agents/). */
282
+ export function getTeamsAgentsDir() { return TEAMS_AGENTS_DIR; }
283
+ /** Path to cloud dispatch cache (~/.agents/.cache/cloud/). */
284
+ export function getCloudDir() { return CLOUD_DIR; }
285
+ /** Path to terminal session metadata (~/.agents/.cache/terminals/). */
286
+ export function getTerminalsDir() { return TERMINALS_DIR; }
287
+ /** Path to runtime logs (~/.agents/.cache/logs/). */
288
+ export function getLogsDir() { return LOGS_DIR; }
289
+ /** Path to per-process runtime state (~/.agents/.cache/state/). */
290
+ export function getRuntimeStateDir() { return RUNTIME_STATE_DIR; }
291
+ /** Path to swarmify-extension scratch (~/.agents/.cache/swarmify/). */
292
+ export function getSwarmifyDir() { return SWARMIFY_DIR; }
293
+ /** Path to browser runtime data — chrome-data, pids (~/.agents/.cache/browser/). */
294
+ export function getBrowserRuntimeDir() { return BROWSER_RUNTIME_DIR; }
295
+ /** Path to helper subprocess scratch (~/.agents/.cache/helpers/). */
296
+ export function getHelpersDir() { return HELPERS_DIR; }
297
+ /** Path to scheduler daemon scratch (~/.agents/.cache/helpers/daemon/). */
298
+ export function getDaemonDir() { return DAEMON_DIR; }
299
+ /** Path to PTY server scratch (~/.agents/.cache/helpers/pty/). */
300
+ export function getPtyDir() { return PTY_DIR; }
301
+ /** Path to remote-resource auto-pull cache (~/.agents/.cache/.fetch/). */
302
+ export function getFetchCacheDir() { return FETCH_CACHE_DIR; }
303
+ /** Path to the CLI version cache file (~/.agents/.cache/.cli-version-cache.json). */
304
+ export function getCliVersionCachePath() { return CLI_VERSION_CACHE_FILE; }
305
+ /** Path to the models cache file (~/.agents/.cache/.models-cache.json). */
306
+ export function getModelsCachePath() { return MODELS_CACHE_FILE; }
307
+ /** Path to the daily update-check sentinel (~/.agents/.cache/.update-check). */
308
+ export function getUpdateCheckPath() { return UPDATE_CHECK_FILE; }
309
+ /** Path to the migration sentinel (~/.agents/.cache/.migrated). */
310
+ export function getMigratedSentinelPath() { return MIGRATED_SENTINEL_FILE; }
194
311
  /** Path to soft-deleted version dirs (~/.agents/trash/versions/). */
195
312
  export function getTrashVersionsDir() { return path.join(TRASH_DIR, 'versions'); }
313
+ /** Path to soft-deleted skills (~/.agents/trash/skills/). */
314
+ export function getTrashSkillsDir() { return path.join(TRASH_DIR, 'skills'); }
315
+ /** Path to soft-deleted commands (~/.agents/trash/commands/). */
316
+ export function getTrashCommandsDir() { return path.join(TRASH_DIR, 'commands'); }
317
+ /** Path to soft-deleted hooks (~/.agents/trash/hooks/). */
318
+ export function getTrashHooksDir() { return path.join(TRASH_DIR, 'hooks'); }
319
+ /** Path to soft-deleted plugins (~/.agents/trash/plugins/). */
320
+ export function getTrashPluginsDir() { return path.join(TRASH_DIR, 'plugins'); }
321
+ /** Path to soft-deleted subagents (~/.agents/trash/subagents/). */
322
+ export function getTrashSubagentsDir() { return path.join(TRASH_DIR, 'subagents'); }
323
+ export function getTrashWorkflowsDir() { return path.join(TRASH_DIR, 'workflows'); }
196
324
  /**
197
325
  * Path to a single user-level extra DotAgent repo clone (~/.agents-<alias>/).
198
326
  *
@@ -242,6 +370,10 @@ export function ensureAgentsDir() {
242
370
  if (!fs.existsSync(SYSTEM_AGENTS_DIR)) {
243
371
  fs.mkdirSync(SYSTEM_AGENTS_DIR, opts);
244
372
  }
373
+ if (!fs.existsSync(HISTORY_DIR))
374
+ fs.mkdirSync(HISTORY_DIR, opts);
375
+ if (!fs.existsSync(CACHE_DIR))
376
+ fs.mkdirSync(CACHE_DIR, opts);
245
377
  if (!fs.existsSync(PACKAGES_DIR))
246
378
  fs.mkdirSync(PACKAGES_DIR, opts);
247
379
  if (!fs.existsSync(ROUTINES_DIR))
@@ -425,9 +557,19 @@ export function getPackageLocalPath(source) {
425
557
  .replace(/\//g, '-');
426
558
  return path.join(PACKAGES_DIR, sanitized);
427
559
  }
428
- export function recordVersionResources(agent, version, resourceType, resources) {
429
- if (resources.length === 0)
430
- return;
560
+ /**
561
+ * @deprecated No-op. Use ensureVersionResourcePatterns instead.
562
+ * Kept for backward compat with command files that still call it.
563
+ */
564
+ export function recordVersionResources(_agent, _version, _resourceType, _resources) {
565
+ // intentional no-op — tracking moved to pattern-based ensureVersionResourcePatterns
566
+ }
567
+ /**
568
+ * Write default resource selection patterns for an agent@version.
569
+ * Only writes each field when it is not already set, preserving user customization.
570
+ * Pass all resource types you want to initialize in one call to batch the write.
571
+ */
572
+ export function ensureVersionResourcePatterns(agent, version, updates) {
431
573
  const meta = readMeta();
432
574
  if (!meta.versions)
433
575
  meta.versions = {};
@@ -435,10 +577,16 @@ export function recordVersionResources(agent, version, resourceType, resources)
435
577
  meta.versions[agent] = {};
436
578
  if (!meta.versions[agent][version])
437
579
  meta.versions[agent][version] = {};
438
- const existing = meta.versions[agent][version][resourceType] || [];
439
- const merged = [...new Set([...existing, ...resources])];
440
- meta.versions[agent][version][resourceType] = merged;
441
- writeMeta(meta);
580
+ const vr = meta.versions[agent][version];
581
+ let changed = false;
582
+ for (const [type, patterns] of Object.entries(updates)) {
583
+ if (!vr[type] || vr[type].length === 0) {
584
+ vr[type] = patterns;
585
+ changed = true;
586
+ }
587
+ }
588
+ if (changed)
589
+ writeMeta(meta);
442
590
  }
443
591
  export function getVersionResources(agent, version) {
444
592
  const meta = readMeta();