daedalus-cli 3.28.8 → 3.28.9

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 CHANGED
@@ -1,3 +1,10 @@
1
+ ## [3.28.9](https://github.com/bgill55/daedalus/compare/v3.28.8...v3.28.9) (2026-08-12)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **ui:** single calm render layer + structured run manifest (PR [#110](https://github.com/bgill55/daedalus/issues/110)) ([a5e4526](https://github.com/bgill55/daedalus/commit/a5e4526c1297cd8c60d777edfaca974546eff4f3))
7
+
1
8
  ## [3.28.8](https://github.com/bgill55/daedalus/compare/v3.28.7...v3.28.8) (2026-08-12)
2
9
 
3
10
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=agents-manifest.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents-manifest.test.d.ts","sourceRoot":"","sources":["../../src/commands/agents-manifest.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,53 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { mkdtempSync, rmSync, readdirSync, readFileSync, existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+ import { writeAutopilotManifest } from './agents.js';
6
+ const origCwd = process.cwd();
7
+ describe('autopilot run manifest', () => {
8
+ let dir;
9
+ afterEach(() => {
10
+ process.chdir(origCwd);
11
+ if (dir && existsSync(dir))
12
+ rmSync(dir, { recursive: true, force: true });
13
+ });
14
+ it('writes a valid JSON manifest to .daedalus/ with the expected shape', () => {
15
+ dir = mkdtempSync(join(tmpdir(), 'daedalus-manifest-'));
16
+ process.chdir(dir);
17
+ const manifest = {
18
+ feature: 'add loading spinner',
19
+ branch: 'daedalus-autopilot-add-loading-spinner',
20
+ remote: 'bgill55/daedalus',
21
+ mode: 'git',
22
+ outcome: 'pr-opened',
23
+ tasksPlanned: 4,
24
+ tasksDone: 4,
25
+ filesChanged: ['src/ui/loading.ts', 'src/ui/spinner.ts'],
26
+ testResult: { ok: true, detail: '' },
27
+ finishedAt: new Date().toISOString(),
28
+ };
29
+ writeAutopilotManifest(manifest);
30
+ const daedalusDir = join(dir, '.daedalus');
31
+ expect(existsSync(daedalusDir)).toBe(true);
32
+ const files = readdirSync(daedalusDir).filter((f) => f.endsWith('.json'));
33
+ expect(files.length).toBe(1);
34
+ const written = JSON.parse(readFileSync(join(daedalusDir, files[0]), 'utf8'));
35
+ expect(written.feature).toBe('add loading spinner');
36
+ expect(written.branch).toBe('daedalus-autopilot-add-loading-spinner');
37
+ expect(written.mode).toBe('git');
38
+ expect(written.outcome).toBe('pr-opened');
39
+ expect(written.filesChanged).toEqual(['src/ui/loading.ts', 'src/ui/spinner.ts']);
40
+ expect(written.testResult.ok).toBe(true);
41
+ });
42
+ it('does not throw when run outside a writable location', () => {
43
+ dir = mkdtempSync(join(tmpdir(), 'daedalus-manifest-'));
44
+ process.chdir(dir);
45
+ // Should not throw even though we only test the happy path writes fine.
46
+ expect(() => writeAutopilotManifest({
47
+ feature: 'x', branch: 'b', remote: null, mode: 'local-only',
48
+ outcome: 'committed-local', tasksPlanned: 0, tasksDone: 0,
49
+ filesChanged: [], testResult: null, finishedAt: '',
50
+ })).not.toThrow();
51
+ });
52
+ });
53
+ //# sourceMappingURL=agents-manifest.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents-manifest.test.js","sourceRoot":"","sources":["../../src/commands/agents-manifest.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAA0B,MAAM,aAAa,CAAC;AAE7E,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;AAE9B,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,IAAI,GAAW,CAAC;IAChB,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACvB,IAAI,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC;YAAE,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC;QACxD,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEnB,MAAM,QAAQ,GAAsB;YAClC,OAAO,EAAE,qBAAqB;YAC9B,MAAM,EAAE,wCAAwC;YAChD,MAAM,EAAE,kBAAkB;YAC1B,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,WAAW;YACpB,YAAY,EAAE,CAAC;YACf,SAAS,EAAE,CAAC;YACZ,YAAY,EAAE,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;YACxD,UAAU,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;YACpC,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACrC,CAAC;QAEF,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QAEjC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC3C,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1E,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QAC9E,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACpD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QACtE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1C,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,mBAAmB,EAAE,mBAAmB,CAAC,CAAC,CAAC;QACjF,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC;QACxD,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,wEAAwE;QACxE,MAAM,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC;YAClC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY;YAC3D,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC;YACzD,YAAY,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE;SACnD,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,3 +1,21 @@
1
1
  import type { Command } from './types.js';
2
+ interface AutopilotManifest {
3
+ feature: string;
4
+ branch: string;
5
+ remote: string | null;
6
+ mode: 'git' | 'local-only' | 'non-git';
7
+ outcome: 'committed' | 'committed-local' | 'pr-opened' | 'stopped-verify' | 'stopped-error' | 'no-changes';
8
+ tasksPlanned: number;
9
+ tasksDone: number;
10
+ filesChanged: string[];
11
+ testResult: {
12
+ ok: boolean;
13
+ detail: string;
14
+ } | null;
15
+ finishedAt: string;
16
+ }
17
+ declare function writeAutopilotManifest(m: AutopilotManifest): void;
18
+ export { writeAutopilotManifest };
19
+ export type { AutopilotManifest };
2
20
  export declare const agentCommands: Command[];
3
21
  //# sourceMappingURL=agents.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/commands/agents.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAgE1C,eAAO,MAAM,aAAa,EAAE,OAAO,EA2sClC,CAAA"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/commands/agents.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAoE1C,UAAU,iBAAiB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,KAAK,GAAG,YAAY,GAAG,SAAS,CAAC;IACvC,OAAO,EAAE,WAAW,GAAG,iBAAiB,GAAG,WAAW,GAAG,gBAAgB,GAAG,eAAe,GAAG,YAAY,CAAC;IAC3G,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACnD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,iBAAS,sBAAsB,CAAC,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAU1D;AAED,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAClC,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAElC,eAAO,MAAM,aAAa,EAAE,OAAO,EA0uClC,CAAA"}
@@ -39,7 +39,7 @@ function safeGitAdd(cwd) {
39
39
  const exclude = staged.filter((f) => SECRET_FILE_PATTERN.test(f) || isGitIgnored(cwd, f));
40
40
  if (exclude.length > 0) {
41
41
  execSync(`git reset -q -- ${exclude.map((f) => JSON.stringify(f)).join(' ')}`, { cwd, stdio: 'ignore' });
42
- console.log(pc.yellow(`[CHECK] Excluded ${exclude.length} secret/ignored file(s) from commit (e.g. .env) — not staged.`));
42
+ console.log(pc.dim(`[CHECK] Excluded ${exclude.length} secret/ignored file(s) from commit (e.g. .env) — not staged.`));
43
43
  }
44
44
  }
45
45
  catch {
@@ -75,6 +75,19 @@ async function runAutopilotVerify(cwd) {
75
75
  }
76
76
  return { ok: true, detail: '' };
77
77
  }
78
+ function writeAutopilotManifest(m) {
79
+ try {
80
+ const dir = path.join(process.cwd(), '.daedalus');
81
+ fs.mkdirSync(dir, { recursive: true });
82
+ const file = path.join(dir, `run-${Date.now()}.json`);
83
+ fs.writeFileSync(file, JSON.stringify(m, null, 2), 'utf8');
84
+ console.log(pc.dim(`[INFO] Run manifest written to ${file}`));
85
+ }
86
+ catch {
87
+ // best-effort — manifest is a convenience, never block the session on it
88
+ }
89
+ }
90
+ export { writeAutopilotManifest };
78
91
  export const agentCommands = [
79
92
  {
80
93
  name: '/spawn',
@@ -828,186 +841,219 @@ export const agentCommands = [
828
841
  console.log(pc.yellow('[WARN] Usage: /autopilot <feature description>'));
829
842
  return;
830
843
  }
831
- let isGitRepo = true;
844
+ const manifest = {
845
+ feature: idea,
846
+ branch: '',
847
+ remote: null,
848
+ mode: 'non-git',
849
+ outcome: 'stopped-error',
850
+ tasksPlanned: 0,
851
+ tasksDone: 0,
852
+ filesChanged: [],
853
+ testResult: null,
854
+ finishedAt: '',
855
+ };
856
+ const emitManifest = () => {
857
+ manifest.finishedAt = new Date().toISOString();
858
+ writeAutopilotManifest(manifest);
859
+ };
832
860
  try {
833
- execSync('git rev-parse --is-inside-work-tree', { cwd: ctx.toolContext.projectRoot, stdio: 'ignore' });
834
- }
835
- catch {
836
- isGitRepo = false;
837
- }
838
- if (!isGitRepo) {
839
- console.log(pc.cyan('[INFO] Non-git directory detected. Auto-initializing Git repository for autonomous branch safety...'));
861
+ let isGitRepo = true;
840
862
  try {
841
- const cwd = ctx.toolContext.projectRoot || process.cwd();
842
- execSync('git init', { cwd });
843
- const gitIgnorePath = path.join(cwd, '.gitignore');
844
- if (!fs.existsSync(gitIgnorePath)) {
845
- fs.writeFileSync(gitIgnorePath, "node_modules/\ndist/\n.daedalus/\n", 'utf8');
846
- }
847
- safeGitAdd(cwd);
848
- execSync('git commit -m "initial clean setup"', { cwd });
849
- isGitRepo = true;
850
- console.log(pc.green('[OK] Git repository initialized with tracking branch support.'));
863
+ execSync('git rev-parse --is-inside-work-tree', { cwd: ctx.toolContext.projectRoot, stdio: 'ignore' });
851
864
  }
852
865
  catch {
853
- console.log(pc.yellow('[WARNING] Working directory is not a git repository. Autonomous changes will NOT be tracked in a git branch.'));
854
- }
855
- }
856
- const repoInfo = isGitRepo ? getGitRepoInfo(ctx.toolContext.projectRoot) : null;
857
- if (!repoInfo) {
858
- console.log(pc.yellow('[INFO] No GitHub remote found. Running in local-only mode (no PR will be created).'));
859
- }
860
- const slug = idea.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 40);
861
- const branchName = `daedalus-autopilot-${slug}`;
862
- if (isGitRepo) {
863
- try {
864
- execSync(`git checkout -B ${branchName}`, { cwd: ctx.toolContext.projectRoot });
865
- console.log(pc.green(`[OK] Created branch: ${branchName}`));
866
+ isGitRepo = false;
866
867
  }
867
- catch (err) {
868
- const msg = err instanceof Error ? errMessage(err) : String(err);
869
- console.log(pc.red(`[ERROR] Failed to create branch: ${msg}`));
870
- return;
871
- }
872
- }
873
- const goal = `Implement the following feature: ${idea}`;
874
- console.log(pc.cyan(`\n[AUTOPILOT] Starting autonomous implementation...`));
875
- process.env.DAEDALUS_AUTO_APPROVE = 'true';
876
- process.env.DAEDALUS_ALLOW_INSTALL = 'true';
877
- try {
878
- const { Orchestrator } = await import('../agents/orchestrator.js');
879
- const orchestrator = new Orchestrator(ctx.router, ctx.messages, ctx.toolContext, ctx.sessionManager, ctx.config?.modelOverride);
880
- const result = await orchestrator.run(goal);
881
- console.log(pc.white(`\n${result}`));
882
- const orchestrationFailed = result.startsWith('Orchestration failed') || result.includes('## Orchestration Hit Verification Failures');
883
- const wasAborted = result.includes('## Orchestration Paused');
884
- if (orchestrationFailed || wasAborted) {
885
- // Print Self-Evaluating Autopilot Post-Mortem Report before rolling back
886
- const cols = process.stdout.columns || 80;
887
- const lineLen = Math.max(20, Math.min(70, cols - 6));
888
- console.log(`\n ${pc.bold(pc.red('─ Autopilot Post-Mortem ─'))} ${pc.dim('─'.repeat(Math.max(10, lineLen - 25)))}`);
889
- const failed = orchestrator.results?.filter((r) => !r.success) || [];
890
- if (failed.length > 0) {
891
- failed.forEach((f, idx) => {
892
- console.log(` ${pc.bold(pc.red(`❌ Failed Step ${idx + 1}:`))} ${pc.bold(`[${f.role}]`)} ${f.goal}`);
893
- console.log(` ${pc.yellow(`📌 Diagnostic:`)} ${f.summary.split('\n')[0]}`);
894
- });
868
+ if (!isGitRepo) {
869
+ console.log(pc.cyan('[INFO] Non-git directory detected. Auto-initializing Git repository for autonomous branch safety...'));
870
+ try {
871
+ const cwd = ctx.toolContext.projectRoot || process.cwd();
872
+ execSync('git init', { cwd });
873
+ const gitIgnorePath = path.join(cwd, '.gitignore');
874
+ if (!fs.existsSync(gitIgnorePath)) {
875
+ fs.writeFileSync(gitIgnorePath, "node_modules/\ndist/\n.daedalus/\n", 'utf8');
876
+ }
877
+ safeGitAdd(cwd);
878
+ execSync('git commit -m "initial clean setup"', { cwd });
879
+ isGitRepo = true;
880
+ console.log(pc.green('[OK] Git repository initialized with tracking branch support.'));
895
881
  }
896
- else {
897
- console.log(` ${pc.yellow(' Verification check failed required files failed artifact or build checks.')}`);
882
+ catch {
883
+ console.log(pc.yellow('[WARNING] Working directory is not a git repository. Autonomous changes will NOT be tracked in a git branch.'));
898
884
  }
899
- console.log(`\n ${pc.cyan('💡 Recommendations:')}`);
900
- console.log(` - Target missing file: ${pc.bold(`/task create <file>`)}`);
901
- console.log(` - Re-run autopilot: ${pc.bold(`/autopilot ${idea}`)}`);
902
- console.log(` ${pc.dim('─'.repeat(lineLen + 2))}\n`);
903
- throw new Error(orchestrationFailed ? 'Orchestration reported failure' : 'Orchestration was paused/aborted');
904
885
  }
905
- }
906
- catch (err) {
907
- const msg = err instanceof Error ? errMessage(err) : String(err);
908
- console.log(pc.red(`\n[ERROR] Run stopped: ${msg}`));
886
+ const repoInfo = isGitRepo ? getGitRepoInfo(ctx.toolContext.projectRoot) : null;
887
+ if (!repoInfo) {
888
+ console.log(pc.yellow('[INFO] No GitHub remote found. Running in local-only mode (no PR will be created).'));
889
+ }
890
+ const slug = idea.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 40);
891
+ const branchName = `daedalus-autopilot-${slug}`;
892
+ manifest.remote = repoInfo ? `${repoInfo.owner}/${repoInfo.repo}` : null;
893
+ manifest.mode = !isGitRepo ? 'non-git' : repoInfo ? 'git' : 'local-only';
894
+ manifest.branch = branchName;
909
895
  if (isGitRepo) {
910
- console.log(pc.dim('[CHECK] Verification did not pass — keeping the implemented changes on the branch for review.'));
911
- // In remote-backed repos, discard the failed branch to keep main clean.
912
- // In local-only mode (no remote), keep the branch so the user can
913
- // inspect and fix the work instead of losing it.
914
- if (repoInfo) {
915
- try {
916
- execSync('git reset --hard', { cwd: ctx.toolContext.projectRoot });
917
- execSync('git checkout main', { cwd: ctx.toolContext.projectRoot });
918
- execSync(`git branch -D ${branchName}`, { cwd: ctx.toolContext.projectRoot });
919
- console.log(pc.green('[OK] Branch cleaned up; main is untouched.'));
920
- }
921
- catch (rollbackErr) {
922
- const rbMsg = rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr);
923
- console.log(pc.red(`[ERROR] Cleanup failed: ${rbMsg}. Manual cleanup may be needed.`));
924
- }
896
+ try {
897
+ execSync(`git checkout -B ${branchName}`, { cwd: ctx.toolContext.projectRoot });
898
+ console.log(pc.green(`[OK] Created branch: ${branchName}`));
925
899
  }
926
- else {
927
- console.log(pc.cyan(`[INFO] Local-only mode: keeping branch '${branchName}' with the implemented changes for inspection. Fix and commit manually.`));
900
+ catch (err) {
901
+ const msg = err instanceof Error ? errMessage(err) : String(err);
902
+ console.log(pc.red(`[ERROR] Failed to create branch: ${msg}`));
903
+ return;
928
904
  }
929
905
  }
930
- return;
931
- }
932
- if (isGitRepo) {
933
- console.log(pc.cyan('\n[AUTOPILOT] Verifying build & tests before commit...'));
934
- const verify = await runAutopilotVerify(ctx.toolContext.projectRoot);
935
- if (!verify.ok) {
936
- console.log(pc.red(`\n[ERROR] Verification did not pass — holding the changes on the branch instead of committing. ${verify.detail}`));
937
- console.log(pc.cyan(`[INFO] Branch '${branchName}' is kept with the implemented changes for inspection.`));
938
- return;
939
- }
940
- console.log(pc.green('[OK] Build & tests passed.'));
941
- console.log(pc.cyan('\n[AUTOPILOT] Committing changes...'));
906
+ const goal = `Implement the following feature: ${idea}`;
907
+ console.log(pc.cyan(`\n[AUTOPILOT] Starting autonomous implementation...`));
908
+ process.env.DAEDALUS_AUTO_APPROVE = 'true';
909
+ process.env.DAEDALUS_ALLOW_INSTALL = 'true';
942
910
  try {
943
- safeGitAdd(ctx.toolContext.projectRoot);
944
- const cleanTitle = idea.replace(/[^a-zA-Z0-9 ]/g, '').trim();
945
- execSync(`git commit -m "feat: ${cleanTitle}"`, { cwd: ctx.toolContext.projectRoot });
946
- console.log(pc.green('[OK] Changes committed.'));
911
+ const { Orchestrator } = await import('../agents/orchestrator.js');
912
+ const orchestrator = new Orchestrator(ctx.router, ctx.messages, ctx.toolContext, ctx.sessionManager, ctx.config?.modelOverride);
913
+ const result = await orchestrator.run(goal);
914
+ console.log(pc.white(`\n${result}`));
915
+ const orchestrationFailed = result.startsWith('Orchestration failed') || result.includes('## Orchestration Hit Verification Failures');
916
+ const wasAborted = result.includes('## Orchestration Paused');
917
+ if (orchestrationFailed || wasAborted) {
918
+ // Print Self-Evaluating Autopilot Post-Mortem Report before rolling back
919
+ const cols = process.stdout.columns || 80;
920
+ const lineLen = Math.max(20, Math.min(70, cols - 6));
921
+ console.log(`\n ${pc.bold(pc.red('─ Autopilot Post-Mortem ─'))} ${pc.dim('─'.repeat(Math.max(10, lineLen - 25)))}`);
922
+ const failed = orchestrator.results?.filter((r) => !r.success) || [];
923
+ if (failed.length > 0) {
924
+ failed.forEach((f, idx) => {
925
+ console.log(` ${pc.bold(pc.red(`❌ Failed Step ${idx + 1}:`))} ${pc.bold(`[${f.role}]`)} ${f.goal}`);
926
+ console.log(` ${pc.yellow(`📌 Diagnostic:`)} ${f.summary.split('\n')[0]}`);
927
+ });
928
+ }
929
+ else {
930
+ console.log(` ${pc.yellow('❌ Verification check failed — required files failed artifact or build checks.')}`);
931
+ }
932
+ console.log(`\n ${pc.cyan('💡 Recommendations:')}`);
933
+ console.log(` - Target missing file: ${pc.bold(`/task create <file>`)}`);
934
+ console.log(` - Re-run autopilot: ${pc.bold(`/autopilot ${idea}`)}`);
935
+ console.log(` ${pc.dim('─'.repeat(lineLen + 2))}\n`);
936
+ throw new Error(orchestrationFailed ? 'Orchestration reported failure' : 'Orchestration was paused/aborted');
937
+ }
947
938
  }
948
939
  catch (err) {
949
940
  const msg = err instanceof Error ? errMessage(err) : String(err);
950
- if (msg.includes('nothing to commit')) {
951
- console.log(pc.yellow('[INFO] No changes to commit.'));
941
+ console.log(pc.red(`\n[ERROR] Run stopped: ${msg}`));
942
+ manifest.outcome = 'stopped-error';
943
+ if (isGitRepo) {
944
+ console.log(pc.dim('[CHECK] Verification did not pass — keeping the implemented changes on the branch for review.'));
945
+ // In remote-backed repos, discard the failed branch to keep main clean.
946
+ // In local-only mode (no remote), keep the branch so the user can
947
+ // inspect and fix the work instead of losing it.
948
+ if (repoInfo) {
949
+ try {
950
+ execSync('git reset --hard', { cwd: ctx.toolContext.projectRoot });
951
+ execSync('git checkout main', { cwd: ctx.toolContext.projectRoot });
952
+ execSync(`git branch -D ${branchName}`, { cwd: ctx.toolContext.projectRoot });
953
+ console.log(pc.green('[OK] Branch cleaned up; main is untouched.'));
954
+ }
955
+ catch (rollbackErr) {
956
+ const rbMsg = rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr);
957
+ console.log(pc.red(`[ERROR] Cleanup failed: ${rbMsg}. Manual cleanup may be needed.`));
958
+ }
959
+ }
960
+ else {
961
+ console.log(pc.cyan(`[INFO] Local-only mode: keeping branch '${branchName}' with the implemented changes for inspection. Fix and commit manually.`));
962
+ }
952
963
  }
953
- else {
954
- console.log(pc.red(`[ERROR] Failed to commit: ${msg}`));
964
+ return;
965
+ }
966
+ if (isGitRepo) {
967
+ console.log(pc.cyan('\n[AUTOPILOT] Verifying build & tests before commit...'));
968
+ const verify = await runAutopilotVerify(ctx.toolContext.projectRoot);
969
+ if (!verify.ok) {
970
+ console.log(pc.red(`\n[ERROR] Verification did not pass — holding the changes on the branch instead of committing. ${verify.detail}`));
971
+ console.log(pc.cyan(`[INFO] Branch '${branchName}' is kept with the implemented changes for inspection.`));
972
+ manifest.outcome = 'stopped-verify';
973
+ manifest.testResult = verify;
955
974
  return;
956
975
  }
957
- }
958
- }
959
- else {
960
- console.log(pc.yellow('\n[INFO] Non-git working directory. Autonomous implementation completed directly on files.'));
961
- }
962
- if (repoInfo) {
963
- console.log(pc.cyan('\n[AUTOPILOT] Pushing branch and creating PR...'));
964
- let token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
965
- if (!token) {
976
+ console.log(pc.green('[OK] Build & tests passed.'));
977
+ console.log(pc.cyan('\n[AUTOPILOT] Committing changes...'));
966
978
  try {
967
- token = execSync('gh auth token', { encoding: 'utf8' }).trim();
979
+ safeGitAdd(ctx.toolContext.projectRoot);
980
+ const cleanTitle = idea.replace(/[^a-zA-Z0-9 ]/g, '').trim();
981
+ execSync(`git commit -m "feat: ${cleanTitle}"`, { cwd: ctx.toolContext.projectRoot });
982
+ console.log(pc.green('[OK] Changes committed.'));
983
+ try {
984
+ const diff = execSync(`git diff --name-only HEAD~1 HEAD`, { cwd: ctx.toolContext.projectRoot, encoding: 'utf8' });
985
+ manifest.filesChanged = diff.split('\n').map((s) => s.trim()).filter(Boolean);
986
+ }
987
+ catch { /* best-effort */ }
968
988
  }
969
- catch {
970
- console.log(pc.yellow('[INFO] No GitHub token found. Run `gh auth login` or set GITHUB_TOKEN.'));
971
- console.log(pc.yellow(`[INFO] Branch ${branchName} is ready locally. Push manually.`));
972
- return;
989
+ catch (err) {
990
+ const msg = err instanceof Error ? errMessage(err) : String(err);
991
+ if (msg.includes('nothing to commit')) {
992
+ console.log(pc.yellow('[INFO] No changes to commit.'));
993
+ }
994
+ else {
995
+ console.log(pc.red(`[ERROR] Failed to commit: ${msg}`));
996
+ return;
997
+ }
973
998
  }
974
999
  }
975
- try {
976
- execSync(`git push -u origin ${branchName} --force`, { cwd: ctx.toolContext.projectRoot });
977
- const prResponse = await fetch(`https://api.github.com/repos/${repoInfo.owner}/${repoInfo.repo}/pulls`, {
978
- method: 'POST',
979
- headers: {
980
- 'Authorization': `Bearer ${token}`,
981
- 'Content-Type': 'application/json',
982
- },
983
- body: JSON.stringify({
984
- title: `[Autopilot] ${idea}`,
985
- head: branchName,
986
- base: 'main',
987
- body: `## Description\n\nAutonomously implemented by Daedalus Autopilot.\n\n**Feature:** ${idea}\n\n---\n_Generated by \`/autopilot\`_`,
988
- }),
989
- });
990
- if (prResponse.ok) {
991
- const pr = await prResponse.json();
992
- console.log(pc.green(`\n[OK] Pull Request created: ${pr.html_url}`));
1000
+ else {
1001
+ console.log(pc.yellow('\n[INFO] Non-git working directory. Autonomous implementation completed directly on files.'));
1002
+ }
1003
+ if (repoInfo) {
1004
+ console.log(pc.cyan('\n[AUTOPILOT] Pushing branch and creating PR...'));
1005
+ let token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
1006
+ if (!token) {
1007
+ try {
1008
+ token = execSync('gh auth token', { encoding: 'utf8' }).trim();
1009
+ }
1010
+ catch {
1011
+ console.log(pc.yellow('[INFO] No GitHub token found. Run `gh auth login` or set GITHUB_TOKEN.'));
1012
+ console.log(pc.yellow(`[INFO] Branch ${branchName} is ready locally. Push manually.`));
1013
+ return;
1014
+ }
993
1015
  }
994
- else {
995
- const errText = await prResponse.text();
996
- console.log(pc.red(`[ERROR] Failed to create PR: ${prResponse.status} ${errText}`));
997
- console.log(pc.yellow(`[INFO] Branch ${branchName} is pushed. Create PR manually.`));
1016
+ try {
1017
+ execSync(`git push -u origin ${branchName} --force`, { cwd: ctx.toolContext.projectRoot });
1018
+ const prResponse = await fetch(`https://api.github.com/repos/${repoInfo.owner}/${repoInfo.repo}/pulls`, {
1019
+ method: 'POST',
1020
+ headers: {
1021
+ 'Authorization': `Bearer ${token}`,
1022
+ 'Content-Type': 'application/json',
1023
+ },
1024
+ body: JSON.stringify({
1025
+ title: `[Autopilot] ${idea}`,
1026
+ head: branchName,
1027
+ base: 'main',
1028
+ body: `## Description\n\nAutonomously implemented by Daedalus Autopilot.\n\n**Feature:** ${idea}\n\n---\n_Generated by \`/autopilot\`_`,
1029
+ }),
1030
+ });
1031
+ if (prResponse.ok) {
1032
+ const pr = await prResponse.json();
1033
+ console.log(pc.green(`\n[OK] Pull Request created: ${pr.html_url}`));
1034
+ }
1035
+ else {
1036
+ const errText = await prResponse.text();
1037
+ console.log(pc.red(`[ERROR] Failed to create PR: ${prResponse.status} ${errText}`));
1038
+ console.log(pc.yellow(`[INFO] Branch ${branchName} is pushed. Create PR manually.`));
1039
+ }
1040
+ }
1041
+ catch (err) {
1042
+ const msg = err instanceof Error ? errMessage(err) : String(err);
1043
+ console.log(pc.red(`[ERROR] Push/PR failed: ${msg}`));
1044
+ console.log(pc.yellow(`[INFO] Branch ${branchName} is ready locally.`));
998
1045
  }
999
1046
  }
1000
- catch (err) {
1001
- const msg = err instanceof Error ? errMessage(err) : String(err);
1002
- console.log(pc.red(`[ERROR] Push/PR failed: ${msg}`));
1003
- console.log(pc.yellow(`[INFO] Branch ${branchName} is ready locally.`));
1047
+ else {
1048
+ console.log(pc.yellow('\n[INFO] No GitHub remote configured. Implementation is committed locally.'));
1049
+ console.log(pc.yellow(`[INFO] Branch: ${branchName}`));
1004
1050
  }
1051
+ console.log(pc.cyan(`\n[AUTOPILOT] Done! Run 'git checkout main' to return to main branch.`));
1052
+ manifest.outcome = repoInfo ? 'pr-opened' : 'committed-local';
1005
1053
  }
1006
- else {
1007
- console.log(pc.yellow('\n[INFO] No GitHub remote configured. Implementation is committed locally.'));
1008
- console.log(pc.yellow(`[INFO] Branch: ${branchName}`));
1054
+ finally {
1055
+ emitManifest();
1009
1056
  }
1010
- console.log(pc.cyan(`\n[AUTOPILOT] Done! Run 'git checkout main' to return to main branch.`));
1011
1057
  }
1012
1058
  },
1013
1059
  {