@memnexus-ai/mx-agent-cli 0.1.207 → 0.1.208
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__/start-report-integration.test.d.ts +12 -0
- package/dist/__tests__/start-report-integration.test.d.ts.map +1 -0
- package/dist/__tests__/start-report-integration.test.js +123 -0
- package/dist/__tests__/start-report-integration.test.js.map +1 -0
- package/dist/__tests__/startup-report.test.d.ts +13 -0
- package/dist/__tests__/startup-report.test.d.ts.map +1 -0
- package/dist/__tests__/startup-report.test.js +245 -0
- package/dist/__tests__/startup-report.test.js.map +1 -0
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +34 -0
- package/dist/commands/start.js.map +1 -1
- package/dist/lib/claude.d.ts.map +1 -1
- package/dist/lib/claude.js +15 -3
- package/dist/lib/claude.js.map +1 -1
- package/dist/lib/startup-report.d.ts +127 -0
- package/dist/lib/startup-report.d.ts.map +1 -0
- package/dist/lib/startup-report.js +331 -0
- package/dist/lib/startup-report.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test: driving runStart to a successful hand-off writes ONE durable
|
|
3
|
+
* StartupReport JSONL line capturing the start-path output (platform v2.58 Phase
|
|
4
|
+
* 1, #4350).
|
|
5
|
+
*
|
|
6
|
+
* Every boundary is stubbed (git, network, filesystem sync, locking, launcher)
|
|
7
|
+
* so runStart reaches the `finalize('ok')` immediately before session launch.
|
|
8
|
+
* We then read the JSONL file from a temp HOME and assert the documented schema
|
|
9
|
+
* plus a non-empty entries[] captured from the start command's own output.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=start-report-integration.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-report-integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/start-report-integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test: driving runStart to a successful hand-off writes ONE durable
|
|
3
|
+
* StartupReport JSONL line capturing the start-path output (platform v2.58 Phase
|
|
4
|
+
* 1, #4350).
|
|
5
|
+
*
|
|
6
|
+
* Every boundary is stubbed (git, network, filesystem sync, locking, launcher)
|
|
7
|
+
* so runStart reaches the `finalize('ok')` immediately before session launch.
|
|
8
|
+
* We then read the JSONL file from a temp HOME and assert the documented schema
|
|
9
|
+
* plus a non-empty entries[] captured from the start command's own output.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
12
|
+
import { mkdtempSync, readFileSync, rmSync } from 'fs';
|
|
13
|
+
import { tmpdir } from 'os';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
// hasOrigin=false (git remote probe returns non-zero) → skip the fetch/rebase
|
|
16
|
+
// block and land on the config-sync + launch tail deterministically.
|
|
17
|
+
vi.mock('child_process', () => ({
|
|
18
|
+
spawnSync: vi.fn(() => ({ status: 1, stdout: '', stderr: '' })),
|
|
19
|
+
}));
|
|
20
|
+
vi.mock('../lib/worktree.js', () => ({
|
|
21
|
+
findProjectRoot: vi.fn(() => '/fake/project'),
|
|
22
|
+
findWorktreePath: vi.fn(() => '/fake/project/.worktrees/demo-wt'),
|
|
23
|
+
getSlot: vi.fn(() => 0),
|
|
24
|
+
getCurrentBranch: vi.fn(() => 'worktree/demo'),
|
|
25
|
+
}));
|
|
26
|
+
// claude.js is real for the log helpers but we don't want real config sync or a
|
|
27
|
+
// real session launch, so stub the heavy operations to no-ops. The direct
|
|
28
|
+
// console.log calls inside runStart itself still run and get captured by the tee.
|
|
29
|
+
vi.mock('../lib/claude.js', () => ({
|
|
30
|
+
syncClaudeConfig: vi.fn(),
|
|
31
|
+
deployBaseHooks: vi.fn(),
|
|
32
|
+
deployClaudeMd: vi.fn(),
|
|
33
|
+
writeClaudeLocal: vi.fn(),
|
|
34
|
+
launchClaudeSession: vi.fn(async () => { }),
|
|
35
|
+
logSuccess: vi.fn(),
|
|
36
|
+
logDim: vi.fn(),
|
|
37
|
+
logWarning: vi.fn(),
|
|
38
|
+
}));
|
|
39
|
+
vi.mock('../lib/agent-session.js', () => ({
|
|
40
|
+
launchAgentSession: vi.fn(async () => { }),
|
|
41
|
+
}));
|
|
42
|
+
vi.mock('../lib/catalog.js', () => ({
|
|
43
|
+
getTeam: vi.fn(() => undefined),
|
|
44
|
+
}));
|
|
45
|
+
vi.mock('../lib/project-config.js', () => ({
|
|
46
|
+
loadProjectConfig: vi.fn(() => ({})),
|
|
47
|
+
resolveProjectConfig: vi.fn(() => ({
|
|
48
|
+
baseBranch: 'main',
|
|
49
|
+
worktreeDir: '.worktrees',
|
|
50
|
+
teamsDir: 'mx-agent-system/teams',
|
|
51
|
+
roleguidesDir: 'mx-agent-system/roleguides',
|
|
52
|
+
configSource: 'mx-agent-system/agent-config',
|
|
53
|
+
coordinationMode: 'off',
|
|
54
|
+
})),
|
|
55
|
+
}));
|
|
56
|
+
vi.mock('../lib/config-generation.js', () => ({
|
|
57
|
+
checkConfigGeneration: vi.fn(),
|
|
58
|
+
}));
|
|
59
|
+
vi.mock('../lib/config.js', () => ({
|
|
60
|
+
getConfigValue: vi.fn(() => undefined),
|
|
61
|
+
setConfigValue: vi.fn(),
|
|
62
|
+
}));
|
|
63
|
+
vi.mock('../lib/leader-lock.js', () => ({
|
|
64
|
+
claimLock: vi.fn(() => ({ ok: true })),
|
|
65
|
+
checkLiveness: vi.fn(() => ({ state: 'stale', lockAgeSeconds: null })),
|
|
66
|
+
formatLockEvidence: vi.fn(() => ''),
|
|
67
|
+
lockPath: vi.fn(() => '/fake/project/.worktrees/demo-wt/.leader.lock'),
|
|
68
|
+
readLock: vi.fn(() => ({ lock: null, parseError: null })),
|
|
69
|
+
removeLock: vi.fn(),
|
|
70
|
+
writeLock: vi.fn(),
|
|
71
|
+
updateChildPid: vi.fn(),
|
|
72
|
+
}));
|
|
73
|
+
vi.mock('../lib/coordination-token.js', () => ({
|
|
74
|
+
resolveCoordinationToken: vi.fn(() => ({ status: 'not-on-roster' })),
|
|
75
|
+
}));
|
|
76
|
+
import { runStart } from '../commands/start.js';
|
|
77
|
+
import { startupReport } from '../lib/startup-report.js';
|
|
78
|
+
let home;
|
|
79
|
+
const OLD_HOME = process.env.HOME;
|
|
80
|
+
let logSpy;
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
home = mkdtempSync(join(tmpdir(), 'mx-startlog-int-'));
|
|
83
|
+
// Set HOME as a property so Node's env proxy stays intact (os.homedir()
|
|
84
|
+
// reads it); a wholesale `process.env = {...}` would break that.
|
|
85
|
+
process.env.HOME = home;
|
|
86
|
+
startupReport._reset();
|
|
87
|
+
vi.clearAllMocks();
|
|
88
|
+
// Neutralize the npm version check network call.
|
|
89
|
+
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('no network in test')));
|
|
90
|
+
logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
91
|
+
});
|
|
92
|
+
afterEach(() => {
|
|
93
|
+
logSpy.mockRestore();
|
|
94
|
+
vi.unstubAllGlobals();
|
|
95
|
+
startupReport._reset();
|
|
96
|
+
rmSync(home, { recursive: true, force: true });
|
|
97
|
+
if (OLD_HOME === undefined)
|
|
98
|
+
delete process.env.HOME;
|
|
99
|
+
else
|
|
100
|
+
process.env.HOME = OLD_HOME;
|
|
101
|
+
});
|
|
102
|
+
describe('runStart → durable StartupReport', () => {
|
|
103
|
+
it('writes one JSONL report with schema_version, outcome ok, and entries[]', async () => {
|
|
104
|
+
await runStart({ name: 'demo' });
|
|
105
|
+
const file = join(home, '.mx-agent', 'logs', 'startup', 'demo-wt.jsonl');
|
|
106
|
+
const lines = readFileSync(file, 'utf-8').trim().split('\n');
|
|
107
|
+
expect(lines).toHaveLength(1);
|
|
108
|
+
const report = JSON.parse(lines[0]);
|
|
109
|
+
expect(report.schema_version).toBe(1);
|
|
110
|
+
expect(report.outcome).toBe('ok');
|
|
111
|
+
expect(report.team).toBe('demo');
|
|
112
|
+
expect(report.worktree).toBe('demo-wt');
|
|
113
|
+
expect(report.project).toBe('project'); // basename('/fake/project')
|
|
114
|
+
expect(typeof report.cli_version).toBe('string');
|
|
115
|
+
expect(Array.isArray(report.entries)).toBe(true);
|
|
116
|
+
expect(report.entries.length).toBeGreaterThan(0);
|
|
117
|
+
// The banner line the command prints is captured verbatim (ANSI stripped).
|
|
118
|
+
const messages = report.entries.map((e) => e.message).join('\n');
|
|
119
|
+
expect(messages).toContain('Starting Agent Team');
|
|
120
|
+
expect(messages).toContain('Team:');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
//# sourceMappingURL=start-report-integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-report-integration.test.js","sourceRoot":"","sources":["../../src/__tests__/start-report-integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the durable startup-log collector (platform v2.58 Phase 1, #4350).
|
|
3
|
+
*
|
|
4
|
+
* Covers the module's contract:
|
|
5
|
+
* - entry recording (level + optional code)
|
|
6
|
+
* - finalize writes one JSON line with the documented schema; idempotent
|
|
7
|
+
* - two-generation rotation at the 5 MB threshold
|
|
8
|
+
* - finalize NEVER throws, even when the log directory is unwritable
|
|
9
|
+
* - level inference from glyphs and ANSI colors
|
|
10
|
+
* - the console-capture tee records every print without altering output
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=startup-report.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-report.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/startup-report.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the durable startup-log collector (platform v2.58 Phase 1, #4350).
|
|
3
|
+
*
|
|
4
|
+
* Covers the module's contract:
|
|
5
|
+
* - entry recording (level + optional code)
|
|
6
|
+
* - finalize writes one JSON line with the documented schema; idempotent
|
|
7
|
+
* - two-generation rotation at the 5 MB threshold
|
|
8
|
+
* - finalize NEVER throws, even when the log directory is unwritable
|
|
9
|
+
* - level inference from glyphs and ANSI colors
|
|
10
|
+
* - the console-capture tee records every print without altering output
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
13
|
+
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, rmSync, chmodSync } from 'fs';
|
|
14
|
+
import { tmpdir } from 'os';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
import { startupReport, inferLevel, stripAnsi, redactUrlCredentials } from '../lib/startup-report.js';
|
|
17
|
+
let home;
|
|
18
|
+
const OLD_HOME = process.env.HOME;
|
|
19
|
+
/** Path where finalize writes for a given worktree/team name under the temp HOME. */
|
|
20
|
+
function logPath(name) {
|
|
21
|
+
return join(home, '.mx-agent', 'logs', 'startup', `${name}.jsonl`);
|
|
22
|
+
}
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
home = mkdtempSync(join(tmpdir(), 'mx-startlog-home-'));
|
|
25
|
+
// Set HOME as a property (keeps Node's env proxy → os.homedir() sees it).
|
|
26
|
+
// A wholesale `process.env = {...}` would drop the proxy and break homedir().
|
|
27
|
+
process.env.HOME = home; // os.homedir() honors $HOME on POSIX
|
|
28
|
+
startupReport._reset();
|
|
29
|
+
});
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
startupReport._reset();
|
|
32
|
+
rmSync(home, { recursive: true, force: true });
|
|
33
|
+
if (OLD_HOME === undefined)
|
|
34
|
+
delete process.env.HOME;
|
|
35
|
+
else
|
|
36
|
+
process.env.HOME = OLD_HOME;
|
|
37
|
+
});
|
|
38
|
+
describe('stripAnsi', () => {
|
|
39
|
+
it('removes SGR color/style escapes', () => {
|
|
40
|
+
expect(stripAnsi('\x1b[32m✓ ok\x1b[39m')).toBe('✓ ok');
|
|
41
|
+
expect(stripAnsi('plain')).toBe('plain');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe('inferLevel', () => {
|
|
45
|
+
it('reads glyph markers first (survives colors off)', () => {
|
|
46
|
+
expect(inferLevel(' ✓ Deployed AGENTS.md', 'out')).toBe('success');
|
|
47
|
+
expect(inferLevel(' ⚠ Could not fetch', 'out')).toBe('warning');
|
|
48
|
+
expect(inferLevel(' ✗ Refusing to start', 'out')).toBe('error');
|
|
49
|
+
});
|
|
50
|
+
it('falls back to ANSI color when no glyph', () => {
|
|
51
|
+
expect(inferLevel('\x1b[31mError: boom\x1b[39m', 'out')).toBe('error');
|
|
52
|
+
expect(inferLevel('\x1b[33mheads up\x1b[39m', 'out')).toBe('warning');
|
|
53
|
+
expect(inferLevel('\x1b[32mgreen\x1b[39m', 'out')).toBe('success');
|
|
54
|
+
expect(inferLevel('\x1b[2mdim line\x1b[22m', 'out')).toBe('dim');
|
|
55
|
+
expect(inferLevel('\x1b[90mgray\x1b[39m', 'out')).toBe('dim');
|
|
56
|
+
});
|
|
57
|
+
it('biases by stream when plain text has no marker', () => {
|
|
58
|
+
expect(inferLevel('Team: demo', 'out')).toBe('info');
|
|
59
|
+
expect(inferLevel('[mx-agent] skipping behavioral content', 'err')).toBe('warning');
|
|
60
|
+
expect(inferLevel('Error: not found', 'error-console')).toBe('error');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('URL credential redaction', () => {
|
|
64
|
+
it('strips userinfo from a credentialed https remote', () => {
|
|
65
|
+
expect(redactUrlCredentials('https://x-access-token:SECRET@github.com/org/repo.git'))
|
|
66
|
+
.toBe('https://***@github.com/org/repo.git');
|
|
67
|
+
});
|
|
68
|
+
it('leaves a plain https URL unchanged', () => {
|
|
69
|
+
expect(redactUrlCredentials('https://github.com/org/repo.git'))
|
|
70
|
+
.toBe('https://github.com/org/repo.git');
|
|
71
|
+
});
|
|
72
|
+
it('does NOT mangle an ssh-style git@github.com: remote (no scheme://)', () => {
|
|
73
|
+
expect(redactUrlCredentials('git@github.com:org/repo.git'))
|
|
74
|
+
.toBe('git@github.com:org/repo.git');
|
|
75
|
+
});
|
|
76
|
+
it('record() persists the scrubbed message', () => {
|
|
77
|
+
startupReport.record('warning', 'fatal: could not read from https://user:pw@example.com/x.git');
|
|
78
|
+
const entries = startupReport._entries();
|
|
79
|
+
expect(entries[0].message).toBe('fatal: could not read from https://***@example.com/x.git');
|
|
80
|
+
expect(entries[0].message).not.toContain('pw');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('record + finalize', () => {
|
|
84
|
+
it('buffers entries with level and optional code', () => {
|
|
85
|
+
startupReport.record('info', 'starting');
|
|
86
|
+
startupReport.record('warning', 'heads up', 'obs-token-missing');
|
|
87
|
+
const entries = startupReport._entries();
|
|
88
|
+
expect(entries).toHaveLength(2);
|
|
89
|
+
expect(entries[0]).toMatchObject({ level: 'info', message: 'starting' });
|
|
90
|
+
expect(entries[0].code).toBeUndefined();
|
|
91
|
+
expect(entries[1]).toMatchObject({ level: 'warning', message: 'heads up', code: 'obs-token-missing' });
|
|
92
|
+
expect(typeof entries[0].ts).toBe('string');
|
|
93
|
+
});
|
|
94
|
+
it('writes one JSON line with the documented schema', () => {
|
|
95
|
+
startupReport.setContext({
|
|
96
|
+
project: 'memnexus',
|
|
97
|
+
team: 'demo',
|
|
98
|
+
worktree: 'demo-20260721',
|
|
99
|
+
cliVersion: '9.9.9',
|
|
100
|
+
machine: 'test-host',
|
|
101
|
+
});
|
|
102
|
+
startupReport.record('info', 'line one');
|
|
103
|
+
startupReport.record('success', 'line two');
|
|
104
|
+
startupReport.finalize('ok');
|
|
105
|
+
const raw = readFileSync(logPath('demo-20260721'), 'utf-8').trim().split('\n');
|
|
106
|
+
expect(raw).toHaveLength(1);
|
|
107
|
+
const report = JSON.parse(raw[0]);
|
|
108
|
+
expect(report.schema_version).toBe(1);
|
|
109
|
+
expect(report.project).toBe('memnexus');
|
|
110
|
+
expect(report.team).toBe('demo');
|
|
111
|
+
expect(report.worktree).toBe('demo-20260721');
|
|
112
|
+
expect(report.cli_version).toBe('9.9.9');
|
|
113
|
+
expect(report.machine).toBe('test-host');
|
|
114
|
+
expect(report.outcome).toBe('ok');
|
|
115
|
+
expect(typeof report.started_at).toBe('string');
|
|
116
|
+
expect(typeof report.finished_at).toBe('string');
|
|
117
|
+
expect(report.entries).toHaveLength(2);
|
|
118
|
+
expect(report.entries[1]).toMatchObject({ level: 'success', message: 'line two' });
|
|
119
|
+
});
|
|
120
|
+
it('is idempotent — a second finalize is a no-op (single line total)', () => {
|
|
121
|
+
startupReport.setContext({ team: 'demo' });
|
|
122
|
+
startupReport.record('info', 'once');
|
|
123
|
+
startupReport.finalize('ok');
|
|
124
|
+
startupReport.finalize('error'); // must be ignored
|
|
125
|
+
const raw = readFileSync(logPath('demo'), 'utf-8').trim().split('\n');
|
|
126
|
+
expect(raw).toHaveLength(1);
|
|
127
|
+
expect(JSON.parse(raw[0]).outcome).toBe('ok');
|
|
128
|
+
});
|
|
129
|
+
it('falls back to team, then "unknown", for the file name', () => {
|
|
130
|
+
startupReport.setContext({ team: 'teamx' }); // no worktree
|
|
131
|
+
startupReport.record('info', 'x');
|
|
132
|
+
startupReport.finalize('refused');
|
|
133
|
+
expect(existsSync(logPath('teamx'))).toBe(true);
|
|
134
|
+
});
|
|
135
|
+
it('appends across invocations (multiple JSON lines accumulate)', () => {
|
|
136
|
+
startupReport.setContext({ worktree: 'wt' });
|
|
137
|
+
startupReport.record('info', 'first');
|
|
138
|
+
startupReport.finalize('ok');
|
|
139
|
+
startupReport._reset();
|
|
140
|
+
startupReport.setContext({ worktree: 'wt' });
|
|
141
|
+
startupReport.record('info', 'second');
|
|
142
|
+
startupReport.finalize('refused');
|
|
143
|
+
const lines = readFileSync(logPath('wt'), 'utf-8').trim().split('\n');
|
|
144
|
+
expect(lines).toHaveLength(2);
|
|
145
|
+
expect(JSON.parse(lines[0]).outcome).toBe('ok');
|
|
146
|
+
expect(JSON.parse(lines[1]).outcome).toBe('refused');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('rotation at the 5 MB threshold', () => {
|
|
150
|
+
it('renames the current file to .1 before appending when over 5 MB', () => {
|
|
151
|
+
const dir = join(home, '.mx-agent', 'logs', 'startup');
|
|
152
|
+
mkdirSync(dir, { recursive: true });
|
|
153
|
+
const target = logPath('big');
|
|
154
|
+
// Pre-seed a file just over 5 MB.
|
|
155
|
+
writeFileSync(target, 'x'.repeat(5 * 1024 * 1024 + 10));
|
|
156
|
+
startupReport.setContext({ worktree: 'big' });
|
|
157
|
+
startupReport.record('info', 'after rotation');
|
|
158
|
+
startupReport.finalize('ok');
|
|
159
|
+
expect(existsSync(`${target}.1`)).toBe(true);
|
|
160
|
+
// The new primary file holds exactly the one fresh report line.
|
|
161
|
+
const lines = readFileSync(target, 'utf-8').trim().split('\n');
|
|
162
|
+
expect(lines).toHaveLength(1);
|
|
163
|
+
expect(JSON.parse(lines[0]).entries[0].message).toBe('after rotation');
|
|
164
|
+
});
|
|
165
|
+
it('does not rotate a small file', () => {
|
|
166
|
+
startupReport.setContext({ worktree: 'small' });
|
|
167
|
+
startupReport.record('info', 'a');
|
|
168
|
+
startupReport.finalize('ok');
|
|
169
|
+
expect(existsSync(`${logPath('small')}.1`)).toBe(false);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
describe('never throws on unwritable log dir', () => {
|
|
173
|
+
it('finalize swallows I/O errors when the home dir is read-only', () => {
|
|
174
|
+
// Make HOME read-only so mkdirSync(~/.mx-agent/...) fails with EACCES.
|
|
175
|
+
// (Skipped implicitly when running as root, where perms are ignored — the
|
|
176
|
+
// assertion below still holds because finalize never throws regardless.)
|
|
177
|
+
chmodSync(home, 0o555);
|
|
178
|
+
startupReport.setContext({ worktree: 'ro' });
|
|
179
|
+
startupReport.record('error', 'boom');
|
|
180
|
+
expect(() => startupReport.finalize('error')).not.toThrow();
|
|
181
|
+
// Restore perms so afterEach cleanup can remove the dir.
|
|
182
|
+
chmodSync(home, 0o755);
|
|
183
|
+
});
|
|
184
|
+
it('record never throws', () => {
|
|
185
|
+
expect(() => startupReport.record('info', 'ok')).not.toThrow();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
describe('console-capture tee', () => {
|
|
189
|
+
it('records every print with an inferred level and still emits output', () => {
|
|
190
|
+
startupReport.setContext({ worktree: 'tee' });
|
|
191
|
+
// Spy the REAL console before installing the tee so we can prove output
|
|
192
|
+
// still reaches the terminal exactly once per call.
|
|
193
|
+
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
194
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
195
|
+
startupReport.installConsoleCapture();
|
|
196
|
+
console.log('\x1b[32m ✓ Deployed\x1b[39m');
|
|
197
|
+
console.log(' Team: demo'); // plain info
|
|
198
|
+
console.log('\x1b[33m ⚠ heads up\x1b[39m');
|
|
199
|
+
console.log(' '); // whitespace-only — must NOT be recorded
|
|
200
|
+
console.error('\x1b[31mError: nope\x1b[39m');
|
|
201
|
+
process.stderr.write('[mx-agent] skipping behavioral content\n');
|
|
202
|
+
startupReport.removeConsoleCapture();
|
|
203
|
+
const entries = startupReport._entries();
|
|
204
|
+
const levels = entries.map((e) => e.level);
|
|
205
|
+
expect(levels).toEqual(['success', 'info', 'warning', 'error', 'warning']);
|
|
206
|
+
expect(entries[0].message).toBe(' ✓ Deployed');
|
|
207
|
+
expect(entries.find((e) => e.message.includes('skipping behavioral'))).toBeTruthy();
|
|
208
|
+
// Output still went out: 4 console.log calls (incl. the whitespace one).
|
|
209
|
+
expect(logSpy).toHaveBeenCalledTimes(4);
|
|
210
|
+
expect(errSpy).toHaveBeenCalledTimes(1);
|
|
211
|
+
logSpy.mockRestore();
|
|
212
|
+
errSpy.mockRestore();
|
|
213
|
+
});
|
|
214
|
+
it('records a REAL console.error exactly ONCE (no double via stderr chain)', () => {
|
|
215
|
+
// Do NOT mock console.error — the native impl dispatches through the teed
|
|
216
|
+
// process.stderr.write, which is exactly the double-count path under test.
|
|
217
|
+
// Redirect the raw stderr fd write to a sink so the line does not clutter
|
|
218
|
+
// test output, but leave console.error / process.stderr.write real so the
|
|
219
|
+
// tee sees the genuine dispatch chain.
|
|
220
|
+
startupReport.setContext({ worktree: 'realerr' });
|
|
221
|
+
const realStderrWrite = process.stderr.write.bind(process.stderr);
|
|
222
|
+
const sink = vi.fn(() => true);
|
|
223
|
+
process.stderr.write = sink;
|
|
224
|
+
try {
|
|
225
|
+
startupReport.installConsoleCapture();
|
|
226
|
+
console.error('\x1b[31mTeam \'bogus\' not found.\x1b[39m');
|
|
227
|
+
startupReport.removeConsoleCapture();
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
process.stderr.write = realStderrWrite;
|
|
231
|
+
}
|
|
232
|
+
const entries = startupReport._entries();
|
|
233
|
+
expect(entries).toHaveLength(1);
|
|
234
|
+
expect(entries[0].level).toBe('error');
|
|
235
|
+
expect(entries[0].message).toBe("Team 'bogus' not found.");
|
|
236
|
+
});
|
|
237
|
+
it('removeConsoleCapture restores the original console.log', () => {
|
|
238
|
+
const before = console.log;
|
|
239
|
+
startupReport.installConsoleCapture();
|
|
240
|
+
expect(console.log).not.toBe(before);
|
|
241
|
+
startupReport.removeConsoleCapture();
|
|
242
|
+
expect(console.log).toBe(before);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
//# sourceMappingURL=startup-report.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-report.test.js","sourceRoot":"","sources":["../../src/__tests__/startup-report.test.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;AA0BH;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,8BAA8B,UAG1C,CAAC;AACF,eAAO,MAAM,2BAA2B,aAEtC,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAK3D;AA4BD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAgFjG;AA4FD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAsC5E;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6FAA6F;IAC7F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kGAAkG;IAClG,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAID,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA0mBnE"}
|
package/dist/commands/start.js
CHANGED
|
@@ -18,6 +18,7 @@ import { checkConfigGeneration } from '../lib/config-generation.js';
|
|
|
18
18
|
import { getConfigValue, setConfigValue } from '../lib/config.js';
|
|
19
19
|
import { checkLiveness, claimLock, formatLockEvidence, lockPath, readLock, removeLock, writeLock } from '../lib/leader-lock.js';
|
|
20
20
|
import { resolveCoordinationToken } from '../lib/coordination-token.js';
|
|
21
|
+
import { startupReport } from '../lib/startup-report.js';
|
|
21
22
|
const PACKAGE_NAME = '@memnexus-ai/mx-agent-cli';
|
|
22
23
|
/**
|
|
23
24
|
* Agent-config / hook paths whose rebase conflict must FAIL CLOSED
|
|
@@ -69,6 +70,7 @@ function refuseLiveLeader(worktreePath, team, lock, result) {
|
|
|
69
70
|
console.log(chalk.yellow(' Two leaders in one worktree interleave writes and corrupt team state (#3946).'));
|
|
70
71
|
console.log(chalk.yellow(` If you are certain no other session is running: mx-agent unlock ${team}`));
|
|
71
72
|
console.log();
|
|
73
|
+
startupReport.finalize('refused');
|
|
72
74
|
process.exit(1);
|
|
73
75
|
}
|
|
74
76
|
/** Register a process-exit backstop that removes OUR lock on any exit path
|
|
@@ -322,6 +324,18 @@ export function restoreOwnStash(worktreePath, stashSha) {
|
|
|
322
324
|
const MAX_DIRECTIVE_LENGTH = 2000;
|
|
323
325
|
export async function runStart(options) {
|
|
324
326
|
const { name, experimental = false, directive, testInject = false } = options;
|
|
327
|
+
// ── Startup log capture (#4350) ────────────────────────────────────
|
|
328
|
+
// Tee every line this command prints into ONE durable StartupReport, and
|
|
329
|
+
// register a process-exit safety net so any unhandled exit path still writes
|
|
330
|
+
// a terminal record. Seed the context we already know; worktree/project are
|
|
331
|
+
// filled in once resolved (finalize reads them then). Capture is installed
|
|
332
|
+
// FIRST so even the earliest checks below are recorded.
|
|
333
|
+
startupReport.setContext({
|
|
334
|
+
team: name,
|
|
335
|
+
cliVersion: getInstalledVersion(),
|
|
336
|
+
});
|
|
337
|
+
startupReport.registerExitSafetyNet();
|
|
338
|
+
startupReport.installConsoleCapture();
|
|
325
339
|
// SECURITY: The directive is injected verbatim into the agent prompt.
|
|
326
340
|
// This is safe only because the operator (the person running mx-agent start)
|
|
327
341
|
// is the trust principal. If directive values ever come from untrusted
|
|
@@ -329,6 +343,7 @@ export async function runStart(options) {
|
|
|
329
343
|
// structured prompting (e.g., XML-delimited user input blocks) is required.
|
|
330
344
|
if (directive && directive.length > MAX_DIRECTIVE_LENGTH) {
|
|
331
345
|
console.error(chalk.red(`Error: --directive exceeds ${MAX_DIRECTIVE_LENGTH} characters (got ${directive.length}).`));
|
|
346
|
+
startupReport.finalize('refused');
|
|
332
347
|
process.exit(1);
|
|
333
348
|
}
|
|
334
349
|
let projectRoot;
|
|
@@ -337,8 +352,11 @@ export async function runStart(options) {
|
|
|
337
352
|
}
|
|
338
353
|
catch (err) {
|
|
339
354
|
console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
355
|
+
startupReport.finalize('refused');
|
|
340
356
|
process.exit(1);
|
|
341
357
|
}
|
|
358
|
+
const { basename: basenamePath } = await import('path');
|
|
359
|
+
startupReport.setContext({ project: basenamePath(projectRoot) });
|
|
342
360
|
const cfg = resolveProjectConfig(loadProjectConfig(projectRoot));
|
|
343
361
|
// ── 0. Version staleness check (non-blocking) ──────────────────────
|
|
344
362
|
await warnIfOutdated();
|
|
@@ -353,6 +371,7 @@ export async function runStart(options) {
|
|
|
353
371
|
console.error(chalk.red(`Team '${name}' not found.`));
|
|
354
372
|
console.error(chalk.dim(` No worktree found in ${projectRoot}/.worktrees/ matching '${name}' or '${name}-*'`));
|
|
355
373
|
console.error(chalk.dim(` Run: agent create --roleguide <path> --name ${name}`));
|
|
374
|
+
startupReport.finalize('refused');
|
|
356
375
|
process.exit(1);
|
|
357
376
|
}
|
|
358
377
|
// ── 2. Resolve slot ────────────────────────────────────────────────
|
|
@@ -360,6 +379,8 @@ export async function runStart(options) {
|
|
|
360
379
|
// or just the team slug. Try both.
|
|
361
380
|
const { basename } = await import('path');
|
|
362
381
|
const worktreeDirName = basename(worktreePath);
|
|
382
|
+
// Worktree resolved — record it so finalize writes to the right log file.
|
|
383
|
+
startupReport.setContext({ worktree: worktreeDirName });
|
|
363
384
|
let slot = getSlot(projectRoot, worktreeDirName, cfg.worktreeDir);
|
|
364
385
|
if (slot === null) {
|
|
365
386
|
// Try the short name
|
|
@@ -647,6 +668,7 @@ export async function runStart(options) {
|
|
|
647
668
|
},
|
|
648
669
|
});
|
|
649
670
|
removeLock(worktreePath);
|
|
671
|
+
startupReport.finalize('refused');
|
|
650
672
|
process.exit(1);
|
|
651
673
|
}
|
|
652
674
|
// ── Tier 2: ordinary source conflict ────────────────────────
|
|
@@ -812,6 +834,18 @@ export async function runStart(options) {
|
|
|
812
834
|
: '') +
|
|
813
835
|
`After startup${directive ? ' and the directive' : ''}, act only within the authority, evidence tier, scope, and stop conditions defined by the roleguide.`;
|
|
814
836
|
// ── 9. Launch session ──────────────────────────────────────────────
|
|
837
|
+
// Everything the start command does BEFORE handing off to the session has
|
|
838
|
+
// now succeeded. Finalize the durable startup log with 'ok' here (#4350):
|
|
839
|
+
// all config sync, warnings, and refusal-relevant output happen above this
|
|
840
|
+
// point, and finalizing before launch means a correct 'ok' outcome even
|
|
841
|
+
// though the launched session later drives process.exit with its own code
|
|
842
|
+
// (the exit safety net is a no-op once finalized). finalize also removes the
|
|
843
|
+
// console tee, so the session's own output is left completely untouched.
|
|
844
|
+
// KNOWN LIMITATION (#4350 Phase 2): finalize('ok') runs BEFORE the claude child
|
|
845
|
+
// is confirmed to spawn, so a post-finalize launch failure is not reflected in
|
|
846
|
+
// the recorded outcome. Phase 2 (central shipping + launch-result signal) will
|
|
847
|
+
// revisit whether the outcome should be deferred until spawn is confirmed.
|
|
848
|
+
startupReport.finalize('ok');
|
|
815
849
|
if (experimental) {
|
|
816
850
|
// Experimental: Agent SDK runtime with async Slack communication support.
|
|
817
851
|
// Same setup (steps 1-8) — only the session launcher differs.
|