@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.
- package/README.md +17 -7
- package/dist/commands/browser.d.ts +2 -0
- package/dist/commands/browser.js +388 -0
- package/dist/commands/daemon.js +1 -1
- package/dist/commands/doctor.d.ts +16 -9
- package/dist/commands/doctor.js +248 -12
- package/dist/commands/prune.js +9 -3
- package/dist/commands/refresh-rules.d.ts +15 -0
- package/dist/commands/{refresh-memory.js → refresh-rules.js} +14 -14
- package/dist/commands/routines.js +1 -1
- package/dist/commands/rules.js +100 -4
- package/dist/commands/secrets.js +198 -11
- package/dist/commands/sync.js +19 -0
- package/dist/commands/teams.js +162 -22
- package/dist/commands/trash.d.ts +10 -0
- package/dist/commands/trash.js +187 -0
- package/dist/commands/view.js +46 -13
- package/dist/index.js +62 -4
- package/dist/lib/agents.js +2 -2
- package/dist/lib/browser/cdp.d.ts +24 -0
- package/dist/lib/browser/cdp.js +94 -0
- package/dist/lib/browser/chrome.d.ts +16 -0
- package/dist/lib/browser/chrome.js +157 -0
- package/dist/lib/browser/drivers/local.d.ts +8 -0
- package/dist/lib/browser/drivers/local.js +22 -0
- package/dist/lib/browser/drivers/ssh.d.ts +9 -0
- package/dist/lib/browser/drivers/ssh.js +129 -0
- package/dist/lib/browser/index.d.ts +5 -0
- package/dist/lib/browser/index.js +5 -0
- package/dist/lib/browser/input.d.ts +6 -0
- package/dist/lib/browser/input.js +52 -0
- package/dist/lib/browser/ipc.d.ts +12 -0
- package/dist/lib/browser/ipc.js +223 -0
- package/dist/lib/browser/profiles.d.ts +11 -0
- package/dist/lib/browser/profiles.js +61 -0
- package/dist/lib/browser/refs.d.ts +21 -0
- package/dist/lib/browser/refs.js +88 -0
- package/dist/lib/browser/service.d.ts +45 -0
- package/dist/lib/browser/service.js +404 -0
- package/dist/lib/browser/types.d.ts +73 -0
- package/dist/lib/browser/types.js +7 -0
- package/dist/lib/cloud/codex.js +1 -1
- package/dist/lib/cloud/registry.js +2 -2
- package/dist/lib/cloud/rush.js +2 -2
- package/dist/lib/cloud/store.js +2 -2
- package/dist/lib/daemon.d.ts +1 -1
- package/dist/lib/daemon.js +47 -11
- package/dist/lib/diff-text.d.ts +25 -0
- package/dist/lib/diff-text.js +47 -0
- package/dist/lib/doctor-diff.d.ts +64 -0
- package/dist/lib/doctor-diff.js +497 -0
- package/dist/lib/git.js +3 -3
- package/dist/lib/hooks.d.ts +6 -0
- package/dist/lib/hooks.js +6 -1
- package/dist/lib/migrate.js +77 -0
- package/dist/lib/pty-client.js +3 -3
- package/dist/lib/pty-server.js +36 -7
- package/dist/lib/resources.js +1 -1
- package/dist/lib/rotate.d.ts +8 -1
- package/dist/lib/rotate.js +17 -4
- package/dist/lib/rules/compile.d.ts +104 -0
- package/dist/lib/{memory-compile.js → rules/compile.js} +160 -21
- package/dist/lib/rules/compose.d.ts +78 -0
- package/dist/lib/rules/compose.js +170 -0
- package/dist/lib/{memory.d.ts → rules/rules.d.ts} +5 -5
- package/dist/lib/{memory.js → rules/rules.js} +10 -10
- package/dist/lib/secrets/AgentsKeychain.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/AgentsKeychain.app/Contents/MacOS/AgentsKeychain +0 -0
- package/dist/lib/secrets/bundles.d.ts +61 -4
- package/dist/lib/secrets/bundles.js +222 -54
- package/dist/lib/secrets/index.d.ts +24 -5
- package/dist/lib/secrets/index.js +70 -41
- package/dist/lib/session/active.js +5 -5
- package/dist/lib/session/db.js +4 -4
- package/dist/lib/session/discover.js +2 -2
- package/dist/lib/session/render.js +21 -7
- package/dist/lib/shims.d.ts +28 -4
- package/dist/lib/shims.js +72 -14
- package/dist/lib/state.d.ts +22 -28
- package/dist/lib/state.js +83 -76
- package/dist/lib/sync-manifest.d.ts +2 -2
- package/dist/lib/sync-manifest.js +5 -5
- package/dist/lib/teams/agents.d.ts +4 -2
- package/dist/lib/teams/agents.js +11 -4
- package/dist/lib/teams/api.d.ts +1 -1
- package/dist/lib/teams/api.js +2 -2
- package/dist/lib/teams/index.d.ts +1 -0
- package/dist/lib/teams/index.js +1 -0
- package/dist/lib/teams/persistence.js +3 -3
- package/dist/lib/teams/registry.d.ts +8 -1
- package/dist/lib/teams/registry.js +8 -2
- package/dist/lib/teams/worktree.d.ts +30 -0
- package/dist/lib/teams/worktree.js +96 -0
- package/dist/lib/types.d.ts +12 -6
- package/dist/lib/types.js +3 -3
- package/dist/lib/versions.d.ts +30 -2
- package/dist/lib/versions.js +127 -105
- package/package.json +1 -1
- package/scripts/postinstall.js +29 -0
- package/dist/commands/refresh-memory.d.ts +0 -15
- 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 '
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 '
|
|
12
|
-
import { getResolvedRulesDir, getUserRulesDir, getProjectAgentsDir } from '
|
|
13
|
-
import { getEffectiveHome } from '
|
|
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
|
|
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
|
|
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 !==
|
|
56
|
-
return
|
|
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 =
|
|
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
|
|
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
|
|
286
|
+
export function listCentralRules() {
|
|
287
287
|
const seen = new Set();
|
|
288
288
|
for (const dir of [getUserRulesDir(), getResolvedRulesDir()]) {
|
|
289
289
|
if (!fs.existsSync(dir))
|
|
Binary file
|
|
Binary file
|
|
@@ -1,23 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Secret bundles -- named sets of keychain-backed environment variables.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
|
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 };
|