@memnexus-ai/mx-agent-cli 0.1.50 → 0.1.52
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/dist/__tests__/claude-sync.test.js +20 -136
- package/dist/__tests__/claude-sync.test.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +18 -137
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/init.d.ts +2 -12
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +21 -70
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.d.ts +0 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +9 -39
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +18 -225
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +15 -28
- package/dist/commands/update.js.map +1 -1
- package/dist/index.js +1 -16
- package/dist/index.js.map +1 -1
- package/dist/lib/claude.d.ts +4 -15
- package/dist/lib/claude.d.ts.map +1 -1
- package/dist/lib/claude.js +44 -331
- package/dist/lib/claude.js.map +1 -1
- package/dist/lib/memory.d.ts +6 -1
- package/dist/lib/memory.d.ts.map +1 -1
- package/dist/lib/memory.js +11 -8
- package/dist/lib/memory.js.map +1 -1
- package/dist/lib/project-config.d.ts +7 -7
- package/dist/lib/project-config.d.ts.map +1 -1
- package/dist/lib/project-config.js +6 -3
- package/dist/lib/project-config.js.map +1 -1
- package/dist/lib/templates.d.ts +1 -39
- package/dist/lib/templates.d.ts.map +1 -1
- package/dist/lib/templates.js +25 -1239
- package/dist/lib/templates.js.map +1 -1
- package/dist/lib/worktree.d.ts +1 -19
- package/dist/lib/worktree.d.ts.map +1 -1
- package/dist/lib/worktree.js +4 -55
- package/dist/lib/worktree.js.map +1 -1
- package/package.json +2 -3
- package/README.md +0 -104
- package/dist/commands/new.d.ts +0 -30
- package/dist/commands/new.d.ts.map +0 -1
- package/dist/commands/new.js +0 -150
- package/dist/commands/new.js.map +0 -1
package/dist/commands/init.js
CHANGED
|
@@ -7,22 +7,12 @@
|
|
|
7
7
|
* Creates:
|
|
8
8
|
* <projectRoot>/
|
|
9
9
|
* mx-agent.config.json
|
|
10
|
-
* .claude/
|
|
11
|
-
* settings.json (Claude Code permissions + hook registration)
|
|
12
|
-
* hooks/
|
|
13
|
-
* worktree-guard.sh (worktree isolation)
|
|
14
|
-
* auto-checkpoint.sh (pre-compact memory checkpoint)
|
|
15
|
-
* reload-checkpoint.sh (session resume from checkpoint)
|
|
16
|
-
* set-terminal-appearance.sh (VS Code tab title/color)
|
|
17
10
|
* mx-agent-system/
|
|
18
11
|
* agent-config/
|
|
19
12
|
* CLAUDE.md.template
|
|
20
|
-
* settings.json
|
|
13
|
+
* settings.json
|
|
21
14
|
* hooks/
|
|
22
15
|
* worktree-guard.sh
|
|
23
|
-
* auto-checkpoint.sh
|
|
24
|
-
* reload-checkpoint.sh
|
|
25
|
-
* set-terminal-appearance.sh
|
|
26
16
|
* teams/ (empty, for team catalog entries)
|
|
27
17
|
* roleguides/ (empty, for roleguide markdown files)
|
|
28
18
|
*/
|
|
@@ -30,8 +20,7 @@ import { existsSync, mkdirSync, writeFileSync, chmodSync } from 'fs';
|
|
|
30
20
|
import { join } from 'path';
|
|
31
21
|
import chalk from 'chalk';
|
|
32
22
|
import { findProjectRoot } from '../lib/worktree.js';
|
|
33
|
-
import { SETTINGS_JSON_TEMPLATE, WORKTREE_GUARD_SH_TEMPLATE,
|
|
34
|
-
import { validateProjectConfig } from '../lib/project-config.js';
|
|
23
|
+
import { SETTINGS_JSON_TEMPLATE, WORKTREE_GUARD_SH_TEMPLATE, CLAUDE_MD_TEMPLATE } from '../lib/templates.js';
|
|
35
24
|
function logCreated(path) {
|
|
36
25
|
console.log(chalk.green(' ✓ created ') + chalk.dim(path));
|
|
37
26
|
}
|
|
@@ -57,28 +46,8 @@ function writeFile(filePath, content, force) {
|
|
|
57
46
|
export async function runInit(options) {
|
|
58
47
|
const { force = false } = options;
|
|
59
48
|
let projectRoot;
|
|
60
|
-
if (options.projectRoot) {
|
|
61
|
-
projectRoot = options.projectRoot;
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
try {
|
|
65
|
-
projectRoot = findProjectRoot();
|
|
66
|
-
}
|
|
67
|
-
catch (err) {
|
|
68
|
-
console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
69
|
-
process.exit(1);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
// Validate CLI option values before any file writes
|
|
73
|
-
const configFromOpts = {};
|
|
74
|
-
if (options.baseBranch)
|
|
75
|
-
configFromOpts.baseBranch = options.baseBranch;
|
|
76
|
-
if (options.worktreeDir)
|
|
77
|
-
configFromOpts.worktreeDir = options.worktreeDir;
|
|
78
|
-
if (options.configSource)
|
|
79
|
-
configFromOpts.configSource = options.configSource;
|
|
80
49
|
try {
|
|
81
|
-
|
|
50
|
+
projectRoot = findProjectRoot();
|
|
82
51
|
}
|
|
83
52
|
catch (err) {
|
|
84
53
|
console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
@@ -88,6 +57,7 @@ export async function runInit(options) {
|
|
|
88
57
|
const baseBranch = options.baseBranch ?? 'main';
|
|
89
58
|
const worktreeDir = options.worktreeDir ?? '.worktrees';
|
|
90
59
|
const configSource = options.configSource ?? 'mx-agent-system/agent-config';
|
|
60
|
+
const projectSlug = options.projectSlug ?? '';
|
|
91
61
|
console.log();
|
|
92
62
|
console.log(chalk.cyan.bold('Initialising mx-agent'));
|
|
93
63
|
console.log(chalk.dim('─'.repeat(50)));
|
|
@@ -95,18 +65,23 @@ export async function runInit(options) {
|
|
|
95
65
|
console.log(` ${chalk.bold('Base branch:')} ${baseBranch}`);
|
|
96
66
|
console.log(` ${chalk.bold('Worktree dir:')} ${worktreeDir}`);
|
|
97
67
|
console.log(` ${chalk.bold('Config source:')} ${configSource}`);
|
|
68
|
+
if (projectSlug)
|
|
69
|
+
console.log(` ${chalk.bold('Project slug:')} ${projectSlug}`);
|
|
98
70
|
if (force)
|
|
99
71
|
console.log(chalk.yellow(' --force: existing files will be overwritten'));
|
|
100
72
|
console.log();
|
|
101
73
|
// ── 1. mx-agent.config.json ────────────────────────────────────────
|
|
102
74
|
const configPath = join(projectRoot, 'mx-agent.config.json');
|
|
103
|
-
const
|
|
75
|
+
const configObj = {
|
|
104
76
|
baseBranch,
|
|
105
77
|
worktreeDir,
|
|
106
78
|
configSource,
|
|
107
79
|
teamsDir: 'mx-agent-system/teams',
|
|
108
80
|
roleguidesDir: 'mx-agent-system/roleguides',
|
|
109
|
-
}
|
|
81
|
+
};
|
|
82
|
+
if (projectSlug)
|
|
83
|
+
configObj.projectSlug = projectSlug;
|
|
84
|
+
const configContent = JSON.stringify(configObj, null, 2) + '\n';
|
|
110
85
|
writeFile(configPath, configContent, force);
|
|
111
86
|
// ── 2. agent-config/settings.json ──────────────────────────────────
|
|
112
87
|
const settingsPath = join(projectRoot, configSource, 'settings.json');
|
|
@@ -114,42 +89,18 @@ export async function runInit(options) {
|
|
|
114
89
|
// ── 3. agent-config/CLAUDE.md.template ─────────────────────────────
|
|
115
90
|
const claudeMdTemplatePath = join(projectRoot, configSource, 'CLAUDE.md.template');
|
|
116
91
|
writeFile(claudeMdTemplatePath, CLAUDE_MD_TEMPLATE, force);
|
|
117
|
-
// ── 4. agent-config/hooks/
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
['reload-checkpoint.sh', RELOAD_CHECKPOINT_SH_TEMPLATE],
|
|
124
|
-
['set-terminal-appearance.sh', SET_TERMINAL_APPEARANCE_SH_TEMPLATE],
|
|
125
|
-
];
|
|
126
|
-
for (const [filename, content] of hookFiles) {
|
|
127
|
-
const hookPath = join(projectRoot, configSource, 'hooks', filename);
|
|
128
|
-
const written = writeFile(hookPath, content, force);
|
|
129
|
-
if (written) {
|
|
130
|
-
try {
|
|
131
|
-
chmodSync(hookPath, 0o755);
|
|
132
|
-
}
|
|
133
|
-
catch { /* non-fatal */ }
|
|
92
|
+
// ── 4. agent-config/hooks/worktree-guard.sh ────────────────────────
|
|
93
|
+
const guardPath = join(projectRoot, configSource, 'hooks', 'worktree-guard.sh');
|
|
94
|
+
const guardWritten = writeFile(guardPath, WORKTREE_GUARD_SH_TEMPLATE, force);
|
|
95
|
+
if (guardWritten) {
|
|
96
|
+
try {
|
|
97
|
+
chmodSync(guardPath, 0o755);
|
|
134
98
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// Claude Code reads .claude/settings.json from the working directory.
|
|
138
|
-
// Without this, new projects prompt for every tool use until the first
|
|
139
|
-
// worktree is created (which deploys via syncClaudeConfig).
|
|
140
|
-
const dotClaudeSettings = join(projectRoot, '.claude', 'settings.json');
|
|
141
|
-
writeFile(dotClaudeSettings, SETTINGS_JSON_TEMPLATE + '\n', force);
|
|
142
|
-
for (const [filename, content] of hookFiles) {
|
|
143
|
-
const hookPath = join(projectRoot, '.claude', 'hooks', filename);
|
|
144
|
-
const written = writeFile(hookPath, content, force);
|
|
145
|
-
if (written) {
|
|
146
|
-
try {
|
|
147
|
-
chmodSync(hookPath, 0o755);
|
|
148
|
-
}
|
|
149
|
-
catch { /* non-fatal */ }
|
|
99
|
+
catch {
|
|
100
|
+
// Non-fatal — user can chmod manually
|
|
150
101
|
}
|
|
151
102
|
}
|
|
152
|
-
// ──
|
|
103
|
+
// ── 5. teams/ and roleguides/ (empty directories, just ensure they exist) ──
|
|
153
104
|
const teamsDir = join(projectRoot, 'mx-agent-system', 'teams');
|
|
154
105
|
const roleguidesDir = join(projectRoot, 'mx-agent-system', 'roleguides');
|
|
155
106
|
for (const dir of [teamsDir, roleguidesDir]) {
|
|
@@ -161,7 +112,7 @@ export async function runInit(options) {
|
|
|
161
112
|
console.log(chalk.dim(' · exists ') + chalk.dim(dir + '/'));
|
|
162
113
|
}
|
|
163
114
|
}
|
|
164
|
-
// ──
|
|
115
|
+
// ── 6. Summary ─────────────────────────────────────────────────────
|
|
165
116
|
console.log();
|
|
166
117
|
console.log(chalk.green.bold('Done!'));
|
|
167
118
|
console.log(chalk.dim('─'.repeat(50)));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/list.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CA+D7C"}
|
package/dist/commands/list.js
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
* agent list
|
|
3
3
|
*
|
|
4
4
|
* Lists all teams from the catalog with their worktree status.
|
|
5
|
-
* Always shows the built-in team-builder bootstrap agent.
|
|
6
5
|
*/
|
|
7
|
-
import { existsSync } from 'fs';
|
|
8
|
-
import { join } from 'path';
|
|
9
6
|
import chalk from 'chalk';
|
|
10
7
|
import { findProjectRoot, findWorktreePath } from '../lib/worktree.js';
|
|
11
8
|
import { listTeams } from '../lib/catalog.js';
|
|
@@ -21,26 +18,19 @@ export async function runList() {
|
|
|
21
18
|
}
|
|
22
19
|
const cfg = resolveProjectConfig(loadProjectConfig(projectRoot));
|
|
23
20
|
const teams = listTeams(projectRoot, cfg.teamsDir);
|
|
24
|
-
// Resolve team-builder roleguide path and worktree status
|
|
25
|
-
const tbRoleguide = join(cfg.roleguidesDir, 'team-builder.md');
|
|
26
|
-
const tbRoleguideExists = existsSync(join(projectRoot, tbRoleguide));
|
|
27
|
-
const tbWorktreePath = findWorktreePath(projectRoot, 'team-builder', cfg.worktreeDir);
|
|
28
21
|
console.log();
|
|
29
22
|
console.log(chalk.cyan.bold('Agent Teams'));
|
|
30
23
|
console.log(chalk.dim('─'.repeat(70)));
|
|
31
|
-
|
|
32
|
-
const colW = { name: 18, display: 24, status: 12, roleguide: 40 };
|
|
33
|
-
const headerLine = ` ${'Team'.padEnd(colW.name)}${'Display Name'.padEnd(colW.display)}${'Status'.padEnd(colW.status)}Roleguide`;
|
|
34
|
-
const divider = chalk.dim(' ' + '─'.repeat(colW.name + colW.display + colW.status + colW.roleguide));
|
|
35
|
-
if (teams.length === 0 && !tbRoleguideExists) {
|
|
24
|
+
if (teams.length === 0) {
|
|
36
25
|
console.log(chalk.dim(' No teams found.'));
|
|
37
|
-
console.log(chalk.dim('
|
|
26
|
+
console.log(chalk.dim(' Create one with: agent create --roleguide <path>'));
|
|
38
27
|
console.log();
|
|
39
28
|
return;
|
|
40
29
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
// Header row
|
|
31
|
+
const colW = { name: 18, display: 24, status: 12, roleguide: 40 };
|
|
32
|
+
console.log(chalk.bold(` ${'Team'.padEnd(colW.name)}${'Display Name'.padEnd(colW.display)}${'Status'.padEnd(colW.status)}Roleguide`));
|
|
33
|
+
console.log(chalk.dim(' ' + '─'.repeat(colW.name + colW.display + colW.status + colW.roleguide)));
|
|
44
34
|
for (const team of teams) {
|
|
45
35
|
const worktreePath = findWorktreePath(projectRoot, team.slug, cfg.worktreeDir);
|
|
46
36
|
let statusText;
|
|
@@ -50,6 +40,8 @@ export async function runList() {
|
|
|
50
40
|
statusColor = chalk.dim;
|
|
51
41
|
}
|
|
52
42
|
else {
|
|
43
|
+
// A simple heuristic: if the worktree path exists, it's "stopped" unless
|
|
44
|
+
// we can detect a running process (which is complex — just report "ready").
|
|
53
45
|
statusText = 'ready';
|
|
54
46
|
statusColor = chalk.green;
|
|
55
47
|
}
|
|
@@ -61,30 +53,8 @@ export async function runList() {
|
|
|
61
53
|
`${statusColor(statusText.padEnd(colW.status))}` +
|
|
62
54
|
chalk.dim(roleguideDisplay));
|
|
63
55
|
}
|
|
64
|
-
// ── Built-in: team-builder ─────────────────────────────────────────
|
|
65
|
-
if (tbRoleguideExists) {
|
|
66
|
-
if (teams.length > 0) {
|
|
67
|
-
console.log(divider);
|
|
68
|
-
}
|
|
69
|
-
const tbStatus = tbWorktreePath ? 'ready' : 'start here';
|
|
70
|
-
const tbStatusColor = tbWorktreePath ? chalk.green : chalk.cyan;
|
|
71
|
-
const tbDisplay = 'Team Builder';
|
|
72
|
-
const tbRoleguideDisplay = tbRoleguide.length > colW.roleguide - 2
|
|
73
|
-
? '...' + tbRoleguide.slice(-(colW.roleguide - 5))
|
|
74
|
-
: tbRoleguide;
|
|
75
|
-
console.log(` ${chalk.cyan('team-builder'.padEnd(colW.name))}` +
|
|
76
|
-
`${tbDisplay.padEnd(colW.display)}` +
|
|
77
|
-
`${tbStatusColor(tbStatus.padEnd(colW.status))}` +
|
|
78
|
-
chalk.dim(tbRoleguideDisplay) +
|
|
79
|
-
chalk.dim(' [bootstrap]'));
|
|
80
|
-
}
|
|
81
56
|
console.log();
|
|
82
|
-
|
|
83
|
-
console.log(chalk.dim(` ${teams.length} team(s) in catalog.`));
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
console.log(chalk.dim(' No catalog teams yet.') + ' ' + chalk.cyan('Run: mx-agent start team-builder'));
|
|
87
|
-
}
|
|
57
|
+
console.log(chalk.dim(` ${teams.length} team(s) in catalog.`));
|
|
88
58
|
console.log();
|
|
89
59
|
}
|
|
90
60
|
//# sourceMappingURL=list.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA2InE"}
|
package/dist/commands/start.js
CHANGED
|
@@ -4,11 +4,9 @@
|
|
|
4
4
|
* Starts an isolated Claude session for a named team in its worktree.
|
|
5
5
|
*/
|
|
6
6
|
import { spawnSync } from 'child_process';
|
|
7
|
-
import { existsSync, mkdirSync } from 'fs';
|
|
8
|
-
import { join } from 'path';
|
|
9
7
|
import chalk from 'chalk';
|
|
10
|
-
import { findProjectRoot, findWorktreePath, getSlot, getCurrentBranch,
|
|
11
|
-
import { syncClaudeConfig, deployClaudeMd, writeClaudeLocal, launchClaudeSession
|
|
8
|
+
import { findProjectRoot, findWorktreePath, getSlot, getCurrentBranch, } from '../lib/worktree.js';
|
|
9
|
+
import { syncClaudeConfig, deployClaudeMd, writeClaudeLocal, launchClaudeSession } from '../lib/claude.js';
|
|
12
10
|
import { getTeam } from '../lib/catalog.js';
|
|
13
11
|
import { loadProjectConfig, resolveProjectConfig } from '../lib/project-config.js';
|
|
14
12
|
export async function runStart(options) {
|
|
@@ -23,40 +21,12 @@ export async function runStart(options) {
|
|
|
23
21
|
}
|
|
24
22
|
const cfg = resolveProjectConfig(loadProjectConfig(projectRoot));
|
|
25
23
|
// ── 1. Locate the worktree ─────────────────────────────────────────
|
|
26
|
-
|
|
27
|
-
let autoProvisionedRoleguide;
|
|
24
|
+
const worktreePath = findWorktreePath(projectRoot, name, cfg.worktreeDir);
|
|
28
25
|
if (!worktreePath) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
join(projectRoot, cfg.roleguidesDir, `${name}.md`),
|
|
34
|
-
join(projectRoot, cfg.roleguidesDir, `${name}-leader.md`),
|
|
35
|
-
].find(p => existsSync(p));
|
|
36
|
-
if (candidateRoleguide) {
|
|
37
|
-
// Store relative roleguide path for use in the initial prompt (step 3).
|
|
38
|
-
// Without this, the fallback convention assumes *-leader.md which is
|
|
39
|
-
// wrong for roleguides like team-builder.md.
|
|
40
|
-
autoProvisionedRoleguide = candidateRoleguide.slice(projectRoot.length + 1);
|
|
41
|
-
console.log(chalk.dim(` No worktree found for '${name}' — auto-provisioning from roleguide...`));
|
|
42
|
-
const slot = assignSlot(projectRoot, name, cfg.worktreeDir);
|
|
43
|
-
const worktreeDirName = generateWorktreeDirName(name);
|
|
44
|
-
const branch = generateBranchName(name);
|
|
45
|
-
const newWorktreePath = join(projectRoot, cfg.worktreeDir, worktreeDirName);
|
|
46
|
-
mkdirSync(join(projectRoot, cfg.worktreeDir), { recursive: true });
|
|
47
|
-
fetchOrigin(projectRoot);
|
|
48
|
-
createGitWorktree(newWorktreePath, branch, cfg.baseBranch, projectRoot);
|
|
49
|
-
addToGitSafeDirectory(newWorktreePath);
|
|
50
|
-
createHuskySymlink(projectRoot, newWorktreePath);
|
|
51
|
-
logSuccess(`Worktree provisioned: ${newWorktreePath}`);
|
|
52
|
-
worktreePath = newWorktreePath;
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
console.error(chalk.red(`Team '${name}' not found.`));
|
|
56
|
-
console.error(chalk.dim(` No worktree found in ${projectRoot}/${cfg.worktreeDir}/ matching '${name}' or '${name}-*'`));
|
|
57
|
-
console.error(chalk.dim(` Run: mx-agent create --roleguide <path> --name ${name}`));
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
26
|
+
console.error(chalk.red(`Team '${name}' not found.`));
|
|
27
|
+
console.error(chalk.dim(` No worktree found in ${projectRoot}/.worktrees/ matching '${name}' or '${name}-*'`));
|
|
28
|
+
console.error(chalk.dim(` Run: agent create --roleguide <path> --name ${name}`));
|
|
29
|
+
process.exit(1);
|
|
60
30
|
}
|
|
61
31
|
// ── 2. Resolve slot ────────────────────────────────────────────────
|
|
62
32
|
// The slot key in .slots.json may be the full dir name (e.g. "retrieval-20260222")
|
|
@@ -77,10 +47,6 @@ export async function runStart(options) {
|
|
|
77
47
|
if (team?.roleguide) {
|
|
78
48
|
roleguide = team.roleguide;
|
|
79
49
|
}
|
|
80
|
-
else if (autoProvisionedRoleguide) {
|
|
81
|
-
// Auto-provision found the actual roleguide file — use it directly
|
|
82
|
-
roleguide = autoProvisionedRoleguide;
|
|
83
|
-
}
|
|
84
50
|
else {
|
|
85
51
|
// Fallback convention
|
|
86
52
|
roleguide = `${cfg.roleguidesDir}/${name}-leader.md`;
|
|
@@ -98,8 +64,6 @@ export async function runStart(options) {
|
|
|
98
64
|
// ── 4. Rebase worktree onto latest origin/<baseBranch> ───────────
|
|
99
65
|
// Ensures the worktree has the latest roleguides, CLI fixes, and
|
|
100
66
|
// templates even if it was created before those commits landed.
|
|
101
|
-
// Rebase failure is blocking — a session must not start on stale state.
|
|
102
|
-
const DIVERGENCE_WARN_THRESHOLD = 20;
|
|
103
67
|
console.log(chalk.dim(` Syncing with origin/${cfg.baseBranch}...`));
|
|
104
68
|
const fetchResult = spawnSync('git', ['fetch', 'origin', cfg.baseBranch], {
|
|
105
69
|
cwd: worktreePath,
|
|
@@ -107,200 +71,30 @@ export async function runStart(options) {
|
|
|
107
71
|
encoding: 'utf-8',
|
|
108
72
|
});
|
|
109
73
|
if (fetchResult.status !== 0) {
|
|
110
|
-
// Fetch failure is non-fatal (offline / no remote) but surfaced clearly.
|
|
111
74
|
console.log(chalk.yellow(` ⚠ Could not fetch origin/${cfg.baseBranch} — starting with local state.`));
|
|
112
|
-
const fetchErr = fetchResult.stderr?.trim();
|
|
113
|
-
if (fetchErr)
|
|
114
|
-
console.log(chalk.dim(` ${fetchErr}`));
|
|
115
75
|
}
|
|
116
76
|
else {
|
|
117
|
-
// ── Divergence check ─────────────────────────────────────────
|
|
118
|
-
const behindR = spawnSync('git', ['rev-list', '--count', `HEAD..origin/${cfg.baseBranch}`], { cwd: worktreePath, stdio: 'pipe', encoding: 'utf-8' });
|
|
119
|
-
const aheadR = spawnSync('git', ['rev-list', '--count', `origin/${cfg.baseBranch}..HEAD`], { cwd: worktreePath, stdio: 'pipe', encoding: 'utf-8' });
|
|
120
|
-
const commitsBehind = parseInt(behindR.stdout.trim(), 10) || 0;
|
|
121
|
-
const commitsAhead = parseInt(aheadR.stdout.trim(), 10) || 0;
|
|
122
|
-
if (commitsBehind >= DIVERGENCE_WARN_THRESHOLD) {
|
|
123
|
-
console.log();
|
|
124
|
-
console.log(chalk.yellow(` ⚠ This branch is ${commitsBehind} commits behind origin/${cfg.baseBranch}.`));
|
|
125
|
-
if (commitsAhead > 0) {
|
|
126
|
-
console.log(chalk.yellow(` It also has ${commitsAhead} unmerged commit${commitsAhead === 1 ? '' : 's'}.`));
|
|
127
|
-
console.log(chalk.yellow(` Consider opening a PR for completed work before continuing to reduce conflict risk.`));
|
|
128
|
-
}
|
|
129
|
-
console.log();
|
|
130
|
-
}
|
|
131
|
-
// ── Auto-stash dirty worktree before rebase ─────────────────
|
|
132
|
-
const statusR = spawnSync('git', ['status', '--porcelain'], {
|
|
133
|
-
cwd: worktreePath, stdio: 'pipe', encoding: 'utf-8',
|
|
134
|
-
});
|
|
135
|
-
const hasDirtyFiles = statusR.stdout.trim().length > 0;
|
|
136
|
-
let didStash = false;
|
|
137
|
-
if (hasDirtyFiles) {
|
|
138
|
-
const stashR = spawnSync('git', ['stash', 'push', '-m', 'mx-agent-start: auto-stash before rebase'], {
|
|
139
|
-
cwd: worktreePath, stdio: 'pipe', encoding: 'utf-8',
|
|
140
|
-
});
|
|
141
|
-
if (stashR.status === 0 && !stashR.stdout.includes('No local changes')) {
|
|
142
|
-
didStash = true;
|
|
143
|
-
console.log(chalk.dim(' Stashed uncommitted changes before rebase.'));
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
// ── Rebase with auto-skip for already-upstream commits ──────
|
|
147
77
|
const rebaseResult = spawnSync('git', ['rebase', `origin/${cfg.baseBranch}`], {
|
|
148
78
|
cwd: worktreePath,
|
|
149
79
|
stdio: 'pipe',
|
|
150
80
|
encoding: 'utf-8',
|
|
151
81
|
});
|
|
152
82
|
if (rebaseResult.status !== 0) {
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// differs slightly, it raises a conflict instead. We detect this
|
|
158
|
-
// by checking whether the conflicting commit's changes are already
|
|
159
|
-
// present in origin/main and auto-skip if so.
|
|
160
|
-
const MAX_AUTO_SKIP = 20; // safety cap
|
|
161
|
-
let skipped = 0;
|
|
162
|
-
let resolved = false;
|
|
163
|
-
for (let i = 0; i < MAX_AUTO_SKIP; i++) {
|
|
164
|
-
// Check if we're still mid-rebase
|
|
165
|
-
const rebaseHeadR = spawnSync('git', ['rev-parse', '--verify', 'REBASE_HEAD'], {
|
|
166
|
-
cwd: worktreePath, stdio: 'pipe', encoding: 'utf-8',
|
|
167
|
-
});
|
|
168
|
-
if (rebaseHeadR.status !== 0) {
|
|
169
|
-
// No longer in rebase — either resolved or something unexpected
|
|
170
|
-
resolved = true;
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
const rebaseHead = rebaseHeadR.stdout.trim();
|
|
174
|
-
// Check if this commit's patch-id matches any commit already on main.
|
|
175
|
-
// patch-id produces a content-based hash of the diff, ignoring whitespace.
|
|
176
|
-
// Uses array-form spawnSync + manual piping to avoid shell interpolation.
|
|
177
|
-
const showR = spawnSync('git', ['-C', worktreePath, 'show', rebaseHead], {
|
|
178
|
-
stdio: 'pipe',
|
|
179
|
-
});
|
|
180
|
-
const patchIdR = spawnSync('git', ['-C', worktreePath, 'patch-id', '--stable'], {
|
|
181
|
-
input: showR.stdout,
|
|
182
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
183
|
-
encoding: 'utf-8',
|
|
184
|
-
});
|
|
185
|
-
const branchPatchId = patchIdR.stdout.trim().split(/\s+/)[0] || '';
|
|
186
|
-
let isAlreadyUpstream = false;
|
|
187
|
-
if (branchPatchId) {
|
|
188
|
-
// Get patch-ids for recent main commits in one shot:
|
|
189
|
-
// `git log -100 -p` outputs all diffs, piped to `git patch-id --stable`
|
|
190
|
-
const logR = spawnSync('git', ['-C', worktreePath, 'log', '-100', '-p', `origin/${cfg.baseBranch}`], {
|
|
191
|
-
stdio: 'pipe',
|
|
192
|
-
});
|
|
193
|
-
const mainPatchR = spawnSync('git', ['-C', worktreePath, 'patch-id', '--stable'], {
|
|
194
|
-
input: logR.stdout,
|
|
195
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
196
|
-
encoding: 'utf-8',
|
|
197
|
-
});
|
|
198
|
-
const mainPatchIds = mainPatchR.stdout.trim().split('\n')
|
|
199
|
-
.map(line => line.split(/\s+/)[0])
|
|
200
|
-
.filter(Boolean);
|
|
201
|
-
isAlreadyUpstream = mainPatchIds.includes(branchPatchId);
|
|
202
|
-
}
|
|
203
|
-
if (!isAlreadyUpstream) {
|
|
204
|
-
// Also check: if we accept main's version for all conflicts,
|
|
205
|
-
// does the tree match main exactly? This catches squash-merged
|
|
206
|
-
// commits where patch-id differs due to combined squashing.
|
|
207
|
-
spawnSync('git', ['-C', worktreePath, 'checkout', '--theirs', '.'], {
|
|
208
|
-
stdio: 'pipe',
|
|
209
|
-
});
|
|
210
|
-
spawnSync('git', ['-C', worktreePath, 'add', '-A'], {
|
|
211
|
-
stdio: 'pipe',
|
|
212
|
-
});
|
|
213
|
-
const diffR = spawnSync('git', ['-C', worktreePath, 'diff', '--cached', '--stat', 'HEAD'], {
|
|
214
|
-
stdio: 'pipe', encoding: 'utf-8',
|
|
215
|
-
});
|
|
216
|
-
const diffAfterTheirs = diffR.stdout.trim();
|
|
217
|
-
if (diffAfterTheirs === '') {
|
|
218
|
-
// Tree is identical to main after accepting theirs — commit is redundant
|
|
219
|
-
isAlreadyUpstream = true;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (isAlreadyUpstream) {
|
|
223
|
-
// Skip this commit — its changes are already on main
|
|
224
|
-
const skipR = spawnSync('git', ['rebase', '--skip'], {
|
|
225
|
-
cwd: worktreePath, stdio: 'pipe', encoding: 'utf-8',
|
|
226
|
-
});
|
|
227
|
-
skipped++;
|
|
228
|
-
if (skipR.status === 0) {
|
|
229
|
-
resolved = true;
|
|
230
|
-
break;
|
|
231
|
-
}
|
|
232
|
-
// Skip succeeded but rebase continues with next commit — loop again
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
// Genuine conflict — not already upstream. Abort and block.
|
|
236
|
-
break;
|
|
237
|
-
}
|
|
238
|
-
if (!resolved) {
|
|
239
|
-
// Genuine conflict that couldn't be auto-resolved.
|
|
240
|
-
const conflictR = spawnSync('git', ['diff', '--name-only', '--diff-filter=U'], { cwd: worktreePath, stdio: 'pipe', encoding: 'utf-8' });
|
|
241
|
-
const conflictFiles = conflictR.stdout.trim().split('\n').filter(Boolean);
|
|
242
|
-
spawnSync('git', ['rebase', '--abort'], { cwd: worktreePath, stdio: 'pipe' });
|
|
243
|
-
// Restore stash if we created one
|
|
244
|
-
if (didStash) {
|
|
245
|
-
spawnSync('git', ['stash', 'pop'], { cwd: worktreePath, stdio: 'pipe' });
|
|
246
|
-
}
|
|
247
|
-
console.error();
|
|
248
|
-
console.error(chalk.red.bold(` ✗ Rebase onto origin/${cfg.baseBranch} failed — session blocked.`));
|
|
249
|
-
console.error(chalk.dim(' ─────────────────────────────────────────────'));
|
|
250
|
-
if (conflictFiles.length > 0) {
|
|
251
|
-
console.error(chalk.red(` Conflicting files (${conflictFiles.length}):`));
|
|
252
|
-
for (const f of conflictFiles) {
|
|
253
|
-
console.error(chalk.dim(` • ${f}`));
|
|
254
|
-
}
|
|
255
|
-
console.error();
|
|
256
|
-
}
|
|
257
|
-
console.error(chalk.bold(' To resolve:'));
|
|
258
|
-
console.error(` 1. Run the rebase manually:`);
|
|
259
|
-
console.error(chalk.cyan(` git -C ${worktreePath} rebase origin/${cfg.baseBranch}`));
|
|
260
|
-
console.error(` 2. Fix each conflict, then stage the resolved files:`);
|
|
261
|
-
console.error(chalk.cyan(` git -C ${worktreePath} add <file>`));
|
|
262
|
-
console.error(` 3. Continue the rebase:`);
|
|
263
|
-
console.error(chalk.cyan(` git -C ${worktreePath} rebase --continue`));
|
|
264
|
-
console.error(` 4. Re-run:`);
|
|
265
|
-
console.error(chalk.cyan(` mx-agent start ${name}`));
|
|
266
|
-
console.error();
|
|
267
|
-
console.error(chalk.dim(` Tip: if this branch has completed work, open a PR first.`));
|
|
268
|
-
console.error(chalk.dim(` Smaller PRs merged frequently reduce conflict surface.`));
|
|
269
|
-
console.error();
|
|
270
|
-
process.exit(1);
|
|
271
|
-
}
|
|
272
|
-
if (skipped > 0) {
|
|
273
|
-
console.log(chalk.green(` ✓ Rebased onto origin/${cfg.baseBranch} (auto-skipped ${skipped} already-upstream commit${skipped === 1 ? '' : 's'})`));
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
else if (commitsBehind > 0) {
|
|
277
|
-
console.log(chalk.green(` ✓ Rebased ${commitsBehind} commit${commitsBehind === 1 ? '' : 's'} from origin/${cfg.baseBranch}`));
|
|
83
|
+
// Abort the rebase so the worktree is left in a clean state
|
|
84
|
+
spawnSync('git', ['rebase', '--abort'], { cwd: worktreePath, stdio: 'pipe' });
|
|
85
|
+
console.log(chalk.yellow(` ⚠ Could not rebase onto origin/${cfg.baseBranch} (conflicts) — starting with local state.`));
|
|
86
|
+
console.log(chalk.dim(` Resolve manually: git -C ${worktreePath} rebase origin/${cfg.baseBranch}`));
|
|
278
87
|
}
|
|
279
88
|
else {
|
|
280
89
|
console.log(chalk.green(` ✓ Worktree up to date with origin/${cfg.baseBranch}`));
|
|
281
90
|
}
|
|
282
|
-
// ── Restore stashed changes ───────────────────────────────
|
|
283
|
-
if (didStash) {
|
|
284
|
-
const popR = spawnSync('git', ['stash', 'pop'], {
|
|
285
|
-
cwd: worktreePath, stdio: 'pipe', encoding: 'utf-8',
|
|
286
|
-
});
|
|
287
|
-
if (popR.status === 0) {
|
|
288
|
-
console.log(chalk.dim(' Restored stashed changes.'));
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
console.log(chalk.yellow(' ⚠ Could not restore stashed changes — run `git stash pop` manually.'));
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
91
|
}
|
|
295
92
|
console.log();
|
|
296
|
-
// ── 5.
|
|
297
|
-
|
|
298
|
-
// ──
|
|
299
|
-
createHuskySymlink(projectRoot, worktreePath);
|
|
300
|
-
syncClaudeConfig(worktreePath, { ...cfg, projectRoot });
|
|
301
|
-
// ── 7. Deploy fresh CLAUDE.md ─────────────────────────────────────
|
|
93
|
+
// ── 5. Sync Claude config from origin/<baseBranch> ────────────────
|
|
94
|
+
syncClaudeConfig(worktreePath, cfg);
|
|
95
|
+
// ── 5. Deploy fresh CLAUDE.md ─────────────────────────────────────
|
|
302
96
|
deployClaudeMd(projectRoot, worktreePath, cfg);
|
|
303
|
-
// ──
|
|
97
|
+
// ── 6. Regenerate CLAUDE.local.md ────────────────────────────────
|
|
304
98
|
writeClaudeLocal({
|
|
305
99
|
projectRoot,
|
|
306
100
|
worktreePath,
|
|
@@ -308,16 +102,15 @@ export async function runStart(options) {
|
|
|
308
102
|
branch,
|
|
309
103
|
slot,
|
|
310
104
|
});
|
|
311
|
-
// ──
|
|
105
|
+
// ── 7. Print isolation summary ─────────────────────────────────────
|
|
312
106
|
console.log();
|
|
313
107
|
console.log(chalk.bold.green(' Isolation Enabled:'));
|
|
314
108
|
console.log(chalk.dim(' ─────────────────────────────────────────────'));
|
|
315
109
|
console.log(` ${chalk.green('✓')} Directory restricted to worktree`);
|
|
316
110
|
console.log(` ${chalk.green('✓')} git checkout/switch to main blocked`);
|
|
317
111
|
console.log(` ${chalk.green('✓')} git push origin main blocked`);
|
|
318
|
-
console.log(` ${chalk.green('✓')} Git identity: Agent: ${name} <agent-${name}@agents.local>`);
|
|
319
112
|
console.log();
|
|
320
|
-
// ──
|
|
113
|
+
// ── 8. Build initial prompt ────────────────────────────────────────
|
|
321
114
|
// Three-step startup sequence:
|
|
322
115
|
// 1. Read the roleguide — understand the role and which named memories to check
|
|
323
116
|
// 2. Run the "How to Start a Session" checklist from the roleguide (named memories)
|
|
@@ -331,7 +124,7 @@ export async function runStart(options) {
|
|
|
331
124
|
`Try \`mx memories get --name "${name}-leader-state"\` (if not already checked in step 2). ` +
|
|
332
125
|
`Then run \`mx memories recap --recent 24h\` to catch any in-progress checkpoints or iteration state that may not be in a named memory. ` +
|
|
333
126
|
`If step 3 reveals you were mid-iteration, resume from that point rather than starting a new iteration.`;
|
|
334
|
-
// ──
|
|
127
|
+
// ── 9. Launch Claude ───────────────────────────────────────────────
|
|
335
128
|
launchClaudeSession({
|
|
336
129
|
worktreePath,
|
|
337
130
|
worktreeName: worktreeDirName,
|