ctx-cc 3.4.4 → 4.0.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/README.md +34 -289
- package/agents/ctx-arch-mapper.md +5 -3
- package/agents/ctx-auditor.md +5 -3
- package/agents/ctx-concerns-mapper.md +5 -3
- package/agents/ctx-criteria-suggester.md +6 -4
- package/agents/ctx-debugger.md +5 -3
- package/agents/ctx-designer.md +488 -114
- package/agents/ctx-discusser.md +5 -3
- package/agents/ctx-executor.md +5 -3
- package/agents/ctx-handoff.md +6 -4
- package/agents/ctx-learner.md +5 -3
- package/agents/ctx-mapper.md +4 -3
- package/agents/ctx-ml-analyst.md +600 -0
- package/agents/ctx-ml-engineer.md +933 -0
- package/agents/ctx-ml-reviewer.md +485 -0
- package/agents/ctx-ml-scientist.md +626 -0
- package/agents/ctx-parallelizer.md +4 -3
- package/agents/ctx-planner.md +5 -3
- package/agents/ctx-predictor.md +4 -3
- package/agents/ctx-qa.md +5 -3
- package/agents/ctx-quality-mapper.md +5 -3
- package/agents/ctx-researcher.md +5 -3
- package/agents/ctx-reviewer.md +6 -4
- package/agents/ctx-team-coordinator.md +5 -3
- package/agents/ctx-tech-mapper.md +5 -3
- package/agents/ctx-verifier.md +5 -3
- package/bin/ctx.js +168 -27
- package/commands/brand.md +309 -0
- package/commands/ctx.md +234 -114
- package/commands/design.md +304 -0
- package/commands/experiment.md +251 -0
- package/commands/help.md +57 -7
- package/commands/metrics.md +1 -1
- package/commands/milestone.md +1 -1
- package/commands/ml-status.md +197 -0
- package/commands/monitor.md +1 -1
- package/commands/train.md +266 -0
- package/commands/visual-qa.md +559 -0
- package/commands/voice.md +1 -1
- package/hooks/post-tool-use.js +39 -0
- package/hooks/pre-tool-use.js +93 -0
- package/hooks/subagent-stop.js +32 -0
- package/package.json +9 -3
- package/plugin.json +45 -0
- package/skills/ctx-design-system/SKILL.md +572 -0
- package/skills/ctx-ml-experiment/SKILL.md +334 -0
- package/skills/ctx-ml-pipeline/SKILL.md +437 -0
- package/skills/ctx-orchestrator/SKILL.md +91 -0
- package/skills/ctx-review-gate/SKILL.md +111 -0
- package/skills/ctx-state/SKILL.md +100 -0
- package/skills/ctx-visual-qa/SKILL.md +587 -0
- package/src/agents.js +109 -0
- package/src/auto.js +287 -0
- package/src/capabilities.js +171 -0
- package/src/commits.js +94 -0
- package/src/config.js +112 -0
- package/src/context.js +241 -0
- package/src/handoff.js +156 -0
- package/src/hooks.js +218 -0
- package/src/install.js +119 -51
- package/src/lifecycle.js +194 -0
- package/src/metrics.js +198 -0
- package/src/pipeline.js +269 -0
- package/src/review-gate.js +244 -0
- package/src/runner.js +120 -0
- package/src/skills.js +143 -0
- package/src/state.js +267 -0
- package/src/worktree.js +244 -0
- package/templates/PRD.json +1 -1
- package/templates/config.json +1 -237
- package/workflows/ctx-router.md +0 -485
- package/workflows/map-codebase.md +0 -329
package/src/worktree.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { readState, writeState } from './state.js';
|
|
5
|
+
import { buildContext } from './context.js';
|
|
6
|
+
import { runAgent } from './runner.js';
|
|
7
|
+
|
|
8
|
+
const WORKTREES_DIR = '.ctx/worktrees';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parse a dependency graph from the parallelizer agent output.
|
|
12
|
+
* Input format: array of { taskId, title, dependsOn: [taskId] }
|
|
13
|
+
* Returns waves: [ [task, task], [task], ... ]
|
|
14
|
+
*/
|
|
15
|
+
export function buildWaves(tasks) {
|
|
16
|
+
if (!tasks || tasks.length === 0) return [];
|
|
17
|
+
|
|
18
|
+
const completed = new Set();
|
|
19
|
+
const waves = [];
|
|
20
|
+
const remaining = [...tasks];
|
|
21
|
+
let safetyCounter = 0;
|
|
22
|
+
|
|
23
|
+
while (remaining.length > 0 && safetyCounter < 100) {
|
|
24
|
+
safetyCounter++;
|
|
25
|
+
const wave = [];
|
|
26
|
+
|
|
27
|
+
for (let i = remaining.length - 1; i >= 0; i--) {
|
|
28
|
+
const task = remaining[i];
|
|
29
|
+
const deps = task.dependsOn || [];
|
|
30
|
+
const allDepsComplete = deps.every(d => completed.has(d));
|
|
31
|
+
|
|
32
|
+
if (allDepsComplete) {
|
|
33
|
+
wave.push(task);
|
|
34
|
+
remaining.splice(i, 1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (wave.length === 0) {
|
|
39
|
+
// Circular dependency — force remaining into a single wave
|
|
40
|
+
waves.push(remaining.splice(0));
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
waves.push(wave);
|
|
45
|
+
for (const t of wave) completed.add(t.taskId);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return waves;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create a git worktree for a task.
|
|
53
|
+
* Returns the worktree path.
|
|
54
|
+
*/
|
|
55
|
+
export function createWorktree(projectDir, taskId) {
|
|
56
|
+
const worktreeBase = path.join(projectDir, WORKTREES_DIR);
|
|
57
|
+
if (!fs.existsSync(worktreeBase)) fs.mkdirSync(worktreeBase, { recursive: true });
|
|
58
|
+
|
|
59
|
+
const worktreePath = path.join(worktreeBase, taskId);
|
|
60
|
+
const branchName = `ctx-task-${taskId}`;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Create branch and worktree
|
|
64
|
+
execSync(`git worktree add "${worktreePath}" -b "${branchName}" HEAD`, {
|
|
65
|
+
cwd: projectDir, encoding: 'utf-8', timeout: 10000,
|
|
66
|
+
});
|
|
67
|
+
return worktreePath;
|
|
68
|
+
} catch (err) {
|
|
69
|
+
throw new Error(`Failed to create worktree for ${taskId}: ${err.message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Merge a worktree branch back to the current branch.
|
|
75
|
+
* Returns { merged: boolean, conflicts: boolean, error: string|null }
|
|
76
|
+
*/
|
|
77
|
+
export function mergeWorktree(projectDir, taskId) {
|
|
78
|
+
const branchName = `ctx-task-${taskId}`;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const result = execSync(`git merge --no-ff "${branchName}" -m "ctx(merge): task ${taskId}"`, {
|
|
82
|
+
cwd: projectDir, encoding: 'utf-8', timeout: 10000,
|
|
83
|
+
});
|
|
84
|
+
return { merged: true, conflicts: false, error: null };
|
|
85
|
+
} catch (err) {
|
|
86
|
+
if (err.message.includes('CONFLICT') || err.message.includes('Merge conflict')) {
|
|
87
|
+
// Abort the merge
|
|
88
|
+
try { execSync('git merge --abort', { cwd: projectDir, timeout: 5000 }); } catch {}
|
|
89
|
+
return { merged: false, conflicts: true, error: `Merge conflicts in task ${taskId}` };
|
|
90
|
+
}
|
|
91
|
+
return { merged: false, conflicts: false, error: err.message };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Clean up a worktree and its branch.
|
|
97
|
+
*/
|
|
98
|
+
export function cleanupWorktree(projectDir, taskId) {
|
|
99
|
+
const worktreePath = path.join(projectDir, WORKTREES_DIR, taskId);
|
|
100
|
+
const branchName = `ctx-task-${taskId}`;
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
execSync(`git worktree remove "${worktreePath}" --force`, {
|
|
104
|
+
cwd: projectDir, timeout: 10000,
|
|
105
|
+
});
|
|
106
|
+
} catch {
|
|
107
|
+
// Manual cleanup
|
|
108
|
+
if (fs.existsSync(worktreePath)) fs.rmSync(worktreePath, { recursive: true, force: true });
|
|
109
|
+
try { execSync('git worktree prune', { cwd: projectDir, timeout: 5000 }); } catch {}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Delete branch
|
|
113
|
+
try {
|
|
114
|
+
execSync(`git branch -D "${branchName}"`, { cwd: projectDir, timeout: 5000 });
|
|
115
|
+
} catch {}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Execute a wave of tasks in parallel using git worktrees.
|
|
120
|
+
* Each task runs in its own worktree with a separate claude process.
|
|
121
|
+
*
|
|
122
|
+
* Returns { results: [{ taskId, success, error }], conflicts: [taskId] }
|
|
123
|
+
*/
|
|
124
|
+
export async function executeWave({ wave, agentsDir, agentFile, projectDir, ctxDir, streaming = false, timeout = 300000 }) {
|
|
125
|
+
const agentPath = path.join(agentsDir, agentFile);
|
|
126
|
+
|
|
127
|
+
// Create worktrees for all tasks
|
|
128
|
+
const worktrees = [];
|
|
129
|
+
for (const task of wave) {
|
|
130
|
+
try {
|
|
131
|
+
const wtPath = createWorktree(projectDir, task.taskId);
|
|
132
|
+
worktrees.push({ task, worktreePath: wtPath });
|
|
133
|
+
} catch (err) {
|
|
134
|
+
worktrees.push({ task, worktreePath: null, error: err.message });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Run agents in parallel (non-streaming for parallel execution)
|
|
139
|
+
const promises = worktrees.map(async ({ task, worktreePath, error }) => {
|
|
140
|
+
if (error) return { taskId: task.taskId, success: false, error };
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const { context } = buildContext('execute', worktreePath, ctxDir);
|
|
144
|
+
await runAgent({
|
|
145
|
+
agentPath,
|
|
146
|
+
message: `Execute task: ${task.title}\n\n${task.description || ''}`,
|
|
147
|
+
streaming: false, // Must be non-streaming for parallel
|
|
148
|
+
timeout,
|
|
149
|
+
context,
|
|
150
|
+
});
|
|
151
|
+
return { taskId: task.taskId, success: true, error: null };
|
|
152
|
+
} catch (err) {
|
|
153
|
+
return { taskId: task.taskId, success: false, error: err.message };
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const results = await Promise.all(promises);
|
|
158
|
+
|
|
159
|
+
// Merge successful worktrees back
|
|
160
|
+
const conflicts = [];
|
|
161
|
+
for (const result of results) {
|
|
162
|
+
if (result.success) {
|
|
163
|
+
const merge = mergeWorktree(projectDir, result.taskId);
|
|
164
|
+
if (merge.conflicts) {
|
|
165
|
+
conflicts.push(result.taskId);
|
|
166
|
+
result.success = false;
|
|
167
|
+
result.error = merge.error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Cleanup worktree
|
|
171
|
+
cleanupWorktree(projectDir, result.taskId);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { results, conflicts };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Execute all waves sequentially (wave N+1 waits for wave N).
|
|
179
|
+
*
|
|
180
|
+
* Options:
|
|
181
|
+
* waves, agentsDir, agentFile, projectDir, ctxDir, streaming, timeout, onWave
|
|
182
|
+
*
|
|
183
|
+
* Returns { waveResults: [...], conflicts: [...] }
|
|
184
|
+
*/
|
|
185
|
+
export async function executeAllWaves({ waves, agentsDir, agentFile, projectDir, ctxDir, streaming = false, timeout = 300000, onWave = null }) {
|
|
186
|
+
const allResults = [];
|
|
187
|
+
const allConflicts = [];
|
|
188
|
+
|
|
189
|
+
for (let i = 0; i < waves.length; i++) {
|
|
190
|
+
const wave = waves[i];
|
|
191
|
+
if (onWave) onWave({ waveIndex: i, total: waves.length, taskCount: wave.length, status: 'starting' });
|
|
192
|
+
|
|
193
|
+
const { results, conflicts } = await executeWave({
|
|
194
|
+
wave, agentsDir, agentFile, projectDir, ctxDir, streaming, timeout,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
allResults.push({ waveIndex: i, results });
|
|
198
|
+
allConflicts.push(...conflicts);
|
|
199
|
+
|
|
200
|
+
if (onWave) onWave({ waveIndex: i, total: waves.length, taskCount: wave.length, status: 'completed', results });
|
|
201
|
+
|
|
202
|
+
// Update state with wave progress
|
|
203
|
+
const state = readState(ctxDir);
|
|
204
|
+
if (state) {
|
|
205
|
+
state.waveProgress = state.waveProgress || [];
|
|
206
|
+
state.waveProgress.push({
|
|
207
|
+
waveIndex: i,
|
|
208
|
+
completedAt: new Date().toISOString(),
|
|
209
|
+
taskResults: results.map(r => ({ taskId: r.taskId, success: r.success })),
|
|
210
|
+
});
|
|
211
|
+
writeState(ctxDir, state);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Re-queue conflicting tasks as sequential
|
|
216
|
+
if (allConflicts.length > 0) {
|
|
217
|
+
const state = readState(ctxDir);
|
|
218
|
+
if (state) {
|
|
219
|
+
state.conflictTasks = allConflicts;
|
|
220
|
+
writeState(ctxDir, state);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return { waveResults: allResults, conflicts: allConflicts };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Format wave execution results for display.
|
|
229
|
+
*/
|
|
230
|
+
export function formatWaveResults(waveResults, conflicts) {
|
|
231
|
+
const lines = [];
|
|
232
|
+
for (const wave of waveResults) {
|
|
233
|
+
lines.push(` Wave ${wave.waveIndex + 1}:`);
|
|
234
|
+
for (const r of wave.results) {
|
|
235
|
+
const icon = r.success ? '✓' : '✗';
|
|
236
|
+
lines.push(` ${icon} ${r.taskId}${r.error ? ` — ${r.error}` : ''}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (conflicts.length > 0) {
|
|
240
|
+
lines.push('');
|
|
241
|
+
lines.push(` ⚠ Merge conflicts (re-queued as sequential): ${conflicts.join(', ')}`);
|
|
242
|
+
}
|
|
243
|
+
return lines.join('\n');
|
|
244
|
+
}
|
package/templates/PRD.json
CHANGED
package/templates/config.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://ctx.dev/schemas/config.json",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5",
|
|
4
4
|
|
|
5
5
|
"models": {
|
|
6
6
|
"architect": {
|
|
@@ -109,38 +109,6 @@
|
|
|
109
109
|
"forceCheckpointThreshold": 0.7
|
|
110
110
|
},
|
|
111
111
|
|
|
112
|
-
"parallelization": {
|
|
113
|
-
"enabled": true,
|
|
114
|
-
"maxParallelTasks": 3,
|
|
115
|
-
"requireDependencyAnalysis": true,
|
|
116
|
-
"fileLocking": true
|
|
117
|
-
},
|
|
118
|
-
|
|
119
|
-
"review": {
|
|
120
|
-
"enabled": true,
|
|
121
|
-
"beforeCommit": true,
|
|
122
|
-
"blockOnCritical": true,
|
|
123
|
-
"blockOnHigh": true,
|
|
124
|
-
"warnOnMedium": true,
|
|
125
|
-
"autoFix": false,
|
|
126
|
-
"checks": {
|
|
127
|
-
"typeErrors": true,
|
|
128
|
-
"importResolution": true,
|
|
129
|
-
"circularDeps": true,
|
|
130
|
-
"security": true,
|
|
131
|
-
"bestPractices": true,
|
|
132
|
-
"consoleLogs": true,
|
|
133
|
-
"emptyBlocks": true
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
|
|
137
|
-
"criteria": {
|
|
138
|
-
"autoSuggest": true,
|
|
139
|
-
"minCriteria": 5,
|
|
140
|
-
"maxCriteria": 15,
|
|
141
|
-
"categories": ["happy_path", "validation", "error_states", "security", "performance"]
|
|
142
|
-
},
|
|
143
|
-
|
|
144
112
|
"debug": {
|
|
145
113
|
"maxAttempts": 10,
|
|
146
114
|
"persistSessions": true,
|
|
@@ -148,214 +116,10 @@
|
|
|
148
116
|
"autoResume": true
|
|
149
117
|
},
|
|
150
118
|
|
|
151
|
-
"integrations": {
|
|
152
|
-
"linear": {
|
|
153
|
-
"enabled": false,
|
|
154
|
-
"apiKey": "env:LINEAR_API_KEY",
|
|
155
|
-
"teamId": null,
|
|
156
|
-
"projectId": null,
|
|
157
|
-
"syncOnCreate": true,
|
|
158
|
-
"syncOnVerify": true,
|
|
159
|
-
"closeOnComplete": true,
|
|
160
|
-
"statusMapping": {
|
|
161
|
-
"pending": "Todo",
|
|
162
|
-
"executing": "In Progress",
|
|
163
|
-
"verifying": "In Review",
|
|
164
|
-
"complete": "Done"
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
"jira": {
|
|
168
|
-
"enabled": false,
|
|
169
|
-
"baseUrl": null,
|
|
170
|
-
"email": "env:JIRA_EMAIL",
|
|
171
|
-
"apiToken": "env:JIRA_API_TOKEN",
|
|
172
|
-
"projectKey": null,
|
|
173
|
-
"issueType": "Story",
|
|
174
|
-
"syncOnCreate": true,
|
|
175
|
-
"syncOnVerify": true,
|
|
176
|
-
"transitionMapping": {
|
|
177
|
-
"executing": "In Progress",
|
|
178
|
-
"verifying": "In Review",
|
|
179
|
-
"complete": "Done"
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
"github": {
|
|
183
|
-
"enabled": false,
|
|
184
|
-
"repository": null,
|
|
185
|
-
"token": "env:GITHUB_TOKEN",
|
|
186
|
-
"syncIssues": true,
|
|
187
|
-
"closeOnComplete": true,
|
|
188
|
-
"createPROnComplete": false,
|
|
189
|
-
"labels": {
|
|
190
|
-
"feature": "enhancement",
|
|
191
|
-
"bug": "bug",
|
|
192
|
-
"design": "design"
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
"slack": {
|
|
196
|
-
"enabled": false,
|
|
197
|
-
"webhookUrl": "env:SLACK_WEBHOOK_URL",
|
|
198
|
-
"notifyOnPhaseComplete": false,
|
|
199
|
-
"notifyOnVerifyFail": true,
|
|
200
|
-
"notifyOnHandoff": false
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
|
|
204
119
|
"ui": {
|
|
205
120
|
"showTokenUsage": true,
|
|
206
121
|
"showModelInfo": true,
|
|
207
122
|
"showWaveProgress": true,
|
|
208
123
|
"verboseOutput": false
|
|
209
|
-
},
|
|
210
|
-
|
|
211
|
-
"team": {
|
|
212
|
-
"enabled": false,
|
|
213
|
-
"members": [],
|
|
214
|
-
"fileLocking": {
|
|
215
|
-
"enabled": true,
|
|
216
|
-
"expiryMinutes": 60,
|
|
217
|
-
"autoRelease": true
|
|
218
|
-
},
|
|
219
|
-
"notifications": {
|
|
220
|
-
"onPhaseStart": false,
|
|
221
|
-
"onPhaseComplete": true,
|
|
222
|
-
"onVerifyFail": true,
|
|
223
|
-
"onConflict": true,
|
|
224
|
-
"onMilestoneComplete": true
|
|
225
|
-
},
|
|
226
|
-
"branching": {
|
|
227
|
-
"strategy": "feature",
|
|
228
|
-
"prefix": "ctx"
|
|
229
|
-
}
|
|
230
|
-
},
|
|
231
|
-
|
|
232
|
-
"audit": {
|
|
233
|
-
"enabled": true,
|
|
234
|
-
"retention": {
|
|
235
|
-
"daily": "90d",
|
|
236
|
-
"weekly": "1y",
|
|
237
|
-
"monthly": "3y"
|
|
238
|
-
},
|
|
239
|
-
"logging": {
|
|
240
|
-
"tokens": true,
|
|
241
|
-
"costs": true,
|
|
242
|
-
"decisions": true,
|
|
243
|
-
"fileAccess": true,
|
|
244
|
-
"externalCalls": true
|
|
245
|
-
},
|
|
246
|
-
"compliance": {
|
|
247
|
-
"soc2": false,
|
|
248
|
-
"hipaa": false,
|
|
249
|
-
"gdpr": false
|
|
250
|
-
},
|
|
251
|
-
"reports": {
|
|
252
|
-
"dailySummary": true,
|
|
253
|
-
"weeklySummary": true,
|
|
254
|
-
"exportFormat": "json"
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
|
|
258
|
-
"metrics": {
|
|
259
|
-
"enabled": true,
|
|
260
|
-
"collection": {
|
|
261
|
-
"tokens": true,
|
|
262
|
-
"costs": true,
|
|
263
|
-
"timing": true,
|
|
264
|
-
"quality": true
|
|
265
|
-
},
|
|
266
|
-
"aggregation": {
|
|
267
|
-
"daily": true,
|
|
268
|
-
"weekly": true,
|
|
269
|
-
"monthly": true
|
|
270
|
-
},
|
|
271
|
-
"costRates": {
|
|
272
|
-
"opus": {"input": 0.015, "output": 0.075},
|
|
273
|
-
"sonnet": {"input": 0.003, "output": 0.015},
|
|
274
|
-
"haiku": {"input": 0.00025, "output": 0.00125}
|
|
275
|
-
},
|
|
276
|
-
"roi": {
|
|
277
|
-
"developerHourlyRate": 150,
|
|
278
|
-
"hoursPerStoryManual": 4
|
|
279
|
-
},
|
|
280
|
-
"alerts": {
|
|
281
|
-
"dailyCostThreshold": 10,
|
|
282
|
-
"weeklyStoriesTarget": 10
|
|
283
|
-
}
|
|
284
|
-
},
|
|
285
|
-
|
|
286
|
-
"milestone": {
|
|
287
|
-
"autoTag": true,
|
|
288
|
-
"tagFormat": "v{version}",
|
|
289
|
-
"archiveOnComplete": true,
|
|
290
|
-
"requireAudit": true,
|
|
291
|
-
"gapPhasesEnabled": true
|
|
292
|
-
},
|
|
293
|
-
|
|
294
|
-
"learning": {
|
|
295
|
-
"enabled": true,
|
|
296
|
-
"observePatterns": true,
|
|
297
|
-
"observeDecisions": true,
|
|
298
|
-
"observeFailures": true,
|
|
299
|
-
"minConfidence": 0.7,
|
|
300
|
-
"applyPatterns": true,
|
|
301
|
-
"enforceConventions": true,
|
|
302
|
-
"memoryPath": ".ctx/memory/"
|
|
303
|
-
},
|
|
304
|
-
|
|
305
|
-
"predict": {
|
|
306
|
-
"enabled": true,
|
|
307
|
-
"autoSuggest": {
|
|
308
|
-
"onMilestoneComplete": true,
|
|
309
|
-
"onAllStoriesPass": true,
|
|
310
|
-
"weeklyDigest": false
|
|
311
|
-
},
|
|
312
|
-
"categories": ["revenue", "engagement", "retention", "operations", "performance", "security"],
|
|
313
|
-
"maxSuggestions": 10,
|
|
314
|
-
"minROI": 5,
|
|
315
|
-
"includeQuickWins": true,
|
|
316
|
-
"researchDepth": "balanced"
|
|
317
|
-
},
|
|
318
|
-
|
|
319
|
-
"monitor": {
|
|
320
|
-
"enabled": false,
|
|
321
|
-
"pollInterval": "5m",
|
|
322
|
-
"autoFix": {
|
|
323
|
-
"enabled": false,
|
|
324
|
-
"safePatterns": ["null-check", "import-missing", "type-mismatch"],
|
|
325
|
-
"requireReview": true,
|
|
326
|
-
"createPR": true
|
|
327
|
-
},
|
|
328
|
-
"sentry": {
|
|
329
|
-
"enabled": false,
|
|
330
|
-
"token": "env:SENTRY_AUTH_TOKEN",
|
|
331
|
-
"organization": null,
|
|
332
|
-
"project": null,
|
|
333
|
-
"environment": "production"
|
|
334
|
-
},
|
|
335
|
-
"logrocket": {
|
|
336
|
-
"enabled": false,
|
|
337
|
-
"appId": null,
|
|
338
|
-
"apiKey": "env:LOGROCKET_API_KEY"
|
|
339
|
-
},
|
|
340
|
-
"alerts": {
|
|
341
|
-
"errorSpike": {
|
|
342
|
-
"threshold": 50,
|
|
343
|
-
"window": "5m"
|
|
344
|
-
},
|
|
345
|
-
"slack": true,
|
|
346
|
-
"pagerduty": false
|
|
347
|
-
}
|
|
348
|
-
},
|
|
349
|
-
|
|
350
|
-
"voice": {
|
|
351
|
-
"enabled": false,
|
|
352
|
-
"engine": "auto",
|
|
353
|
-
"language": "en-US",
|
|
354
|
-
"wakeWord": "Hey CTX",
|
|
355
|
-
"confirmCommands": true,
|
|
356
|
-
"dictationEndPhrase": "done",
|
|
357
|
-
"dictationPauseSeconds": 3,
|
|
358
|
-
"noiseReduction": true,
|
|
359
|
-
"minConfidence": 0.7
|
|
360
124
|
}
|
|
361
125
|
}
|