@ai-sdlc/orchestrator 0.1.0
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/adapters.d.ts +60 -0
- package/dist/adapters.js +160 -0
- package/dist/admission.d.ts +49 -0
- package/dist/admission.js +90 -0
- package/dist/analysis/analyzer.d.ts +10 -0
- package/dist/analysis/analyzer.js +67 -0
- package/dist/analysis/complexity-scorer.d.ts +24 -0
- package/dist/analysis/complexity-scorer.js +39 -0
- package/dist/analysis/context-builder.d.ts +14 -0
- package/dist/analysis/context-builder.js +79 -0
- package/dist/analysis/convention-detector.d.ts +9 -0
- package/dist/analysis/convention-detector.js +177 -0
- package/dist/analysis/dependency-parser.d.ts +17 -0
- package/dist/analysis/dependency-parser.js +167 -0
- package/dist/analysis/file-walker.d.ts +18 -0
- package/dist/analysis/file-walker.js +158 -0
- package/dist/analysis/hotspot-analyzer.d.ts +28 -0
- package/dist/analysis/hotspot-analyzer.js +85 -0
- package/dist/analysis/index.d.ts +13 -0
- package/dist/analysis/index.js +12 -0
- package/dist/analysis/pattern-detector.d.ts +10 -0
- package/dist/analysis/pattern-detector.js +86 -0
- package/dist/analysis/types.d.ts +94 -0
- package/dist/analysis/types.js +5 -0
- package/dist/audit-archival.d.ts +48 -0
- package/dist/audit-archival.js +72 -0
- package/dist/audit-export.d.ts +45 -0
- package/dist/audit-export.js +100 -0
- package/dist/audit-extended.d.ts +28 -0
- package/dist/audit-extended.js +38 -0
- package/dist/audit-scheduler.d.ts +32 -0
- package/dist/audit-scheduler.js +52 -0
- package/dist/audit-sqlite-sink.d.ts +11 -0
- package/dist/audit-sqlite-sink.js +51 -0
- package/dist/autonomy-tracker.d.ts +76 -0
- package/dist/autonomy-tracker.js +277 -0
- package/dist/check-runs.d.ts +37 -0
- package/dist/check-runs.js +72 -0
- package/dist/cli/commands/agents.d.ts +6 -0
- package/dist/cli/commands/agents.js +33 -0
- package/dist/cli/commands/complexity.d.ts +6 -0
- package/dist/cli/commands/complexity.js +35 -0
- package/dist/cli/commands/cost.d.ts +6 -0
- package/dist/cli/commands/cost.js +53 -0
- package/dist/cli/commands/dashboard.d.ts +9 -0
- package/dist/cli/commands/dashboard.js +50 -0
- package/dist/cli/commands/health.d.ts +6 -0
- package/dist/cli/commands/health.js +33 -0
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.js +127 -0
- package/dist/cli/commands/routing.d.ts +6 -0
- package/dist/cli/commands/routing.js +46 -0
- package/dist/cli/commands/run.d.ts +6 -0
- package/dist/cli/commands/run.js +36 -0
- package/dist/cli/commands/start.d.ts +6 -0
- package/dist/cli/commands/start.js +26 -0
- package/dist/cli/commands/status.d.ts +6 -0
- package/dist/cli/commands/status.js +34 -0
- package/dist/cli/dashboard-renderer.d.ts +24 -0
- package/dist/cli/dashboard-renderer.js +129 -0
- package/dist/cli/formatters/index.d.ts +6 -0
- package/dist/cli/formatters/index.js +18 -0
- package/dist/cli/formatters/json.d.ts +5 -0
- package/dist/cli/formatters/json.js +7 -0
- package/dist/cli/formatters/minimal.d.ts +5 -0
- package/dist/cli/formatters/minimal.js +36 -0
- package/dist/cli/formatters/table.d.ts +5 -0
- package/dist/cli/formatters/table.js +184 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +35 -0
- package/dist/compliance-extended.d.ts +41 -0
- package/dist/compliance-extended.js +58 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.js +66 -0
- package/dist/context-enrichment.d.ts +33 -0
- package/dist/context-enrichment.js +125 -0
- package/dist/cost-governance.d.ts +22 -0
- package/dist/cost-governance.js +106 -0
- package/dist/cost-tracker.d.ts +75 -0
- package/dist/cost-tracker.js +177 -0
- package/dist/defaults.d.ts +140 -0
- package/dist/defaults.js +218 -0
- package/dist/deploy/flyio-target.d.ts +19 -0
- package/dist/deploy/flyio-target.js +114 -0
- package/dist/deploy/index.d.ts +15 -0
- package/dist/deploy/index.js +11 -0
- package/dist/deploy/kubernetes-target.d.ts +25 -0
- package/dist/deploy/kubernetes-target.js +124 -0
- package/dist/deploy/metrics-collector.d.ts +31 -0
- package/dist/deploy/metrics-collector.js +75 -0
- package/dist/deploy/rollout-controller.d.ts +42 -0
- package/dist/deploy/rollout-controller.js +192 -0
- package/dist/deploy/rollout-types.d.ts +86 -0
- package/dist/deploy/rollout-types.js +5 -0
- package/dist/deploy/types.d.ts +47 -0
- package/dist/deploy/types.js +5 -0
- package/dist/deploy/vercel-target.d.ts +16 -0
- package/dist/deploy/vercel-target.js +115 -0
- package/dist/discovery.d.ts +26 -0
- package/dist/discovery.js +46 -0
- package/dist/episodic-enhanced.d.ts +88 -0
- package/dist/episodic-enhanced.js +212 -0
- package/dist/execute.d.ts +81 -0
- package/dist/execute.js +703 -0
- package/dist/fix-ci.d.ts +61 -0
- package/dist/fix-ci.js +380 -0
- package/dist/handoff-executor.d.ts +61 -0
- package/dist/handoff-executor.js +119 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +87 -0
- package/dist/instrumented.d.ts +27 -0
- package/dist/instrumented.js +34 -0
- package/dist/logger.d.ts +17 -0
- package/dist/logger.js +37 -0
- package/dist/multi-repo/impact-analyzer.d.ts +22 -0
- package/dist/multi-repo/impact-analyzer.js +95 -0
- package/dist/multi-repo/index.d.ts +5 -0
- package/dist/multi-repo/index.js +4 -0
- package/dist/multi-repo/monorepo-detector.d.ts +16 -0
- package/dist/multi-repo/monorepo-detector.js +246 -0
- package/dist/multi-repo/service-map-builder.d.ts +24 -0
- package/dist/multi-repo/service-map-builder.js +235 -0
- package/dist/multi-repo/types.d.ts +54 -0
- package/dist/multi-repo/types.js +5 -0
- package/dist/notifications/index.d.ts +4 -0
- package/dist/notifications/index.js +4 -0
- package/dist/notifications/notification-router.d.ts +62 -0
- package/dist/notifications/notification-router.js +206 -0
- package/dist/notifications/slack-messenger.d.ts +26 -0
- package/dist/notifications/slack-messenger.js +69 -0
- package/dist/notifications/teams-messenger.d.ts +21 -0
- package/dist/notifications/teams-messenger.js +85 -0
- package/dist/notifications.d.ts +13 -0
- package/dist/notifications.js +15 -0
- package/dist/orchestration.d.ts +20 -0
- package/dist/orchestration.js +53 -0
- package/dist/orchestrator.d.ts +160 -0
- package/dist/orchestrator.js +372 -0
- package/dist/otel-exporter.d.ts +37 -0
- package/dist/otel-exporter.js +128 -0
- package/dist/plugin.d.ts +45 -0
- package/dist/plugin.js +8 -0
- package/dist/policy-evaluators.d.ts +40 -0
- package/dist/policy-evaluators.js +57 -0
- package/dist/process-escalation.d.ts +38 -0
- package/dist/process-escalation.js +129 -0
- package/dist/progressive-gates.d.ts +54 -0
- package/dist/progressive-gates.js +135 -0
- package/dist/provenance.d.ts +29 -0
- package/dist/provenance.js +61 -0
- package/dist/reconcilers.d.ts +28 -0
- package/dist/reconcilers.js +37 -0
- package/dist/runners/claude-code.d.ts +17 -0
- package/dist/runners/claude-code.js +200 -0
- package/dist/runners/codex.d.ts +19 -0
- package/dist/runners/codex.js +153 -0
- package/dist/runners/copilot-stub.d.ts +9 -0
- package/dist/runners/copilot-stub.js +15 -0
- package/dist/runners/copilot.d.ts +15 -0
- package/dist/runners/copilot.js +119 -0
- package/dist/runners/cursor-stub.d.ts +9 -0
- package/dist/runners/cursor-stub.js +15 -0
- package/dist/runners/cursor.d.ts +20 -0
- package/dist/runners/cursor.js +140 -0
- package/dist/runners/devin-stub.d.ts +9 -0
- package/dist/runners/devin-stub.js +15 -0
- package/dist/runners/generic-llm.d.ts +51 -0
- package/dist/runners/generic-llm.js +125 -0
- package/dist/runners/index.d.ts +8 -0
- package/dist/runners/index.js +7 -0
- package/dist/runners/runner-registry.d.ts +50 -0
- package/dist/runners/runner-registry.js +156 -0
- package/dist/runners/types.d.ts +59 -0
- package/dist/runners/types.js +7 -0
- package/dist/security.d.ts +51 -0
- package/dist/security.js +70 -0
- package/dist/shared.d.ts +134 -0
- package/dist/shared.js +284 -0
- package/dist/state/index.d.ts +4 -0
- package/dist/state/index.js +3 -0
- package/dist/state/schema.d.ts +16 -0
- package/dist/state/schema.js +264 -0
- package/dist/state/store.d.ts +109 -0
- package/dist/state/store.js +644 -0
- package/dist/state/types.d.ts +190 -0
- package/dist/state/types.js +5 -0
- package/dist/structured-logger.d.ts +20 -0
- package/dist/structured-logger.js +64 -0
- package/dist/task-decomposer.d.ts +61 -0
- package/dist/task-decomposer.js +265 -0
- package/dist/telemetry-extended.d.ts +24 -0
- package/dist/telemetry-extended.js +31 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.js +7 -0
- package/dist/validate-agent-output.d.ts +30 -0
- package/dist/validate-agent-output.js +81 -0
- package/dist/validate-issue.d.ts +25 -0
- package/dist/validate-issue.js +102 -0
- package/dist/watch.d.ts +43 -0
- package/dist/watch.js +99 -0
- package/dist/webhook-manager.d.ts +38 -0
- package/dist/webhook-manager.js +66 -0
- package/package.json +59 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AutonomyTracker — computes real agent metrics from pipeline run history,
|
|
3
|
+
* evaluates promotion/demotion eligibility, and persists state changes.
|
|
4
|
+
*
|
|
5
|
+
* Replaces the hardcoded `totalTasksCompleted: 1` in execute.ts.
|
|
6
|
+
* RFC reference: Lines 264-280 (autonomy metrics).
|
|
7
|
+
*/
|
|
8
|
+
import type { StateStore } from './state/store.js';
|
|
9
|
+
export interface AgentPerformanceMetrics {
|
|
10
|
+
totalTasks: number;
|
|
11
|
+
successCount: number;
|
|
12
|
+
failureCount: number;
|
|
13
|
+
successRate: number;
|
|
14
|
+
prApprovalRate: number;
|
|
15
|
+
rollbackCount: number;
|
|
16
|
+
securityIncidents: number;
|
|
17
|
+
avgDurationMs: number;
|
|
18
|
+
avgCostUsd: number;
|
|
19
|
+
recentFailures: number;
|
|
20
|
+
timeAtLevelMs: number;
|
|
21
|
+
}
|
|
22
|
+
export interface PromotionEvaluation {
|
|
23
|
+
eligible: boolean;
|
|
24
|
+
fromLevel: number;
|
|
25
|
+
toLevel: number;
|
|
26
|
+
unmetConditions: string[];
|
|
27
|
+
metrics: AgentPerformanceMetrics;
|
|
28
|
+
}
|
|
29
|
+
export interface DemotionEvaluation {
|
|
30
|
+
shouldDemote: boolean;
|
|
31
|
+
fromLevel: number;
|
|
32
|
+
toLevel: number;
|
|
33
|
+
reasons: string[];
|
|
34
|
+
metrics: AgentPerformanceMetrics;
|
|
35
|
+
}
|
|
36
|
+
export interface PromotionProximity {
|
|
37
|
+
currentLevel: number;
|
|
38
|
+
nextLevel: number;
|
|
39
|
+
conditionProgress: Array<{
|
|
40
|
+
condition: string;
|
|
41
|
+
current: number;
|
|
42
|
+
required: number;
|
|
43
|
+
met: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export declare class AutonomyTracker {
|
|
47
|
+
private store;
|
|
48
|
+
constructor(store: StateStore);
|
|
49
|
+
/**
|
|
50
|
+
* Record a task outcome and update the autonomy ledger.
|
|
51
|
+
*/
|
|
52
|
+
recordTaskOutcome(agentName: string, success: boolean, opts?: {
|
|
53
|
+
durationMs?: number;
|
|
54
|
+
costUsd?: number;
|
|
55
|
+
prApproved?: boolean;
|
|
56
|
+
rollback?: boolean;
|
|
57
|
+
securityIncident?: boolean;
|
|
58
|
+
}): void;
|
|
59
|
+
/**
|
|
60
|
+
* Get computed performance metrics for an agent.
|
|
61
|
+
*/
|
|
62
|
+
getAgentMetrics(agentName: string): AgentPerformanceMetrics;
|
|
63
|
+
/**
|
|
64
|
+
* Evaluate promotion eligibility and persist if promoted.
|
|
65
|
+
*/
|
|
66
|
+
evaluateAndPersistPromotion(agentName: string): PromotionEvaluation;
|
|
67
|
+
/**
|
|
68
|
+
* Evaluate demotion triggers and persist if demoted.
|
|
69
|
+
*/
|
|
70
|
+
evaluateAndPersistDemotion(agentName: string): DemotionEvaluation;
|
|
71
|
+
/**
|
|
72
|
+
* Get proximity to next promotion (for dashboard/reporting).
|
|
73
|
+
*/
|
|
74
|
+
getPromotionProximity(agentName: string): PromotionProximity;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=autonomy-tracker.d.ts.map
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AutonomyTracker — computes real agent metrics from pipeline run history,
|
|
3
|
+
* evaluates promotion/demotion eligibility, and persists state changes.
|
|
4
|
+
*
|
|
5
|
+
* Replaces the hardcoded `totalTasksCompleted: 1` in execute.ts.
|
|
6
|
+
* RFC reference: Lines 264-280 (autonomy metrics).
|
|
7
|
+
*/
|
|
8
|
+
/** Minimum tasks required per level for promotion. */
|
|
9
|
+
const PROMOTION_TASK_THRESHOLDS = {
|
|
10
|
+
0: 3,
|
|
11
|
+
1: 10,
|
|
12
|
+
2: 25,
|
|
13
|
+
3: 50,
|
|
14
|
+
};
|
|
15
|
+
/** Minimum success rate required per level for promotion. */
|
|
16
|
+
const PROMOTION_SUCCESS_RATE = {
|
|
17
|
+
0: 0.8,
|
|
18
|
+
1: 0.85,
|
|
19
|
+
2: 0.9,
|
|
20
|
+
3: 0.95,
|
|
21
|
+
};
|
|
22
|
+
/** Minimum time at level (ms) before promotion. */
|
|
23
|
+
const MIN_TIME_AT_LEVEL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
24
|
+
export class AutonomyTracker {
|
|
25
|
+
store;
|
|
26
|
+
constructor(store) {
|
|
27
|
+
this.store = store;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Record a task outcome and update the autonomy ledger.
|
|
31
|
+
*/
|
|
32
|
+
recordTaskOutcome(agentName, success, opts) {
|
|
33
|
+
const existing = this.store.getAutonomyLedger(agentName);
|
|
34
|
+
const now = new Date().toISOString();
|
|
35
|
+
const entry = {
|
|
36
|
+
agentName,
|
|
37
|
+
currentLevel: existing?.currentLevel ?? 0,
|
|
38
|
+
totalTasks: (existing?.totalTasks ?? 0) + 1,
|
|
39
|
+
successCount: (existing?.successCount ?? 0) + (success ? 1 : 0),
|
|
40
|
+
failureCount: (existing?.failureCount ?? 0) + (success ? 0 : 1),
|
|
41
|
+
lastTaskAt: now,
|
|
42
|
+
metrics: existing?.metrics,
|
|
43
|
+
prApprovalRate: existing?.prApprovalRate ?? 0,
|
|
44
|
+
rollbackCount: (existing?.rollbackCount ?? 0) + (opts?.rollback ? 1 : 0),
|
|
45
|
+
securityIncidents: (existing?.securityIncidents ?? 0) + (opts?.securityIncident ? 1 : 0),
|
|
46
|
+
promotedAt: existing?.promotedAt,
|
|
47
|
+
demotedAt: existing?.demotedAt,
|
|
48
|
+
timeAtLevelMs: existing?.timeAtLevelMs ?? 0,
|
|
49
|
+
};
|
|
50
|
+
// Update PR approval rate (running average)
|
|
51
|
+
if (opts?.prApproved !== undefined) {
|
|
52
|
+
const totalPRs = entry.totalTasks;
|
|
53
|
+
const prevApproved = Math.round((existing?.prApprovalRate ?? 0) * (existing?.totalTasks ?? 0));
|
|
54
|
+
const newApproved = prevApproved + (opts.prApproved ? 1 : 0);
|
|
55
|
+
entry.prApprovalRate = totalPRs > 0 ? newApproved / totalPRs : 0;
|
|
56
|
+
}
|
|
57
|
+
// Update time at level
|
|
58
|
+
if (existing?.lastTaskAt) {
|
|
59
|
+
const elapsed = new Date(now).getTime() - new Date(existing.lastTaskAt).getTime();
|
|
60
|
+
entry.timeAtLevelMs = (existing.timeAtLevelMs ?? 0) + elapsed;
|
|
61
|
+
}
|
|
62
|
+
this.store.upsertAutonomyLedger(entry);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get computed performance metrics for an agent.
|
|
66
|
+
*/
|
|
67
|
+
getAgentMetrics(agentName) {
|
|
68
|
+
const ledger = this.store.getAutonomyLedger(agentName);
|
|
69
|
+
if (!ledger) {
|
|
70
|
+
return {
|
|
71
|
+
totalTasks: 0,
|
|
72
|
+
successCount: 0,
|
|
73
|
+
failureCount: 0,
|
|
74
|
+
successRate: 0,
|
|
75
|
+
prApprovalRate: 0,
|
|
76
|
+
rollbackCount: 0,
|
|
77
|
+
securityIncidents: 0,
|
|
78
|
+
avgDurationMs: 0,
|
|
79
|
+
avgCostUsd: 0,
|
|
80
|
+
recentFailures: 0,
|
|
81
|
+
timeAtLevelMs: 0,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// Get recent pipeline runs for duration/cost stats
|
|
85
|
+
const runs = this.store
|
|
86
|
+
.getPipelineRuns(undefined, 100)
|
|
87
|
+
.filter((r) => r.agentName === agentName);
|
|
88
|
+
const durations = runs
|
|
89
|
+
.filter((r) => r.startedAt && r.completedAt)
|
|
90
|
+
.map((r) => new Date(r.completedAt).getTime() - new Date(r.startedAt).getTime());
|
|
91
|
+
const costs = runs.filter((r) => r.costUsd != null && r.costUsd > 0).map((r) => r.costUsd);
|
|
92
|
+
// Count recent failures (last 7 days)
|
|
93
|
+
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
94
|
+
const recentFailures = runs.filter((r) => r.status === 'failed' && r.startedAt && r.startedAt >= sevenDaysAgo).length;
|
|
95
|
+
return {
|
|
96
|
+
totalTasks: ledger.totalTasks,
|
|
97
|
+
successCount: ledger.successCount,
|
|
98
|
+
failureCount: ledger.failureCount,
|
|
99
|
+
successRate: ledger.totalTasks > 0 ? ledger.successCount / ledger.totalTasks : 0,
|
|
100
|
+
prApprovalRate: ledger.prApprovalRate ?? 0,
|
|
101
|
+
rollbackCount: ledger.rollbackCount ?? 0,
|
|
102
|
+
securityIncidents: ledger.securityIncidents ?? 0,
|
|
103
|
+
avgDurationMs: durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
|
|
104
|
+
avgCostUsd: costs.length > 0 ? costs.reduce((a, b) => a + b, 0) / costs.length : 0,
|
|
105
|
+
recentFailures,
|
|
106
|
+
timeAtLevelMs: ledger.timeAtLevelMs ?? 0,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Evaluate promotion eligibility and persist if promoted.
|
|
111
|
+
*/
|
|
112
|
+
evaluateAndPersistPromotion(agentName) {
|
|
113
|
+
const metrics = this.getAgentMetrics(agentName);
|
|
114
|
+
const ledger = this.store.getAutonomyLedger(agentName);
|
|
115
|
+
const currentLevel = ledger?.currentLevel ?? 0;
|
|
116
|
+
const nextLevel = currentLevel + 1;
|
|
117
|
+
if (nextLevel > 4) {
|
|
118
|
+
return {
|
|
119
|
+
eligible: false,
|
|
120
|
+
fromLevel: currentLevel,
|
|
121
|
+
toLevel: currentLevel,
|
|
122
|
+
unmetConditions: ['Already at maximum level'],
|
|
123
|
+
metrics,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const unmetConditions = [];
|
|
127
|
+
// Check task threshold
|
|
128
|
+
const taskThreshold = PROMOTION_TASK_THRESHOLDS[currentLevel] ?? 50;
|
|
129
|
+
if (metrics.totalTasks < taskThreshold) {
|
|
130
|
+
unmetConditions.push(`Need ${taskThreshold} tasks, have ${metrics.totalTasks}`);
|
|
131
|
+
}
|
|
132
|
+
// Check success rate
|
|
133
|
+
const rateThreshold = PROMOTION_SUCCESS_RATE[currentLevel] ?? 0.9;
|
|
134
|
+
if (metrics.successRate < rateThreshold) {
|
|
135
|
+
unmetConditions.push(`Need ${Math.round(rateThreshold * 100)}% success rate, have ${Math.round(metrics.successRate * 100)}%`);
|
|
136
|
+
}
|
|
137
|
+
// Check no recent security incidents
|
|
138
|
+
if (metrics.securityIncidents > 0) {
|
|
139
|
+
unmetConditions.push(`${metrics.securityIncidents} security incidents must be resolved`);
|
|
140
|
+
}
|
|
141
|
+
// Check time at level
|
|
142
|
+
if (metrics.timeAtLevelMs < MIN_TIME_AT_LEVEL_MS) {
|
|
143
|
+
const daysNeeded = Math.ceil(MIN_TIME_AT_LEVEL_MS / (24 * 60 * 60 * 1000));
|
|
144
|
+
const daysAt = Math.floor(metrics.timeAtLevelMs / (24 * 60 * 60 * 1000));
|
|
145
|
+
unmetConditions.push(`Need ${daysNeeded}d at level, have ${daysAt}d`);
|
|
146
|
+
}
|
|
147
|
+
const eligible = unmetConditions.length === 0;
|
|
148
|
+
if (eligible && ledger) {
|
|
149
|
+
// Persist promotion
|
|
150
|
+
const now = new Date().toISOString();
|
|
151
|
+
this.store.upsertAutonomyLedger({
|
|
152
|
+
...ledger,
|
|
153
|
+
currentLevel: nextLevel,
|
|
154
|
+
promotedAt: now,
|
|
155
|
+
timeAtLevelMs: 0, // Reset time at level
|
|
156
|
+
});
|
|
157
|
+
// Record autonomy event
|
|
158
|
+
this.store.saveAutonomyEvent({
|
|
159
|
+
agentName,
|
|
160
|
+
eventType: 'promotion',
|
|
161
|
+
fromLevel: currentLevel,
|
|
162
|
+
toLevel: nextLevel,
|
|
163
|
+
trigger: 'auto-promotion',
|
|
164
|
+
metricsSnapshot: JSON.stringify(metrics),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
eligible,
|
|
169
|
+
fromLevel: currentLevel,
|
|
170
|
+
toLevel: eligible ? nextLevel : currentLevel,
|
|
171
|
+
unmetConditions,
|
|
172
|
+
metrics,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Evaluate demotion triggers and persist if demoted.
|
|
177
|
+
*/
|
|
178
|
+
evaluateAndPersistDemotion(agentName) {
|
|
179
|
+
const metrics = this.getAgentMetrics(agentName);
|
|
180
|
+
const ledger = this.store.getAutonomyLedger(agentName);
|
|
181
|
+
const currentLevel = ledger?.currentLevel ?? 0;
|
|
182
|
+
if (currentLevel === 0) {
|
|
183
|
+
return {
|
|
184
|
+
shouldDemote: false,
|
|
185
|
+
fromLevel: 0,
|
|
186
|
+
toLevel: 0,
|
|
187
|
+
reasons: [],
|
|
188
|
+
metrics,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const reasons = [];
|
|
192
|
+
// Demotion trigger: high recent failure rate
|
|
193
|
+
if (metrics.recentFailures >= 3) {
|
|
194
|
+
reasons.push(`${metrics.recentFailures} recent failures in 7 days`);
|
|
195
|
+
}
|
|
196
|
+
// Demotion trigger: security incident
|
|
197
|
+
if (metrics.securityIncidents > 0) {
|
|
198
|
+
reasons.push(`${metrics.securityIncidents} security incident(s)`);
|
|
199
|
+
}
|
|
200
|
+
// Demotion trigger: rollbacks
|
|
201
|
+
if (metrics.rollbackCount >= 2) {
|
|
202
|
+
reasons.push(`${metrics.rollbackCount} rollbacks`);
|
|
203
|
+
}
|
|
204
|
+
// Demotion trigger: success rate dropped below threshold
|
|
205
|
+
if (metrics.totalTasks >= 5 && metrics.successRate < 0.6) {
|
|
206
|
+
reasons.push(`Success rate dropped to ${Math.round(metrics.successRate * 100)}%`);
|
|
207
|
+
}
|
|
208
|
+
const shouldDemote = reasons.length > 0;
|
|
209
|
+
const toLevel = shouldDemote ? Math.max(0, currentLevel - 1) : currentLevel;
|
|
210
|
+
if (shouldDemote && ledger) {
|
|
211
|
+
const now = new Date().toISOString();
|
|
212
|
+
this.store.upsertAutonomyLedger({
|
|
213
|
+
...ledger,
|
|
214
|
+
currentLevel: toLevel,
|
|
215
|
+
demotedAt: now,
|
|
216
|
+
timeAtLevelMs: 0,
|
|
217
|
+
});
|
|
218
|
+
this.store.saveAutonomyEvent({
|
|
219
|
+
agentName,
|
|
220
|
+
eventType: 'demotion',
|
|
221
|
+
fromLevel: currentLevel,
|
|
222
|
+
toLevel,
|
|
223
|
+
trigger: reasons.join('; '),
|
|
224
|
+
metricsSnapshot: JSON.stringify(metrics),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
shouldDemote,
|
|
229
|
+
fromLevel: currentLevel,
|
|
230
|
+
toLevel,
|
|
231
|
+
reasons,
|
|
232
|
+
metrics,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get proximity to next promotion (for dashboard/reporting).
|
|
237
|
+
*/
|
|
238
|
+
getPromotionProximity(agentName) {
|
|
239
|
+
const metrics = this.getAgentMetrics(agentName);
|
|
240
|
+
const ledger = this.store.getAutonomyLedger(agentName);
|
|
241
|
+
const currentLevel = ledger?.currentLevel ?? 0;
|
|
242
|
+
const nextLevel = Math.min(currentLevel + 1, 4);
|
|
243
|
+
const taskThreshold = PROMOTION_TASK_THRESHOLDS[currentLevel] ?? 50;
|
|
244
|
+
const rateThreshold = PROMOTION_SUCCESS_RATE[currentLevel] ?? 0.9;
|
|
245
|
+
return {
|
|
246
|
+
currentLevel,
|
|
247
|
+
nextLevel,
|
|
248
|
+
conditionProgress: [
|
|
249
|
+
{
|
|
250
|
+
condition: 'Tasks completed',
|
|
251
|
+
current: metrics.totalTasks,
|
|
252
|
+
required: taskThreshold,
|
|
253
|
+
met: metrics.totalTasks >= taskThreshold,
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
condition: 'Success rate',
|
|
257
|
+
current: Math.round(metrics.successRate * 100),
|
|
258
|
+
required: Math.round(rateThreshold * 100),
|
|
259
|
+
met: metrics.successRate >= rateThreshold,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
condition: 'No security incidents',
|
|
263
|
+
current: metrics.securityIncidents,
|
|
264
|
+
required: 0,
|
|
265
|
+
met: metrics.securityIncidents === 0,
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
condition: 'Time at level (days)',
|
|
269
|
+
current: Math.floor(metrics.timeAtLevelMs / (24 * 60 * 60 * 1000)),
|
|
270
|
+
required: Math.ceil(MIN_TIME_AT_LEVEL_MS / (24 * 60 * 60 * 1000)),
|
|
271
|
+
met: metrics.timeAtLevelMs >= MIN_TIME_AT_LEVEL_MS,
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=autonomy-tracker.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Check Run integration via `gh` CLI.
|
|
3
|
+
* Creates one check run per gate result. Falls back gracefully when `gh` is unavailable.
|
|
4
|
+
*/
|
|
5
|
+
export interface CheckRunInput {
|
|
6
|
+
name: string;
|
|
7
|
+
headSha: string;
|
|
8
|
+
status: 'queued' | 'in_progress' | 'completed';
|
|
9
|
+
conclusion?: 'success' | 'failure' | 'neutral' | 'skipped';
|
|
10
|
+
title?: string;
|
|
11
|
+
summary?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a GitHub Check Run for a gate result.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createCheckRun(input: CheckRunInput): Promise<{
|
|
17
|
+
success: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* Update an existing GitHub Check Run.
|
|
22
|
+
*/
|
|
23
|
+
export declare function updateCheckRun(checkRunId: number, input: Partial<CheckRunInput>): Promise<{
|
|
24
|
+
success: boolean;
|
|
25
|
+
error?: string;
|
|
26
|
+
}>;
|
|
27
|
+
export interface GateResult {
|
|
28
|
+
gate: string;
|
|
29
|
+
verdict: 'pass' | 'fail' | 'skip';
|
|
30
|
+
message?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Report gate results as GitHub Check Runs.
|
|
34
|
+
* Creates one check run per gate result. Falls back silently on failure.
|
|
35
|
+
*/
|
|
36
|
+
export declare function reportGateCheckRuns(headSha: string, gateResults: GateResult[]): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=check-runs.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Check Run integration via `gh` CLI.
|
|
3
|
+
* Creates one check run per gate result. Falls back gracefully when `gh` is unavailable.
|
|
4
|
+
*/
|
|
5
|
+
import { execFile } from 'node:child_process';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
7
|
+
import { DEFAULT_GH_CLI_TIMEOUT_MS } from './defaults.js';
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
async function ghApiCall(method, endpoint, body) {
|
|
10
|
+
try {
|
|
11
|
+
const args = ['api', endpoint, '--method', method];
|
|
12
|
+
for (const [key, value] of Object.entries(body)) {
|
|
13
|
+
args.push('-f', `${key}=${String(value)}`);
|
|
14
|
+
}
|
|
15
|
+
await execFileAsync('gh', args, { timeout: DEFAULT_GH_CLI_TIMEOUT_MS });
|
|
16
|
+
return { success: true };
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20
|
+
return { success: false, error: message };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create a GitHub Check Run for a gate result.
|
|
25
|
+
*/
|
|
26
|
+
export async function createCheckRun(input) {
|
|
27
|
+
const body = {
|
|
28
|
+
name: input.name,
|
|
29
|
+
head_sha: input.headSha,
|
|
30
|
+
status: input.status,
|
|
31
|
+
};
|
|
32
|
+
if (input.conclusion)
|
|
33
|
+
body.conclusion = input.conclusion;
|
|
34
|
+
if (input.title || input.summary) {
|
|
35
|
+
body['output[title]'] = input.title ?? input.name;
|
|
36
|
+
body['output[summary]'] = input.summary ?? '';
|
|
37
|
+
}
|
|
38
|
+
return ghApiCall('POST', 'repos/{owner}/{repo}/check-runs', body);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Update an existing GitHub Check Run.
|
|
42
|
+
*/
|
|
43
|
+
export async function updateCheckRun(checkRunId, input) {
|
|
44
|
+
const body = {};
|
|
45
|
+
if (input.status)
|
|
46
|
+
body.status = input.status;
|
|
47
|
+
if (input.conclusion)
|
|
48
|
+
body.conclusion = input.conclusion;
|
|
49
|
+
if (input.title || input.summary) {
|
|
50
|
+
body['output[title]'] = input.title ?? '';
|
|
51
|
+
body['output[summary]'] = input.summary ?? '';
|
|
52
|
+
}
|
|
53
|
+
return ghApiCall('PATCH', `repos/{owner}/{repo}/check-runs/${checkRunId}`, body);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Report gate results as GitHub Check Runs.
|
|
57
|
+
* Creates one check run per gate result. Falls back silently on failure.
|
|
58
|
+
*/
|
|
59
|
+
export async function reportGateCheckRuns(headSha, gateResults) {
|
|
60
|
+
for (const result of gateResults) {
|
|
61
|
+
const conclusion = result.verdict === 'pass' ? 'success' : result.verdict === 'fail' ? 'failure' : 'neutral';
|
|
62
|
+
await createCheckRun({
|
|
63
|
+
name: `AI-SDLC: ${result.gate}`,
|
|
64
|
+
headSha,
|
|
65
|
+
status: 'completed',
|
|
66
|
+
conclusion,
|
|
67
|
+
title: `Gate: ${result.gate}`,
|
|
68
|
+
summary: result.message ?? `Gate ${result.gate}: ${result.verdict}`,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=check-runs.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-sdlc agents [name] — show agent roster, autonomy levels, performance.
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { Orchestrator } from '../../orchestrator.js';
|
|
6
|
+
import { formatOutput } from '../formatters/index.js';
|
|
7
|
+
export const agentsCommand = new Command('agents')
|
|
8
|
+
.description('Show agent roster with autonomy levels and performance')
|
|
9
|
+
.argument('[name]', 'Filter by agent name')
|
|
10
|
+
.option('--state <path>', 'SQLite state database path')
|
|
11
|
+
.action(async (name, opts, cmd) => {
|
|
12
|
+
const globalOpts = cmd.parent?.opts() ?? {};
|
|
13
|
+
const format = globalOpts.format ?? 'table';
|
|
14
|
+
const orchestrator = new Orchestrator({
|
|
15
|
+
configDir: globalOpts.config,
|
|
16
|
+
statePath: opts.state,
|
|
17
|
+
});
|
|
18
|
+
try {
|
|
19
|
+
let agents = await orchestrator.agents();
|
|
20
|
+
if (name) {
|
|
21
|
+
agents = agents.filter((a) => a.agentName === name);
|
|
22
|
+
}
|
|
23
|
+
console.log(formatOutput(format, { type: 'agents', agents }));
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
27
|
+
process.exitCode = 1;
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
orchestrator.close();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-sdlc complexity [--analyze] — show codebase complexity profile.
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { Orchestrator } from '../../orchestrator.js';
|
|
6
|
+
import { formatOutput } from '../formatters/index.js';
|
|
7
|
+
export const complexityCommand = new Command('complexity')
|
|
8
|
+
.description('Show codebase complexity profile')
|
|
9
|
+
.option('--analyze', 'Force a fresh analysis (skip cache)')
|
|
10
|
+
.option('--state <path>', 'SQLite state database path')
|
|
11
|
+
.action(async (opts, cmd) => {
|
|
12
|
+
const globalOpts = cmd.parent?.opts() ?? {};
|
|
13
|
+
const format = globalOpts.format ?? 'table';
|
|
14
|
+
const orchestrator = new Orchestrator({
|
|
15
|
+
configDir: globalOpts.config,
|
|
16
|
+
statePath: opts.state,
|
|
17
|
+
workDir: process.cwd(),
|
|
18
|
+
});
|
|
19
|
+
try {
|
|
20
|
+
const { profile, context } = await orchestrator.complexity({ analyze: opts.analyze });
|
|
21
|
+
console.log(formatOutput(format, {
|
|
22
|
+
type: 'complexity',
|
|
23
|
+
profile,
|
|
24
|
+
context,
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
29
|
+
process.exitCode = 1;
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
orchestrator.close();
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=complexity.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-sdlc cost [--last <dur>] [--agent <name>] [--budget <usd>]
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { Orchestrator } from '../../orchestrator.js';
|
|
6
|
+
import { formatOutput } from '../formatters/index.js';
|
|
7
|
+
export const costCommand = new Command('cost')
|
|
8
|
+
.description('Show cost summary and budget status')
|
|
9
|
+
.option('--last <duration>', 'Time range (e.g., 7d, 30d, 1h)')
|
|
10
|
+
.option('--agent <name>', 'Filter by agent name')
|
|
11
|
+
.option('--budget <usd>', 'Budget in USD', parseFloat)
|
|
12
|
+
.option('--state <path>', 'SQLite state database path')
|
|
13
|
+
.action(async (opts, cmd) => {
|
|
14
|
+
const globalOpts = cmd.parent?.parent?.opts() ?? {};
|
|
15
|
+
const format = globalOpts.format ?? 'table';
|
|
16
|
+
const orchestrator = new Orchestrator({
|
|
17
|
+
configDir: globalOpts.config,
|
|
18
|
+
statePath: opts.state,
|
|
19
|
+
});
|
|
20
|
+
try {
|
|
21
|
+
const since = opts.last ? computeSince(opts.last) : undefined;
|
|
22
|
+
const { summary, budget } = await orchestrator.cost({ since, budget: opts.budget });
|
|
23
|
+
const data = {
|
|
24
|
+
type: 'cost',
|
|
25
|
+
summary,
|
|
26
|
+
budget,
|
|
27
|
+
agent: opts.agent,
|
|
28
|
+
period: opts.last ?? 'all-time',
|
|
29
|
+
};
|
|
30
|
+
// Filter by agent if requested
|
|
31
|
+
if (opts.agent && summary.costByAgent) {
|
|
32
|
+
data.filteredCost = summary.costByAgent[opts.agent] ?? 0;
|
|
33
|
+
}
|
|
34
|
+
console.log(formatOutput(format, data));
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
orchestrator.close();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
function computeSince(duration) {
|
|
45
|
+
const match = duration.match(/^(\d+)([dhm])$/);
|
|
46
|
+
if (!match)
|
|
47
|
+
return new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
48
|
+
const value = parseInt(match[1], 10);
|
|
49
|
+
const unit = match[2];
|
|
50
|
+
const ms = unit === 'd' ? value * 86400000 : unit === 'h' ? value * 3600000 : value * 60000;
|
|
51
|
+
return new Date(Date.now() - ms).toISOString();
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=cost.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-sdlc dashboard — live TUI monitoring dashboard.
|
|
3
|
+
*
|
|
4
|
+
* Uses setInterval + ANSI repainting. Ctrl-C to exit.
|
|
5
|
+
* D1: Zero dependencies — pure ANSI escape codes.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
export declare const dashboardCommand: Command;
|
|
9
|
+
//# sourceMappingURL=dashboard.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-sdlc dashboard — live TUI monitoring dashboard.
|
|
3
|
+
*
|
|
4
|
+
* Uses setInterval + ANSI repainting. Ctrl-C to exit.
|
|
5
|
+
* D1: Zero dependencies — pure ANSI escape codes.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { Orchestrator } from '../../orchestrator.js';
|
|
9
|
+
import { renderDashboardFrame } from '../dashboard-renderer.js';
|
|
10
|
+
import { DEFAULT_DASHBOARD_REFRESH_MS } from '../../defaults.js';
|
|
11
|
+
export const dashboardCommand = new Command('dashboard')
|
|
12
|
+
.description('Live monitoring dashboard (Ctrl-C to exit)')
|
|
13
|
+
.option('--refresh <ms>', 'Refresh interval in ms', String(DEFAULT_DASHBOARD_REFRESH_MS))
|
|
14
|
+
.option('--state <path>', 'SQLite state database path')
|
|
15
|
+
.action(async (opts, cmd) => {
|
|
16
|
+
const globalOpts = cmd.parent?.parent?.opts() ?? {};
|
|
17
|
+
const refreshMs = parseInt(opts.refresh, 10) || DEFAULT_DASHBOARD_REFRESH_MS;
|
|
18
|
+
const orchestrator = new Orchestrator({
|
|
19
|
+
configDir: globalOpts.config,
|
|
20
|
+
statePath: opts.state,
|
|
21
|
+
});
|
|
22
|
+
// Clear screen and hide cursor
|
|
23
|
+
process.stdout.write('\x1B[?25l\x1B[2J\x1B[H');
|
|
24
|
+
const render = async () => {
|
|
25
|
+
try {
|
|
26
|
+
const data = await orchestrator.dashboard();
|
|
27
|
+
const width = process.stdout.columns || 80;
|
|
28
|
+
const frame = renderDashboardFrame(data, width);
|
|
29
|
+
// Move cursor to top-left and write frame
|
|
30
|
+
process.stdout.write('\x1B[H' + frame);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Dashboard rendering is best-effort
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
// Initial render
|
|
37
|
+
await render();
|
|
38
|
+
// Polling loop
|
|
39
|
+
const interval = setInterval(render, refreshMs);
|
|
40
|
+
// Graceful shutdown
|
|
41
|
+
const cleanup = () => {
|
|
42
|
+
clearInterval(interval);
|
|
43
|
+
process.stdout.write('\x1B[?25h\n'); // Show cursor
|
|
44
|
+
orchestrator.close();
|
|
45
|
+
process.exit(0);
|
|
46
|
+
};
|
|
47
|
+
process.on('SIGINT', cleanup);
|
|
48
|
+
process.on('SIGTERM', cleanup);
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=dashboard.js.map
|