@phnx-labs/agents-cli 1.14.2 → 1.14.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 (101) hide show
  1. package/README.md +17 -7
  2. package/dist/commands/browser.d.ts +2 -0
  3. package/dist/commands/browser.js +388 -0
  4. package/dist/commands/daemon.js +1 -1
  5. package/dist/commands/doctor.d.ts +16 -9
  6. package/dist/commands/doctor.js +248 -12
  7. package/dist/commands/prune.js +9 -3
  8. package/dist/commands/refresh-rules.d.ts +15 -0
  9. package/dist/commands/{refresh-memory.js → refresh-rules.js} +14 -14
  10. package/dist/commands/routines.js +1 -1
  11. package/dist/commands/rules.js +100 -4
  12. package/dist/commands/secrets.js +198 -11
  13. package/dist/commands/sync.js +19 -0
  14. package/dist/commands/teams.js +162 -22
  15. package/dist/commands/trash.d.ts +10 -0
  16. package/dist/commands/trash.js +187 -0
  17. package/dist/commands/view.js +46 -13
  18. package/dist/index.js +62 -4
  19. package/dist/lib/agents.js +2 -2
  20. package/dist/lib/browser/cdp.d.ts +24 -0
  21. package/dist/lib/browser/cdp.js +94 -0
  22. package/dist/lib/browser/chrome.d.ts +16 -0
  23. package/dist/lib/browser/chrome.js +157 -0
  24. package/dist/lib/browser/drivers/local.d.ts +8 -0
  25. package/dist/lib/browser/drivers/local.js +22 -0
  26. package/dist/lib/browser/drivers/ssh.d.ts +9 -0
  27. package/dist/lib/browser/drivers/ssh.js +129 -0
  28. package/dist/lib/browser/index.d.ts +5 -0
  29. package/dist/lib/browser/index.js +5 -0
  30. package/dist/lib/browser/input.d.ts +6 -0
  31. package/dist/lib/browser/input.js +52 -0
  32. package/dist/lib/browser/ipc.d.ts +12 -0
  33. package/dist/lib/browser/ipc.js +223 -0
  34. package/dist/lib/browser/profiles.d.ts +11 -0
  35. package/dist/lib/browser/profiles.js +61 -0
  36. package/dist/lib/browser/refs.d.ts +21 -0
  37. package/dist/lib/browser/refs.js +88 -0
  38. package/dist/lib/browser/service.d.ts +45 -0
  39. package/dist/lib/browser/service.js +404 -0
  40. package/dist/lib/browser/types.d.ts +73 -0
  41. package/dist/lib/browser/types.js +7 -0
  42. package/dist/lib/cloud/codex.js +1 -1
  43. package/dist/lib/cloud/registry.js +2 -2
  44. package/dist/lib/cloud/rush.js +2 -2
  45. package/dist/lib/cloud/store.js +2 -2
  46. package/dist/lib/daemon.d.ts +1 -1
  47. package/dist/lib/daemon.js +47 -11
  48. package/dist/lib/diff-text.d.ts +25 -0
  49. package/dist/lib/diff-text.js +47 -0
  50. package/dist/lib/doctor-diff.d.ts +64 -0
  51. package/dist/lib/doctor-diff.js +497 -0
  52. package/dist/lib/git.js +3 -3
  53. package/dist/lib/hooks.d.ts +6 -0
  54. package/dist/lib/hooks.js +6 -1
  55. package/dist/lib/migrate.js +77 -0
  56. package/dist/lib/pty-client.js +3 -3
  57. package/dist/lib/pty-server.js +36 -7
  58. package/dist/lib/resources.js +1 -1
  59. package/dist/lib/rotate.d.ts +8 -1
  60. package/dist/lib/rotate.js +17 -4
  61. package/dist/lib/rules/compile.d.ts +104 -0
  62. package/dist/lib/{memory-compile.js → rules/compile.js} +160 -21
  63. package/dist/lib/rules/compose.d.ts +78 -0
  64. package/dist/lib/rules/compose.js +170 -0
  65. package/dist/lib/{memory.d.ts → rules/rules.d.ts} +5 -5
  66. package/dist/lib/{memory.js → rules/rules.js} +10 -10
  67. package/dist/lib/secrets/AgentsKeychain.app/Contents/CodeResources +0 -0
  68. package/dist/lib/secrets/AgentsKeychain.app/Contents/MacOS/AgentsKeychain +0 -0
  69. package/dist/lib/secrets/bundles.d.ts +61 -4
  70. package/dist/lib/secrets/bundles.js +222 -54
  71. package/dist/lib/secrets/index.d.ts +24 -5
  72. package/dist/lib/secrets/index.js +70 -41
  73. package/dist/lib/session/active.js +5 -5
  74. package/dist/lib/session/db.js +4 -4
  75. package/dist/lib/session/discover.js +2 -2
  76. package/dist/lib/session/render.js +21 -7
  77. package/dist/lib/shims.d.ts +28 -4
  78. package/dist/lib/shims.js +72 -14
  79. package/dist/lib/state.d.ts +22 -28
  80. package/dist/lib/state.js +83 -76
  81. package/dist/lib/sync-manifest.d.ts +2 -2
  82. package/dist/lib/sync-manifest.js +5 -5
  83. package/dist/lib/teams/agents.d.ts +4 -2
  84. package/dist/lib/teams/agents.js +11 -4
  85. package/dist/lib/teams/api.d.ts +1 -1
  86. package/dist/lib/teams/api.js +2 -2
  87. package/dist/lib/teams/index.d.ts +1 -0
  88. package/dist/lib/teams/index.js +1 -0
  89. package/dist/lib/teams/persistence.js +3 -3
  90. package/dist/lib/teams/registry.d.ts +8 -1
  91. package/dist/lib/teams/registry.js +8 -2
  92. package/dist/lib/teams/worktree.d.ts +30 -0
  93. package/dist/lib/teams/worktree.js +96 -0
  94. package/dist/lib/types.d.ts +12 -6
  95. package/dist/lib/types.js +3 -3
  96. package/dist/lib/versions.d.ts +30 -2
  97. package/dist/lib/versions.js +127 -105
  98. package/package.json +1 -1
  99. package/scripts/postinstall.js +29 -0
  100. package/dist/commands/refresh-memory.d.ts +0 -15
  101. package/dist/lib/memory-compile.d.ts +0 -66
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Rules composition — assemble a fully-inlined rules document from layered
3
+ * `subrules/` fragments and `rules.yaml` preset definitions.
4
+ *
5
+ * The model:
6
+ * - Every DotAgents repo holds `<repo>/rules/subrules/*.md` (rule fragments)
7
+ * and `<repo>/rules/rules.yaml` (preset definitions).
8
+ * - Layers are read in precedence order (highest first):
9
+ * project > user > extra > system.
10
+ * - The active preset's `subrules:` list is resolved against the layer set
11
+ * using per-name shadowing — a project subrule shadows a user/system one
12
+ * of the same name.
13
+ * - Subrules in the user / extra / project layers that the preset did NOT
14
+ * name are auto-appended in precedence order. (System auto-append is
15
+ * opt-in only: system never auto-appends to avoid noise.)
16
+ * - Output is a single concatenated string with no `@-import` syntax.
17
+ *
18
+ * No filesystem writes happen here — callers (`syncResourcesToVersion`,
19
+ * project-rules compile) decide where to land the composed output.
20
+ */
21
+ export type LayerScope = 'project' | 'user' | 'extra' | 'system';
22
+ export interface RulesLayer {
23
+ scope: LayerScope;
24
+ rulesDir: string;
25
+ /** Set when scope is 'extra'; undefined otherwise. */
26
+ alias?: string;
27
+ }
28
+ export interface PresetDef {
29
+ /** Subrule names (without `.md`), in concatenation order. */
30
+ subrules: string[];
31
+ }
32
+ export interface RulesYaml {
33
+ presets?: Record<string, PresetDef>;
34
+ }
35
+ export interface ComposeOptions {
36
+ /** Defaults to `"default"`. */
37
+ preset?: string;
38
+ /** Layers in precedence order, highest first. */
39
+ layers: RulesLayer[];
40
+ }
41
+ export interface ComposedSubrule {
42
+ name: string;
43
+ sourcePath: string;
44
+ layerScope: LayerScope;
45
+ layerAlias?: string;
46
+ }
47
+ export interface ComposeResult {
48
+ /** Fully concatenated, no @-imports. */
49
+ content: string;
50
+ /** The preset name that was applied. */
51
+ preset: string;
52
+ /** The layer that defined the preset. */
53
+ presetLayer: LayerScope;
54
+ /** Subrules included, in concatenation order. */
55
+ subrules: ComposedSubrule[];
56
+ }
57
+ /**
58
+ * Compose a rules document from the given layers.
59
+ *
60
+ * Throws when the requested preset isn't defined in any layer's rules.yaml —
61
+ * means the caller passed a typo or no layer ships the named preset.
62
+ */
63
+ export declare function composeRules(opts: ComposeOptions): ComposeResult;
64
+ /**
65
+ * Discover layers for use at sync time (no cwd) or runtime (with cwd).
66
+ *
67
+ * Project layer is included only when cwd is given AND `<cwd>/.agents/rules/`
68
+ * exists. Without cwd, only user / extras / system are surfaced — matching
69
+ * the home-file write at sync time.
70
+ */
71
+ export declare function discoverRulesLayers(opts?: {
72
+ cwd?: string;
73
+ }): RulesLayer[];
74
+ /** Convenience wrapper — discovers layers from state, then composes. */
75
+ export declare function composeRulesFromState(opts?: {
76
+ preset?: string;
77
+ cwd?: string;
78
+ }): ComposeResult;
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Rules composition — assemble a fully-inlined rules document from layered
3
+ * `subrules/` fragments and `rules.yaml` preset definitions.
4
+ *
5
+ * The model:
6
+ * - Every DotAgents repo holds `<repo>/rules/subrules/*.md` (rule fragments)
7
+ * and `<repo>/rules/rules.yaml` (preset definitions).
8
+ * - Layers are read in precedence order (highest first):
9
+ * project > user > extra > system.
10
+ * - The active preset's `subrules:` list is resolved against the layer set
11
+ * using per-name shadowing — a project subrule shadows a user/system one
12
+ * of the same name.
13
+ * - Subrules in the user / extra / project layers that the preset did NOT
14
+ * name are auto-appended in precedence order. (System auto-append is
15
+ * opt-in only: system never auto-appends to avoid noise.)
16
+ * - Output is a single concatenated string with no `@-import` syntax.
17
+ *
18
+ * No filesystem writes happen here — callers (`syncResourcesToVersion`,
19
+ * project-rules compile) decide where to land the composed output.
20
+ */
21
+ import * as fs from 'fs';
22
+ import * as path from 'path';
23
+ import * as yaml from 'yaml';
24
+ import { getResolvedRulesDir, getUserRulesDir, getProjectAgentsDir, getEnabledExtraRepos, } from '../state.js';
25
+ const SUBRULES_DIR_NAME = 'subrules';
26
+ const RULES_YAML_NAME = 'rules.yaml';
27
+ const DEFAULT_PRESET = 'default';
28
+ const SUBRULES_README = 'README.md';
29
+ function readRulesYaml(rulesDir) {
30
+ const p = path.join(rulesDir, RULES_YAML_NAME);
31
+ if (!fs.existsSync(p))
32
+ return null;
33
+ try {
34
+ const parsed = yaml.parse(fs.readFileSync(p, 'utf-8'));
35
+ return parsed || {};
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ function resolvePreset(layers, preset) {
42
+ for (const layer of layers) {
43
+ const yml = readRulesYaml(layer.rulesDir);
44
+ if (!yml?.presets)
45
+ continue;
46
+ const def = yml.presets[preset];
47
+ if (def)
48
+ return { def, layer };
49
+ }
50
+ return null;
51
+ }
52
+ function findSubrule(layers, name) {
53
+ for (const layer of layers) {
54
+ const p = path.join(layer.rulesDir, SUBRULES_DIR_NAME, `${name}.md`);
55
+ if (fs.existsSync(p))
56
+ return { sourcePath: p, layer };
57
+ }
58
+ return null;
59
+ }
60
+ function listLayerSubruleNames(layer) {
61
+ const dir = path.join(layer.rulesDir, SUBRULES_DIR_NAME);
62
+ if (!fs.existsSync(dir))
63
+ return [];
64
+ try {
65
+ return fs
66
+ .readdirSync(dir)
67
+ .filter((f) => f.endsWith('.md') && f !== SUBRULES_README)
68
+ .map((f) => f.slice(0, -3))
69
+ .sort();
70
+ }
71
+ catch {
72
+ return [];
73
+ }
74
+ }
75
+ /**
76
+ * Compose a rules document from the given layers.
77
+ *
78
+ * Throws when the requested preset isn't defined in any layer's rules.yaml —
79
+ * means the caller passed a typo or no layer ships the named preset.
80
+ */
81
+ export function composeRules(opts) {
82
+ const presetName = opts.preset || DEFAULT_PRESET;
83
+ const presetMatch = resolvePreset(opts.layers, presetName);
84
+ if (!presetMatch) {
85
+ throw new Error(`Preset "${presetName}" not found in any rules.yaml across the active layers.`);
86
+ }
87
+ const composed = [];
88
+ const seen = new Set();
89
+ // 1. Preset's named subrules, resolved by per-name shadowing.
90
+ for (const name of presetMatch.def.subrules || []) {
91
+ if (seen.has(name))
92
+ continue;
93
+ const found = findSubrule(opts.layers, name);
94
+ if (!found)
95
+ continue; // missing subrule: skip silently — same as @-import miss
96
+ composed.push({
97
+ name,
98
+ sourcePath: found.sourcePath,
99
+ layerScope: found.layer.scope,
100
+ layerAlias: found.layer.alias,
101
+ });
102
+ seen.add(name);
103
+ }
104
+ // 2. Auto-append: any subrule in a non-system layer not yet included.
105
+ // Honors precedence — project layer's auto-appends come first.
106
+ for (const layer of opts.layers) {
107
+ if (layer.scope === 'system')
108
+ continue;
109
+ for (const name of listLayerSubruleNames(layer)) {
110
+ if (seen.has(name))
111
+ continue;
112
+ composed.push({
113
+ name,
114
+ sourcePath: path.join(layer.rulesDir, SUBRULES_DIR_NAME, `${name}.md`),
115
+ layerScope: layer.scope,
116
+ layerAlias: layer.alias,
117
+ });
118
+ seen.add(name);
119
+ }
120
+ }
121
+ // 3. Concatenate. Trim trailing whitespace on each fragment so spacing is
122
+ // predictable — fragments often end in a newline already.
123
+ const parts = composed.map((c) => fs.readFileSync(c.sourcePath, 'utf-8').replace(/\s+$/, ''));
124
+ const content = parts.length === 0 ? '' : parts.join('\n\n') + '\n';
125
+ return {
126
+ content,
127
+ preset: presetName,
128
+ presetLayer: presetMatch.layer.scope,
129
+ subrules: composed,
130
+ };
131
+ }
132
+ /**
133
+ * Discover layers for use at sync time (no cwd) or runtime (with cwd).
134
+ *
135
+ * Project layer is included only when cwd is given AND `<cwd>/.agents/rules/`
136
+ * exists. Without cwd, only user / extras / system are surfaced — matching
137
+ * the home-file write at sync time.
138
+ */
139
+ export function discoverRulesLayers(opts = {}) {
140
+ const layers = [];
141
+ if (opts.cwd) {
142
+ const projectAgentsDir = getProjectAgentsDir(opts.cwd);
143
+ if (projectAgentsDir) {
144
+ const rulesDir = path.join(projectAgentsDir, 'rules');
145
+ if (fs.existsSync(rulesDir)) {
146
+ layers.push({ scope: 'project', rulesDir });
147
+ }
148
+ }
149
+ }
150
+ const userRulesDir = getUserRulesDir();
151
+ if (fs.existsSync(userRulesDir)) {
152
+ layers.push({ scope: 'user', rulesDir: userRulesDir });
153
+ }
154
+ for (const extra of getEnabledExtraRepos()) {
155
+ const rulesDir = path.join(extra.dir, 'rules');
156
+ if (fs.existsSync(rulesDir)) {
157
+ layers.push({ scope: 'extra', rulesDir, alias: extra.alias });
158
+ }
159
+ }
160
+ const systemRulesDir = getResolvedRulesDir();
161
+ if (fs.existsSync(systemRulesDir)) {
162
+ layers.push({ scope: 'system', rulesDir: systemRulesDir });
163
+ }
164
+ return layers;
165
+ }
166
+ /** Convenience wrapper — discovers layers from state, then composes. */
167
+ export function composeRulesFromState(opts = {}) {
168
+ const layers = discoverRulesLayers({ cwd: opts.cwd });
169
+ return composeRules({ preset: opts.preset, layers });
170
+ }
@@ -6,7 +6,7 @@
6
6
  * GEMINI.md, etc.). This module handles reading, managing includes, and
7
7
  * refreshing rules files across version homes.
8
8
  */
9
- import type { AgentId } from './types.js';
9
+ import type { AgentId } from '../types.js';
10
10
  export type InstructionsScope = 'user' | 'project';
11
11
  export interface InstalledInstructions {
12
12
  agentId: AgentId;
@@ -23,7 +23,7 @@ export interface DiscoveredInstructions {
23
23
  * Central rules filename constant.
24
24
  * All agents map to this file in ~/.agents/rules/, renamed per-agent when synced.
25
25
  */
26
- export declare const CENTRAL_MEMORY_FILENAME = "AGENTS.md";
26
+ export declare const CENTRAL_RULES_FILENAME = "AGENTS.md";
27
27
  /**
28
28
  * Get the canonical central rules filename for an agent's instructionsFile.
29
29
  * Central storage uses AGENTS.md, which gets renamed per-agent when syncing:
@@ -32,12 +32,12 @@ export declare const CENTRAL_MEMORY_FILENAME = "AGENTS.md";
32
32
  * - Cursor: AGENTS.md → .cursorrules
33
33
  * - Codex/OpenCode: AGENTS.md → AGENTS.md (no rename)
34
34
  */
35
- export declare function getCentralMemoryFileName(agentId: AgentId): string;
35
+ export declare function getCentralRulesFileName(agentId: AgentId): string;
36
36
  export declare function getInstructionsPath(agentId: AgentId, scope: InstructionsScope, cwd?: string): string;
37
37
  export declare function instructionsExists(agentId: AgentId, scope?: InstructionsScope, cwd?: string): boolean;
38
38
  export declare function discoverInstructionsFromRepo(repoPath: string): DiscoveredInstructions[];
39
39
  export declare function resolveInstructionsSource(repoPath: string, agentId: AgentId): string | null;
40
- export declare function discoverMemoryFilesFromRepo(repoPath: string): string[];
40
+ export declare function discoverRuleFilesFromRepo(repoPath: string): string[];
41
41
  export declare function installInstructions(sourcePath: string, agentId: AgentId, method?: 'symlink' | 'copy'): {
42
42
  path: string;
43
43
  method: 'symlink' | 'copy';
@@ -60,4 +60,4 @@ export declare function installInstructionsCentrally(repoPath: string, filesToIn
60
60
  /**
61
61
  * List top-level rules files from user and system dirs (user wins on collision).
62
62
  */
63
- export declare function listCentralMemory(): string[];
63
+ export declare function listCentralRules(): string[];
@@ -8,14 +8,14 @@
8
8
  */
9
9
  import * as fs from 'fs';
10
10
  import * as path from 'path';
11
- import { AGENTS, ALL_AGENT_IDS } from './agents.js';
12
- import { getResolvedRulesDir, getUserRulesDir, getProjectAgentsDir } from './state.js';
13
- import { getEffectiveHome } from './versions.js';
11
+ import { AGENTS, ALL_AGENT_IDS } from '../agents.js';
12
+ import { getResolvedRulesDir, getUserRulesDir, getProjectAgentsDir } from '../state.js';
13
+ import { getEffectiveHome } from '../versions.js';
14
14
  /**
15
15
  * Central rules filename constant.
16
16
  * All agents map to this file in ~/.agents/rules/, renamed per-agent when synced.
17
17
  */
18
- export const CENTRAL_MEMORY_FILENAME = 'AGENTS.md';
18
+ export const CENTRAL_RULES_FILENAME = 'AGENTS.md';
19
19
  const RULES_DOC_FILENAME = 'README.md';
20
20
  function isSyncableRuleMarkdown(filename) {
21
21
  return filename.endsWith('.md') && filename !== RULES_DOC_FILENAME;
@@ -46,14 +46,14 @@ function listRuleMarkdownFiles(rulesDir) {
46
46
  * - Cursor: AGENTS.md → .cursorrules
47
47
  * - Codex/OpenCode: AGENTS.md → AGENTS.md (no rename)
48
48
  */
49
- export function getCentralMemoryFileName(agentId) {
49
+ export function getCentralRulesFileName(agentId) {
50
50
  const agent = AGENTS[agentId];
51
51
  const instrFile = agent.instructionsFile;
52
52
  // If it contains a path separator, extract just the filename
53
53
  const filename = instrFile.includes('/') ? path.basename(instrFile) : instrFile;
54
54
  // If the agent's instructionsFile isn't AGENTS.md, it was renamed FROM AGENTS.md
55
- if (filename !== CENTRAL_MEMORY_FILENAME) {
56
- return CENTRAL_MEMORY_FILENAME;
55
+ if (filename !== CENTRAL_RULES_FILENAME) {
56
+ return CENTRAL_RULES_FILENAME;
57
57
  }
58
58
  return filename;
59
59
  }
@@ -75,7 +75,7 @@ export function getInstructionsPath(agentId, scope, cwd = process.cwd()) {
75
75
  const projectAgentsDir = getProjectAgentsDir(cwd);
76
76
  if (projectAgentsDir) {
77
77
  const projectRulesDir = path.join(projectAgentsDir, 'rules');
78
- const centralName = getCentralMemoryFileName(agentId);
78
+ const centralName = getCentralRulesFileName(agentId);
79
79
  const candidates = [
80
80
  path.join(projectRulesDir, centralName),
81
81
  path.join(projectRulesDir, agent.instructionsFile),
@@ -142,7 +142,7 @@ export function resolveInstructionsSource(repoPath, agentId) {
142
142
  }
143
143
  return null;
144
144
  }
145
- export function discoverMemoryFilesFromRepo(repoPath) {
145
+ export function discoverRuleFilesFromRepo(repoPath) {
146
146
  const rulesDir = path.join(repoPath, 'rules');
147
147
  if (!fs.existsSync(rulesDir)) {
148
148
  return [];
@@ -283,7 +283,7 @@ export function installInstructionsCentrally(repoPath, filesToInstall) {
283
283
  /**
284
284
  * List top-level rules files from user and system dirs (user wins on collision).
285
285
  */
286
- export function listCentralMemory() {
286
+ export function listCentralRules() {
287
287
  const seen = new Set();
288
288
  for (const dir of [getUserRulesDir(), getResolvedRulesDir()]) {
289
289
  if (!fs.existsSync(dir))
@@ -1,23 +1,53 @@
1
1
  /**
2
2
  * Secret bundles -- named sets of keychain-backed environment variables.
3
3
  *
4
- * Each bundle is a YAML file in ~/.agents/secrets/ declaring key names.
5
- * Values live in the macOS Keychain and are injected into the agent's
6
- * environment at spawn time via `agents run --secrets <bundle>`.
4
+ * Bundle metadata (name, description, vars map) is stored in the macOS
5
+ * Keychain as a JSON blob under `agents-cli.bundles.<name>`. Bundles created
6
+ * with `--icloud-sync` write the metadata to the iCloud-synced keychain so
7
+ * the full bundle definition (not just secret values) propagates across
8
+ * the user's Macs. Nothing about secrets ever lives in plaintext on disk.
9
+ *
10
+ * Secret values keep their old layout: one keychain item per key under
11
+ * `agents-cli.secrets.<bundle>.<key>`, sync-state matching the bundle's
12
+ * `icloud_sync` flag.
7
13
  */
8
14
  import { type BundleValue, type SecretRef } from './index.js';
15
+ /** Allowed values for a secret's `type` metadata field. */
16
+ export declare const SECRET_TYPES: readonly ["api-key", "token", "password", "url", "database-url", "ssh-key", "certificate", "webhook", "note"];
17
+ export type SecretType = typeof SECRET_TYPES[number];
18
+ /** Per-secret metadata. All fields optional; absent ones omitted at write time. */
19
+ export interface VarMeta {
20
+ type?: SecretType;
21
+ /** ISO date 'YYYY-MM-DD'. Always future-dated at write time. */
22
+ expires?: string;
23
+ /** Singular freeform note. */
24
+ note?: string;
25
+ }
9
26
  /** A named set of environment variable definitions backed by various secret providers. */
10
27
  export interface SecretsBundle {
11
28
  name: string;
12
29
  description?: string;
13
30
  allow_exec?: boolean;
14
- /** When true, keychain-backed values are stored in iCloud Keychain so they sync across the user's Macs. */
31
+ /** When true, keychain-backed values and bundle metadata sync via iCloud Keychain. */
15
32
  icloud_sync?: boolean;
16
33
  vars: Record<string, BundleValue>;
34
+ /** Optional per-var metadata, keyed by var name (parallel to `vars`). */
35
+ meta?: Record<string, VarMeta>;
17
36
  }
37
+ export declare const RESERVED_ENV_NAMES: Set<string>;
38
+ export declare function bundleToEnvPrefix(name: string): string;
39
+ export declare function isReservedEnvName(key: string): boolean;
18
40
  /** Validate a bundle name against the allowed pattern. Throws on invalid input. */
19
41
  export declare function validateBundleName(name: string): void;
20
42
  export declare function validateEnvKey(key: string): void;
43
+ /** Assert that `t` is one of the known SECRET_TYPES. Throws with the allowed list otherwise. */
44
+ export declare function validateSecretType(t: string): asserts t is SecretType;
45
+ /**
46
+ * Validate an `expires` value. Accepts strict 'YYYY-MM-DD' only and rejects
47
+ * any date <= now. We compare against end-of-day UTC for the chosen date so
48
+ * "today" is treated as past (per spec).
49
+ */
50
+ export declare function validateExpiresFutureDated(iso: string): void;
21
51
  export declare function bundleExists(name: string): boolean;
22
52
  export declare function readBundle(name: string): SecretsBundle;
23
53
  export declare function writeBundle(bundle: SecretsBundle): void;
@@ -31,9 +61,36 @@ export interface BundleEntryInfo {
31
61
  export declare function describeBundle(bundle: SecretsBundle): BundleEntryInfo[];
32
62
  export declare function resolveBundleEnv(bundle: SecretsBundle): Record<string, string>;
33
63
  export declare function keychainRef(key: string): string;
64
+ /** Options for rotateBundleSecret. */
65
+ export interface RotateOptions {
66
+ /** New plaintext value to write into keychain (replaces the old one). */
67
+ newValue: string;
68
+ /** When true, drop existing meta for this key. Mutually exclusive with `meta`. */
69
+ clearMeta?: boolean;
70
+ /** Patch to merge into existing meta. Undefined fields preserve current values. */
71
+ meta?: Partial<VarMeta>;
72
+ }
73
+ /**
74
+ * Rotate a keychain-backed secret in `bundle`. Errors if `key` is not present
75
+ * in the bundle (use `add` to introduce a new key). Preserves existing meta
76
+ * unless `clearMeta` or a `meta` patch is supplied.
77
+ */
78
+ export declare function rotateBundleSecret(bundle: SecretsBundle, key: string, opts: RotateOptions): void;
34
79
  export declare function keychainItemsForBundle(bundle: SecretsBundle): Array<{
35
80
  key: string;
36
81
  item: string;
37
82
  }>;
38
83
  export declare function parseDotenv(content: string): Record<string, string>;
84
+ /**
85
+ * One-shot migration: move legacy YAML bundles into the keychain. Scans both
86
+ * `~/.agents/secrets/` and `~/.agents-system/secrets/` — past versions of the
87
+ * CLI sometimes wrote bundles into the system repo even though that's never
88
+ * been a legitimate location. After migration the directories are removed so
89
+ * the system repo never carries a `secrets/` subdir again.
90
+ *
91
+ * Idempotent: re-runs after the dirs are gone are no-ops. Called eagerly at
92
+ * the top of every `agents secrets` subcommand. Skipped on the latency-
93
+ * sensitive `agents run` path.
94
+ */
95
+ export declare function migrateLegacyBundles(): void;
39
96
  export type { SecretRef };