@ai-sdlc/orchestrator 0.5.0 → 0.9.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/action-enforcement.d.ts +26 -0
- package/dist/action-enforcement.js +70 -0
- package/dist/admission-composite.d.ts +67 -0
- package/dist/admission-composite.js +158 -0
- package/dist/admission-enrichment.d.ts +142 -0
- package/dist/admission-enrichment.js +331 -0
- package/dist/admission-hc.d.ts +62 -0
- package/dist/admission-hc.js +83 -0
- package/dist/admission-score.d.ts +148 -0
- package/dist/admission-score.js +237 -0
- package/dist/analysis/analyzer.js +3 -2
- package/dist/analysis/convention-detector.d.ts +85 -2
- package/dist/analysis/convention-detector.js +375 -70
- package/dist/analysis/diff-analyzer.d.ts +33 -0
- package/dist/analysis/diff-analyzer.js +122 -0
- package/dist/analysis/hotspot-analyzer.js +3 -1
- package/dist/analysis/index.d.ts +2 -1
- package/dist/analysis/index.js +2 -1
- package/dist/artifacts/index.d.ts +65 -0
- package/dist/artifacts/index.js +142 -0
- package/dist/backlog-adapter.d.ts +121 -0
- package/dist/backlog-adapter.js +438 -0
- package/dist/calibration.d.ts +43 -0
- package/dist/calibration.js +76 -0
- package/dist/cli/commands/agents.d.ts +24 -0
- package/dist/cli/commands/agents.js +66 -1
- package/dist/cli/commands/git-remote.d.ts +49 -0
- package/dist/cli/commands/git-remote.js +91 -0
- package/dist/cli/commands/health.d.ts +4 -0
- package/dist/cli/commands/health.js +13 -1
- package/dist/cli/commands/init.d.ts +26 -0
- package/dist/cli/commands/init.js +164 -22
- package/dist/cli/commands/mcp-setup.d.ts +31 -0
- package/dist/cli/commands/mcp-setup.js +78 -8
- package/dist/cli/formatters/table.js +15 -2
- package/dist/cli/index.d.ts +14 -1
- package/dist/cli/index.js +81 -20
- package/dist/cli/versions.d.ts +57 -0
- package/dist/cli/versions.js +128 -0
- package/dist/code-area-classifier.d.ts +21 -0
- package/dist/code-area-classifier.js +48 -0
- package/dist/config.d.ts +33 -1
- package/dist/config.js +78 -8
- package/dist/cycle-utils.d.ts +51 -0
- package/dist/cycle-utils.js +77 -0
- package/dist/database/adapters/external.d.ts +24 -0
- package/dist/database/adapters/external.js +80 -0
- package/dist/database/adapters/neon.d.ts +41 -0
- package/dist/database/adapters/neon.js +98 -0
- package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
- package/dist/database/adapters/pg-snapshot-restore.js +68 -0
- package/dist/database/adapters/sqlite-copy.d.ts +32 -0
- package/dist/database/adapters/sqlite-copy.js +145 -0
- package/dist/database/connection-injection.d.ts +35 -0
- package/dist/database/connection-injection.js +93 -0
- package/dist/database/index.d.ts +12 -0
- package/dist/database/index.js +23 -0
- package/dist/database/registry.d.ts +13 -0
- package/dist/database/registry.js +27 -0
- package/dist/database/topology.d.ts +52 -0
- package/dist/database/topology.js +44 -0
- package/dist/database/types.d.ts +89 -0
- package/dist/database/types.js +26 -0
- package/dist/defaults.d.ts +5 -0
- package/dist/defaults.js +5 -0
- package/dist/design-authority.d.ts +40 -0
- package/dist/design-authority.js +71 -0
- package/dist/design-lookahead.d.ts +64 -0
- package/dist/design-lookahead.js +86 -0
- package/dist/design-quality-trend.d.ts +87 -0
- package/dist/design-quality-trend.js +190 -0
- package/dist/design-system-context.d.ts +46 -0
- package/dist/design-system-context.js +80 -0
- package/dist/design-system-correction-loop.d.ts +64 -0
- package/dist/design-system-correction-loop.js +128 -0
- package/dist/design-system-metrics.d.ts +61 -0
- package/dist/design-system-metrics.js +104 -0
- package/dist/design-system-stewardship.d.ts +22 -0
- package/dist/design-system-stewardship.js +85 -0
- package/dist/design-system-validation.d.ts +37 -0
- package/dist/design-system-validation.js +88 -0
- package/dist/dispatch/index.d.ts +4 -0
- package/dist/dispatch/index.js +4 -0
- package/dist/dispatch/merge-gate.d.ts +46 -0
- package/dist/dispatch/merge-gate.js +90 -0
- package/dist/dispatch/requeue.d.ts +57 -0
- package/dist/dispatch/requeue.js +131 -0
- package/dist/dispatch/worker-pool.d.ts +62 -0
- package/dist/dispatch/worker-pool.js +60 -0
- package/dist/execute.d.ts +40 -0
- package/dist/execute.js +295 -30
- package/dist/fix-ci.js +40 -3
- package/dist/fix-review.d.ts +66 -0
- package/dist/fix-review.js +448 -0
- package/dist/harness/adapters/claude-code.d.ts +29 -0
- package/dist/harness/adapters/claude-code.js +191 -0
- package/dist/harness/adapters/codex.d.ts +25 -0
- package/dist/harness/adapters/codex.js +61 -0
- package/dist/harness/independence.d.ts +51 -0
- package/dist/harness/independence.js +67 -0
- package/dist/harness/index.d.ts +14 -0
- package/dist/harness/index.js +20 -0
- package/dist/harness/registry.d.ts +17 -0
- package/dist/harness/registry.js +31 -0
- package/dist/harness/types.d.ts +123 -0
- package/dist/harness/types.js +8 -0
- package/dist/harness/version-probe.d.ts +14 -0
- package/dist/harness/version-probe.js +123 -0
- package/dist/index.d.ts +39 -5
- package/dist/index.js +48 -3
- package/dist/models/classifier.d.ts +76 -0
- package/dist/models/classifier.js +221 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.js +3 -0
- package/dist/models/registry.d.ts +97 -0
- package/dist/models/registry.js +173 -0
- package/dist/pillar-breakdown.d.ts +63 -0
- package/dist/pillar-breakdown.js +153 -0
- package/dist/pipeline-cycle-detector.d.ts +70 -0
- package/dist/pipeline-cycle-detector.js +111 -0
- package/dist/priority.d.ts +7 -76
- package/dist/priority.js +18 -5
- package/dist/review-meta.d.ts +65 -0
- package/dist/review-meta.js +149 -0
- package/dist/review.d.ts +35 -0
- package/dist/review.js +84 -0
- package/dist/runners/claude-code-sdk.d.ts +31 -0
- package/dist/runners/claude-code-sdk.js +238 -0
- package/dist/runners/claude-code.d.ts +3 -0
- package/dist/runners/claude-code.js +319 -73
- package/dist/runners/codex.js +4 -1
- package/dist/runners/copilot.js +4 -1
- package/dist/runners/cursor.js +4 -1
- package/dist/runners/git-utils.d.ts +81 -0
- package/dist/runners/git-utils.js +201 -0
- package/dist/runners/index.d.ts +5 -1
- package/dist/runners/index.js +4 -0
- package/dist/runners/review-agent.d.ts +67 -0
- package/dist/runners/review-agent.js +350 -0
- package/dist/runners/runner-registry.js +10 -0
- package/dist/runners/sdk-review-runner.d.ts +65 -0
- package/dist/runners/sdk-review-runner.js +185 -0
- package/dist/runners/security-triage.d.ts +20 -4
- package/dist/runners/security-triage.js +42 -14
- package/dist/runners/types.d.ts +25 -0
- package/dist/runtime/attestations.d.ts +274 -0
- package/dist/runtime/attestations.js +460 -0
- package/dist/runtime/git-env.d.ts +53 -0
- package/dist/runtime/git-env.js +60 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/parallelism-flag.d.ts +10 -0
- package/dist/runtime/parallelism-flag.js +18 -0
- package/dist/runtime/port-allocator.d.ts +21 -0
- package/dist/runtime/port-allocator.js +66 -0
- package/dist/runtime/worktree-pool.d.ts +86 -0
- package/dist/runtime/worktree-pool.js +204 -0
- package/dist/runtime/worktree.d.ts +25 -0
- package/dist/runtime/worktree.js +111 -0
- package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
- package/dist/sa-scoring/auto-calibrate.js +107 -0
- package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
- package/dist/sa-scoring/c1-sa2-computable.js +59 -0
- package/dist/sa-scoring/composite.d.ts +107 -0
- package/dist/sa-scoring/composite.js +139 -0
- package/dist/sa-scoring/depparse-client.d.ts +79 -0
- package/dist/sa-scoring/depparse-client.js +187 -0
- package/dist/sa-scoring/did-compiler.d.ts +122 -0
- package/dist/sa-scoring/did-compiler.js +286 -0
- package/dist/sa-scoring/drift-monitor.d.ts +84 -0
- package/dist/sa-scoring/drift-monitor.js +186 -0
- package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
- package/dist/sa-scoring/exemplar-bank.js +154 -0
- package/dist/sa-scoring/feedback-store.d.ts +100 -0
- package/dist/sa-scoring/feedback-store.js +156 -0
- package/dist/sa-scoring/index.d.ts +71 -0
- package/dist/sa-scoring/index.js +158 -0
- package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
- package/dist/sa-scoring/layer1-deterministic.js +298 -0
- package/dist/sa-scoring/layer2-structural.d.ts +71 -0
- package/dist/sa-scoring/layer2-structural.js +151 -0
- package/dist/sa-scoring/layer3-llm.d.ts +86 -0
- package/dist/sa-scoring/layer3-llm.js +282 -0
- package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
- package/dist/sa-scoring/rescore-orchestrator.js +47 -0
- package/dist/scheduling/burn-down.d.ts +27 -0
- package/dist/scheduling/burn-down.js +43 -0
- package/dist/scheduling/calibration.d.ts +42 -0
- package/dist/scheduling/calibration.js +150 -0
- package/dist/scheduling/index.d.ts +8 -0
- package/dist/scheduling/index.js +8 -0
- package/dist/scheduling/ledger.d.ts +59 -0
- package/dist/scheduling/ledger.js +216 -0
- package/dist/scheduling/off-peak.d.ts +27 -0
- package/dist/scheduling/off-peak.js +112 -0
- package/dist/scheduling/schedule-decision.d.ts +39 -0
- package/dist/scheduling/schedule-decision.js +89 -0
- package/dist/scheduling/tier-analysis.d.ts +47 -0
- package/dist/scheduling/tier-analysis.js +81 -0
- package/dist/scheduling/types.d.ts +140 -0
- package/dist/scheduling/types.js +11 -0
- package/dist/shared.d.ts +13 -0
- package/dist/shared.js +32 -0
- package/dist/state/index.d.ts +1 -1
- package/dist/state/schema.d.ts +6 -1
- package/dist/state/schema.js +276 -1
- package/dist/state/store.d.ts +71 -1
- package/dist/state/store.js +464 -7
- package/dist/state/types.d.ts +174 -0
- package/dist/types.d.ts +1 -1
- package/dist/validate-agent-output.js +4 -1
- package/dist/watch.js +6 -0
- package/dist/workflow-patterns/artifact-writer.d.ts +16 -0
- package/dist/workflow-patterns/artifact-writer.js +34 -0
- package/dist/workflow-patterns/classifiers.d.ts +10 -0
- package/dist/workflow-patterns/classifiers.js +72 -0
- package/dist/workflow-patterns/detector.d.ts +27 -0
- package/dist/workflow-patterns/detector.js +186 -0
- package/dist/workflow-patterns/index.d.ts +8 -0
- package/dist/workflow-patterns/index.js +7 -0
- package/dist/workflow-patterns/proposal-generator.d.ts +15 -0
- package/dist/workflow-patterns/proposal-generator.js +183 -0
- package/dist/workflow-patterns/telemetry-ingest.d.ts +27 -0
- package/dist/workflow-patterns/telemetry-ingest.js +103 -0
- package/dist/workflow-patterns/types.d.ts +61 -0
- package/dist/workflow-patterns/types.js +11 -0
- package/package.json +2 -2
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backlog.md tracker adapter for the admission composite.
|
|
3
|
+
*
|
|
4
|
+
* Backlog.md tasks are markdown files in `backlog/tasks/` (open) or
|
|
5
|
+
* `backlog/completed/` (archived). They carry YAML frontmatter
|
|
6
|
+
* (`id`, `title`, `status`, `priority`, `labels`, `references`,
|
|
7
|
+
* `created_date`, `updated_date`, `assignee`) plus a body that
|
|
8
|
+
* includes a `## Acceptance Criteria` checklist.
|
|
9
|
+
*
|
|
10
|
+
* This module:
|
|
11
|
+
* 1. Parses a task file into `BacklogTaskSnapshot`
|
|
12
|
+
* 2. Maps the snapshot onto `AdmissionInput` using the conventions
|
|
13
|
+
* in the bug-fix doc (priority:p* / size:[SML] / track:* / etc.)
|
|
14
|
+
* 3. Emits `qualityFlags` for "Done with unchecked ACs" zombie closes
|
|
15
|
+
*
|
|
16
|
+
* The GitHub-shaped `mapIssueToPriorityInput` in `admission-score.ts`
|
|
17
|
+
* is left intact; the dispatch sits in `cli-admit`.
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { parse as parseYaml } from 'yaml';
|
|
22
|
+
import { normalizeBacklogPriority } from './admission-score.js';
|
|
23
|
+
// ── Parser ──────────────────────────────────────────────────────────
|
|
24
|
+
/**
|
|
25
|
+
* Parse a Backlog.md task markdown file into `BacklogTaskSnapshot`.
|
|
26
|
+
* Throws when the frontmatter is missing or malformed.
|
|
27
|
+
*/
|
|
28
|
+
export function parseBacklogTask(content, sourcePath) {
|
|
29
|
+
const fm = extractFrontmatter(content);
|
|
30
|
+
if (!fm)
|
|
31
|
+
throw new Error('Backlog task is missing YAML frontmatter');
|
|
32
|
+
const id = String(fm.id ?? '').trim();
|
|
33
|
+
if (!id)
|
|
34
|
+
throw new Error('Backlog task frontmatter is missing `id`');
|
|
35
|
+
const numericId = parseNumericId(id);
|
|
36
|
+
const labels = normaliseLabels(fm.labels);
|
|
37
|
+
const references = normaliseStringArray(fm.references);
|
|
38
|
+
const dependencies = normaliseStringArray(fm.dependencies);
|
|
39
|
+
const description = extractSection(content, 'Description');
|
|
40
|
+
const acceptanceCriteria = extractAcceptanceCriteria(content);
|
|
41
|
+
return {
|
|
42
|
+
id,
|
|
43
|
+
numericId,
|
|
44
|
+
title: String(fm.title ?? '').trim(),
|
|
45
|
+
description,
|
|
46
|
+
status: String(fm.status ?? '').trim(),
|
|
47
|
+
priority: fm.priority == null ? null : String(fm.priority).trim(),
|
|
48
|
+
labels,
|
|
49
|
+
createdDate: String(fm.created_date ?? '').trim(),
|
|
50
|
+
updatedDate: String(fm.updated_date ?? '').trim(),
|
|
51
|
+
createdBy: fm.created_by ? String(fm.created_by).trim() : undefined,
|
|
52
|
+
acceptanceCriteria,
|
|
53
|
+
references,
|
|
54
|
+
dependencies,
|
|
55
|
+
sourcePath,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Resolve a Backlog task id ("AISDLC-42" or "task-42") to a snapshot
|
|
60
|
+
* by searching the `backlog/tasks/` and `backlog/completed/` directories
|
|
61
|
+
* under `backlogRoot` (typically the project root).
|
|
62
|
+
*/
|
|
63
|
+
export function loadBacklogTaskFromRoot(backlogRoot, id) {
|
|
64
|
+
const idLower = id.toLowerCase();
|
|
65
|
+
const candidates = [
|
|
66
|
+
join(backlogRoot, 'backlog', 'tasks'),
|
|
67
|
+
join(backlogRoot, 'backlog', 'completed'),
|
|
68
|
+
];
|
|
69
|
+
for (const dir of candidates) {
|
|
70
|
+
if (!existsSync(dir))
|
|
71
|
+
continue;
|
|
72
|
+
for (const entry of readdirSync(dir)) {
|
|
73
|
+
if (!entry.toLowerCase().startsWith(`${idLower} `) &&
|
|
74
|
+
!entry.toLowerCase().startsWith(`${idLower}.`)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const path = join(dir, entry);
|
|
78
|
+
const content = readFileSync(path, 'utf-8');
|
|
79
|
+
return parseBacklogTask(content, path);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
// ── Frontmatter helpers (no external YAML dep — handle the subset
|
|
85
|
+
// Backlog.md emits: scalar, sequence, null, "Done"-quoted strings) ──
|
|
86
|
+
function extractFrontmatter(content) {
|
|
87
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
88
|
+
if (!match)
|
|
89
|
+
return undefined;
|
|
90
|
+
return parseSimpleYaml(match[1]);
|
|
91
|
+
}
|
|
92
|
+
function parseSimpleYaml(src) {
|
|
93
|
+
const out = {};
|
|
94
|
+
const lines = src.split(/\r?\n/);
|
|
95
|
+
let i = 0;
|
|
96
|
+
while (i < lines.length) {
|
|
97
|
+
const line = lines[i];
|
|
98
|
+
if (!line.trim() || line.trim().startsWith('#')) {
|
|
99
|
+
i++;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const m = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.*)$/);
|
|
103
|
+
if (!m) {
|
|
104
|
+
i++;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const key = m[1];
|
|
108
|
+
let value = m[2].trim();
|
|
109
|
+
if (value === '' || value === '[]') {
|
|
110
|
+
// Inline empty or block sequence follows.
|
|
111
|
+
const seq = [];
|
|
112
|
+
let j = i + 1;
|
|
113
|
+
while (j < lines.length && /^\s+-\s+/.test(lines[j])) {
|
|
114
|
+
seq.push(lines[j]
|
|
115
|
+
.replace(/^\s+-\s+/, '')
|
|
116
|
+
.trim()
|
|
117
|
+
.replace(/^['"]|['"]$/g, ''));
|
|
118
|
+
j++;
|
|
119
|
+
}
|
|
120
|
+
out[key] = value === '[]' ? [] : seq.length > 0 ? seq : '';
|
|
121
|
+
i = j;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
// Strip wrapping quotes if present.
|
|
125
|
+
if ((value.startsWith("'") && value.endsWith("'")) ||
|
|
126
|
+
(value.startsWith('"') && value.endsWith('"'))) {
|
|
127
|
+
value = value.slice(1, -1);
|
|
128
|
+
}
|
|
129
|
+
out[key] = value;
|
|
130
|
+
i++;
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
function normaliseLabels(raw) {
|
|
135
|
+
if (Array.isArray(raw))
|
|
136
|
+
return raw.map((s) => String(s).trim()).filter(Boolean);
|
|
137
|
+
if (typeof raw === 'string' && raw.trim())
|
|
138
|
+
return [raw.trim()];
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
function normaliseStringArray(raw) {
|
|
142
|
+
if (Array.isArray(raw))
|
|
143
|
+
return raw.map((s) => String(s).trim()).filter(Boolean);
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
function parseNumericId(id) {
|
|
147
|
+
const m = id.match(/(\d+)$/);
|
|
148
|
+
return m ? Number(m[1]) : 0;
|
|
149
|
+
}
|
|
150
|
+
function extractSection(content, name) {
|
|
151
|
+
// Match `## <name>` until the next `## ` header or end of file.
|
|
152
|
+
const re = new RegExp(`##\\s+${name}[\\s\\S]*?(?=\\n##\\s|$)`, 'i');
|
|
153
|
+
const match = content.match(re);
|
|
154
|
+
if (!match)
|
|
155
|
+
return '';
|
|
156
|
+
return match[0].replace(/^##\s+\S+\s*/i, '').trim();
|
|
157
|
+
}
|
|
158
|
+
function extractAcceptanceCriteria(content) {
|
|
159
|
+
const section = extractSection(content, 'Acceptance Criteria');
|
|
160
|
+
if (!section)
|
|
161
|
+
return [];
|
|
162
|
+
const out = [];
|
|
163
|
+
for (const line of section.split(/\r?\n/)) {
|
|
164
|
+
const m = line.match(/^\s*-\s+\[([ x])\]\s+(?:#(\d+)\s+)?(.*)$/i);
|
|
165
|
+
if (!m)
|
|
166
|
+
continue;
|
|
167
|
+
out.push({
|
|
168
|
+
index: m[2] ? Number(m[2]) : out.length + 1,
|
|
169
|
+
checked: m[1].toLowerCase() === 'x',
|
|
170
|
+
text: m[3].trim(),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return out;
|
|
174
|
+
}
|
|
175
|
+
const DEFAULT_SOUL_TRACKS = {
|
|
176
|
+
// Generic defaults — projects override via `.ai-sdlc/soul-tracks.json`.
|
|
177
|
+
'track:ops': 0.55,
|
|
178
|
+
'track:infra': 0.55,
|
|
179
|
+
'track:hygiene': 0.55,
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Map a Backlog snapshot onto an `AdmissionInput` plus the
|
|
183
|
+
* `PriorityInput` overrides cli-admit feeds into the composite.
|
|
184
|
+
* The label-mapping table is the load-bearing piece; tests in
|
|
185
|
+
* `backlog-adapter.test.ts` pin each row.
|
|
186
|
+
*/
|
|
187
|
+
export function mapBacklogTaskToAdmissionInput(snap, options = {}) {
|
|
188
|
+
const labels = snap.labels;
|
|
189
|
+
const labelSet = new Set(labels.map((l) => l.toLowerCase()));
|
|
190
|
+
const tracks = { ...DEFAULT_SOUL_TRACKS, ...(options.soulTracks ?? {}) };
|
|
191
|
+
const authorAssociation = snap.createdBy && options.maintainers?.includes(snap.createdBy) ? 'OWNER' : 'MEMBER';
|
|
192
|
+
// Defaults — overridden below as labels apply.
|
|
193
|
+
let soulAlignment = 0.5;
|
|
194
|
+
let bugSeverity;
|
|
195
|
+
let complexity;
|
|
196
|
+
// ── Priority labels → explicit priority signal ────────────────
|
|
197
|
+
// priority:p0..p3 + frontmatter `priority:` field.
|
|
198
|
+
let explicitPriority;
|
|
199
|
+
if (labelSet.has('priority:p0'))
|
|
200
|
+
explicitPriority = 1.0;
|
|
201
|
+
else if (labelSet.has('priority:p1'))
|
|
202
|
+
explicitPriority = 0.75;
|
|
203
|
+
else if (labelSet.has('priority:p2'))
|
|
204
|
+
explicitPriority = 0.5;
|
|
205
|
+
else if (labelSet.has('priority:p3'))
|
|
206
|
+
explicitPriority = 0.25;
|
|
207
|
+
else if (snap.priority?.toLowerCase() === 'high')
|
|
208
|
+
explicitPriority = 0.75;
|
|
209
|
+
else if (snap.priority?.toLowerCase() === 'medium')
|
|
210
|
+
explicitPriority = 0.5;
|
|
211
|
+
else if (snap.priority?.toLowerCase() === 'low')
|
|
212
|
+
explicitPriority = 0.25;
|
|
213
|
+
// ── Size labels → complexity ─────────────────────────────────
|
|
214
|
+
if (labelSet.has('size:s'))
|
|
215
|
+
complexity = 2;
|
|
216
|
+
else if (labelSet.has('size:m'))
|
|
217
|
+
complexity = 5;
|
|
218
|
+
else if (labelSet.has('size:l'))
|
|
219
|
+
complexity = 7;
|
|
220
|
+
else if (labelSet.has('size:xl'))
|
|
221
|
+
complexity = 9;
|
|
222
|
+
// Otherwise derive from AC count below.
|
|
223
|
+
// ── Soul-alignment signals ────────────────────────────────────
|
|
224
|
+
if (labelSet.has('source:rfc') || labelSet.has('source:spec')) {
|
|
225
|
+
soulAlignment = Math.max(soulAlignment, 0.9);
|
|
226
|
+
}
|
|
227
|
+
if (labelSet.has('governance') || labelSet.has('compliance')) {
|
|
228
|
+
soulAlignment = Math.max(soulAlignment, 0.85);
|
|
229
|
+
}
|
|
230
|
+
for (const label of labels) {
|
|
231
|
+
const key = label.toLowerCase();
|
|
232
|
+
if (key.startsWith('track:') && tracks[key] !== undefined) {
|
|
233
|
+
soulAlignment = Math.max(soulAlignment, tracks[key]);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// ── Bug-class labels ──────────────────────────────────────────
|
|
237
|
+
if (labelSet.has('bug') || labelSet.has('regression') || labelSet.has('defect')) {
|
|
238
|
+
bugSeverity = 3;
|
|
239
|
+
}
|
|
240
|
+
if (labelSet.has('security') || labelSet.has('vulnerability')) {
|
|
241
|
+
bugSeverity = 5;
|
|
242
|
+
soulAlignment = Math.max(soulAlignment, 0.7);
|
|
243
|
+
}
|
|
244
|
+
if (labelSet.has('critical') || labelSet.has('p0')) {
|
|
245
|
+
bugSeverity = Math.max(bugSeverity ?? 0, 5);
|
|
246
|
+
}
|
|
247
|
+
// ── AC progress → complexity proxy + quality flags ───────────
|
|
248
|
+
const acTotal = snap.acceptanceCriteria.length;
|
|
249
|
+
const acChecked = snap.acceptanceCriteria.filter((a) => a.checked).length;
|
|
250
|
+
const acProgress = acTotal === 0 ? null : acChecked / acTotal;
|
|
251
|
+
if (complexity === undefined && acTotal > 0) {
|
|
252
|
+
// 1 AC → 1.6, 5 ACs → 4, 9 ACs → 6.4, 13 ACs → 8.8 (capped at 9).
|
|
253
|
+
complexity = Math.min(1 + acTotal * 0.6, 9);
|
|
254
|
+
}
|
|
255
|
+
const qualityFlags = [];
|
|
256
|
+
let defectRiskFactor;
|
|
257
|
+
const isZombieClose = snap.status.toLowerCase() === 'done' && acTotal > 0 && acProgress !== null && acProgress < 1.0;
|
|
258
|
+
if (isZombieClose) {
|
|
259
|
+
const severity = acProgress < 0.5 ? 'high' : 'medium';
|
|
260
|
+
qualityFlags.push({
|
|
261
|
+
kind: 'unchecked-acs-on-done',
|
|
262
|
+
detail: `${acTotal - acChecked}/${acTotal} ACs unchecked`,
|
|
263
|
+
severity,
|
|
264
|
+
});
|
|
265
|
+
defectRiskFactor = severity === 'high' ? 0.3 : 0.15;
|
|
266
|
+
}
|
|
267
|
+
// ── Demand / drift / consensus heuristics ─────────────────────
|
|
268
|
+
let demandSignal;
|
|
269
|
+
let competitiveDrift = 0;
|
|
270
|
+
for (const label of labels) {
|
|
271
|
+
const key = label.toLowerCase();
|
|
272
|
+
if (key.startsWith('source:') && key.includes('-tonight')) {
|
|
273
|
+
demandSignal = Math.max(demandSignal ?? 0, 0.7);
|
|
274
|
+
competitiveDrift += 0.2;
|
|
275
|
+
}
|
|
276
|
+
if (key === 'scope:v1-ship' || key === 'scope:v1') {
|
|
277
|
+
competitiveDrift += 0.6;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
competitiveDrift = Math.min(competitiveDrift, 1);
|
|
281
|
+
// teamConsensus: someone is on the hook.
|
|
282
|
+
const teamConsensus = snap.createdBy ? 0.4 : undefined;
|
|
283
|
+
// builderConviction: anchored in references? Multiple references to
|
|
284
|
+
// existing files = author did the homework.
|
|
285
|
+
const builderConviction = snap.references.length >= 2 ? 0.7 : snap.references.length === 1 ? 0.6 : undefined;
|
|
286
|
+
// commentCount / reactionCount have no Backlog analog yet; default 0.
|
|
287
|
+
const reactionCount = 0;
|
|
288
|
+
const commentCount = 0;
|
|
289
|
+
// Construct the body the existing scorer expects (Description +
|
|
290
|
+
// Acceptance Criteria) — preserves the GitHub mapper's complexity
|
|
291
|
+
// regex for callers that bypass the Backlog mapping path.
|
|
292
|
+
const renderedBody = buildAdmissionBody(snap, complexity);
|
|
293
|
+
const input = {
|
|
294
|
+
issueNumber: snap.numericId,
|
|
295
|
+
title: snap.title,
|
|
296
|
+
body: renderedBody,
|
|
297
|
+
labels,
|
|
298
|
+
reactionCount,
|
|
299
|
+
commentCount,
|
|
300
|
+
createdAt: normaliseIsoDate(snap.createdDate),
|
|
301
|
+
authorAssociation,
|
|
302
|
+
authorLogin: snap.createdBy,
|
|
303
|
+
backlogContext: {
|
|
304
|
+
priority: normalizeBacklogPriority(snap.priority),
|
|
305
|
+
dependencyCount: snap.dependencies.length,
|
|
306
|
+
referenceCount: snap.references.length,
|
|
307
|
+
acceptanceCriteriaCount: snap.acceptanceCriteria.length,
|
|
308
|
+
status: snap.status,
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
const priorityInputOverrides = {};
|
|
312
|
+
if (soulAlignment !== 0.5)
|
|
313
|
+
priorityInputOverrides.soulAlignment = soulAlignment;
|
|
314
|
+
if (bugSeverity !== undefined)
|
|
315
|
+
priorityInputOverrides.bugSeverity = bugSeverity;
|
|
316
|
+
if (complexity !== undefined)
|
|
317
|
+
priorityInputOverrides.complexity = complexity;
|
|
318
|
+
if (explicitPriority !== undefined)
|
|
319
|
+
priorityInputOverrides.explicitPriority = explicitPriority;
|
|
320
|
+
if (demandSignal !== undefined)
|
|
321
|
+
priorityInputOverrides.demandSignal = demandSignal;
|
|
322
|
+
if (competitiveDrift > 0)
|
|
323
|
+
priorityInputOverrides.competitiveDrift = competitiveDrift;
|
|
324
|
+
if (teamConsensus !== undefined)
|
|
325
|
+
priorityInputOverrides.teamConsensus = teamConsensus;
|
|
326
|
+
if (builderConviction !== undefined)
|
|
327
|
+
priorityInputOverrides.builderConviction = builderConviction;
|
|
328
|
+
if (defectRiskFactor !== undefined)
|
|
329
|
+
priorityInputOverrides.defectRiskFactor = defectRiskFactor;
|
|
330
|
+
if (qualityFlags.length > 0)
|
|
331
|
+
priorityInputOverrides.qualityFlags = qualityFlags;
|
|
332
|
+
return { input, priorityInputOverrides, qualityFlags };
|
|
333
|
+
}
|
|
334
|
+
function buildAdmissionBody(snap, complexity) {
|
|
335
|
+
const parts = [];
|
|
336
|
+
if (snap.description)
|
|
337
|
+
parts.push(snap.description);
|
|
338
|
+
if (complexity !== undefined) {
|
|
339
|
+
parts.push('');
|
|
340
|
+
parts.push('### Complexity');
|
|
341
|
+
parts.push('');
|
|
342
|
+
parts.push(String(Math.round(complexity)));
|
|
343
|
+
}
|
|
344
|
+
if (snap.acceptanceCriteria.length > 0) {
|
|
345
|
+
parts.push('');
|
|
346
|
+
parts.push('### Acceptance Criteria');
|
|
347
|
+
parts.push('');
|
|
348
|
+
for (const ac of snap.acceptanceCriteria) {
|
|
349
|
+
parts.push(`- [${ac.checked ? 'x' : ' '}] ${ac.text}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return parts.join('\n');
|
|
353
|
+
}
|
|
354
|
+
function normaliseIsoDate(raw) {
|
|
355
|
+
// Backlog stores '2026-03-08 22:27' — coerce to ISO for `cli-admit`.
|
|
356
|
+
if (!raw)
|
|
357
|
+
return new Date().toISOString();
|
|
358
|
+
if (/T\d{2}:\d{2}/.test(raw))
|
|
359
|
+
return raw;
|
|
360
|
+
const m = raw.match(/^(\d{4}-\d{2}-\d{2})[\s T](\d{2}:\d{2})(?::(\d{2}))?$/);
|
|
361
|
+
if (!m)
|
|
362
|
+
return new Date(raw).toISOString();
|
|
363
|
+
return `${m[1]}T${m[2]}:${m[3] ?? '00'}Z`;
|
|
364
|
+
}
|
|
365
|
+
// ── Soul-tracks loader ─────────────────────────────────────────────
|
|
366
|
+
/**
|
|
367
|
+
* Load `.ai-sdlc/soul-tracks.json` from the given config root.
|
|
368
|
+
* Format: `{ "track:enchantment": 0.85, "track:reflect": 0.85, ... }`.
|
|
369
|
+
* Missing file → empty object (caller falls back to DEFAULT_SOUL_TRACKS).
|
|
370
|
+
*/
|
|
371
|
+
export function loadSoulTracks(configRoot) {
|
|
372
|
+
const path = join(configRoot, '.ai-sdlc', 'soul-tracks.json');
|
|
373
|
+
if (!existsSync(path))
|
|
374
|
+
return {};
|
|
375
|
+
try {
|
|
376
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
377
|
+
const out = {};
|
|
378
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
379
|
+
if (typeof value === 'number' && value >= 0 && value <= 1)
|
|
380
|
+
out[key] = value;
|
|
381
|
+
}
|
|
382
|
+
return out;
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
return {};
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Load `.ai-sdlc/maintainers.yaml` from the given config root.
|
|
390
|
+
*
|
|
391
|
+
* Accepts either of two shapes:
|
|
392
|
+
*
|
|
393
|
+
* maintainers:
|
|
394
|
+
* - alice
|
|
395
|
+
* - bob
|
|
396
|
+
*
|
|
397
|
+
* or with metadata (only `login` is read, the rest is forward-looking):
|
|
398
|
+
*
|
|
399
|
+
* maintainers:
|
|
400
|
+
* - login: alice
|
|
401
|
+
* role: owner
|
|
402
|
+
* - login: bob
|
|
403
|
+
*
|
|
404
|
+
* A bare top-level list (`- alice\n- bob`) is also accepted. Returns
|
|
405
|
+
* an empty array on missing file or any parse error — callers can
|
|
406
|
+
* still pass `--maintainers` explicitly to override.
|
|
407
|
+
*/
|
|
408
|
+
export function loadMaintainers(configRoot) {
|
|
409
|
+
const path = join(configRoot, '.ai-sdlc', 'maintainers.yaml');
|
|
410
|
+
if (!existsSync(path))
|
|
411
|
+
return [];
|
|
412
|
+
try {
|
|
413
|
+
const doc = parseYaml(readFileSync(path, 'utf-8'));
|
|
414
|
+
const list = Array.isArray(doc)
|
|
415
|
+
? doc
|
|
416
|
+
: doc &&
|
|
417
|
+
typeof doc === 'object' &&
|
|
418
|
+
Array.isArray(doc.maintainers)
|
|
419
|
+
? doc.maintainers
|
|
420
|
+
: [];
|
|
421
|
+
const out = [];
|
|
422
|
+
for (const entry of list) {
|
|
423
|
+
if (typeof entry === 'string' && entry.trim()) {
|
|
424
|
+
out.push(entry.trim());
|
|
425
|
+
}
|
|
426
|
+
else if (entry && typeof entry === 'object' && 'login' in entry) {
|
|
427
|
+
const login = String(entry.login).trim();
|
|
428
|
+
if (login)
|
|
429
|
+
out.push(login);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return out;
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
return [];
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
//# sourceMappingURL=backlog-adapter.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C6 — Category-scoped calibration (RFC-0008 §10 Amendment 6).
|
|
3
|
+
*
|
|
4
|
+
* Replaces PPA v1.0's scalar calibrationCoefficient with per-category
|
|
5
|
+
* coefficients derived from feedback signals. Categories are
|
|
6
|
+
* PillarContribution labels (product / design / engineering) or
|
|
7
|
+
* arbitrary labels the caller chooses.
|
|
8
|
+
*
|
|
9
|
+
* Cκ_category = clamp([0.7, 1.3], 1.0 + (accepts - escalates) / max(1, total) × 0.3)
|
|
10
|
+
*
|
|
11
|
+
* Per v1.1 note: this adjusts the multiplicative Cκ term, NOT SA-2
|
|
12
|
+
* directly. Per-dimension calibration lands in PPA v1.1 §17.
|
|
13
|
+
*/
|
|
14
|
+
import type { SaDimension } from './state/types.js';
|
|
15
|
+
import type { SAFeedbackStore } from './sa-scoring/feedback-store.js';
|
|
16
|
+
export declare const CALIBRATION_MIN = 0.7;
|
|
17
|
+
export declare const CALIBRATION_MAX = 1.3;
|
|
18
|
+
/** Slope of the feedback-driven adjustment. Spec §10 uses 0.3. */
|
|
19
|
+
export declare const CALIBRATION_SLOPE = 0.3;
|
|
20
|
+
export interface CategoryFeedback {
|
|
21
|
+
accepts: number;
|
|
22
|
+
dismisses: number;
|
|
23
|
+
escalates: number;
|
|
24
|
+
overrides?: number;
|
|
25
|
+
}
|
|
26
|
+
export declare function computeCalibrationCoefficient(feedback: CategoryFeedback): number;
|
|
27
|
+
export interface BuildCategoryCoefficientsInput {
|
|
28
|
+
/** Which SA dimension to pull feedback from. Defaults to SA-1. */
|
|
29
|
+
dimension?: SaDimension;
|
|
30
|
+
/** Trailing-window filter. */
|
|
31
|
+
since?: string;
|
|
32
|
+
/** Categories to evaluate; when absent, uses all seen in the window. */
|
|
33
|
+
categories?: readonly string[];
|
|
34
|
+
/** Minimum feedback count per category before returning a coefficient. */
|
|
35
|
+
minSampleSize?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Aggregate feedback rows by category and return `{category: coefficient}`.
|
|
39
|
+
* Categories with fewer than `minSampleSize` samples are omitted — the
|
|
40
|
+
* scalar fallback applies to them.
|
|
41
|
+
*/
|
|
42
|
+
export declare function buildCategoryCoefficients(feedback: SAFeedbackStore, input?: BuildCategoryCoefficientsInput): Record<string, number>;
|
|
43
|
+
//# sourceMappingURL=calibration.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C6 — Category-scoped calibration (RFC-0008 §10 Amendment 6).
|
|
3
|
+
*
|
|
4
|
+
* Replaces PPA v1.0's scalar calibrationCoefficient with per-category
|
|
5
|
+
* coefficients derived from feedback signals. Categories are
|
|
6
|
+
* PillarContribution labels (product / design / engineering) or
|
|
7
|
+
* arbitrary labels the caller chooses.
|
|
8
|
+
*
|
|
9
|
+
* Cκ_category = clamp([0.7, 1.3], 1.0 + (accepts - escalates) / max(1, total) × 0.3)
|
|
10
|
+
*
|
|
11
|
+
* Per v1.1 note: this adjusts the multiplicative Cκ term, NOT SA-2
|
|
12
|
+
* directly. Per-dimension calibration lands in PPA v1.1 §17.
|
|
13
|
+
*/
|
|
14
|
+
export const CALIBRATION_MIN = 0.7;
|
|
15
|
+
export const CALIBRATION_MAX = 1.3;
|
|
16
|
+
/** Slope of the feedback-driven adjustment. Spec §10 uses 0.3. */
|
|
17
|
+
export const CALIBRATION_SLOPE = 0.3;
|
|
18
|
+
export function computeCalibrationCoefficient(feedback) {
|
|
19
|
+
const total = feedback.accepts + feedback.dismisses + feedback.escalates;
|
|
20
|
+
if (total === 0)
|
|
21
|
+
return 1.0;
|
|
22
|
+
const delta = (feedback.accepts - feedback.escalates) / Math.max(1, total);
|
|
23
|
+
return clamp(1.0 + delta * CALIBRATION_SLOPE, CALIBRATION_MIN, CALIBRATION_MAX);
|
|
24
|
+
}
|
|
25
|
+
function clamp(value, min, max) {
|
|
26
|
+
return Math.min(max, Math.max(min, value));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Aggregate feedback rows by category and return `{category: coefficient}`.
|
|
30
|
+
* Categories with fewer than `minSampleSize` samples are omitted — the
|
|
31
|
+
* scalar fallback applies to them.
|
|
32
|
+
*/
|
|
33
|
+
export function buildCategoryCoefficients(feedback, input = {}) {
|
|
34
|
+
const window = {
|
|
35
|
+
dimension: input.dimension,
|
|
36
|
+
since: input.since,
|
|
37
|
+
};
|
|
38
|
+
const events = feedback.list(window).filter((e) => e.category);
|
|
39
|
+
const buckets = new Map();
|
|
40
|
+
for (const e of events) {
|
|
41
|
+
const key = e.category;
|
|
42
|
+
if (input.categories && !input.categories.includes(key))
|
|
43
|
+
continue;
|
|
44
|
+
const current = buckets.get(key) ?? {
|
|
45
|
+
accepts: 0,
|
|
46
|
+
dismisses: 0,
|
|
47
|
+
escalates: 0,
|
|
48
|
+
overrides: 0,
|
|
49
|
+
};
|
|
50
|
+
switch (e.signal) {
|
|
51
|
+
case 'accept':
|
|
52
|
+
current.accepts++;
|
|
53
|
+
break;
|
|
54
|
+
case 'dismiss':
|
|
55
|
+
current.dismisses++;
|
|
56
|
+
break;
|
|
57
|
+
case 'escalate':
|
|
58
|
+
current.escalates++;
|
|
59
|
+
break;
|
|
60
|
+
case 'override':
|
|
61
|
+
current.overrides = (current.overrides ?? 0) + 1;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
buckets.set(key, current);
|
|
65
|
+
}
|
|
66
|
+
const minSize = input.minSampleSize ?? 1;
|
|
67
|
+
const result = {};
|
|
68
|
+
for (const [category, f] of buckets) {
|
|
69
|
+
const samples = f.accepts + f.dismisses + f.escalates;
|
|
70
|
+
if (samples < minSize)
|
|
71
|
+
continue;
|
|
72
|
+
result[category] = computeCalibrationCoefficient(f);
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=calibration.js.map
|
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ai-sdlc agents [name] — show agent roster, autonomy levels, performance.
|
|
3
|
+
*
|
|
4
|
+
* AISDLC-78 (AC #10): in addition to the autonomy ledger (which only
|
|
5
|
+
* shows agents that have actually executed at least once), we now also
|
|
6
|
+
* scan the project's .ai-sdlc directory for AgentRole resources and
|
|
7
|
+
* surface declared-but-not-executed agents. Fresh installs that ran
|
|
8
|
+
* `ai-sdlc init` previously saw an empty roster even though
|
|
9
|
+
* `agent-role.yaml` declared `default-agent`; now the user sees the
|
|
10
|
+
* declared roster + a "(declared, not yet executed)" hint.
|
|
3
11
|
*/
|
|
4
12
|
import { Command } from 'commander';
|
|
13
|
+
interface DeclaredAgent {
|
|
14
|
+
agentName: string;
|
|
15
|
+
currentLevel: number;
|
|
16
|
+
totalTasks: number;
|
|
17
|
+
successCount: number;
|
|
18
|
+
failureCount: number;
|
|
19
|
+
lastTaskAt?: string;
|
|
20
|
+
declaredOnly: true;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Scan the config directory for AgentRole YAML resources and return the
|
|
24
|
+
* declared agent metadata. Errors are swallowed (the autonomy ledger
|
|
25
|
+
* is the primary source of truth — declared agents are a hint).
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadDeclaredAgents(configDir: string): DeclaredAgent[];
|
|
5
28
|
export declare const agentsCommand: Command;
|
|
29
|
+
export {};
|
|
6
30
|
//# sourceMappingURL=agents.d.ts.map
|
|
@@ -1,9 +1,67 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ai-sdlc agents [name] — show agent roster, autonomy levels, performance.
|
|
3
|
+
*
|
|
4
|
+
* AISDLC-78 (AC #10): in addition to the autonomy ledger (which only
|
|
5
|
+
* shows agents that have actually executed at least once), we now also
|
|
6
|
+
* scan the project's .ai-sdlc directory for AgentRole resources and
|
|
7
|
+
* surface declared-but-not-executed agents. Fresh installs that ran
|
|
8
|
+
* `ai-sdlc init` previously saw an empty roster even though
|
|
9
|
+
* `agent-role.yaml` declared `default-agent`; now the user sees the
|
|
10
|
+
* declared roster + a "(declared, not yet executed)" hint.
|
|
3
11
|
*/
|
|
4
12
|
import { Command } from 'commander';
|
|
13
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
14
|
+
import { join, resolve } from 'node:path';
|
|
15
|
+
import { parse as parseYaml } from 'yaml';
|
|
5
16
|
import { Orchestrator } from '../../orchestrator.js';
|
|
6
17
|
import { formatOutput } from '../formatters/index.js';
|
|
18
|
+
/**
|
|
19
|
+
* Scan the config directory for AgentRole YAML resources and return the
|
|
20
|
+
* declared agent metadata. Errors are swallowed (the autonomy ledger
|
|
21
|
+
* is the primary source of truth — declared agents are a hint).
|
|
22
|
+
*/
|
|
23
|
+
export function loadDeclaredAgents(configDir) {
|
|
24
|
+
const dir = resolve(configDir);
|
|
25
|
+
if (!existsSync(dir))
|
|
26
|
+
return [];
|
|
27
|
+
let files;
|
|
28
|
+
try {
|
|
29
|
+
files = readdirSync(dir).filter((f) => f.endsWith('.yaml') || f.endsWith('.yml'));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const declared = [];
|
|
35
|
+
for (const file of files) {
|
|
36
|
+
try {
|
|
37
|
+
const raw = readFileSync(join(dir, file), 'utf-8');
|
|
38
|
+
const docs = raw
|
|
39
|
+
.split(/^---\s*$/m)
|
|
40
|
+
.map((s) => s.trim())
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
for (const docText of docs) {
|
|
43
|
+
const doc = parseYaml(docText);
|
|
44
|
+
if (!doc || doc.kind !== 'AgentRole')
|
|
45
|
+
continue;
|
|
46
|
+
const name = doc.metadata?.name;
|
|
47
|
+
if (!name)
|
|
48
|
+
continue;
|
|
49
|
+
declared.push({
|
|
50
|
+
agentName: name,
|
|
51
|
+
currentLevel: 0,
|
|
52
|
+
totalTasks: 0,
|
|
53
|
+
successCount: 0,
|
|
54
|
+
failureCount: 0,
|
|
55
|
+
declaredOnly: true,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// ignore malformed YAML — surfaced by `ai-sdlc validate`
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return declared;
|
|
64
|
+
}
|
|
7
65
|
export const agentsCommand = new Command('agents')
|
|
8
66
|
.description('Show agent roster with autonomy levels and performance')
|
|
9
67
|
.argument('[name]', 'Filter by agent name')
|
|
@@ -11,12 +69,19 @@ export const agentsCommand = new Command('agents')
|
|
|
11
69
|
.action(async (name, opts, cmd) => {
|
|
12
70
|
const globalOpts = cmd.parent?.opts() ?? {};
|
|
13
71
|
const format = globalOpts.format ?? 'table';
|
|
72
|
+
const configDir = globalOpts.config ?? '.ai-sdlc';
|
|
14
73
|
const orchestrator = new Orchestrator({
|
|
15
74
|
configDir: globalOpts.config,
|
|
16
75
|
statePath: opts.state,
|
|
17
76
|
});
|
|
18
77
|
try {
|
|
19
|
-
|
|
78
|
+
const ledger = await orchestrator.agents();
|
|
79
|
+
const executedNames = new Set(ledger.map((a) => a.agentName));
|
|
80
|
+
const declared = loadDeclaredAgents(configDir).filter((d) => !executedNames.has(d.agentName));
|
|
81
|
+
let agents = [
|
|
82
|
+
...ledger.map((a) => ({ ...a, declaredOnly: false })),
|
|
83
|
+
...declared.map((d) => ({ ...d })),
|
|
84
|
+
];
|
|
20
85
|
if (name) {
|
|
21
86
|
agents = agents.filter((a) => a.agentName === name);
|
|
22
87
|
}
|