@phnx-labs/agents-cli 1.20.5 → 1.20.6
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 +1 -1
- package/dist/commands/browser.js +31 -4
- package/dist/commands/computer.js +10 -2
- package/dist/commands/defaults.d.ts +7 -0
- package/dist/commands/defaults.js +89 -0
- package/dist/commands/exec.js +24 -6
- package/dist/commands/rules.js +3 -3
- package/dist/commands/secrets.js +46 -9
- package/dist/commands/setup.js +2 -2
- package/dist/commands/teams.js +108 -11
- package/dist/commands/view.d.ts +12 -1
- package/dist/commands/view.js +121 -38
- package/dist/index.js +38 -21
- package/dist/lib/agents.d.ts +10 -6
- package/dist/lib/agents.js +23 -14
- package/dist/lib/browser/chrome.d.ts +10 -0
- package/dist/lib/browser/chrome.js +84 -3
- package/dist/lib/exec.js +24 -4
- package/dist/lib/migrate.js +6 -4
- package/dist/lib/permissions.d.ts +23 -0
- package/dist/lib/permissions.js +89 -7
- package/dist/lib/plugin-marketplace.js +1 -1
- package/dist/lib/project-launch.d.ts +5 -0
- package/dist/lib/project-launch.js +37 -0
- package/dist/lib/pty-server.js +7 -4
- package/dist/lib/resources/rules.js +1 -1
- package/dist/lib/resources/skills.js +1 -1
- package/dist/lib/resources.d.ts +2 -0
- package/dist/lib/resources.js +2 -1
- package/dist/lib/rotate.js +6 -18
- package/dist/lib/run-config.d.ts +9 -0
- package/dist/lib/run-config.js +35 -0
- package/dist/lib/run-defaults.d.ts +42 -0
- package/dist/lib/run-defaults.js +180 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/install-helper.d.ts +11 -3
- package/dist/lib/secrets/install-helper.js +48 -6
- package/dist/lib/secrets/linux.d.ts +12 -0
- package/dist/lib/secrets/linux.js +30 -16
- package/dist/lib/shims.d.ts +9 -1
- package/dist/lib/shims.js +35 -3
- package/dist/lib/staleness/detectors/hooks.js +1 -1
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/teams/api.d.ts +67 -0
- package/dist/lib/teams/api.js +78 -0
- package/dist/lib/types.d.ts +15 -6
- package/dist/lib/versions.js +4 -4
- package/package.json +5 -2
package/dist/lib/exec.js
CHANGED
|
@@ -118,6 +118,7 @@ export function buildExecEnv(options) {
|
|
|
118
118
|
}
|
|
119
119
|
delete result.CODEX_HOME;
|
|
120
120
|
delete result.COPILOT_HOME;
|
|
121
|
+
delete result.KIMI_CODE_HOME;
|
|
121
122
|
}
|
|
122
123
|
else if (options.agent === 'codex') {
|
|
123
124
|
const cwd = options.cwd || process.cwd();
|
|
@@ -130,6 +131,7 @@ export function buildExecEnv(options) {
|
|
|
130
131
|
}
|
|
131
132
|
delete result.CLAUDE_CONFIG_DIR;
|
|
132
133
|
delete result.COPILOT_HOME;
|
|
134
|
+
delete result.KIMI_CODE_HOME;
|
|
133
135
|
}
|
|
134
136
|
else if (options.agent === 'copilot') {
|
|
135
137
|
// Copilot honors COPILOT_HOME (relocates ~/.copilot, including settings,
|
|
@@ -145,11 +147,28 @@ export function buildExecEnv(options) {
|
|
|
145
147
|
}
|
|
146
148
|
delete result.CLAUDE_CONFIG_DIR;
|
|
147
149
|
delete result.CODEX_HOME;
|
|
150
|
+
delete result.KIMI_CODE_HOME;
|
|
151
|
+
}
|
|
152
|
+
else if (options.agent === 'kimi') {
|
|
153
|
+
// Kimi honors KIMI_CODE_HOME (relocates ~/.kimi-code, including config,
|
|
154
|
+
// skills, hooks, sessions). Pin it at the per-version home.
|
|
155
|
+
const cwd = options.cwd || process.cwd();
|
|
156
|
+
const resolvedVersion = options.version ?? resolveVersion('kimi', cwd);
|
|
157
|
+
const version = options.version
|
|
158
|
+
? resolvedVersion
|
|
159
|
+
: (resolvedVersion && isVersionInstalled('kimi', resolvedVersion) ? resolvedVersion : null);
|
|
160
|
+
if (version) {
|
|
161
|
+
result.KIMI_CODE_HOME = path.join(getVersionHomePath('kimi', version), '.kimi-code');
|
|
162
|
+
}
|
|
163
|
+
delete result.CLAUDE_CONFIG_DIR;
|
|
164
|
+
delete result.CODEX_HOME;
|
|
165
|
+
delete result.COPILOT_HOME;
|
|
148
166
|
}
|
|
149
167
|
else {
|
|
150
168
|
delete result.CLAUDE_CONFIG_DIR;
|
|
151
169
|
delete result.CODEX_HOME;
|
|
152
170
|
delete result.COPILOT_HOME;
|
|
171
|
+
delete result.KIMI_CODE_HOME;
|
|
153
172
|
}
|
|
154
173
|
return {
|
|
155
174
|
...result,
|
|
@@ -322,14 +341,15 @@ export const AGENT_COMMANDS = {
|
|
|
322
341
|
modelFlag: '--model',
|
|
323
342
|
},
|
|
324
343
|
kimi: {
|
|
325
|
-
base: ['kimi
|
|
344
|
+
base: ['kimi'],
|
|
326
345
|
promptFlag: '-p',
|
|
327
346
|
modeFlags: {
|
|
328
|
-
plan: [],
|
|
347
|
+
plan: ['--plan'],
|
|
329
348
|
edit: [],
|
|
330
|
-
|
|
349
|
+
auto: ['--auto'],
|
|
350
|
+
skip: ['--yolo'],
|
|
331
351
|
},
|
|
332
|
-
jsonFlags: [],
|
|
352
|
+
jsonFlags: ['--output-format', 'stream-json'],
|
|
333
353
|
modelFlag: '--model',
|
|
334
354
|
},
|
|
335
355
|
};
|
package/dist/lib/migrate.js
CHANGED
|
@@ -8,6 +8,7 @@ import * as fs from 'fs';
|
|
|
8
8
|
import * as path from 'path';
|
|
9
9
|
import * as os from 'os';
|
|
10
10
|
import * as yaml from 'yaml';
|
|
11
|
+
import { AGENTS, agentConfigDirName } from './agents.js';
|
|
11
12
|
const HOME = process.env.HOME ?? os.homedir();
|
|
12
13
|
const USER_DIR = path.join(HOME, '.agents');
|
|
13
14
|
/** Canonical system-repo location (post-fold). */
|
|
@@ -672,12 +673,13 @@ function repairAgentConfigSymlinks() {
|
|
|
672
673
|
}
|
|
673
674
|
let repaired = 0;
|
|
674
675
|
for (const { agent, version } of defaults) {
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
676
|
+
const configDirName = agent in AGENTS ? agentConfigDirName(agent) : `.${agent}`;
|
|
677
|
+
const userTarget = fs.existsSync(path.join(HISTORY_DIR, 'versions', agent, version, 'home', configDirName))
|
|
678
|
+
? path.join(HISTORY_DIR, 'versions', agent, version, 'home', configDirName)
|
|
679
|
+
: path.join(USER_DIR, 'versions', agent, version, 'home', configDirName);
|
|
678
680
|
if (!fs.existsSync(userTarget))
|
|
679
681
|
continue;
|
|
680
|
-
const symlinkPath = path.join(HOME,
|
|
682
|
+
const symlinkPath = path.join(HOME, configDirName);
|
|
681
683
|
let stat = null;
|
|
682
684
|
try {
|
|
683
685
|
stat = fs.lstatSync(symlinkPath);
|
|
@@ -144,6 +144,29 @@ export type GrokRule = {
|
|
|
144
144
|
tool: string;
|
|
145
145
|
pattern?: string;
|
|
146
146
|
};
|
|
147
|
+
export type KimiRule = {
|
|
148
|
+
decision: 'allow' | 'deny';
|
|
149
|
+
pattern: string;
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Convert a canonical permission set to Kimi Code's `[permission].rules` format.
|
|
153
|
+
* Kimi (`~/.kimi-code/config.toml`) reads rules of the form
|
|
154
|
+
* [[permission.rules]]
|
|
155
|
+
* decision = "allow"
|
|
156
|
+
* pattern = "Bash(git status*)"
|
|
157
|
+
* Tool names are capitalized and the Bash arg-glob uses a trailing `*` (no
|
|
158
|
+
* Claude `:*` separator). Without this conversion the canonical strings match
|
|
159
|
+
* nothing in Kimi's engine and every tool call falls through to a prompt.
|
|
160
|
+
*
|
|
161
|
+
* Each `:*` Bash rule expands to TWO patterns (`cmd*` and `cmd*/**`) so the
|
|
162
|
+
* command auto-approves whether or not its arguments contain a slash — see
|
|
163
|
+
* `kimiBashPatterns` for why Kimi's picomatch matcher needs both.
|
|
164
|
+
*/
|
|
165
|
+
export declare function convertToKimiFormat(set: PermissionSet): {
|
|
166
|
+
permission: {
|
|
167
|
+
rules: KimiRule[];
|
|
168
|
+
};
|
|
169
|
+
};
|
|
147
170
|
/**
|
|
148
171
|
* Convert canonical permission set to OpenCode format.
|
|
149
172
|
* OpenCode uses: { permission: { bash: { "git *": "allow", "rm *": "deny" } } }
|
package/dist/lib/permissions.js
CHANGED
|
@@ -557,6 +557,94 @@ function canonicalToGrokRule(perm, action) {
|
|
|
557
557
|
}
|
|
558
558
|
return { action, tool, pattern };
|
|
559
559
|
}
|
|
560
|
+
/**
|
|
561
|
+
* Parse a canonical permission string preserving the tool's original casing.
|
|
562
|
+
* `parseCanonicalPattern` lowercases the tool name, which is fine for Grok
|
|
563
|
+
* (lowercase tool vocabulary) but wrong for Kimi, whose tool names are
|
|
564
|
+
* capitalized (`Bash`, `Read`, `Grep`). Bare tool names (no parens, e.g.
|
|
565
|
+
* `Read` or an MCP id like `mcp__server__tool`) return `pattern: null`.
|
|
566
|
+
*/
|
|
567
|
+
function parseCanonicalPreserveCase(perm) {
|
|
568
|
+
const m = perm.match(/^([\w-]+)\((.*)\)$/);
|
|
569
|
+
if (m)
|
|
570
|
+
return { tool: m[1], pattern: m[2] };
|
|
571
|
+
return { tool: perm, pattern: null };
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Translate a canonical Bash arg-glob (`cmd:*`) into the Kimi pattern(s) that
|
|
575
|
+
* actually match that command's invocations.
|
|
576
|
+
*
|
|
577
|
+
* Kimi matches Bash arg-globs with picomatch, where `*` does NOT cross `/` and a
|
|
578
|
+
* `**` only globstars when it is its own path segment (`*/**`, `**/`). A plain
|
|
579
|
+
* `cmd*` therefore matches `git status -s` but NOT `git push origin feat/x` or
|
|
580
|
+
* `cat dir/file` — any argument containing a slash falls through to a prompt
|
|
581
|
+
* (verified interactively against kimi 0.12.1). We emit TWO patterns so the
|
|
582
|
+
* command auto-approves whether its args contain a slash or not:
|
|
583
|
+
* - `cmd*` — no-slash args (and the bare command; `*` is zero-or-more).
|
|
584
|
+
* - `cmd*/**` — args with a path: `*` consumes up to the first `/`, then the
|
|
585
|
+
* bounded globstar crosses the remaining slashes.
|
|
586
|
+
* "git push:*" -> ["git push*", "git push*/**"].
|
|
587
|
+
*/
|
|
588
|
+
function kimiBashPatterns(pattern) {
|
|
589
|
+
if (pattern === '*' || pattern === '**')
|
|
590
|
+
return ['*'];
|
|
591
|
+
if (pattern.endsWith(':*')) {
|
|
592
|
+
const prefix = pattern.slice(0, -2);
|
|
593
|
+
return [`${prefix}*`, `${prefix}*/**`];
|
|
594
|
+
}
|
|
595
|
+
// Exact command (no `:*`, e.g. `env`, `pwd`, `true`) — no path args expected.
|
|
596
|
+
return [pattern];
|
|
597
|
+
}
|
|
598
|
+
function canonicalToKimiRules(perm, decision) {
|
|
599
|
+
if (BLANKET_BASH_FORMS.has(perm)) {
|
|
600
|
+
return [{ decision, pattern: 'Bash' }];
|
|
601
|
+
}
|
|
602
|
+
const { tool, pattern } = parseCanonicalPreserveCase(perm);
|
|
603
|
+
// Bare tool name (no parens) — name-only match. Covers `Read`, `Grep`, and
|
|
604
|
+
// MCP tool ids, which Kimi can only match by name anyway.
|
|
605
|
+
if (pattern === null) {
|
|
606
|
+
return [{ decision, pattern: tool }];
|
|
607
|
+
}
|
|
608
|
+
if (tool.toLowerCase() === 'bash') {
|
|
609
|
+
return kimiBashPatterns(pattern).map((p) => ({
|
|
610
|
+
decision,
|
|
611
|
+
pattern: p === '*' ? 'Bash' : `Bash(${p})`,
|
|
612
|
+
}));
|
|
613
|
+
}
|
|
614
|
+
// Non-Bash built-ins (Read/Write/Edit/Grep/Glob/WebFetch...) share Kimi's
|
|
615
|
+
// capitalized tool vocabulary, so pass the tool+pattern through. A `**`/`*`
|
|
616
|
+
// glob means "any" — collapse to a name-only rule.
|
|
617
|
+
if (pattern === '*' || pattern === '**') {
|
|
618
|
+
return [{ decision, pattern: tool }];
|
|
619
|
+
}
|
|
620
|
+
return [{ decision, pattern: `${tool}(${pattern})` }];
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Convert a canonical permission set to Kimi Code's `[permission].rules` format.
|
|
624
|
+
* Kimi (`~/.kimi-code/config.toml`) reads rules of the form
|
|
625
|
+
* [[permission.rules]]
|
|
626
|
+
* decision = "allow"
|
|
627
|
+
* pattern = "Bash(git status*)"
|
|
628
|
+
* Tool names are capitalized and the Bash arg-glob uses a trailing `*` (no
|
|
629
|
+
* Claude `:*` separator). Without this conversion the canonical strings match
|
|
630
|
+
* nothing in Kimi's engine and every tool call falls through to a prompt.
|
|
631
|
+
*
|
|
632
|
+
* Each `:*` Bash rule expands to TWO patterns (`cmd*` and `cmd*/**`) so the
|
|
633
|
+
* command auto-approves whether or not its arguments contain a slash — see
|
|
634
|
+
* `kimiBashPatterns` for why Kimi's picomatch matcher needs both.
|
|
635
|
+
*/
|
|
636
|
+
export function convertToKimiFormat(set) {
|
|
637
|
+
const rules = [];
|
|
638
|
+
for (const perm of set.allow) {
|
|
639
|
+
rules.push(...canonicalToKimiRules(perm, 'allow'));
|
|
640
|
+
}
|
|
641
|
+
if (set.deny) {
|
|
642
|
+
for (const perm of set.deny) {
|
|
643
|
+
rules.push(...canonicalToKimiRules(perm, 'deny'));
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return { permission: { rules } };
|
|
647
|
+
}
|
|
560
648
|
/**
|
|
561
649
|
* Convert canonical permission set to OpenCode format.
|
|
562
650
|
* OpenCode uses: { permission: { bash: { "git *": "allow", "rm *": "deny" } } }
|
|
@@ -1107,13 +1195,7 @@ export function applyPermissionsToVersion(agentId, set, versionHome, merge = tru
|
|
|
1107
1195
|
if (fs.existsSync(configPath)) {
|
|
1108
1196
|
config = TOML.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1109
1197
|
}
|
|
1110
|
-
const newRules =
|
|
1111
|
-
for (const allow of set.allow) {
|
|
1112
|
-
newRules.push({ decision: 'allow', pattern: allow });
|
|
1113
|
-
}
|
|
1114
|
-
for (const deny of set.deny || []) {
|
|
1115
|
-
newRules.push({ decision: 'deny', pattern: deny });
|
|
1116
|
-
}
|
|
1198
|
+
const newRules = convertToKimiFormat(set).permission.rules;
|
|
1117
1199
|
if (merge) {
|
|
1118
1200
|
const existingPermission = (typeof config.permission === 'object' && config.permission !== null && !Array.isArray(config.permission))
|
|
1119
1201
|
? config.permission
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
* the catalog name and on-disk layout are derived, never hard-coded per call.
|
|
27
27
|
*/
|
|
28
28
|
import * as fs from 'fs';
|
|
29
|
+
import { agentConfigDirName } from './agents.js';
|
|
29
30
|
import * as path from 'path';
|
|
30
31
|
import { getPluginsDir, getEnabledExtraRepos, getProjectPluginsDir } from './state.js';
|
|
31
|
-
import { agentConfigDirName } from './agents.js';
|
|
32
32
|
/**
|
|
33
33
|
* Canonical name for the user-repo marketplace (~/.agents/plugins/). Kept as an
|
|
34
34
|
* exported constant for callers that operate on the user repo directly and for
|
|
@@ -60,6 +60,11 @@ export interface LaunchSyncResult {
|
|
|
60
60
|
/**
|
|
61
61
|
* Run the launch-time project compile. Safe to call on every agent launch:
|
|
62
62
|
* each step is idempotent and skips when its inputs are missing.
|
|
63
|
+
*
|
|
64
|
+
* After a successful run, touches the shim-side skip-fast sentinel at
|
|
65
|
+
* `~/.agents/.cache/launch-sync/<agent>@<version>@<projectslug>` so the next
|
|
66
|
+
* shim invocation can skip the node spawn entirely when no source dir is
|
|
67
|
+
* newer than the sentinel (shim schema v17+).
|
|
63
68
|
*/
|
|
64
69
|
export declare function runLaunchSync(opts: LaunchSyncOptions): LaunchSyncResult;
|
|
65
70
|
export { pluginInstallDir };
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
*/
|
|
43
43
|
import * as crypto from 'crypto';
|
|
44
44
|
import * as fs from 'fs';
|
|
45
|
+
import * as os from 'os';
|
|
45
46
|
import * as path from 'path';
|
|
46
47
|
import { supports } from './capabilities.js';
|
|
47
48
|
import { getEnabledExtraRepos, getExtraPluginsDir, getPluginsDir, getProjectAgentsDir, getProjectPluginsDir, getSystemPluginsDir, } from './state.js';
|
|
@@ -52,6 +53,11 @@ import { MARKETPLACE_NAME, PROJECT_MARKETPLACE_NAME, SYSTEM_MARKETPLACE_NAME, ad
|
|
|
52
53
|
/**
|
|
53
54
|
* Run the launch-time project compile. Safe to call on every agent launch:
|
|
54
55
|
* each step is idempotent and skips when its inputs are missing.
|
|
56
|
+
*
|
|
57
|
+
* After a successful run, touches the shim-side skip-fast sentinel at
|
|
58
|
+
* `~/.agents/.cache/launch-sync/<agent>@<version>@<projectslug>` so the next
|
|
59
|
+
* shim invocation can skip the node spawn entirely when no source dir is
|
|
60
|
+
* newer than the sentinel (shim schema v17+).
|
|
55
61
|
*/
|
|
56
62
|
export function runLaunchSync(opts) {
|
|
57
63
|
const result = {
|
|
@@ -74,8 +80,39 @@ export function runLaunchSync(opts) {
|
|
|
74
80
|
result.workspaceSkipped = mirror.skipped;
|
|
75
81
|
// Step 3: scoped plugin marketplaces
|
|
76
82
|
result.marketplaces = synthesizeScopedMarketplaces(opts.agent, opts.version, opts.cwd);
|
|
83
|
+
// Touch the shim's skip-fast sentinel. Best-effort — if this fails the
|
|
84
|
+
// shim just won't skip on the next launch, which is correct fallback.
|
|
85
|
+
touchLaunchSentinel(opts.agent, opts.version, opts.cwd);
|
|
77
86
|
return result;
|
|
78
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Path of the shim's skip-fast sentinel for this (agent, version, cwd) tuple.
|
|
90
|
+
* Must match the SHIM-SIDE format in src/lib/shims.ts (PROJECT_SLUG derivation):
|
|
91
|
+
* slug = PWD with `/` → `_` and ` ` → `_`
|
|
92
|
+
*
|
|
93
|
+
* Cache leak note: this dir accumulates one zero-byte file per
|
|
94
|
+
* (agent, version, project) tuple ever launched. Disk impact is negligible
|
|
95
|
+
* (inodes only). A periodic GC belongs in `agents prune` — follow-up.
|
|
96
|
+
*/
|
|
97
|
+
function launchSentinelPath(agent, version, cwd) {
|
|
98
|
+
const slug = cwd.replace(/\//g, '_').replace(/ /g, '_');
|
|
99
|
+
// Prefer $HOME (respects test overrides + matches bash's $HOME expansion in
|
|
100
|
+
// the shim), fall back to os.homedir() so the lookup never resolves to '/'
|
|
101
|
+
// if HOME is somehow unset.
|
|
102
|
+
const home = process.env.HOME || os.homedir();
|
|
103
|
+
return path.join(home, '.agents', '.cache', 'launch-sync', `${agent}@${version}@${slug}`);
|
|
104
|
+
}
|
|
105
|
+
function touchLaunchSentinel(agent, version, cwd) {
|
|
106
|
+
try {
|
|
107
|
+
const sentinel = launchSentinelPath(agent, version, cwd);
|
|
108
|
+
fs.mkdirSync(path.dirname(sentinel), { recursive: true });
|
|
109
|
+
// Empty content — purely an mtime carrier for the shim's `[ -nt ]` compare.
|
|
110
|
+
fs.writeFileSync(sentinel, '');
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// best-effort
|
|
114
|
+
}
|
|
115
|
+
}
|
|
79
116
|
const CLAUDE_MIRROR_PLANS = [
|
|
80
117
|
{ srcSubdir: 'subagents', destSubdir: 'agents', entriesAreDirs: false },
|
|
81
118
|
{ srcSubdir: 'commands', destSubdir: 'commands', entriesAreDirs: false },
|
package/dist/lib/pty-server.js
CHANGED
|
@@ -155,14 +155,17 @@ export async function runPtyServer() {
|
|
|
155
155
|
let nodePty;
|
|
156
156
|
let XtermTerminal;
|
|
157
157
|
try {
|
|
158
|
-
|
|
158
|
+
// The Homebridge multiarch fork of node-pty: API-identical (same 1.x N-API
|
|
159
|
+
// codebase) but ships prebuilt binaries for Linux glibc + musl, x64 + arm64
|
|
160
|
+
// (plus macOS/Windows), so no compiler is needed on Linux/Alpine/arm64.
|
|
161
|
+
nodePty = await import('@homebridge/node-pty-prebuilt-multiarch');
|
|
159
162
|
// Handle ESM default export
|
|
160
163
|
if (nodePty.default?.spawn)
|
|
161
164
|
nodePty = nodePty.default;
|
|
162
165
|
// Ensure spawn-helper is executable (bun install doesn't set +x on prebuilds)
|
|
163
166
|
try {
|
|
164
167
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
165
|
-
const ptyBase = path.resolve(__dirname, '..', '..', 'node_modules', 'node-pty');
|
|
168
|
+
const ptyBase = path.resolve(__dirname, '..', '..', 'node_modules', '@homebridge', 'node-pty-prebuilt-multiarch');
|
|
166
169
|
const helpers = [
|
|
167
170
|
path.join(ptyBase, 'prebuilds', `${process.platform}-${process.arch}`, 'spawn-helper'),
|
|
168
171
|
path.join(ptyBase, 'build', 'Release', 'spawn-helper'),
|
|
@@ -176,8 +179,8 @@ export async function runPtyServer() {
|
|
|
176
179
|
catch { }
|
|
177
180
|
}
|
|
178
181
|
catch (err) {
|
|
179
|
-
console.error('node-pty is required for PTY support.');
|
|
180
|
-
console.error('Install:
|
|
182
|
+
console.error('node-pty (@homebridge/node-pty-prebuilt-multiarch) is required for PTY support.');
|
|
183
|
+
console.error('Install: bun add @homebridge/node-pty-prebuilt-multiarch');
|
|
181
184
|
process.exit(1);
|
|
182
185
|
}
|
|
183
186
|
try {
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* - All unique subrules across layers are unioned
|
|
10
10
|
*/
|
|
11
11
|
import * as fs from 'fs';
|
|
12
|
+
import { agentConfigDirName } from '../agents.js';
|
|
12
13
|
import * as path from 'path';
|
|
13
14
|
import { getSystemRulesDir, getUserRulesDir, getProjectAgentsDir, getEnabledExtraRepos, } from '../state.js';
|
|
14
|
-
import { agentConfigDirName } from '../agents.js';
|
|
15
15
|
const SUBRULES_DIR = 'subrules';
|
|
16
16
|
const SUBRULES_README = 'README.md';
|
|
17
17
|
/**
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* Format is the same for all agents. Resolution order: project > user > system.
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from 'fs';
|
|
8
|
+
import { agentConfigDirName } from '../agents.js';
|
|
8
9
|
import * as path from 'path';
|
|
9
10
|
import * as yaml from 'yaml';
|
|
10
11
|
import { getSystemSkillsDir, getUserSkillsDir, getProjectAgentsDir, getEnabledExtraRepos, } from '../state.js';
|
|
11
|
-
import { agentConfigDirName } from '../agents.js';
|
|
12
12
|
/** Default provider uses the real state module. */
|
|
13
13
|
const defaultProvider = {
|
|
14
14
|
getSystemSkillsDir,
|
package/dist/lib/resources.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface ResourceEntry {
|
|
|
36
36
|
name: string;
|
|
37
37
|
path: string;
|
|
38
38
|
scope: 'user' | 'project';
|
|
39
|
+
/** One-line description pulled from frontmatter; not all resource kinds have one. */
|
|
40
|
+
description?: string;
|
|
39
41
|
}
|
|
40
42
|
/** A skill resource entry with optional rule count. */
|
|
41
43
|
export interface SkillResourceEntry extends ResourceEntry {
|
package/dist/lib/resources.js
CHANGED
|
@@ -106,7 +106,7 @@ export function getAgentResources(agentId, options = {}) {
|
|
|
106
106
|
const commands = [];
|
|
107
107
|
for (const cmd of listInstalledCommandsWithScope(agentId, cwd, { home })) {
|
|
108
108
|
if (shouldInclude(cmd.scope)) {
|
|
109
|
-
commands.push({ name: cmd.name, path: cmd.path, scope: cmd.scope });
|
|
109
|
+
commands.push({ name: cmd.name, path: cmd.path, scope: cmd.scope, description: cmd.description });
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
// Skills
|
|
@@ -119,6 +119,7 @@ export function getAgentResources(agentId, options = {}) {
|
|
|
119
119
|
path: skill.path,
|
|
120
120
|
scope: skill.scope,
|
|
121
121
|
ruleCount: skill.ruleCount,
|
|
122
|
+
description: skill.metadata.description || undefined,
|
|
122
123
|
});
|
|
123
124
|
}
|
|
124
125
|
}
|
package/dist/lib/rotate.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from 'fs';
|
|
8
8
|
import * as path from 'path';
|
|
9
|
-
import * as yaml from 'yaml';
|
|
10
9
|
import { getAccountInfo } from './agents.js';
|
|
11
|
-
import { readMeta, writeMeta, getHelpersDir
|
|
10
|
+
import { readMeta, writeMeta, getHelpersDir } from './state.js';
|
|
12
11
|
import { listInstalledVersions, getVersionHomePath, resolveVersion } from './versions.js';
|
|
12
|
+
import { getProjectRunConfigs } from './run-config.js';
|
|
13
13
|
import { getUsageInfoByIdentity, getUsageLookupKey, } from './usage.js';
|
|
14
14
|
function getRotateDir() {
|
|
15
15
|
const dir = path.join(getHelpersDir(), 'rotate');
|
|
@@ -33,22 +33,10 @@ export function normalizeRunStrategy(value) {
|
|
|
33
33
|
}
|
|
34
34
|
/** Read project-local run strategy from the nearest agents.yaml, if present. */
|
|
35
35
|
export function getProjectRunStrategy(agent, startPath) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (manifestPath !== userAgentsYaml && fs.existsSync(manifestPath)) {
|
|
41
|
-
try {
|
|
42
|
-
const parsed = yaml.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
43
|
-
const strategy = normalizeRunStrategy(parsed?.run?.[agent]?.strategy);
|
|
44
|
-
if (strategy)
|
|
45
|
-
return strategy;
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
// Ignore malformed project config and keep walking, matching version resolution.
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
dir = path.dirname(dir);
|
|
36
|
+
for (const runConfig of getProjectRunConfigs(startPath)) {
|
|
37
|
+
const strategy = normalizeRunStrategy(runConfig[agent]?.strategy);
|
|
38
|
+
if (strategy)
|
|
39
|
+
return strategy;
|
|
52
40
|
}
|
|
53
41
|
return null;
|
|
54
42
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-local `run:` config discovery.
|
|
3
|
+
*
|
|
4
|
+
* The user/system `agents.yaml` is read through state.ts. Project-local
|
|
5
|
+
* agents.yaml files are discovered from the current working directory upward.
|
|
6
|
+
*/
|
|
7
|
+
import type { RunConfig } from './types.js';
|
|
8
|
+
/** Return project-local run configs from nearest directory upward. */
|
|
9
|
+
export declare function getProjectRunConfigs(startPath?: string): RunConfig[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-local `run:` config discovery.
|
|
3
|
+
*
|
|
4
|
+
* The user/system `agents.yaml` is read through state.ts. Project-local
|
|
5
|
+
* agents.yaml files are discovered from the current working directory upward.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as yaml from 'yaml';
|
|
10
|
+
import { getUserAgentsDir } from './state.js';
|
|
11
|
+
function isRecord(value) {
|
|
12
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
/** Return project-local run configs from nearest directory upward. */
|
|
15
|
+
export function getProjectRunConfigs(startPath = process.cwd()) {
|
|
16
|
+
const configs = [];
|
|
17
|
+
let dir = path.resolve(startPath);
|
|
18
|
+
const userAgentsYaml = path.join(getUserAgentsDir(), 'agents.yaml');
|
|
19
|
+
while (dir !== path.dirname(dir)) {
|
|
20
|
+
const manifestPath = path.join(dir, 'agents.yaml');
|
|
21
|
+
if (manifestPath !== userAgentsYaml && fs.existsSync(manifestPath)) {
|
|
22
|
+
try {
|
|
23
|
+
const parsed = yaml.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
24
|
+
if (isRecord(parsed?.run)) {
|
|
25
|
+
configs.push(parsed.run);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Ignore malformed project config and keep walking.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
dir = path.dirname(dir);
|
|
33
|
+
}
|
|
34
|
+
return configs;
|
|
35
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selector-based defaults for `agents run`.
|
|
3
|
+
*
|
|
4
|
+
* Stored under agents.yaml:
|
|
5
|
+
*
|
|
6
|
+
* run:
|
|
7
|
+
* defaults:
|
|
8
|
+
* "claude:*":
|
|
9
|
+
* mode: auto
|
|
10
|
+
* model: opus
|
|
11
|
+
* "claude:2.1.45":
|
|
12
|
+
* mode: plan
|
|
13
|
+
*/
|
|
14
|
+
import type { AgentId, Mode, RunConfig, RunDefaults } from './types.js';
|
|
15
|
+
export interface ParsedRunDefaultSelector {
|
|
16
|
+
agent: AgentId;
|
|
17
|
+
version: string;
|
|
18
|
+
selector: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ResolvedRunDefaults extends RunDefaults {
|
|
21
|
+
sources: {
|
|
22
|
+
mode?: string;
|
|
23
|
+
model?: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface RunDefaultEntry {
|
|
27
|
+
selector: string;
|
|
28
|
+
defaults: RunDefaults;
|
|
29
|
+
}
|
|
30
|
+
type RunDefaultsInput = {
|
|
31
|
+
mode?: unknown;
|
|
32
|
+
model?: unknown;
|
|
33
|
+
};
|
|
34
|
+
export declare function normalizeRunDefaultMode(input: string): Mode;
|
|
35
|
+
export declare function parseRunDefaultSelector(input: string): ParsedRunDefaultSelector;
|
|
36
|
+
export declare function resolveRunDefaultsFromConfig(runConfig: RunConfig | undefined, agent: AgentId, version?: string | null): ResolvedRunDefaults;
|
|
37
|
+
export declare function resolveRunDefaultsFromConfigs(runConfigs: Array<RunConfig | undefined>, agent: AgentId, version?: string | null): ResolvedRunDefaults;
|
|
38
|
+
export declare function resolveRunDefaults(agent: AgentId, version?: string | null, startPath?: string): ResolvedRunDefaults;
|
|
39
|
+
export declare function listRunDefaults(): RunDefaultEntry[];
|
|
40
|
+
export declare function setRunDefault(selectorInput: string, defaultsInput: RunDefaultsInput): RunDefaultEntry;
|
|
41
|
+
export declare function unsetRunDefault(selectorInput: string): boolean;
|
|
42
|
+
export {};
|