@phnx-labs/agents-cli 1.20.57 → 1.20.58
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/CHANGELOG.md +20 -0
- package/README.md +34 -3
- package/dist/bin/agents +0 -0
- package/dist/commands/defaults.js +24 -0
- package/dist/commands/exec.js +28 -4
- package/dist/commands/secrets.js +28 -19
- package/dist/commands/versions.js +11 -3
- package/dist/commands/view.js +19 -4
- package/dist/lib/agents.d.ts +21 -0
- package/dist/lib/agents.js +28 -4
- package/dist/lib/daemon.d.ts +5 -5
- package/dist/lib/daemon.js +65 -17
- package/dist/lib/git.d.ts +9 -0
- package/dist/lib/git.js +12 -0
- package/dist/lib/hosts/dispatch.d.ts +21 -0
- package/dist/lib/hosts/dispatch.js +88 -5
- package/dist/lib/permissions.d.ts +19 -1
- package/dist/lib/permissions.js +137 -0
- package/dist/lib/project-root.d.ts +65 -0
- package/dist/lib/project-root.js +133 -0
- package/dist/lib/resources/permissions.js +2 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/secrets/agent.d.ts +26 -23
- package/dist/lib/secrets/agent.js +196 -216
- package/dist/lib/secrets/remote.js +1 -0
- package/dist/lib/session/active.d.ts +3 -0
- package/dist/lib/session/active.js +1 -0
- package/dist/lib/session/parse.js +38 -15
- package/dist/lib/session/state.d.ts +4 -1
- package/dist/lib/session/state.js +18 -1
- package/dist/lib/session/types.d.ts +8 -0
- package/dist/lib/staleness/detectors/permissions.js +42 -0
- package/dist/lib/staleness/detectors/subagents.js +30 -0
- package/dist/lib/staleness/writers/subagents.js +13 -1
- package/dist/lib/subagents.d.ts +22 -0
- package/dist/lib/subagents.js +146 -0
- package/dist/lib/teams/agents.d.ts +14 -0
- package/dist/lib/teams/agents.js +158 -22
- package/dist/lib/types.d.ts +13 -0
- package/dist/lib/versions.d.ts +39 -0
- package/dist/lib/versions.js +199 -12
- package/package.json +1 -1
- package/scripts/postinstall.js +26 -11
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projects-root resolution for the `agents run --project <slug>` shorthand.
|
|
3
|
+
*
|
|
4
|
+
* Projects follow a predictable layout — `<root>/<repo>` (e.g.
|
|
5
|
+
* `~/src/github.com/<user>/<repo>`), with git worktrees under
|
|
6
|
+
* `<repo>/.agents/worktrees/<slug>`. The root is auto-inferred from the repo you
|
|
7
|
+
* launch inside (the directory ABOVE the git root) and cached in `agents.yaml`
|
|
8
|
+
* so later runs resolve a bare slug from anywhere. It is stored home-relative
|
|
9
|
+
* (`~/…`) when it sits under `$HOME`, so the SAME value resolves on a remote
|
|
10
|
+
* host whose home differs (`/home/<user>` vs `/Users/<user>`): a `--host` run
|
|
11
|
+
* keeps the `~` and lets the remote login shell expand it (see `remoteCdPrefix`
|
|
12
|
+
* in `hosts/dispatch.ts`), while a local run expands `~` against the local home.
|
|
13
|
+
*/
|
|
14
|
+
import * as os from 'os';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import { readMeta, updateMeta } from './state.js';
|
|
18
|
+
import { getMainRepoRoot } from './git.js';
|
|
19
|
+
const HOME = process.env.HOME ?? os.homedir();
|
|
20
|
+
/** Rewrite an absolute path under the local home to a `~/`-relative string; pass others through. */
|
|
21
|
+
export function toHomeRelative(abs) {
|
|
22
|
+
const rel = path.relative(HOME, abs);
|
|
23
|
+
if (rel === '')
|
|
24
|
+
return '~';
|
|
25
|
+
if (!rel.startsWith('..') && !path.isAbsolute(rel))
|
|
26
|
+
return `~/${rel}`;
|
|
27
|
+
return abs;
|
|
28
|
+
}
|
|
29
|
+
/** Expand a leading `~`/`$HOME` against the LOCAL home. Other paths pass through unchanged. */
|
|
30
|
+
export function expandLocalHome(p) {
|
|
31
|
+
if (p === '~' || p === '$HOME')
|
|
32
|
+
return HOME;
|
|
33
|
+
if (p.startsWith('~/'))
|
|
34
|
+
return path.join(HOME, p.slice(2));
|
|
35
|
+
if (p.startsWith('$HOME/'))
|
|
36
|
+
return path.join(HOME, p.slice(6));
|
|
37
|
+
return p;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Make a `--cwd`/`--project` value portable to a remote host: an absolute path
|
|
41
|
+
* under the LOCAL home (which the local shell already expanded from `~`) becomes
|
|
42
|
+
* `~/…` so the *remote* shell re-roots it at its own home. Paths already anchored
|
|
43
|
+
* at `~`/`$HOME` pass through; other absolute or relative paths are left as-is
|
|
44
|
+
* (used verbatim on the host). Explicit `--remote-cwd` is NOT run through this —
|
|
45
|
+
* it is a literal remote path by contract.
|
|
46
|
+
*/
|
|
47
|
+
export function toRemotePortable(p) {
|
|
48
|
+
if (p.startsWith('~') || p.startsWith('$HOME'))
|
|
49
|
+
return p;
|
|
50
|
+
if (path.isAbsolute(p))
|
|
51
|
+
return toHomeRelative(p);
|
|
52
|
+
return p;
|
|
53
|
+
}
|
|
54
|
+
/** The configured projects root (home-relative or absolute), or undefined when unset. */
|
|
55
|
+
export function getProjectRoot() {
|
|
56
|
+
return readMeta().projectRoot;
|
|
57
|
+
}
|
|
58
|
+
/** Set (override) the cached projects root. Stored home-relative when under `$HOME`. */
|
|
59
|
+
export function setProjectRoot(rootPath) {
|
|
60
|
+
const stored = toHomeRelative(path.resolve(expandLocalHome(rootPath)));
|
|
61
|
+
updateMeta({ projectRoot: stored });
|
|
62
|
+
return stored;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Infer the projects root from `cwd`: the directory ABOVE the git repo root
|
|
66
|
+
* (cwd inside `~/src/github.com/user/repo` → `~/src/github.com/user`). Returns a
|
|
67
|
+
* home-relative string when under `$HOME`; undefined when `cwd` is not in a repo.
|
|
68
|
+
*/
|
|
69
|
+
export async function inferProjectRoot(cwd) {
|
|
70
|
+
try {
|
|
71
|
+
const mainRoot = await getMainRepoRoot(cwd);
|
|
72
|
+
return toHomeRelative(path.dirname(mainRoot));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Resolve the projects root, auto-inferring and caching on first use. Throws an
|
|
80
|
+
* actionable error when it is neither configured nor inferrable from `cwd`.
|
|
81
|
+
*/
|
|
82
|
+
export async function ensureProjectRoot(cwd) {
|
|
83
|
+
const existing = getProjectRoot();
|
|
84
|
+
if (existing)
|
|
85
|
+
return existing;
|
|
86
|
+
const inferred = await inferProjectRoot(cwd);
|
|
87
|
+
if (!inferred) {
|
|
88
|
+
throw new Error('Could not determine your projects root. Run once from inside a project ' +
|
|
89
|
+
'(a git repo under your projects dir) so it can be inferred, or set it:\n' +
|
|
90
|
+
' agents defaults project-root ~/src/github.com/<you>');
|
|
91
|
+
}
|
|
92
|
+
updateMeta({ projectRoot: inferred });
|
|
93
|
+
process.stderr.write(`[project] cached projects root: ${inferred}\n`);
|
|
94
|
+
return inferred;
|
|
95
|
+
}
|
|
96
|
+
/** Parse a `--project` value of the form `<slug>[@<worktree>]`. */
|
|
97
|
+
export function parseProjectRef(ref) {
|
|
98
|
+
const at = ref.indexOf('@');
|
|
99
|
+
if (at === -1)
|
|
100
|
+
return { slug: ref };
|
|
101
|
+
return { slug: ref.slice(0, at), worktree: ref.slice(at + 1) || undefined };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Join a root + `--project` ref into a working directory. Pure (no I/O) so the
|
|
105
|
+
* slug/worktree layout is unit-testable. `forRemote` keeps the path
|
|
106
|
+
* home-relative (`~/…`) for the remote shell to expand; otherwise it is expanded
|
|
107
|
+
* against the local home into an absolute path.
|
|
108
|
+
*/
|
|
109
|
+
export function buildProjectPath(root, ref, forRemote) {
|
|
110
|
+
const { slug, worktree } = parseProjectRef(ref);
|
|
111
|
+
if (!slug)
|
|
112
|
+
throw new Error(`Invalid --project value: "${ref}"`);
|
|
113
|
+
let rel = `${root}/${slug}`;
|
|
114
|
+
if (worktree)
|
|
115
|
+
rel += `/.agents/worktrees/${worktree}`;
|
|
116
|
+
return forRemote ? rel : path.resolve(expandLocalHome(rel));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Resolve a `--project` ref to a working directory, inferring/caching the root.
|
|
120
|
+
*
|
|
121
|
+
* `forRemote: true` returns a home-relative path (`~/…`) so the REMOTE login
|
|
122
|
+
* shell expands `~`/`$HOME` to its own home. `forRemote: false` returns an
|
|
123
|
+
* absolute local path and verifies it exists (so a mistyped slug fails loudly).
|
|
124
|
+
*/
|
|
125
|
+
export async function resolveProjectRef(ref, opts) {
|
|
126
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
127
|
+
const root = await ensureProjectRoot(cwd);
|
|
128
|
+
const resolved = buildProjectPath(root, ref, opts.forRemote);
|
|
129
|
+
if (!opts.forRemote && !fs.existsSync(resolved)) {
|
|
130
|
+
throw new Error(`Project path not found: ${resolved}`);
|
|
131
|
+
}
|
|
132
|
+
return resolved;
|
|
133
|
+
}
|
|
@@ -69,6 +69,8 @@ function getAgentConfigPath(agent, versionHome) {
|
|
|
69
69
|
return path.join(versionHome, '.opencode', 'opencode.jsonc');
|
|
70
70
|
case 'kimi':
|
|
71
71
|
return path.join(versionHome, '.kimi-code', 'config.toml');
|
|
72
|
+
case 'kiro':
|
|
73
|
+
return path.join(versionHome, '.kiro', 'settings', 'permissions.yaml');
|
|
72
74
|
default:
|
|
73
75
|
return null;
|
|
74
76
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - Union: All resources from all layers are combined
|
|
6
6
|
* - Override on name conflict: Higher layer wins (project > user > system)
|
|
7
7
|
*/
|
|
8
|
-
export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'antigravity' | 'grok' | 'kimi' | 'hermes' | 'forge';
|
|
8
|
+
export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'kiro' | 'antigravity' | 'grok' | 'kimi' | 'hermes' | 'forge';
|
|
9
9
|
export type Layer = 'system' | 'user' | 'project';
|
|
10
10
|
export type ResourceKind = 'command' | 'hook' | 'skill' | 'rule' | 'mcp' | 'permission' | 'subagent' | 'workflow' | 'memory';
|
|
11
11
|
/** A resolved resource with its origin layer. */
|
|
@@ -74,23 +74,23 @@ export interface AgentStatusEntry {
|
|
|
74
74
|
expiresAt: number;
|
|
75
75
|
keyCount: number;
|
|
76
76
|
}
|
|
77
|
-
/** True if
|
|
77
|
+
/** True if a legacy standalone-broker launchd plist is still installed. */
|
|
78
78
|
export declare function secretsAgentServiceInstalled(): boolean;
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
80
|
+
* Retire the legacy standalone secrets-agent launchd service: bootout the job
|
|
81
|
+
* (falling back to the legacy `unload`) and remove its plist so the always-on
|
|
82
|
+
* daemon owns the broker socket. Idempotent and best-effort — a no-op when no
|
|
83
|
+
* legacy plist is present. Does NOT wipe held bundles: the booted-out process's
|
|
84
|
+
* memory is gone anyway, and the daemon-hosted broker starts fresh.
|
|
84
85
|
*/
|
|
85
|
-
export declare function
|
|
86
|
+
export declare function retireLegacySecretsAgentService(): void;
|
|
86
87
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* and
|
|
88
|
+
* Stop the persistent broker for `agents secrets stop`: wipe whatever the broker
|
|
89
|
+
* holds (forces Touch ID again on the next read), then retire any legacy
|
|
90
|
+
* standalone service. The daemon-hosted broker itself is left running — it is
|
|
91
|
+
* the always-on backbone, and stopping it would take down unrelated background
|
|
92
|
+
* work (routines, browser IPC, session-sync).
|
|
91
93
|
*/
|
|
92
|
-
export declare function kickstartSecretsAgentService(): void;
|
|
93
|
-
/** Stop + remove the persistent broker service, and wipe whatever it held. */
|
|
94
94
|
export declare function uninstallSecretsAgentService(): Promise<void>;
|
|
95
95
|
export type Request = {
|
|
96
96
|
cmd: 'ping';
|
|
@@ -171,7 +171,9 @@ export declare function shouldWipeOnWatchEvent(chunk: string): boolean;
|
|
|
171
171
|
*/
|
|
172
172
|
export declare function runSecretsAgent(opts?: {
|
|
173
173
|
service?: boolean;
|
|
174
|
-
}): Promise<
|
|
174
|
+
}): Promise<{
|
|
175
|
+
close(): void;
|
|
176
|
+
} | null>;
|
|
175
177
|
/**
|
|
176
178
|
* Host the secrets broker inside the always-on daemon (#416).
|
|
177
179
|
*
|
|
@@ -184,11 +186,11 @@ export declare function runSecretsAgent(opts?: {
|
|
|
184
186
|
* (those would kill the daemon — the daemon is the always-on backbone and
|
|
185
187
|
* manages its own version/lifecycle). The sweep only TTL-evicts.
|
|
186
188
|
*
|
|
187
|
-
* The caller (`runDaemon`)
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* null off-darwin (nothing to broker without biometry).
|
|
189
|
+
* The caller (`runDaemon`) normally invokes this only when no broker answers
|
|
190
|
+
* its initial ping. Binding still arbitrates ownership through the same shared
|
|
191
|
+
* path as the standalone service: a live owner wins, while only an unreachable
|
|
192
|
+
* stale socket is reclaimed. Returns a handle the daemon closes on shutdown,
|
|
193
|
+
* or null off-darwin (nothing to broker without biometry).
|
|
192
194
|
*/
|
|
193
195
|
export declare function startHostedBroker(): Promise<{
|
|
194
196
|
close(): void;
|
|
@@ -277,10 +279,11 @@ export declare function agentPing(): Promise<{
|
|
|
277
279
|
* Ensure a broker is running and reachable. Returns true once the socket answers
|
|
278
280
|
* a ping. macOS only.
|
|
279
281
|
*
|
|
280
|
-
* Prefers the
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
* Only when the
|
|
284
|
-
*
|
|
282
|
+
* Prefers the always-on daemon, which hosts the broker socket (#416): retire any
|
|
283
|
+
* legacy standalone launchd service so the daemon owns the socket, then bring the
|
|
284
|
+
* daemon up (Path 0) — one supervised backbone that survives the whole login
|
|
285
|
+
* session, so subsequent reads never cold-start. Only when the daemon can't be
|
|
286
|
+
* used do we fall back to a one-off detached broker (Path 1) — the model that
|
|
287
|
+
* gets starved under heavy load, so it's last.
|
|
285
288
|
*/
|
|
286
289
|
export declare function ensureAgentRunning(timeoutMs?: number): Promise<boolean>;
|