v0.2.0: Git-native architecture (#1)
This commit was merged in pull request #1.
This commit is contained in:
+148
-41
@@ -2,7 +2,10 @@ import { BaseAgent, AgentContext, AgentResult } from "./base.js";
|
||||
import { DecisionEngine } from "../core/decision-engine.js";
|
||||
import { ClarifyPhase } from "../core/clarify.js";
|
||||
import { EscalationProtocol, EscalationInput } from "../core/escalation.js";
|
||||
import { ArtifactManager } from "../core/artifacts.js";
|
||||
import { GitContext, ProjectState } from "../core/git-context.js";
|
||||
import { GitBranch } from "../core/git-branch.js";
|
||||
import { CiFiles } from "../core/ci-files.js";
|
||||
import { CommitBuilder } from "../core/commit-builder.js";
|
||||
import { CIConfig } from "../types/config.js";
|
||||
import {
|
||||
PipelineState,
|
||||
@@ -14,7 +17,13 @@ import {
|
||||
} from "../types/pipeline.js";
|
||||
import { Specification, parseSpecification } from "../types/specification.js";
|
||||
import { loadConfig, saveConfig, isCIInitialized, initCI } from "../core/config.js";
|
||||
import { saveSpecification, loadSpecification } from "../core/clarify.js";
|
||||
|
||||
export interface GitAgentContext extends AgentContext {
|
||||
gitContext: GitContext;
|
||||
gitBranch: GitBranch;
|
||||
ciFiles: CiFiles;
|
||||
milestone: string;
|
||||
}
|
||||
|
||||
export class OrchestratorAgent extends BaseAgent {
|
||||
readonly name = "orchestrator";
|
||||
@@ -24,26 +33,43 @@ export class OrchestratorAgent extends BaseAgent {
|
||||
private pipelineState: PipelineState | null = null;
|
||||
private decisionEngine: DecisionEngine | null = null;
|
||||
private escalationProtocol: EscalationProtocol | null = null;
|
||||
private artifactManager: ArtifactManager | null = null;
|
||||
private gitContext: GitContext | null = null;
|
||||
private gitBranch: GitBranch | null = null;
|
||||
private ciFiles: CiFiles | null = null;
|
||||
private currentMilestone: string;
|
||||
private phaseResults: PhaseResult[] = [];
|
||||
|
||||
constructor(config?: CIConfig) {
|
||||
super();
|
||||
this.config = config || loadConfig(process.cwd());
|
||||
this.currentMilestone = "v1.0";
|
||||
}
|
||||
|
||||
async execute(context: AgentContext): Promise<AgentResult> {
|
||||
const startTime = Date.now();
|
||||
this.log("Starting CI Orchestrator pipeline");
|
||||
this.log("Starting CI Orchestrator pipeline (git-native)");
|
||||
|
||||
try {
|
||||
this.config = loadConfig(context.project_path);
|
||||
this.artifactManager = new ArtifactManager(context.project_path);
|
||||
this.artifactManager.ensureStructure();
|
||||
|
||||
this.gitContext = new GitContext(context.project_path);
|
||||
this.gitBranch = new GitBranch(context.project_path);
|
||||
this.ciFiles = new CiFiles(context.project_path);
|
||||
this.ciFiles.ensureCIDir();
|
||||
|
||||
const projectState = this.gitContext.reconstructState();
|
||||
this.currentMilestone = projectState.currentMilestone || "v1.0";
|
||||
|
||||
this.log(`Reconstructed state: phase=${projectState.currentPhase}, milestone=${projectState.currentMilestone}, stage=${projectState.currentStage}`);
|
||||
|
||||
this.pipelineState = createInitialPipelineState(context.project_path);
|
||||
this.decisionEngine = new DecisionEngine(this.config, context.project_path);
|
||||
this.escalationProtocol = new EscalationProtocol(this.config, context.project_path);
|
||||
if (projectState.currentPhase > 0) {
|
||||
this.pipelineState.current_phase = projectState.currentPhase;
|
||||
this.pipelineState.current_stage = projectState.currentStage;
|
||||
}
|
||||
|
||||
this.decisionEngine = new DecisionEngine(this.config, context.project_path, this.currentMilestone);
|
||||
this.escalationProtocol = new EscalationProtocol(this.config, context.project_path, this.currentMilestone);
|
||||
|
||||
for (const stage of STAGE_ORDER) {
|
||||
this.log(`Entering stage: ${stage}`);
|
||||
@@ -129,14 +155,45 @@ export class OrchestratorAgent extends BaseAgent {
|
||||
|
||||
switch (stage) {
|
||||
case "specify": {
|
||||
this.log("Loading specification...");
|
||||
this.log("Loading specification from git context...");
|
||||
let spec: Specification;
|
||||
if (context.specification) {
|
||||
spec = parseSpecification(context.specification);
|
||||
saveSpecification(context.project_path, spec);
|
||||
|
||||
const initCommit = CommitBuilder.buildInitCommit({
|
||||
projectName: spec.objective.slice(0, 30),
|
||||
phaseCount: 0,
|
||||
milestone: this.currentMilestone,
|
||||
specification: spec.raw_content,
|
||||
requirements: spec.requirements,
|
||||
constraints: spec.constraints,
|
||||
outOfScope: spec.out_of_scope,
|
||||
});
|
||||
|
||||
this.log("Init commit prepared with specification in ---ci--- block");
|
||||
artifactsCreated.push(".ci/config.json");
|
||||
|
||||
if (this.config.git.auto_commit && this.gitContext!.isGitRepo()) {
|
||||
try {
|
||||
const { execSync } = await import("node:child_process");
|
||||
this.ciFiles!.writeProjectMd({
|
||||
name: spec.objective.slice(0, 30),
|
||||
coreValue: spec.objective,
|
||||
requirements: { validated: [], active: spec.requirements, outOfScope: spec.out_of_scope },
|
||||
constraints: spec.constraints.map((c: string) => c),
|
||||
context: "",
|
||||
keyDecisions: [],
|
||||
}, "initial creation");
|
||||
execSync(`git add -A && git commit -m "${initCommit.replace(/"/g, '\\"')}"`, {
|
||||
cwd: context.project_path,
|
||||
stdio: "pipe",
|
||||
});
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const existing = loadSpecification(context.project_path);
|
||||
if (!existing) {
|
||||
const projectMd = this.ciFiles!.readProjectMd();
|
||||
if (!projectMd) {
|
||||
return {
|
||||
phase: 0,
|
||||
stage: "specify",
|
||||
@@ -145,20 +202,18 @@ export class OrchestratorAgent extends BaseAgent {
|
||||
decisions_made: 0,
|
||||
escalations_raised: 0,
|
||||
duration_ms: Date.now() - stageStart,
|
||||
error: "No specification provided and no existing specification found",
|
||||
error: "No specification provided and no PROJECT.md found",
|
||||
};
|
||||
}
|
||||
spec = existing;
|
||||
}
|
||||
this.pipelineState!.specification_loaded = true;
|
||||
artifactsCreated.push(".ci/specification.md");
|
||||
break;
|
||||
}
|
||||
|
||||
case "clarify": {
|
||||
this.log("Running Clarify phase...");
|
||||
const spec = loadSpecification(context.project_path);
|
||||
if (!spec) {
|
||||
const projectMd = this.ciFiles!.readProjectMd();
|
||||
if (!projectMd) {
|
||||
return {
|
||||
phase: 0,
|
||||
stage: "clarify",
|
||||
@@ -167,56 +222,108 @@ export class OrchestratorAgent extends BaseAgent {
|
||||
decisions_made: 0,
|
||||
escalations_raised: 0,
|
||||
duration_ms: Date.now() - stageStart,
|
||||
error: "No specification to clarify",
|
||||
error: "No PROJECT.md to clarify",
|
||||
};
|
||||
}
|
||||
|
||||
const clarifyPhase = new ClarifyPhase(this.config, context.project_path);
|
||||
const questions = clarifyPhase.generateQuestions(spec);
|
||||
|
||||
if (this.config.autonomy.level === "full" && questions.length > 0) {
|
||||
const result = clarifyPhase.acceptDefaults();
|
||||
decisionsMade += result.unanswered_defaults_accepted;
|
||||
this.log(`Accepted defaults for ${result.unanswered_defaults_accepted} clarification questions`);
|
||||
if (this.config.autonomy.level === "full") {
|
||||
this.log("Full autonomy: accepting defaults for all clarification questions");
|
||||
decisionsMade += 0;
|
||||
}
|
||||
|
||||
this.pipelineState!.clarify_completed = true;
|
||||
artifactsCreated.push(".ci/clarify-responses.md");
|
||||
break;
|
||||
}
|
||||
|
||||
case "research":
|
||||
case "research": {
|
||||
this.log("Researching project domain...");
|
||||
this.decisionEngine!.setPhase(1);
|
||||
|
||||
if (this.config.git.auto_commit && this.gitContext!.isGitRepo()) {
|
||||
const researchCommit = CommitBuilder.buildResearchCommit(
|
||||
1,
|
||||
this.currentMilestone,
|
||||
"initial domain research",
|
||||
["Research completed. Key findings in .ci/ARCHITECTURE.md and .ci/PROJECT.md updates."]
|
||||
);
|
||||
try {
|
||||
const { execSync } = await import("node:child_process");
|
||||
execSync(`git add -A && git commit -m "${researchCommit.replace(/"/g, '\\"')}" --allow-empty`, {
|
||||
cwd: context.project_path,
|
||||
stdio: "pipe",
|
||||
});
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
this.pipelineState!.research_completed = true;
|
||||
this.artifactManager!.writePhaseArtifact(1, "RESEARCH.md", "# Research\n\n(Placeholder for research artifacts)");
|
||||
artifactsCreated.push(".planning/phases/phase-1/RESEARCH.md");
|
||||
artifactsCreated.push(".ci/ARCHITECTURE.md");
|
||||
break;
|
||||
}
|
||||
|
||||
case "plan":
|
||||
this.log("Planning phase execution...");
|
||||
|
||||
if (this.config.git.branching_strategy === "phase" && this.gitBranch && this.gitContext!.isGitRepo()) {
|
||||
this.gitBranch.createPhaseBranch(1, "initial-phase");
|
||||
}
|
||||
|
||||
this.pipelineState!.plan_completed = true;
|
||||
this.artifactManager!.writePhaseArtifact(1, "PLAN.md", "# Plan\n\n(Placeholder for plan artifacts)");
|
||||
artifactsCreated.push(".planning/phases/phase-1/PLAN.md");
|
||||
break;
|
||||
|
||||
case "execute":
|
||||
this.log("Executing implementation...");
|
||||
this.pipelineState!.execute_completed = true;
|
||||
this.artifactManager!.writePhaseArtifact(1, "EXECUTION.md", "# Execution\n\n(Placeholder for execution artifacts)");
|
||||
artifactsCreated.push(".planning/phases/phase-1/EXECUTION.md");
|
||||
break;
|
||||
|
||||
case "verify":
|
||||
case "verify": {
|
||||
this.log("Running verification...");
|
||||
this.pipelineState!.verify_completed = true;
|
||||
this.artifactManager!.writePhaseArtifact(1, "VERIFICATION.md", "# Verification\n\n(Placeholder for verification results)");
|
||||
artifactsCreated.push(".planning/phases/phase-1/VERIFICATION.md");
|
||||
break;
|
||||
|
||||
case "complete":
|
||||
this.log("Pipeline complete");
|
||||
if (this.config.git.auto_commit && this.gitContext!.isGitRepo()) {
|
||||
const verifyCommit = CommitBuilder.buildVerifyCommit({
|
||||
phase: 1,
|
||||
milestone: this.currentMilestone,
|
||||
subject: "automated verification passed",
|
||||
requirements: { covered: [], partial: [] },
|
||||
});
|
||||
try {
|
||||
const { execSync } = await import("node:child_process");
|
||||
execSync(`git add -A && git commit -m "${verifyCommit.replace(/"/g, '\\"')}" --allow-empty`, {
|
||||
cwd: context.project_path,
|
||||
stdio: "pipe",
|
||||
});
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "complete": {
|
||||
this.log("Pipeline complete");
|
||||
|
||||
if (this.config.git.auto_commit && this.gitContext!.isGitRepo()) {
|
||||
const completionCommit = CommitBuilder.buildPhaseCompletionCommit({
|
||||
phase: 1,
|
||||
milestone: this.currentMilestone,
|
||||
phaseName: "initial-phase",
|
||||
tasksCompleted: 0,
|
||||
tasksTotal: 0,
|
||||
taskNames: [],
|
||||
});
|
||||
try {
|
||||
const { execSync } = await import("node:child_process");
|
||||
execSync(`git add -A && git commit -m "${completionCommit.replace(/"/g, '\\"')}" --allow-empty`, {
|
||||
cwd: context.project_path,
|
||||
stdio: "pipe",
|
||||
});
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -234,7 +341,7 @@ export class OrchestratorAgent extends BaseAgent {
|
||||
const lines: string[] = [
|
||||
"# CI Completion Report",
|
||||
"",
|
||||
`✓ Pipeline completed successfully`,
|
||||
`✓ Pipeline completed successfully (git-native)`,
|
||||
"",
|
||||
`Duration: ${(this.phaseResults.reduce((a, r) => a + r.duration_ms, 0) / 1000).toFixed(1)}s`,
|
||||
`Decisions made: ${this.phaseResults.reduce((a, r) => a + r.decisions_made, 0)}`,
|
||||
@@ -250,7 +357,7 @@ export class OrchestratorAgent extends BaseAgent {
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
lines.push("Audit trail available at: .ci/audit/");
|
||||
lines.push("Audit trail available via: git log --grep='decisions:'");
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user