@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,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-agent output validation — enforces guardrails before push.
|
|
3
|
+
*
|
|
4
|
+
* Pure validation module with no side effects (fully testable).
|
|
5
|
+
*/
|
|
6
|
+
export interface ValidationContext {
|
|
7
|
+
filesChanged: string[];
|
|
8
|
+
workDir: string;
|
|
9
|
+
constraints: {
|
|
10
|
+
maxFilesPerChange: number;
|
|
11
|
+
requireTests: boolean;
|
|
12
|
+
blockedPaths: string[];
|
|
13
|
+
};
|
|
14
|
+
guardrails: {
|
|
15
|
+
maxLinesPerPR?: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface ValidationViolation {
|
|
19
|
+
rule: string;
|
|
20
|
+
message: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ValidationResult {
|
|
23
|
+
passed: boolean;
|
|
24
|
+
violations: ValidationViolation[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Validate agent output against guardrails and constraints.
|
|
28
|
+
*/
|
|
29
|
+
export declare function validateAgentOutput(ctx: ValidationContext): Promise<ValidationResult>;
|
|
30
|
+
//# sourceMappingURL=validate-agent-output.d.ts.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-agent output validation — enforces guardrails before push.
|
|
3
|
+
*
|
|
4
|
+
* Pure validation module with no side effects (fully testable).
|
|
5
|
+
*/
|
|
6
|
+
import { execFile } from 'node:child_process';
|
|
7
|
+
/**
|
|
8
|
+
* Match a file path against a blocked-path pattern.
|
|
9
|
+
* Supports simple glob suffixes: `foo/**` matches anything under `foo/`.
|
|
10
|
+
*/
|
|
11
|
+
function matchesBlockedPath(filePath, pattern) {
|
|
12
|
+
const base = pattern.replace(/\/?\*\*$/, '');
|
|
13
|
+
return filePath === base || filePath.startsWith(base.endsWith('/') ? base : `${base}/`);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse total changed lines from `git diff --stat` output.
|
|
17
|
+
* The last line looks like: ` 3 files changed, 40 insertions(+), 12 deletions(-)`
|
|
18
|
+
*/
|
|
19
|
+
function parseDiffStatLines(diffStat) {
|
|
20
|
+
const match = diffStat.match(/(\d+)\s+insertions?\(\+\)(?:,\s*(\d+)\s+deletions?\(-\))?/);
|
|
21
|
+
if (!match) {
|
|
22
|
+
// Try deletions-only format: `1 file changed, 5 deletions(-)`
|
|
23
|
+
const delOnly = diffStat.match(/(\d+)\s+deletions?\(-\)/);
|
|
24
|
+
return delOnly ? Number(delOnly[1]) : 0;
|
|
25
|
+
}
|
|
26
|
+
return Number(match[1]) + (match[2] ? Number(match[2]) : 0);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validate agent output against guardrails and constraints.
|
|
30
|
+
*/
|
|
31
|
+
export async function validateAgentOutput(ctx) {
|
|
32
|
+
const violations = [];
|
|
33
|
+
// 1. Blocked paths
|
|
34
|
+
for (const file of ctx.filesChanged) {
|
|
35
|
+
for (const pattern of ctx.constraints.blockedPaths) {
|
|
36
|
+
if (matchesBlockedPath(file, pattern)) {
|
|
37
|
+
violations.push({
|
|
38
|
+
rule: 'blocked-path',
|
|
39
|
+
message: `File \`${file}\` matches blocked path \`${pattern}\``,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// 2. File count
|
|
45
|
+
if (ctx.filesChanged.length > ctx.constraints.maxFilesPerChange) {
|
|
46
|
+
violations.push({
|
|
47
|
+
rule: 'max-files',
|
|
48
|
+
message: `Changed ${ctx.filesChanged.length} files (max ${ctx.constraints.maxFilesPerChange})`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// 3. Max lines per PR
|
|
52
|
+
if (ctx.guardrails.maxLinesPerPR !== undefined) {
|
|
53
|
+
const stdout = await new Promise((resolve, reject) => {
|
|
54
|
+
execFile('git', ['diff', '--stat', 'HEAD~1'], { cwd: ctx.workDir }, (err, out) => {
|
|
55
|
+
if (err)
|
|
56
|
+
reject(err);
|
|
57
|
+
else
|
|
58
|
+
resolve(out);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
const totalLines = parseDiffStatLines(stdout);
|
|
62
|
+
if (totalLines > ctx.guardrails.maxLinesPerPR) {
|
|
63
|
+
violations.push({
|
|
64
|
+
rule: 'max-lines',
|
|
65
|
+
message: `Changed ${totalLines} lines (max ${ctx.guardrails.maxLinesPerPR})`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// 4. Require tests
|
|
70
|
+
if (ctx.constraints.requireTests) {
|
|
71
|
+
const hasTestFile = ctx.filesChanged.some((f) => /\.test\./.test(f) || /\.spec\./.test(f));
|
|
72
|
+
if (!hasTestFile) {
|
|
73
|
+
violations.push({
|
|
74
|
+
rule: 'require-tests',
|
|
75
|
+
message: 'No test file found in changed files',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { passed: violations.length === 0, violations };
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=validate-agent-output.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates a GitHub issue against quality gates using the reference
|
|
3
|
+
* implementation's enforcement engine, with optional expression and LLM
|
|
4
|
+
* evaluation extensions.
|
|
5
|
+
*/
|
|
6
|
+
import { enforce, type EnforcementResult, type QualityGate, type Issue, type ExpressionEvaluator, type LLMEvaluator } from '@ai-sdlc/reference';
|
|
7
|
+
/**
|
|
8
|
+
* Validate an issue against the loaded QualityGate resource.
|
|
9
|
+
* An optional enforceFn can be provided (e.g. instrumented enforcement).
|
|
10
|
+
* Extra metrics (e.g. cost metrics) are merged into the evaluation context.
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateIssue(issue: Issue, qualityGate: QualityGate, enforceFn?: typeof enforce, extraMetrics?: Record<string, number>): EnforcementResult;
|
|
13
|
+
/**
|
|
14
|
+
* Extended validation that also evaluates expression and LLM gate rules.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateIssueWithExtensions(issue: Issue, qualityGate: QualityGate, options?: {
|
|
17
|
+
expressionEvaluator?: ExpressionEvaluator;
|
|
18
|
+
llmEvaluator?: LLMEvaluator;
|
|
19
|
+
}): Promise<EnforcementResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Parse complexity from issue body (GitHub issue template format).
|
|
22
|
+
* Returns 0 if not found.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseComplexity(issueBody: string | undefined): number;
|
|
25
|
+
//# sourceMappingURL=validate-issue.d.ts.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates a GitHub issue against quality gates using the reference
|
|
3
|
+
* implementation's enforcement engine, with optional expression and LLM
|
|
4
|
+
* evaluation extensions.
|
|
5
|
+
*/
|
|
6
|
+
import { enforce, evaluateExpressionRule, evaluateLLMRule, } from '@ai-sdlc/reference';
|
|
7
|
+
import { DEFAULT_GITHUB_REPOSITORY } from './defaults.js';
|
|
8
|
+
/**
|
|
9
|
+
* Build an EvaluationContext from an issue, mapping issue metadata
|
|
10
|
+
* to the metrics expected by the quality gate rules.
|
|
11
|
+
*/
|
|
12
|
+
function buildEvaluationContext(issue) {
|
|
13
|
+
const hasDescription = issue.description ? issue.description.trim().length : 0;
|
|
14
|
+
const hasAcceptanceCriteria = issue.description
|
|
15
|
+
? /## Acceptance Criteria/i.test(issue.description)
|
|
16
|
+
? 1
|
|
17
|
+
: 0
|
|
18
|
+
: 0;
|
|
19
|
+
const complexityMatch = issue.description?.match(/### Complexity\s*\n+\s*(\d+)/i);
|
|
20
|
+
const complexity = complexityMatch ? Number(complexityMatch[1]) : 0;
|
|
21
|
+
return {
|
|
22
|
+
authorType: 'ai-agent',
|
|
23
|
+
repository: DEFAULT_GITHUB_REPOSITORY,
|
|
24
|
+
metrics: {
|
|
25
|
+
'description-length': hasDescription,
|
|
26
|
+
'has-acceptance-criteria': hasAcceptanceCriteria,
|
|
27
|
+
complexity,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Validate an issue against the loaded QualityGate resource.
|
|
33
|
+
* An optional enforceFn can be provided (e.g. instrumented enforcement).
|
|
34
|
+
* Extra metrics (e.g. cost metrics) are merged into the evaluation context.
|
|
35
|
+
*/
|
|
36
|
+
export function validateIssue(issue, qualityGate, enforceFn, extraMetrics) {
|
|
37
|
+
const ctx = buildEvaluationContext(issue);
|
|
38
|
+
if (extraMetrics) {
|
|
39
|
+
Object.assign(ctx.metrics, extraMetrics);
|
|
40
|
+
}
|
|
41
|
+
return (enforceFn ?? enforce)(qualityGate, ctx);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Extended validation that also evaluates expression and LLM gate rules.
|
|
45
|
+
*/
|
|
46
|
+
export async function validateIssueWithExtensions(issue, qualityGate, options) {
|
|
47
|
+
// Start with standard enforcement
|
|
48
|
+
const result = validateIssue(issue, qualityGate);
|
|
49
|
+
// Evaluate expression rules from quality gates
|
|
50
|
+
if (options?.expressionEvaluator) {
|
|
51
|
+
const ctx = buildEvaluationContext(issue);
|
|
52
|
+
for (const gate of qualityGate.spec.gates) {
|
|
53
|
+
if ('expression' in gate.rule) {
|
|
54
|
+
const verdict = evaluateExpressionRule(gate.rule, { ctx }, options.expressionEvaluator);
|
|
55
|
+
if (!verdict.passed) {
|
|
56
|
+
result.allowed = false;
|
|
57
|
+
result.results.push({
|
|
58
|
+
gate: gate.name,
|
|
59
|
+
verdict: 'fail',
|
|
60
|
+
enforcement: gate.enforcement,
|
|
61
|
+
message: verdict.message ??
|
|
62
|
+
`Expression rule failed: ${gate.rule.expression}`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Evaluate LLM rules on issue description
|
|
69
|
+
if (options?.llmEvaluator && issue.description) {
|
|
70
|
+
for (const gate of qualityGate.spec.gates) {
|
|
71
|
+
// Check for LLM evaluation rule pattern (has dimensions and thresholds)
|
|
72
|
+
const rule = gate.rule;
|
|
73
|
+
if ('dimensions' in rule && 'thresholds' in rule) {
|
|
74
|
+
const llmVerdict = await evaluateLLMRule(rule, issue.description, options.llmEvaluator);
|
|
75
|
+
if (!llmVerdict.passed) {
|
|
76
|
+
result.allowed = false;
|
|
77
|
+
const failureDetails = llmVerdict.failures
|
|
78
|
+
.map((f) => `${f.dimension}: ${f.score.toFixed(2)} < ${f.threshold}`)
|
|
79
|
+
.join(', ');
|
|
80
|
+
result.results.push({
|
|
81
|
+
gate: gate.name,
|
|
82
|
+
verdict: 'fail',
|
|
83
|
+
enforcement: gate.enforcement,
|
|
84
|
+
message: `LLM evaluation failed: ${failureDetails}`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Parse complexity from issue body (GitHub issue template format).
|
|
94
|
+
* Returns 0 if not found.
|
|
95
|
+
*/
|
|
96
|
+
export function parseComplexity(issueBody) {
|
|
97
|
+
if (!issueBody)
|
|
98
|
+
return 0;
|
|
99
|
+
const match = issueBody.match(/### Complexity\s*\n+\s*(\d+)/i);
|
|
100
|
+
return match ? Number(match[1]) : 0;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=validate-issue.js.map
|
package/dist/watch.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reconciler watch mode — wraps executePipeline in a continuous reconciliation loop.
|
|
3
|
+
*
|
|
4
|
+
* Uses ReconcilerLoop + createResourceCache from the reference implementation
|
|
5
|
+
* for level-triggered, idempotent reconciliation with backoff.
|
|
6
|
+
*/
|
|
7
|
+
import { type ReconcilerConfig, type ReconcileResult, type Pipeline, type QualityGate, type AutonomyPolicy, type AgentRole, type MetricStore } from '@ai-sdlc/reference';
|
|
8
|
+
import { type ExecuteOptions } from './execute.js';
|
|
9
|
+
export interface WatchOptions {
|
|
10
|
+
/** Override the reconciler config (poll interval, concurrency, backoff). */
|
|
11
|
+
reconcilerConfig?: Partial<ReconcilerConfig>;
|
|
12
|
+
/** Pipeline execution options passed through to executePipeline. */
|
|
13
|
+
executeOptions?: Omit<ExecuteOptions, 'configDir' | 'workDir'>;
|
|
14
|
+
/** Callback invoked when a pipeline reconciliation completes. */
|
|
15
|
+
onReconcile?: (pipelineName: string, result: ReconcileResult) => void;
|
|
16
|
+
/** Optional metric store to instrument reconciliation cycles. */
|
|
17
|
+
metricStore?: MetricStore;
|
|
18
|
+
/** Optional agent roles for pipeline reconciler agent resolution. */
|
|
19
|
+
agents?: Map<string, AgentRole>;
|
|
20
|
+
/** Optional quality gates for gate reconciler evaluation context. */
|
|
21
|
+
qualityGates?: QualityGate[];
|
|
22
|
+
/** Optional autonomy policies for autonomy reconciler evaluation. */
|
|
23
|
+
autonomyPolicies?: AutonomyPolicy[];
|
|
24
|
+
}
|
|
25
|
+
export interface WatchHandle {
|
|
26
|
+
/** Enqueue a pipeline resource for reconciliation. */
|
|
27
|
+
enqueue(pipeline: Pipeline, issueNumber: number): void;
|
|
28
|
+
/** Enqueue a quality gate resource for reconciliation. */
|
|
29
|
+
enqueueGate(gate: QualityGate): void;
|
|
30
|
+
/** Enqueue an autonomy policy resource for reconciliation. */
|
|
31
|
+
enqueueAutonomy(policy: AutonomyPolicy): void;
|
|
32
|
+
/** Stop the reconciliation loop. */
|
|
33
|
+
stop(): void;
|
|
34
|
+
/** Number of items in the queue. */
|
|
35
|
+
readonly queueSize: number;
|
|
36
|
+
/** Number of actively reconciling items. */
|
|
37
|
+
readonly activeCount: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Start a reconciler watch loop that continuously processes pipeline resources.
|
|
41
|
+
*/
|
|
42
|
+
export declare function startWatch(options?: WatchOptions): WatchHandle;
|
|
43
|
+
//# sourceMappingURL=watch.d.ts.map
|
package/dist/watch.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reconciler watch mode — wraps executePipeline in a continuous reconciliation loop.
|
|
3
|
+
*
|
|
4
|
+
* Uses ReconcilerLoop + createResourceCache from the reference implementation
|
|
5
|
+
* for level-triggered, idempotent reconciliation with backoff.
|
|
6
|
+
*/
|
|
7
|
+
import { ReconcilerLoop, createResourceCache, instrumentReconciler, } from '@ai-sdlc/reference';
|
|
8
|
+
import { executePipeline } from './execute.js';
|
|
9
|
+
import { createPipelineReconciler, createGateReconciler, createAutonomyReconciler, } from './reconcilers.js';
|
|
10
|
+
/**
|
|
11
|
+
* Start a reconciler watch loop that continuously processes pipeline resources.
|
|
12
|
+
*/
|
|
13
|
+
export function startWatch(options = {}) {
|
|
14
|
+
const cache = createResourceCache();
|
|
15
|
+
const issueMap = new Map();
|
|
16
|
+
// H2: Build specialized reconcilers for each resource kind
|
|
17
|
+
const _pipelineReconciler = createPipelineReconciler({
|
|
18
|
+
resolveAgent: (name) => options.agents?.get(name),
|
|
19
|
+
taskFn: async () => ({}),
|
|
20
|
+
});
|
|
21
|
+
const gateReconciler = createGateReconciler({
|
|
22
|
+
getContext: (_gate) => ({ metrics: {}, repository: '', authorType: 'ai-agent' }),
|
|
23
|
+
});
|
|
24
|
+
const autonomyReconciler = createAutonomyReconciler({
|
|
25
|
+
getAgentMetrics: () => undefined,
|
|
26
|
+
getActiveTriggers: () => [],
|
|
27
|
+
});
|
|
28
|
+
// Composite reconciler dispatches by resource kind
|
|
29
|
+
let reconcileFn = async (resource) => {
|
|
30
|
+
switch (resource.kind) {
|
|
31
|
+
case 'Pipeline': {
|
|
32
|
+
const pipeline = resource;
|
|
33
|
+
const issueNumber = issueMap.get(pipeline.metadata.name);
|
|
34
|
+
if (!issueNumber) {
|
|
35
|
+
return {
|
|
36
|
+
type: 'error',
|
|
37
|
+
error: new Error(`No issue number for pipeline ${pipeline.metadata.name}`),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
await executePipeline(issueNumber, {
|
|
42
|
+
...options.executeOptions,
|
|
43
|
+
});
|
|
44
|
+
const result = { type: 'success' };
|
|
45
|
+
options.onReconcile?.(pipeline.metadata.name, result);
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
const result = {
|
|
50
|
+
type: 'error',
|
|
51
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
52
|
+
};
|
|
53
|
+
options.onReconcile?.(pipeline.metadata.name, result);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
case 'QualityGate':
|
|
58
|
+
return gateReconciler(resource);
|
|
59
|
+
case 'AutonomyPolicy':
|
|
60
|
+
return autonomyReconciler(resource);
|
|
61
|
+
default:
|
|
62
|
+
return { type: 'success' };
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
// Wrap with instrumentation if metric store is provided
|
|
66
|
+
if (options.metricStore) {
|
|
67
|
+
reconcileFn = instrumentReconciler(reconcileFn, { metricStore: options.metricStore });
|
|
68
|
+
}
|
|
69
|
+
const loop = new ReconcilerLoop(reconcileFn, options.reconcilerConfig);
|
|
70
|
+
loop.start();
|
|
71
|
+
return {
|
|
72
|
+
enqueue(pipeline, issueNumber) {
|
|
73
|
+
issueMap.set(pipeline.metadata.name, issueNumber);
|
|
74
|
+
if (cache.shouldReconcile(pipeline)) {
|
|
75
|
+
loop.enqueue(pipeline);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
enqueueGate(gate) {
|
|
79
|
+
if (cache.shouldReconcile(gate)) {
|
|
80
|
+
loop.enqueue(gate);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
enqueueAutonomy(policy) {
|
|
84
|
+
if (cache.shouldReconcile(policy)) {
|
|
85
|
+
loop.enqueue(policy);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
stop() {
|
|
89
|
+
loop.stop();
|
|
90
|
+
},
|
|
91
|
+
get queueSize() {
|
|
92
|
+
return loop.queueSize;
|
|
93
|
+
},
|
|
94
|
+
get activeCount() {
|
|
95
|
+
return loop.activeCount;
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=watch.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook manager — creates and manages the webhook server,
|
|
3
|
+
* registers providers based on config, and handles lifecycle.
|
|
4
|
+
*/
|
|
5
|
+
import { type WebhookServer, type WebhookBridge, type IssueEvent, type PREvent, type BuildEvent } from '@ai-sdlc/reference';
|
|
6
|
+
export interface WebhookManagerConfig {
|
|
7
|
+
/** Port to listen on for webhooks. */
|
|
8
|
+
port: number;
|
|
9
|
+
/** Host to bind to (defaults to '0.0.0.0'). */
|
|
10
|
+
host?: string;
|
|
11
|
+
/** GitHub webhook secret. */
|
|
12
|
+
githubSecret?: string;
|
|
13
|
+
/** GitLab webhook secret token. */
|
|
14
|
+
gitlabSecretToken?: string;
|
|
15
|
+
/** Jira webhook secret. */
|
|
16
|
+
jiraSecret?: string;
|
|
17
|
+
/** Linear webhook signing secret. */
|
|
18
|
+
linearSigningSecret?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface WebhookBridges {
|
|
21
|
+
issues: WebhookBridge<IssueEvent>;
|
|
22
|
+
pullRequests: WebhookBridge<PREvent>;
|
|
23
|
+
builds: WebhookBridge<BuildEvent>;
|
|
24
|
+
}
|
|
25
|
+
export interface WebhookManager {
|
|
26
|
+
/** Start the webhook server. */
|
|
27
|
+
start(): Promise<void>;
|
|
28
|
+
/** Stop the webhook server. */
|
|
29
|
+
stop(): Promise<void>;
|
|
30
|
+
/** Get the configured bridges for event consumption. */
|
|
31
|
+
readonly bridges: WebhookBridges;
|
|
32
|
+
/** Get the underlying server. */
|
|
33
|
+
readonly server: WebhookServer;
|
|
34
|
+
/** Number of registered providers. */
|
|
35
|
+
readonly providerCount: number;
|
|
36
|
+
}
|
|
37
|
+
export declare function createWebhookManager(config: WebhookManagerConfig): WebhookManager;
|
|
38
|
+
//# sourceMappingURL=webhook-manager.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook manager — creates and manages the webhook server,
|
|
3
|
+
* registers providers based on config, and handles lifecycle.
|
|
4
|
+
*/
|
|
5
|
+
import { createWebhookServer, createWebhookBridge, createGitHubWebhookProvider, createGitLabWebhookProvider, createJiraWebhookProvider, createLinearWebhookProvider, transformIssueEvent, transformPREvent, transformBuildEvent, transformGitLabIssueEvent, transformGitLabMREvent, transformGitLabPipelineEvent, transformJiraIssueEvent, transformLinearIssueEvent, } from '@ai-sdlc/reference';
|
|
6
|
+
// ── Implementation ───────────────────────────────────────────────────
|
|
7
|
+
export function createWebhookManager(config) {
|
|
8
|
+
const server = createWebhookServer({ port: config.port, host: config.host });
|
|
9
|
+
// Create unified bridges
|
|
10
|
+
const issueBridge = createWebhookBridge((payload) => {
|
|
11
|
+
// Try each transformer in order
|
|
12
|
+
return (transformIssueEvent(payload) ??
|
|
13
|
+
transformGitLabIssueEvent(payload) ??
|
|
14
|
+
transformJiraIssueEvent(payload) ??
|
|
15
|
+
transformLinearIssueEvent(payload));
|
|
16
|
+
});
|
|
17
|
+
const prBridge = createWebhookBridge((payload) => {
|
|
18
|
+
return transformPREvent(payload) ?? transformGitLabMREvent(payload);
|
|
19
|
+
});
|
|
20
|
+
const buildBridge = createWebhookBridge((payload) => {
|
|
21
|
+
return transformBuildEvent(payload) ?? transformGitLabPipelineEvent(payload);
|
|
22
|
+
});
|
|
23
|
+
const bridges = {
|
|
24
|
+
issues: issueBridge,
|
|
25
|
+
pullRequests: prBridge,
|
|
26
|
+
builds: buildBridge,
|
|
27
|
+
};
|
|
28
|
+
// Register providers based on config
|
|
29
|
+
if (config.githubSecret) {
|
|
30
|
+
const provider = createGitHubWebhookProvider({ secret: config.githubSecret }, { issues: issueBridge, pullRequests: prBridge, builds: buildBridge });
|
|
31
|
+
server.registerProvider(provider);
|
|
32
|
+
}
|
|
33
|
+
if (config.gitlabSecretToken) {
|
|
34
|
+
const provider = createGitLabWebhookProvider({ secretToken: config.gitlabSecretToken }, { issues: issueBridge, mergeRequests: prBridge, pipelines: buildBridge });
|
|
35
|
+
server.registerProvider(provider);
|
|
36
|
+
}
|
|
37
|
+
if (config.jiraSecret) {
|
|
38
|
+
const provider = createJiraWebhookProvider({ secret: config.jiraSecret }, { issues: issueBridge });
|
|
39
|
+
server.registerProvider(provider);
|
|
40
|
+
}
|
|
41
|
+
if (config.linearSigningSecret) {
|
|
42
|
+
const provider = createLinearWebhookProvider({ signingSecret: config.linearSigningSecret }, { issues: issueBridge });
|
|
43
|
+
server.registerProvider(provider);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
async start() {
|
|
47
|
+
await server.start();
|
|
48
|
+
},
|
|
49
|
+
async stop() {
|
|
50
|
+
issueBridge.close();
|
|
51
|
+
prBridge.close();
|
|
52
|
+
buildBridge.close();
|
|
53
|
+
await server.stop();
|
|
54
|
+
},
|
|
55
|
+
get bridges() {
|
|
56
|
+
return bridges;
|
|
57
|
+
},
|
|
58
|
+
get server() {
|
|
59
|
+
return server;
|
|
60
|
+
},
|
|
61
|
+
get providerCount() {
|
|
62
|
+
return server.providerCount;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=webhook-manager.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ai-sdlc/orchestrator",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI-SDLC Orchestrator — long-running runtime that drives issues through the complete SDLC with AI agents",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/ai-sdlc-framework/ai-sdlc.git",
|
|
9
|
+
"directory": "orchestrator"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public",
|
|
13
|
+
"registry": "https://registry.npmjs.org/"
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist", "!dist/**/*.test.*", "!dist/**/*.map"],
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "dist/index.js",
|
|
18
|
+
"types": "dist/index.d.ts",
|
|
19
|
+
"bin": {
|
|
20
|
+
"ai-sdlc": "dist/cli/index.js"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"import": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./runners": {
|
|
28
|
+
"types": "./dist/runners/index.d.ts",
|
|
29
|
+
"import": "./dist/runners/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./state": {
|
|
32
|
+
"types": "./dist/state/index.d.ts",
|
|
33
|
+
"import": "./dist/state/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./cli": {
|
|
36
|
+
"types": "./dist/cli/index.d.ts",
|
|
37
|
+
"import": "./dist/cli/index.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc",
|
|
42
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
43
|
+
"test": "vitest run",
|
|
44
|
+
"test:watch": "vitest"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@ai-sdlc/reference": "workspace:*",
|
|
48
|
+
"better-sqlite3": "^11.0.0",
|
|
49
|
+
"commander": "^12.0.0",
|
|
50
|
+
"yaml": "^2.7.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/better-sqlite3": "^7.6.0",
|
|
54
|
+
"@types/node": "^22.0.0",
|
|
55
|
+
"tsx": "^4.19.0",
|
|
56
|
+
"typescript": "^5.7.0",
|
|
57
|
+
"vitest": "^3.0.0"
|
|
58
|
+
}
|
|
59
|
+
}
|