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 +7 -0
- package/dist/commands/agents-manifest.test.d.ts +2 -0
- package/dist/commands/agents-manifest.test.d.ts.map +1 -0
- package/dist/commands/agents-manifest.test.js +53 -0
- package/dist/commands/agents-manifest.test.js.map +1 -0
- package/dist/commands/agents.d.ts +18 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +198 -152
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/config.js +5 -5
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/context.js +2 -2
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/index.js +1 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/spinner.js +2 -2
- package/dist/commands/spinner.js.map +1 -1
- package/dist/model.js +2 -2
- package/dist/model.js.map +1 -1
- package/dist/repl.js +1 -1
- package/dist/repl.js.map +1 -1
- package/dist/tui/index.js +1 -1
- package/dist/tui/index.js.map +1 -1
- package/dist/ui/emit.d.ts +6 -0
- package/dist/ui/emit.d.ts.map +1 -0
- package/dist/ui/emit.js +41 -0
- package/dist/ui/emit.js.map +1 -0
- package/dist/ui/emit.test.d.ts +2 -0
- package/dist/ui/emit.test.d.ts.map +1 -0
- package/dist/ui/emit.test.js +108 -0
- package/dist/ui/emit.test.js.map +1 -0
- package/package.json +1 -1
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 @@
|
|
|
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":"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"}
|
|
@@ -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;
|
|
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"}
|
package/dist/commands/agents.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
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
|
-
|
|
897
|
-
console.log(
|
|
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
|
-
|
|
907
|
-
|
|
908
|
-
|
|
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
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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
|
-
|
|
927
|
-
|
|
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
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
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
|
-
|
|
944
|
-
const
|
|
945
|
-
|
|
946
|
-
console.log(pc.
|
|
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
|
-
|
|
951
|
-
|
|
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
|
-
|
|
954
|
-
|
|
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
|
-
|
|
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
|
-
|
|
971
|
-
|
|
972
|
-
|
|
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
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
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
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
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
|
-
|
|
1001
|
-
|
|
1002
|
-
console.log(pc.
|
|
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
|
-
|
|
1007
|
-
|
|
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
|
{
|