agileflow 3.3.0 → 3.4.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/CHANGELOG.md +5 -0
- package/README.md +6 -6
- package/lib/skill-loader.js +0 -1
- package/package.json +1 -1
- package/scripts/agileflow-statusline.sh +81 -0
- package/scripts/claude-tmux.sh +113 -22
- package/scripts/claude-watchdog.sh +225 -0
- package/scripts/generators/agent-registry.js +14 -1
- package/scripts/generators/inject-babysit.js +22 -9
- package/scripts/generators/inject-help.js +19 -9
- package/scripts/lib/audit-cleanup.js +250 -0
- package/scripts/lib/audit-registry.js +248 -0
- package/scripts/lib/feature-catalog.js +3 -3
- package/scripts/lib/gate-enforcer.js +295 -0
- package/scripts/lib/model-profiles.js +98 -0
- package/scripts/lib/signal-detectors.js +1 -1
- package/scripts/lib/skill-catalog.js +557 -0
- package/scripts/lib/skill-recommender.js +311 -0
- package/scripts/lib/tdd-phase-manager.js +455 -0
- package/scripts/lib/team-events.js +34 -3
- package/scripts/lib/tmux-group-colors.js +113 -0
- package/scripts/messaging-bridge.js +209 -1
- package/scripts/spawn-audit-sessions.js +549 -0
- package/scripts/team-manager.js +37 -16
- package/scripts/tmux-close-windows.sh +180 -0
- package/src/core/agents/ads-audit-budget.md +181 -0
- package/src/core/agents/ads-audit-compliance.md +169 -0
- package/src/core/agents/ads-audit-creative.md +164 -0
- package/src/core/agents/ads-audit-google.md +226 -0
- package/src/core/agents/ads-audit-meta.md +183 -0
- package/src/core/agents/ads-audit-tracking.md +197 -0
- package/src/core/agents/ads-consensus.md +322 -0
- package/src/core/agents/brainstorm-analyzer-features.md +169 -0
- package/src/core/agents/brainstorm-analyzer-growth.md +161 -0
- package/src/core/agents/brainstorm-analyzer-integration.md +172 -0
- package/src/core/agents/brainstorm-analyzer-market.md +147 -0
- package/src/core/agents/brainstorm-analyzer-ux.md +167 -0
- package/src/core/agents/brainstorm-consensus.md +237 -0
- package/src/core/agents/completeness-consensus.md +5 -5
- package/src/core/agents/perf-consensus.md +2 -2
- package/src/core/agents/security-consensus.md +2 -2
- package/src/core/agents/seo-analyzer-content.md +167 -0
- package/src/core/agents/seo-analyzer-images.md +187 -0
- package/src/core/agents/seo-analyzer-performance.md +206 -0
- package/src/core/agents/seo-analyzer-schema.md +176 -0
- package/src/core/agents/seo-analyzer-sitemap.md +172 -0
- package/src/core/agents/seo-analyzer-technical.md +144 -0
- package/src/core/agents/seo-consensus.md +289 -0
- package/src/core/agents/test-consensus.md +2 -2
- package/src/core/commands/ads/audit.md +375 -0
- package/src/core/commands/ads/budget.md +97 -0
- package/src/core/commands/ads/competitor.md +112 -0
- package/src/core/commands/ads/creative.md +85 -0
- package/src/core/commands/ads/google.md +112 -0
- package/src/core/commands/ads/landing.md +119 -0
- package/src/core/commands/ads/linkedin.md +112 -0
- package/src/core/commands/ads/meta.md +91 -0
- package/src/core/commands/ads/microsoft.md +115 -0
- package/src/core/commands/ads/plan.md +321 -0
- package/src/core/commands/ads/tiktok.md +129 -0
- package/src/core/commands/ads/youtube.md +124 -0
- package/src/core/commands/ads.md +128 -0
- package/src/core/commands/babysit.md +249 -1284
- package/src/core/commands/{audit → code}/completeness.md +35 -25
- package/src/core/commands/{audit → code}/legal.md +26 -16
- package/src/core/commands/{audit → code}/logic.md +27 -16
- package/src/core/commands/{audit → code}/performance.md +30 -20
- package/src/core/commands/{audit → code}/security.md +32 -19
- package/src/core/commands/{audit → code}/test.md +30 -20
- package/src/core/commands/{discovery → ideate}/brief.md +12 -12
- package/src/core/commands/{discovery/new.md → ideate/discover.md} +13 -13
- package/src/core/commands/ideate/features.md +435 -0
- package/src/core/commands/seo/audit.md +373 -0
- package/src/core/commands/seo/competitor.md +174 -0
- package/src/core/commands/seo/content.md +107 -0
- package/src/core/commands/seo/geo.md +229 -0
- package/src/core/commands/seo/hreflang.md +140 -0
- package/src/core/commands/seo/images.md +96 -0
- package/src/core/commands/seo/page.md +198 -0
- package/src/core/commands/seo/plan.md +163 -0
- package/src/core/commands/seo/programmatic.md +131 -0
- package/src/core/commands/seo/references/cwv-thresholds.md +64 -0
- package/src/core/commands/seo/references/eeat-framework.md +110 -0
- package/src/core/commands/seo/references/quality-gates.md +91 -0
- package/src/core/commands/seo/references/schema-types.md +102 -0
- package/src/core/commands/seo/schema.md +183 -0
- package/src/core/commands/seo/sitemap.md +97 -0
- package/src/core/commands/seo/technical.md +100 -0
- package/src/core/commands/seo.md +107 -0
- package/src/core/commands/skill/list.md +68 -212
- package/src/core/commands/skill/recommend.md +216 -0
- package/src/core/commands/tdd-next.md +238 -0
- package/src/core/commands/tdd.md +210 -0
- package/src/core/experts/_core-expertise.yaml +105 -0
- package/src/core/experts/analytics/expertise.yaml +5 -99
- package/src/core/experts/codebase-query/expertise.yaml +3 -72
- package/src/core/experts/compliance/expertise.yaml +6 -72
- package/src/core/experts/database/expertise.yaml +9 -52
- package/src/core/experts/documentation/expertise.yaml +7 -140
- package/src/core/experts/integrations/expertise.yaml +7 -127
- package/src/core/experts/mentor/expertise.yaml +8 -35
- package/src/core/experts/monitoring/expertise.yaml +7 -49
- package/src/core/experts/performance/expertise.yaml +1 -26
- package/src/core/experts/security/expertise.yaml +9 -34
- package/src/core/experts/ui/expertise.yaml +6 -36
- package/src/core/knowledge/ads/ad-audit-checklist-scoring.md +424 -0
- package/src/core/knowledge/ads/ad-optimization-logic.md +590 -0
- package/src/core/knowledge/ads/ad-technical-specifications.md +385 -0
- package/src/core/knowledge/ads/definitive-advertising-reference-2026.md +506 -0
- package/src/core/knowledge/ads/paid-advertising-research-2026.md +445 -0
- package/src/core/templates/agileflow-metadata.json +15 -1
- package/tools/cli/installers/ide/_base-ide.js +42 -5
- package/tools/cli/installers/ide/claude-code.js +3 -3
- package/tools/cli/lib/content-injector.js +160 -12
- package/tools/cli/lib/docs-setup.js +1 -1
- package/src/core/commands/skill/create.md +0 -698
- package/src/core/commands/skill/delete.md +0 -316
- package/src/core/commands/skill/edit.md +0 -359
- package/src/core/commands/skill/test.md +0 -394
- package/src/core/commands/skill/upgrade.md +0 -552
- package/src/core/templates/skill-template.md +0 -117
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gate-enforcer.js - Workflow Gate Enforcement for Babysit Strict Mode
|
|
3
|
+
*
|
|
4
|
+
* Filters AskUserQuestion options based on workflow gate status.
|
|
5
|
+
* When STRICT=true in babysit, certain options are removed or blocked
|
|
6
|
+
* based on whether gates have been satisfied (tests passed, review done, etc.).
|
|
7
|
+
*
|
|
8
|
+
* Gates:
|
|
9
|
+
* - tests_passed: Must run and pass tests before commit
|
|
10
|
+
* - review_done: Must run code review for 5+ source files before commit
|
|
11
|
+
* - logic_audit_done: Must run logic audit before commit (advisory in non-strict)
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const { filterOptions, getGateStatus, updateGate } = require('./gate-enforcer');
|
|
15
|
+
* const filtered = filterOptions(options, gateState, { strict: true });
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Constants
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gate types that can block workflow progression
|
|
26
|
+
*/
|
|
27
|
+
const GATES = {
|
|
28
|
+
TESTS_PASSED: 'tests_passed',
|
|
29
|
+
REVIEW_DONE: 'review_done',
|
|
30
|
+
LOGIC_AUDIT_DONE: 'logic_audit_done',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Actions that require specific gates to be satisfied
|
|
35
|
+
*/
|
|
36
|
+
const GATE_REQUIREMENTS = {
|
|
37
|
+
commit: {
|
|
38
|
+
strict: [GATES.TESTS_PASSED],
|
|
39
|
+
strict_5plus: [GATES.TESTS_PASSED, GATES.REVIEW_DONE],
|
|
40
|
+
},
|
|
41
|
+
next_story: {
|
|
42
|
+
strict: [GATES.TESTS_PASSED],
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Patterns that identify commit-related options
|
|
48
|
+
*/
|
|
49
|
+
const COMMIT_PATTERNS = [/^commit/i, /^git commit/i, /commit.*changes/i, /commit:?\s/i];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Patterns that identify "skip" options
|
|
53
|
+
*/
|
|
54
|
+
const SKIP_PATTERNS = [/skip.*test/i, /skip.*review/i, /skip.*audit/i, /skip.*verif/i];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Patterns that identify "next story" options
|
|
58
|
+
*/
|
|
59
|
+
const NEXT_STORY_PATTERNS = [/continue to/i, /next story/i, /move to.*US-/i];
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Gate State Management
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create initial gate state for a workflow session
|
|
67
|
+
* @param {Object} options
|
|
68
|
+
* @param {number} options.filesChanged - Number of source files modified
|
|
69
|
+
* @param {boolean} options.strict - Whether strict mode is enabled
|
|
70
|
+
* @returns {Object} Initial gate state
|
|
71
|
+
*/
|
|
72
|
+
function createGateState(options = {}) {
|
|
73
|
+
const { filesChanged = 0, strict = false } = options;
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
strict,
|
|
77
|
+
files_changed: filesChanged,
|
|
78
|
+
gates: {
|
|
79
|
+
[GATES.TESTS_PASSED]: false,
|
|
80
|
+
[GATES.REVIEW_DONE]: false,
|
|
81
|
+
[GATES.LOGIC_AUDIT_DONE]: false,
|
|
82
|
+
},
|
|
83
|
+
history: [],
|
|
84
|
+
created_at: new Date().toISOString(),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Update a gate's status
|
|
90
|
+
* @param {Object} gateState - Current gate state
|
|
91
|
+
* @param {string} gate - Gate name from GATES
|
|
92
|
+
* @param {boolean} passed - Whether the gate passed
|
|
93
|
+
* @param {Object} metadata - Additional info (e.g., test count, review findings)
|
|
94
|
+
* @returns {Object} Updated gate state
|
|
95
|
+
*/
|
|
96
|
+
function updateGate(gateState, gate, passed, metadata = {}) {
|
|
97
|
+
if (!Object.values(GATES).includes(gate)) {
|
|
98
|
+
throw new Error(`Unknown gate: ${gate}. Valid: ${Object.values(GATES).join(', ')}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const updated = {
|
|
102
|
+
...gateState,
|
|
103
|
+
gates: {
|
|
104
|
+
...gateState.gates,
|
|
105
|
+
[gate]: passed,
|
|
106
|
+
},
|
|
107
|
+
history: [
|
|
108
|
+
...gateState.history,
|
|
109
|
+
{
|
|
110
|
+
gate,
|
|
111
|
+
passed,
|
|
112
|
+
at: new Date().toISOString(),
|
|
113
|
+
...metadata,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return updated;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check if all required gates for an action are satisfied
|
|
123
|
+
* @param {Object} gateState - Current gate state
|
|
124
|
+
* @param {string} action - Action to check ('commit', 'next_story')
|
|
125
|
+
* @returns {{ allowed: boolean, missing: string[] }}
|
|
126
|
+
*/
|
|
127
|
+
function checkGates(gateState, action) {
|
|
128
|
+
if (!gateState.strict) {
|
|
129
|
+
return { allowed: true, missing: [] };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let requirements;
|
|
133
|
+
if (action === 'commit' && gateState.files_changed >= 5) {
|
|
134
|
+
requirements = GATE_REQUIREMENTS.commit.strict_5plus || [];
|
|
135
|
+
} else {
|
|
136
|
+
requirements = (GATE_REQUIREMENTS[action] && GATE_REQUIREMENTS[action].strict) || [];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const missing = requirements.filter(gate => !gateState.gates[gate]);
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
allowed: missing.length === 0,
|
|
143
|
+
missing,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ============================================================================
|
|
148
|
+
// Option Filtering
|
|
149
|
+
// ============================================================================
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if an option label matches any pattern in a list
|
|
153
|
+
* @param {string} label - Option label text
|
|
154
|
+
* @param {RegExp[]} patterns - Patterns to match against
|
|
155
|
+
* @returns {boolean}
|
|
156
|
+
*/
|
|
157
|
+
function matchesPattern(label, patterns) {
|
|
158
|
+
if (typeof label !== 'string') return false;
|
|
159
|
+
return patterns.some(p => p.test(label));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Filter AskUserQuestion options based on gate state
|
|
164
|
+
*
|
|
165
|
+
* In strict mode:
|
|
166
|
+
* - Remove "commit" options if tests haven't passed
|
|
167
|
+
* - Remove "commit" options if review not done (5+ files)
|
|
168
|
+
* - Remove "skip tests/review/audit" options
|
|
169
|
+
* - Add gate status hints to option descriptions
|
|
170
|
+
*
|
|
171
|
+
* In non-strict mode:
|
|
172
|
+
* - Options are returned unchanged (soft guidance only)
|
|
173
|
+
*
|
|
174
|
+
* @param {Object[]} options - AskUserQuestion options array
|
|
175
|
+
* @param {Object} gateState - Current gate state
|
|
176
|
+
* @returns {Object[]} Filtered options
|
|
177
|
+
*/
|
|
178
|
+
function filterOptions(options, gateState) {
|
|
179
|
+
if (!Array.isArray(options)) return [];
|
|
180
|
+
if (!gateState || !gateState.strict) {
|
|
181
|
+
return options;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return options
|
|
185
|
+
.filter(opt => {
|
|
186
|
+
const label = opt.label || '';
|
|
187
|
+
|
|
188
|
+
// In strict mode, remove skip options entirely
|
|
189
|
+
if (matchesPattern(label, SKIP_PATTERNS)) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check commit gates
|
|
194
|
+
if (matchesPattern(label, COMMIT_PATTERNS)) {
|
|
195
|
+
const { allowed } = checkGates(gateState, 'commit');
|
|
196
|
+
if (!allowed) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check next story gates
|
|
202
|
+
if (matchesPattern(label, NEXT_STORY_PATTERNS)) {
|
|
203
|
+
const { allowed } = checkGates(gateState, 'next_story');
|
|
204
|
+
if (!allowed) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return true;
|
|
210
|
+
})
|
|
211
|
+
.map(opt => {
|
|
212
|
+
// Add gate status hints to descriptions
|
|
213
|
+
const label = opt.label || '';
|
|
214
|
+
|
|
215
|
+
if (matchesPattern(label, COMMIT_PATTERNS) && gateState.strict) {
|
|
216
|
+
const checks = [];
|
|
217
|
+
if (gateState.gates[GATES.TESTS_PASSED]) checks.push('tests passed');
|
|
218
|
+
if (gateState.gates[GATES.REVIEW_DONE]) checks.push('review done');
|
|
219
|
+
if (gateState.gates[GATES.LOGIC_AUDIT_DONE]) checks.push('audit done');
|
|
220
|
+
|
|
221
|
+
if (checks.length > 0) {
|
|
222
|
+
return {
|
|
223
|
+
...opt,
|
|
224
|
+
description: `${opt.description || ''} [Gates: ${checks.join(', ')}]`.trim(),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return opt;
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get a human-readable gate status summary
|
|
235
|
+
* @param {Object} gateState - Current gate state
|
|
236
|
+
* @returns {string} Status summary
|
|
237
|
+
*/
|
|
238
|
+
function getGateStatusSummary(gateState) {
|
|
239
|
+
if (!gateState || !gateState.strict) {
|
|
240
|
+
return 'Strict mode: OFF (soft guidance)';
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const lines = [`Strict mode: ON | ${gateState.files_changed} files changed`];
|
|
244
|
+
|
|
245
|
+
for (const [gate, passed] of Object.entries(gateState.gates)) {
|
|
246
|
+
const icon = passed ? '✅' : '⬜';
|
|
247
|
+
const name = gate.replace(/_/g, ' ');
|
|
248
|
+
lines.push(` ${icon} ${name}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return lines.join('\n');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get blocking message for a failed gate check
|
|
256
|
+
* @param {string[]} missingGates - List of unsatisfied gates
|
|
257
|
+
* @returns {string} Human-readable blocking message
|
|
258
|
+
*/
|
|
259
|
+
function getBlockingMessage(missingGates) {
|
|
260
|
+
const messages = {
|
|
261
|
+
[GATES.TESTS_PASSED]: 'Run tests first (tests must pass before committing)',
|
|
262
|
+
[GATES.REVIEW_DONE]: 'Run code review first (required for 5+ modified files)',
|
|
263
|
+
[GATES.LOGIC_AUDIT_DONE]: 'Run logic audit first',
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
return missingGates.map(gate => `🚫 ${messages[gate] || gate}`).join('\n');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ============================================================================
|
|
270
|
+
// Exports
|
|
271
|
+
// ============================================================================
|
|
272
|
+
|
|
273
|
+
module.exports = {
|
|
274
|
+
// Constants
|
|
275
|
+
GATES,
|
|
276
|
+
GATE_REQUIREMENTS,
|
|
277
|
+
|
|
278
|
+
// State management
|
|
279
|
+
createGateState,
|
|
280
|
+
updateGate,
|
|
281
|
+
checkGates,
|
|
282
|
+
|
|
283
|
+
// Option filtering
|
|
284
|
+
filterOptions,
|
|
285
|
+
matchesPattern,
|
|
286
|
+
|
|
287
|
+
// Reporting
|
|
288
|
+
getGateStatusSummary,
|
|
289
|
+
getBlockingMessage,
|
|
290
|
+
|
|
291
|
+
// Patterns (for testing)
|
|
292
|
+
COMMIT_PATTERNS,
|
|
293
|
+
SKIP_PATTERNS,
|
|
294
|
+
NEXT_STORY_PATTERNS,
|
|
295
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* model-profiles.js - Model resolution for audit subagents
|
|
3
|
+
*
|
|
4
|
+
* Resolves which model (haiku/sonnet/opus) to use for agent subagents.
|
|
5
|
+
* Models are specified inline via command arguments (MODEL=opus).
|
|
6
|
+
*
|
|
7
|
+
* Resolution order:
|
|
8
|
+
* 1. Explicit MODEL= argument (highest priority)
|
|
9
|
+
* 2. Agent frontmatter model (from .md file)
|
|
10
|
+
* 3. Fallback: 'haiku'
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* const { resolveModel, estimateCost } = require('./model-profiles');
|
|
14
|
+
* const model = resolveModel('opus', 'haiku'); // returns 'opus'
|
|
15
|
+
* const model2 = resolveModel(null, 'sonnet'); // returns 'sonnet'
|
|
16
|
+
* const model3 = resolveModel(null, null); // returns 'haiku'
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const VALID_MODELS = ['haiku', 'sonnet', 'opus'];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolve which model to use for a given agent.
|
|
23
|
+
*
|
|
24
|
+
* Resolution order:
|
|
25
|
+
* 1. Explicit model argument (MODEL= from command)
|
|
26
|
+
* 2. Agent frontmatter model
|
|
27
|
+
* 3. Fallback: 'haiku'
|
|
28
|
+
*
|
|
29
|
+
* @param {string} [explicitModel] - MODEL= argument value
|
|
30
|
+
* @param {string} [frontmatterModel] - Model from agent .md frontmatter
|
|
31
|
+
* @returns {string} Model name: 'haiku', 'sonnet', or 'opus'
|
|
32
|
+
*/
|
|
33
|
+
function resolveModel(explicitModel, frontmatterModel) {
|
|
34
|
+
// 1. Explicit MODEL= argument
|
|
35
|
+
if (explicitModel && VALID_MODELS.includes(explicitModel.toLowerCase())) {
|
|
36
|
+
return explicitModel.toLowerCase();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 2. Frontmatter model
|
|
40
|
+
if (frontmatterModel && VALID_MODELS.includes(frontmatterModel.toLowerCase())) {
|
|
41
|
+
return frontmatterModel.toLowerCase();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 3. Fallback
|
|
45
|
+
return 'haiku';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validate a model name.
|
|
50
|
+
*
|
|
51
|
+
* @param {string} model - Model name to validate
|
|
52
|
+
* @returns {boolean} True if valid
|
|
53
|
+
*/
|
|
54
|
+
function isValidModel(model) {
|
|
55
|
+
return !!model && VALID_MODELS.includes(model.toLowerCase());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Estimate cost multiplier for a model relative to haiku baseline.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} model - Model name
|
|
62
|
+
* @param {number} [analyzerCount=5] - Number of analyzers
|
|
63
|
+
* @returns {{ multiplier: number, model: string, perAnalyzerCost: string }}
|
|
64
|
+
*/
|
|
65
|
+
function estimateCost(model, analyzerCount) {
|
|
66
|
+
let MODEL_PRICING;
|
|
67
|
+
try {
|
|
68
|
+
MODEL_PRICING = require('./team-events').MODEL_PRICING;
|
|
69
|
+
} catch (_) {
|
|
70
|
+
MODEL_PRICING = {
|
|
71
|
+
haiku: { input: 0.8, output: 4.0 },
|
|
72
|
+
sonnet: { input: 3.0, output: 15.0 },
|
|
73
|
+
opus: { input: 15.0, output: 75.0 },
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const count = analyzerCount || 5;
|
|
78
|
+
const resolved = resolveModel(model);
|
|
79
|
+
const pricing = MODEL_PRICING[resolved] || MODEL_PRICING.haiku;
|
|
80
|
+
const haikuPricing = MODEL_PRICING.haiku;
|
|
81
|
+
|
|
82
|
+
const multiplier = pricing.output / haikuPricing.output;
|
|
83
|
+
const perAnalyzer = `$${((pricing.input * 50000) / 1_000_000 + (pricing.output * 10000) / 1_000_000).toFixed(3)}`;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
multiplier: Math.round(multiplier * 100) / 100,
|
|
87
|
+
model: resolved,
|
|
88
|
+
perAnalyzerCost: perAnalyzer,
|
|
89
|
+
totalEstimate: `~$${(count * ((pricing.input * 50000) / 1_000_000 + (pricing.output * 10000) / 1_000_000)).toFixed(2)}`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = {
|
|
94
|
+
VALID_MODELS,
|
|
95
|
+
resolveModel,
|
|
96
|
+
isValidModel,
|
|
97
|
+
estimateCost,
|
|
98
|
+
};
|
|
@@ -546,7 +546,7 @@ const FEATURE_DETECTORS = {
|
|
|
546
546
|
priority: 'medium',
|
|
547
547
|
trigger: `${coreFiles} source files modified - logic audit available`,
|
|
548
548
|
action: 'offer',
|
|
549
|
-
command: '/agileflow:
|
|
549
|
+
command: '/agileflow:code:logic',
|
|
550
550
|
phase: 'post-impl',
|
|
551
551
|
});
|
|
552
552
|
},
|