@phnx-labs/agents-cli 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +143 -39
- package/README.md +6 -6
- package/dist/commands/alias.js +2 -2
- package/dist/commands/browser-picker.d.ts +21 -0
- package/dist/commands/browser-picker.js +114 -0
- package/dist/commands/browser.js +793 -83
- package/dist/commands/cloud.js +8 -0
- package/dist/commands/commands.js +72 -22
- package/dist/commands/daemon.js +2 -2
- package/dist/commands/exec.js +70 -1
- package/dist/commands/hooks.js +71 -26
- package/dist/commands/mcp.js +81 -39
- package/dist/commands/plugins.js +224 -17
- package/dist/commands/prune.js +29 -1
- package/dist/commands/pull.js +3 -3
- package/dist/commands/repo.js +1 -1
- package/dist/commands/routines.js +2 -2
- package/dist/commands/secrets.js +154 -20
- package/dist/commands/sessions.js +62 -19
- package/dist/commands/{init.d.ts → setup.d.ts} +7 -6
- package/dist/commands/{init.js → setup.js} +22 -21
- package/dist/commands/skills.js +60 -19
- package/dist/commands/subagents.js +41 -13
- package/dist/commands/utils.d.ts +16 -0
- package/dist/commands/utils.js +32 -0
- package/dist/commands/view.js +78 -20
- package/dist/commands/workflows.d.ts +10 -0
- package/dist/commands/workflows.js +457 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +48 -36
- package/dist/lib/agents.js +2 -2
- package/dist/lib/auto-pull-worker.js +2 -3
- package/dist/lib/auto-pull.js +2 -2
- package/dist/lib/browser/cdp.d.ts +7 -1
- package/dist/lib/browser/cdp.js +32 -1
- package/dist/lib/browser/chrome.d.ts +10 -0
- package/dist/lib/browser/chrome.js +41 -3
- package/dist/lib/browser/devices.d.ts +4 -0
- package/dist/lib/browser/devices.js +27 -0
- package/dist/lib/browser/drivers/local.js +22 -6
- package/dist/lib/browser/drivers/ssh.js +9 -2
- package/dist/lib/browser/input.d.ts +1 -0
- package/dist/lib/browser/input.js +3 -0
- package/dist/lib/browser/ipc.js +158 -23
- package/dist/lib/browser/profiles.d.ts +10 -2
- package/dist/lib/browser/profiles.js +122 -37
- package/dist/lib/browser/service.d.ts +91 -13
- package/dist/lib/browser/service.js +767 -132
- package/dist/lib/browser/types.d.ts +91 -3
- package/dist/lib/browser/types.js +16 -0
- package/dist/lib/cloud/rush.d.ts +28 -1
- package/dist/lib/cloud/rush.js +69 -14
- package/dist/lib/cloud/store.js +2 -2
- package/dist/lib/commands.d.ts +1 -15
- package/dist/lib/commands.js +11 -7
- package/dist/lib/daemon.js +2 -3
- package/dist/lib/doctor-diff.js +4 -4
- package/dist/lib/events.js +2 -2
- package/dist/lib/hooks.d.ts +11 -7
- package/dist/lib/hooks.js +138 -49
- package/dist/lib/migrate.d.ts +1 -1
- package/dist/lib/migrate.js +1237 -22
- package/dist/lib/models.js +2 -2
- package/dist/lib/permissions.d.ts +8 -66
- package/dist/lib/permissions.js +18 -18
- package/dist/lib/plugins.d.ts +94 -24
- package/dist/lib/plugins.js +702 -123
- package/dist/lib/pty-server.js +9 -10
- package/dist/lib/resource-patterns.d.ts +41 -0
- package/dist/lib/resource-patterns.js +82 -0
- package/dist/lib/resources/hooks.d.ts +5 -1
- package/dist/lib/resources/hooks.js +21 -4
- package/dist/lib/resources/index.d.ts +17 -0
- package/dist/lib/resources/index.js +7 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources/workflows.d.ts +24 -0
- package/dist/lib/resources/workflows.js +110 -0
- package/dist/lib/resources.d.ts +6 -1
- package/dist/lib/resources.js +12 -2
- package/dist/lib/rotate.js +3 -4
- package/dist/lib/session/active.d.ts +3 -0
- package/dist/lib/session/active.js +92 -6
- package/dist/lib/session/cloud.js +2 -2
- package/dist/lib/session/db.d.ts +18 -0
- package/dist/lib/session/db.js +109 -5
- package/dist/lib/session/discover.d.ts +6 -0
- package/dist/lib/session/discover.js +55 -29
- package/dist/lib/session/team-filter.js +2 -2
- package/dist/lib/shims.d.ts +4 -52
- package/dist/lib/shims.js +23 -15
- package/dist/lib/skills.js +6 -2
- package/dist/lib/sqlite.js +10 -4
- package/dist/lib/state.d.ts +101 -16
- package/dist/lib/state.js +179 -31
- package/dist/lib/subagents.d.ts +28 -0
- package/dist/lib/subagents.js +98 -1
- package/dist/lib/sync-manifest.d.ts +1 -1
- package/dist/lib/sync-manifest.js +3 -3
- package/dist/lib/teams/persistence.js +15 -5
- package/dist/lib/teams/registry.js +2 -2
- package/dist/lib/types.d.ts +75 -17
- package/dist/lib/types.js +3 -3
- package/dist/lib/usage.js +2 -2
- package/dist/lib/versions.d.ts +3 -0
- package/dist/lib/versions.js +158 -47
- package/dist/lib/workflows.d.ts +79 -0
- package/dist/lib/workflows.js +233 -0
- package/package.json +1 -5
- package/scripts/postinstall.js +60 -59
- package/dist/commands/fork.d.ts +0 -10
- package/dist/commands/fork.js +0 -146
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow management library.
|
|
3
|
+
*
|
|
4
|
+
* Workflows are directory bundles with a WORKFLOW.md containing YAML frontmatter.
|
|
5
|
+
* They optionally contain subagents/, skills/, and plugins/ subdirectories that
|
|
6
|
+
* are composed at runtime by `agents run <workflow>`.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import * as yaml from 'yaml';
|
|
11
|
+
import { getSystemWorkflowsDir, getUserWorkflowsDir, getTrashWorkflowsDir, getEnabledExtraRepos, } from './state.js';
|
|
12
|
+
import { listInstalledVersions, getVersionHomePath } from './versions.js';
|
|
13
|
+
/** Agents that support running workflows via `agents run`. */
|
|
14
|
+
export const WORKFLOW_CAPABLE_AGENTS = ['claude'];
|
|
15
|
+
/** Parse WORKFLOW.md frontmatter from a workflow directory. Returns null if invalid. */
|
|
16
|
+
export function parseWorkflowFrontmatter(workflowDir) {
|
|
17
|
+
const workflowMdPath = path.join(workflowDir, 'WORKFLOW.md');
|
|
18
|
+
if (!fs.existsSync(workflowMdPath))
|
|
19
|
+
return null;
|
|
20
|
+
try {
|
|
21
|
+
const content = fs.readFileSync(workflowMdPath, 'utf-8');
|
|
22
|
+
const lines = content.split('\n');
|
|
23
|
+
if (lines[0] !== '---')
|
|
24
|
+
return null;
|
|
25
|
+
const endIndex = lines.slice(1).findIndex(l => l === '---');
|
|
26
|
+
if (endIndex < 0)
|
|
27
|
+
return null;
|
|
28
|
+
const frontmatter = lines.slice(1, endIndex + 1).join('\n');
|
|
29
|
+
const parsed = yaml.parse(frontmatter);
|
|
30
|
+
if (!parsed || typeof parsed !== 'object')
|
|
31
|
+
return null;
|
|
32
|
+
return {
|
|
33
|
+
name: parsed.name || '',
|
|
34
|
+
description: parsed.description || '',
|
|
35
|
+
model: parsed.model,
|
|
36
|
+
tools: parsed.tools,
|
|
37
|
+
skills: parsed.skills,
|
|
38
|
+
mcpServers: parsed.mcpServers,
|
|
39
|
+
allowedAgents: parsed.allowedAgents,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Count subagent .md files in a workflow's subagents/ directory. */
|
|
47
|
+
export function countWorkflowSubagents(workflowDir) {
|
|
48
|
+
const subagentsDir = path.join(workflowDir, 'subagents');
|
|
49
|
+
if (!fs.existsSync(subagentsDir))
|
|
50
|
+
return 0;
|
|
51
|
+
try {
|
|
52
|
+
return fs.readdirSync(subagentsDir).filter(f => f.endsWith('.md')).length;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Discover all workflow directories (those containing WORKFLOW.md) in a local path.
|
|
60
|
+
* Checks if the path itself is a workflow, then scans a top-level workflows/ subdirectory,
|
|
61
|
+
* then falls back to scanning all immediate subdirectories.
|
|
62
|
+
*/
|
|
63
|
+
export function discoverWorkflowsFromRepo(repoPath) {
|
|
64
|
+
const results = [];
|
|
65
|
+
// The path itself may be a single workflow directory.
|
|
66
|
+
if (fs.existsSync(path.join(repoPath, 'WORKFLOW.md'))) {
|
|
67
|
+
const frontmatter = parseWorkflowFrontmatter(repoPath);
|
|
68
|
+
if (frontmatter) {
|
|
69
|
+
return [{
|
|
70
|
+
name: path.basename(repoPath),
|
|
71
|
+
path: repoPath,
|
|
72
|
+
frontmatter,
|
|
73
|
+
subagentCount: countWorkflowSubagents(repoPath),
|
|
74
|
+
}];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Try a workflows/ subdirectory first, then fall back to scanning root subdirectories.
|
|
78
|
+
const workflowsSubdir = path.join(repoPath, 'workflows');
|
|
79
|
+
const scanDir = fs.existsSync(workflowsSubdir) ? workflowsSubdir : repoPath;
|
|
80
|
+
let entries;
|
|
81
|
+
try {
|
|
82
|
+
entries = fs.readdirSync(scanDir, { withFileTypes: true });
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
if (!entry.isDirectory() || entry.name.startsWith('.'))
|
|
89
|
+
continue;
|
|
90
|
+
const workflowPath = path.join(scanDir, entry.name);
|
|
91
|
+
const frontmatter = parseWorkflowFrontmatter(workflowPath);
|
|
92
|
+
if (frontmatter) {
|
|
93
|
+
results.push({
|
|
94
|
+
name: entry.name,
|
|
95
|
+
path: workflowPath,
|
|
96
|
+
frontmatter,
|
|
97
|
+
subagentCount: countWorkflowSubagents(workflowPath),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* List all workflows in central storage.
|
|
105
|
+
* User layer (~/.agents/workflows/) wins over system (~/.agents-system/workflows/).
|
|
106
|
+
*/
|
|
107
|
+
export function listInstalledWorkflows() {
|
|
108
|
+
const result = new Map();
|
|
109
|
+
const extraRepos = getEnabledExtraRepos();
|
|
110
|
+
const searchDirs = [
|
|
111
|
+
getUserWorkflowsDir(),
|
|
112
|
+
getSystemWorkflowsDir(),
|
|
113
|
+
...extraRepos.map(r => path.join(r.dir, 'workflows')),
|
|
114
|
+
];
|
|
115
|
+
for (const dir of searchDirs) {
|
|
116
|
+
if (!fs.existsSync(dir))
|
|
117
|
+
continue;
|
|
118
|
+
let entries;
|
|
119
|
+
try {
|
|
120
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
for (const entry of entries) {
|
|
126
|
+
if (!entry.isDirectory() || entry.name.startsWith('.'))
|
|
127
|
+
continue;
|
|
128
|
+
if (result.has(entry.name))
|
|
129
|
+
continue; // Higher-priority layer already present
|
|
130
|
+
const workflowPath = path.join(dir, entry.name);
|
|
131
|
+
const frontmatter = parseWorkflowFrontmatter(workflowPath);
|
|
132
|
+
if (!frontmatter)
|
|
133
|
+
continue;
|
|
134
|
+
result.set(entry.name, {
|
|
135
|
+
name: entry.name,
|
|
136
|
+
path: workflowPath,
|
|
137
|
+
frontmatter,
|
|
138
|
+
subagentCount: countWorkflowSubagents(workflowPath),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
/** Copy a workflow directory into user central storage (~/.agents/workflows/<name>/). */
|
|
145
|
+
export function installWorkflowCentrally(sourcePath, name) {
|
|
146
|
+
const targetPath = path.join(getUserWorkflowsDir(), name);
|
|
147
|
+
try {
|
|
148
|
+
fs.mkdirSync(getUserWorkflowsDir(), { recursive: true });
|
|
149
|
+
if (fs.existsSync(targetPath)) {
|
|
150
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
151
|
+
}
|
|
152
|
+
fs.cpSync(sourcePath, targetPath, { recursive: true });
|
|
153
|
+
return { success: true };
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
return { success: false, error: err.message };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** Move a workflow from user central storage to trash. */
|
|
160
|
+
export function removeWorkflow(name) {
|
|
161
|
+
const sourcePath = path.join(getUserWorkflowsDir(), name);
|
|
162
|
+
if (!fs.existsSync(sourcePath)) {
|
|
163
|
+
return { success: false, error: `Workflow '${name}' not found in ~/.agents/workflows/` };
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const trashDir = getTrashWorkflowsDir();
|
|
167
|
+
fs.mkdirSync(trashDir, { recursive: true });
|
|
168
|
+
fs.renameSync(sourcePath, path.join(trashDir, `${name}-${Date.now()}`));
|
|
169
|
+
return { success: true };
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
return { success: false, error: err.message };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** List workflow names synced into a specific agent version home (at {versionHome}/workflows/). */
|
|
176
|
+
export function listWorkflowsForAgent(_agent, versionHome) {
|
|
177
|
+
const workflowsDir = path.join(versionHome, 'workflows');
|
|
178
|
+
if (!fs.existsSync(workflowsDir))
|
|
179
|
+
return [];
|
|
180
|
+
try {
|
|
181
|
+
return fs.readdirSync(workflowsDir, { withFileTypes: true })
|
|
182
|
+
.filter(d => d.isDirectory() && fs.existsSync(path.join(workflowsDir, d.name, 'WORKFLOW.md')))
|
|
183
|
+
.map(d => d.name);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/** Copy a workflow directory into a version home at {versionHome}/workflows/<name>/. */
|
|
190
|
+
export function syncWorkflowToVersion(workflowPath, name, _agent, versionHome) {
|
|
191
|
+
const targetDir = path.join(versionHome, 'workflows', name);
|
|
192
|
+
try {
|
|
193
|
+
fs.mkdirSync(path.join(versionHome, 'workflows'), { recursive: true });
|
|
194
|
+
if (fs.existsSync(targetDir)) {
|
|
195
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
196
|
+
}
|
|
197
|
+
fs.cpSync(workflowPath, targetDir, { recursive: true });
|
|
198
|
+
return { success: true };
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
return { success: false, error: err.message };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/** Remove a workflow from a specific agent version home. */
|
|
205
|
+
export function removeWorkflowFromVersion(agent, version, name) {
|
|
206
|
+
const versionHome = getVersionHomePath(agent, version);
|
|
207
|
+
const targetDir = path.join(versionHome, 'workflows', name);
|
|
208
|
+
if (!fs.existsSync(targetDir)) {
|
|
209
|
+
return { success: false, error: `Workflow '${name}' not synced to ${agent}@${version}` };
|
|
210
|
+
}
|
|
211
|
+
try {
|
|
212
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
213
|
+
return { success: true };
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
return { success: false, error: err.message };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/** Iterate all installed (agent, version) pairs that support workflows. */
|
|
220
|
+
export function iterWorkflowsCapableVersions(filter) {
|
|
221
|
+
const result = [];
|
|
222
|
+
for (const agentId of WORKFLOW_CAPABLE_AGENTS) {
|
|
223
|
+
if (filter?.agent && filter.agent !== agentId)
|
|
224
|
+
continue;
|
|
225
|
+
const versions = listInstalledVersions(agentId);
|
|
226
|
+
for (const version of versions) {
|
|
227
|
+
if (filter?.version && filter.version !== version)
|
|
228
|
+
continue;
|
|
229
|
+
result.push({ agent: agentId, version });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return result;
|
|
233
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phnx-labs/agents-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -66,9 +66,7 @@
|
|
|
66
66
|
"node": ">=22.5.0"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@aws-sdk/client-s3": "3.1033.0",
|
|
70
69
|
"@inquirer/prompts": "^7.0.0",
|
|
71
|
-
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
72
70
|
"@types/proper-lockfile": "^4.1.4",
|
|
73
71
|
"@xterm/headless": "^6.0.0",
|
|
74
72
|
"@zed-industries/agent-client-protocol": "^0.4.5",
|
|
@@ -81,7 +79,6 @@
|
|
|
81
79
|
"node-pty": "1.1.0",
|
|
82
80
|
"ora": "^8.1.0",
|
|
83
81
|
"proper-lockfile": "^4.1.2",
|
|
84
|
-
"semver": "^7.6.0",
|
|
85
82
|
"simple-git": "3.36.0",
|
|
86
83
|
"smol-toml": "^1.6.1",
|
|
87
84
|
"yaml": "^2.8.3"
|
|
@@ -90,7 +87,6 @@
|
|
|
90
87
|
"@types/diff": "^6.0.0",
|
|
91
88
|
"@types/marked-terminal": "^6.1.1",
|
|
92
89
|
"@types/node": "^22.0.0",
|
|
93
|
-
"@types/semver": "^7.5.0",
|
|
94
90
|
"tsx": "^4.19.0",
|
|
95
91
|
"typescript": "^5.5.0",
|
|
96
92
|
"vitest": "^2.0.0"
|
package/scripts/postinstall.js
CHANGED
|
@@ -9,7 +9,7 @@ import * as os from 'os';
|
|
|
9
9
|
import * as readline from 'readline';
|
|
10
10
|
|
|
11
11
|
const HOME = os.homedir();
|
|
12
|
-
const SHIMS_DIR = path.join(HOME, '.agents
|
|
12
|
+
const SHIMS_DIR = path.join(HOME, '.agents', '.cache', 'shims');
|
|
13
13
|
const SYSTEM_DIR = path.join(HOME, '.agents-system');
|
|
14
14
|
const USER_DIR = path.join(HOME, '.agents');
|
|
15
15
|
|
|
@@ -20,7 +20,7 @@ if (!isGlobalInstall) {
|
|
|
20
20
|
fs.mkdirSync(USER_DIR, { recursive: true, mode: 0o700 });
|
|
21
21
|
console.log(`
|
|
22
22
|
agents-cli installed locally.
|
|
23
|
-
To complete setup, run: npx agents
|
|
23
|
+
To complete setup, run: npx agents setup
|
|
24
24
|
`);
|
|
25
25
|
process.exit(0);
|
|
26
26
|
}
|
|
@@ -111,7 +111,8 @@ const exportLine = shellName === 'fish'
|
|
|
111
111
|
? `fish_add_path ${SHIMS_DIR}`
|
|
112
112
|
: `export PATH="${SHIMS_DIR}:$PATH"`;
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
// Shorthands that delegate to `agents <name>` — written unconditionally on install.
|
|
115
|
+
const ALIASES = ['sessions', 'secrets', 'browser', 'pty', 'teams'];
|
|
115
116
|
|
|
116
117
|
function writeAliasShims() {
|
|
117
118
|
const written = [];
|
|
@@ -124,6 +125,27 @@ function writeAliasShims() {
|
|
|
124
125
|
return written;
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
function getVersion() {
|
|
129
|
+
const pkgPath = new URL('../package.json', import.meta.url).pathname;
|
|
130
|
+
try {
|
|
131
|
+
return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).version;
|
|
132
|
+
} catch { return null; }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getChangelogSection(version) {
|
|
136
|
+
const changelogPath = new URL('../CHANGELOG.md', import.meta.url).pathname;
|
|
137
|
+
if (!fs.existsSync(changelogPath)) return null;
|
|
138
|
+
const lines = fs.readFileSync(changelogPath, 'utf-8').split('\n');
|
|
139
|
+
let inSection = false;
|
|
140
|
+
const section = [];
|
|
141
|
+
for (const line of lines) {
|
|
142
|
+
if (line.startsWith(`## ${version}`)) { inSection = true; continue; }
|
|
143
|
+
if (inSection && line.startsWith('## ')) break;
|
|
144
|
+
if (inSection) section.push(line);
|
|
145
|
+
}
|
|
146
|
+
return section.length ? section.join('\n').trim() : null;
|
|
147
|
+
}
|
|
148
|
+
|
|
127
149
|
function ask(question) {
|
|
128
150
|
return new Promise((resolve) => {
|
|
129
151
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -134,33 +156,18 @@ function ask(question) {
|
|
|
134
156
|
});
|
|
135
157
|
}
|
|
136
158
|
|
|
137
|
-
|
|
138
|
-
if (!
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
so you can type \`sessions\` instead of \`agents sessions\`.
|
|
143
|
-
|
|
144
|
-
1) Let's do it
|
|
145
|
-
2) Skip this time
|
|
146
|
-
3) I'll do it manually if needed
|
|
147
|
-
`);
|
|
148
|
-
const answer = await ask('Choose [1/2/3] (default 1): ');
|
|
149
|
-
if (answer === '' || answer === '1') return 'install';
|
|
150
|
-
if (answer === '3') return 'manual';
|
|
151
|
-
return 'skip';
|
|
159
|
+
function isAlreadyConfigured(rcFile) {
|
|
160
|
+
if (!fs.existsSync(rcFile)) return false;
|
|
161
|
+
const content = fs.readFileSync(rcFile, 'utf-8');
|
|
162
|
+
// Accept either the new path or the legacy ~/.agents-system/shims path
|
|
163
|
+
return content.includes('.agents/.cache/shims') || content.includes('.agents-system/shims');
|
|
152
164
|
}
|
|
153
165
|
|
|
154
166
|
async function main() {
|
|
155
167
|
// Opt-in: AGENTS_INIT_SHELL=1 npm install -g @phnx-labs/agents-cli
|
|
156
168
|
if (process.env.AGENTS_INIT_SHELL === '1') {
|
|
157
169
|
const rcFile = getShellRc();
|
|
158
|
-
|
|
159
|
-
if (fs.existsSync(rcFile)) {
|
|
160
|
-
const content = fs.readFileSync(rcFile, 'utf-8');
|
|
161
|
-
alreadyConfigured = content.includes('.agents-system/shims');
|
|
162
|
-
}
|
|
163
|
-
if (!alreadyConfigured) {
|
|
170
|
+
if (!isAlreadyConfigured(rcFile)) {
|
|
164
171
|
const addition = `\n# agents-cli: version switching for AI coding agents\n${exportLine}\n`;
|
|
165
172
|
fs.mkdirSync(path.dirname(rcFile), { recursive: true });
|
|
166
173
|
fs.appendFileSync(rcFile, addition);
|
|
@@ -169,53 +176,47 @@ async function main() {
|
|
|
169
176
|
}
|
|
170
177
|
writeAliasShims();
|
|
171
178
|
console.log(` Installed bare-command aliases: ${ALIASES.join(', ')}\n`);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// Default: offer to auto-add shims to PATH (like homebrew does)
|
|
176
|
-
const rcFile = getShellRc();
|
|
177
|
-
let alreadyConfigured = false;
|
|
178
|
-
if (fs.existsSync(rcFile)) {
|
|
179
|
-
const content = fs.readFileSync(rcFile, 'utf-8');
|
|
180
|
-
alreadyConfigured = content.includes('.agents-system/shims');
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
console.log(`\nagents-cli installed.`);
|
|
179
|
+
} else {
|
|
180
|
+
// Default: offer to auto-add shims to PATH (like homebrew does)
|
|
181
|
+
const rcFile = getShellRc();
|
|
184
182
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
183
|
+
console.log(`\nagents-cli installed.`);
|
|
184
|
+
|
|
185
|
+
if (!isAlreadyConfigured(rcFile) && process.stdin.isTTY && process.stdout.isTTY) {
|
|
186
|
+
const answer = await ask(`\nAdd shims to PATH in ~/${path.basename(rcFile)}? [Y/n] `);
|
|
187
|
+
if (answer === '' || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
188
|
+
const addition = `\n# agents-cli: version switching for AI coding agents\n${exportLine}\n`;
|
|
189
|
+
fs.mkdirSync(path.dirname(rcFile), { recursive: true });
|
|
190
|
+
fs.appendFileSync(rcFile, addition);
|
|
191
|
+
console.log(`\n Added ${SHIMS_DIR} to PATH in ${path.basename(rcFile)}`);
|
|
192
|
+
console.log(` Restart your shell or run: source ~/${path.basename(rcFile)}\n`);
|
|
193
|
+
} else {
|
|
194
|
+
console.log(`
|
|
195
195
|
To enable version-aware shims, add this to your shell config:
|
|
196
196
|
|
|
197
197
|
${exportLine}
|
|
198
198
|
`);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
}
|
|
200
|
+
} else if (!isAlreadyConfigured(rcFile)) {
|
|
201
|
+
console.log(`
|
|
202
202
|
To enable version-aware shims, add this to your shell config:
|
|
203
203
|
|
|
204
204
|
${exportLine}
|
|
205
205
|
`);
|
|
206
|
-
|
|
206
|
+
}
|
|
207
207
|
|
|
208
|
-
const choice = await promptForAliases();
|
|
209
|
-
if (choice === 'install') {
|
|
210
208
|
const written = writeAliasShims();
|
|
211
|
-
console.log(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
209
|
+
console.log(` Installed shorthands: ${written.join(', ')}`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const version = getVersion();
|
|
213
|
+
if (version) {
|
|
214
|
+
const section = getChangelogSection(version);
|
|
215
|
+
if (section) {
|
|
216
|
+
console.log(`\nWhat's new in ${version}:\n`);
|
|
217
|
+
console.log(section);
|
|
218
|
+
console.log('');
|
|
217
219
|
}
|
|
218
|
-
console.log('');
|
|
219
220
|
}
|
|
220
221
|
}
|
|
221
222
|
|
package/dist/commands/fork.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Repository fork command.
|
|
3
|
-
*
|
|
4
|
-
* Registers the `agents fork` command which forks the default system
|
|
5
|
-
* config repo to the user's GitHub account, reconfigures remotes
|
|
6
|
-
* (origin -> user fork, upstream -> system repo), and pushes.
|
|
7
|
-
*/
|
|
8
|
-
import type { Command } from 'commander';
|
|
9
|
-
/** Register the `agents fork` command. */
|
|
10
|
-
export declare function registerForkCommand(program: Command): void;
|
package/dist/commands/fork.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Repository fork command.
|
|
3
|
-
*
|
|
4
|
-
* Registers the `agents fork` command which forks the default system
|
|
5
|
-
* config repo to the user's GitHub account, reconfigures remotes
|
|
6
|
-
* (origin -> user fork, upstream -> system repo), and pushes.
|
|
7
|
-
*/
|
|
8
|
-
import chalk from 'chalk';
|
|
9
|
-
import ora from 'ora';
|
|
10
|
-
import { getAgentsDir, ensureAgentsDir } from '../lib/state.js';
|
|
11
|
-
import { isGitRepo, isSystemRepoOrigin, getGitHubUsername, hasLocalChanges, setUpstreamRemote, setRemoteUrl, getRemoteUrl, } from '../lib/git.js';
|
|
12
|
-
import { DEFAULT_SYSTEM_REPO, systemRepoSlug } from '../lib/types.js';
|
|
13
|
-
import { isPromptCancelled } from './utils.js';
|
|
14
|
-
/** Register the `agents fork` command. */
|
|
15
|
-
export function registerForkCommand(program) {
|
|
16
|
-
program
|
|
17
|
-
.command('fork')
|
|
18
|
-
.description('Copy the default config repo to your own GitHub so you can push changes. Runs once after init.')
|
|
19
|
-
.addHelpText('after', `
|
|
20
|
-
Examples:
|
|
21
|
-
# Fork the default repo to your GitHub account
|
|
22
|
-
agents fork
|
|
23
|
-
|
|
24
|
-
When to use:
|
|
25
|
-
- You initialized with 'agents init' using the default config
|
|
26
|
-
- You've customized commands, skills, or settings
|
|
27
|
-
- You want to save your changes to your own GitHub repo
|
|
28
|
-
|
|
29
|
-
What it does:
|
|
30
|
-
1. Creates a fork of the default repo under your GitHub account (gh:yourname/.agents)
|
|
31
|
-
2. Reconfigures remotes: origin -> your fork, upstream -> default repo
|
|
32
|
-
3. Commits any local changes you've made
|
|
33
|
-
4. Pushes everything to your new fork
|
|
34
|
-
|
|
35
|
-
After forking:
|
|
36
|
-
- 'agents push' sends your changes to your fork
|
|
37
|
-
- 'agents pull --upstream' pulls updates from the default repo
|
|
38
|
-
|
|
39
|
-
Requirements:
|
|
40
|
-
- GitHub CLI authenticated (run 'gh auth login' if needed)
|
|
41
|
-
- ~/.agents/ must be tracking the default repo (not already forked)
|
|
42
|
-
`)
|
|
43
|
-
.action(async () => {
|
|
44
|
-
try {
|
|
45
|
-
const agentsDir = getAgentsDir();
|
|
46
|
-
ensureAgentsDir();
|
|
47
|
-
// Check if ~/.agents/ is a git repo
|
|
48
|
-
if (!isGitRepo(agentsDir)) {
|
|
49
|
-
console.log(chalk.yellow('~/.agents/ is not a git repository.'));
|
|
50
|
-
console.log(chalk.gray('\nInitialize first:'));
|
|
51
|
-
console.log(chalk.cyan(' agents pull'));
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
// Check if already forked (origin is not system repo)
|
|
55
|
-
if (!await isSystemRepoOrigin(agentsDir)) {
|
|
56
|
-
const currentOrigin = await getRemoteUrl(agentsDir);
|
|
57
|
-
console.log(chalk.green('Already forked!'));
|
|
58
|
-
console.log(chalk.gray(`\nOrigin: ${currentOrigin}`));
|
|
59
|
-
console.log(chalk.gray('\nTo push your changes:'));
|
|
60
|
-
console.log(chalk.cyan(' agents push'));
|
|
61
|
-
console.log(chalk.gray('\nTo pull updates from system repo:'));
|
|
62
|
-
console.log(chalk.cyan(' agents pull --upstream'));
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
// Get GitHub username
|
|
66
|
-
const spinner = ora('Checking GitHub...').start();
|
|
67
|
-
const username = await getGitHubUsername();
|
|
68
|
-
if (!username) {
|
|
69
|
-
spinner.fail('GitHub CLI not authenticated');
|
|
70
|
-
console.log(chalk.gray('\nTo authenticate:'));
|
|
71
|
-
console.log(chalk.cyan(' gh auth login'));
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
spinner.succeed(`Logged in as ${username}`);
|
|
75
|
-
// Fork the system repo
|
|
76
|
-
const forkSpinner = ora('Forking system repo...').start();
|
|
77
|
-
const { execFile } = await import('child_process');
|
|
78
|
-
const { promisify } = await import('util');
|
|
79
|
-
const execFileAsync = promisify(execFile);
|
|
80
|
-
const repoSlug = systemRepoSlug(DEFAULT_SYSTEM_REPO);
|
|
81
|
-
try {
|
|
82
|
-
// gh repo fork creates a fork and optionally clones it
|
|
83
|
-
// We just want to create the fork on GitHub, not clone
|
|
84
|
-
await execFileAsync('gh', ['repo', 'fork', repoSlug, '--clone=false']);
|
|
85
|
-
forkSpinner.succeed(`Forked to ${username}/.agents`);
|
|
86
|
-
}
|
|
87
|
-
catch (err) {
|
|
88
|
-
const errorMsg = err.message;
|
|
89
|
-
// Check if fork already exists
|
|
90
|
-
if (errorMsg.includes('already exists') || errorMsg.includes('409')) {
|
|
91
|
-
forkSpinner.info(`Fork ${username}/.agents already exists`);
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
forkSpinner.fail(`Failed to fork: ${errorMsg}`);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// Reconfigure remotes
|
|
99
|
-
const remoteSpinner = ora('Reconfiguring remotes...').start();
|
|
100
|
-
// Set current origin as upstream
|
|
101
|
-
await setUpstreamRemote(agentsDir, `https://github.com/${repoSlug}.git`);
|
|
102
|
-
// Set user's fork as new origin
|
|
103
|
-
await setRemoteUrl(agentsDir, `https://github.com/${username}/.agents.git`);
|
|
104
|
-
remoteSpinner.succeed('Reconfigured remotes');
|
|
105
|
-
console.log(chalk.gray(` origin -> ${username}/.agents`));
|
|
106
|
-
console.log(chalk.gray(` upstream -> ${repoSlug}`));
|
|
107
|
-
// Commit any local changes
|
|
108
|
-
if (await hasLocalChanges(agentsDir)) {
|
|
109
|
-
const commitSpinner = ora('Committing local changes...').start();
|
|
110
|
-
const simpleGit = (await import('simple-git')).default;
|
|
111
|
-
const git = simpleGit(agentsDir);
|
|
112
|
-
await git.add('-A');
|
|
113
|
-
await git.commit('Local changes before fork');
|
|
114
|
-
commitSpinner.succeed('Committed local changes');
|
|
115
|
-
}
|
|
116
|
-
// Push to new origin
|
|
117
|
-
const pushSpinner = ora('Pushing to your fork...').start();
|
|
118
|
-
try {
|
|
119
|
-
const simpleGit = (await import('simple-git')).default;
|
|
120
|
-
const git = simpleGit(agentsDir);
|
|
121
|
-
await git.push('origin', 'main', ['--set-upstream']);
|
|
122
|
-
pushSpinner.succeed('Pushed to your fork');
|
|
123
|
-
}
|
|
124
|
-
catch (err) {
|
|
125
|
-
pushSpinner.fail(`Push failed: ${err.message}`);
|
|
126
|
-
console.log(chalk.yellow('\nYou can push manually later:'));
|
|
127
|
-
console.log(chalk.cyan(' agents push'));
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
console.log(chalk.green('\nFork complete!'));
|
|
131
|
-
console.log(chalk.gray(`\nView: https://github.com/${username}/.agents`));
|
|
132
|
-
console.log(chalk.gray('\nTo push future changes:'));
|
|
133
|
-
console.log(chalk.cyan(' agents push'));
|
|
134
|
-
console.log(chalk.gray('\nTo pull updates from system repo:'));
|
|
135
|
-
console.log(chalk.cyan(' agents pull --upstream'));
|
|
136
|
-
}
|
|
137
|
-
catch (err) {
|
|
138
|
-
if (isPromptCancelled(err)) {
|
|
139
|
-
console.log(chalk.yellow('\nCancelled'));
|
|
140
|
-
process.exit(0);
|
|
141
|
-
}
|
|
142
|
-
console.error(chalk.red(err.message));
|
|
143
|
-
process.exit(1);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
}
|