chati-dev 4.0.10 → 4.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/README.md +5 -0
- package/bin/chati.js +23 -2
- package/framework/agents/build/dev.md +43 -10
- package/framework/agents/discover/brief.md +38 -8
- package/framework/agents/discover/brownfield-wu.md +44 -3
- package/framework/agents/discover/greenfield-wu.md +14 -0
- package/framework/agents/plan/architect.md +6 -0
- package/framework/agents/plan/detail.md +25 -0
- package/framework/agents/plan/tasks.md +29 -1
- package/framework/agents/plan/ux-brand-architect.md +215 -0
- package/framework/agents/plan/ux-component-engineer.md +289 -0
- package/framework/agents/plan/ux-researcher.md +166 -0
- package/framework/agents/plan/ux.md +126 -1008
- package/framework/agents/quality/qa-implementation.md +121 -22
- package/framework/agents/quality/qa-planning.md +18 -0
- package/framework/config.yaml +15 -4
- package/framework/constitution.md +8 -2
- package/framework/context/root.md +1 -1
- package/framework/data/entity-registry.yaml +59 -3
- package/framework/hooks/constitution-guard.js +67 -1
- package/framework/hooks/license-guard.js +4 -4
- package/framework/hooks/model-governance.js +2 -1
- package/framework/hooks/prism-engine.js +74 -6
- package/framework/hooks/read-protection.js +1 -1
- package/framework/hooks/session-digest.js +159 -7
- package/framework/hooks/settings.json +16 -4
- package/framework/hooks/style-guard.js +134 -0
- package/framework/hooks/undercover-guard.js +220 -0
- package/framework/intelligence/context-engine.md +21 -0
- package/framework/intelligence/hooks-performance.md +54 -0
- package/framework/intelligence/memory-layer.md +47 -0
- package/framework/migrations/v4.0-to-v4.1.yaml +165 -0
- package/framework/orchestrator/chati.md +327 -1067
- package/framework/templates/brandbook-html-tmpl.md +107 -0
- package/framework/templates/session-memory-tmpl.yaml +68 -0
- package/package.json +1 -1
- package/src/autonomy/build-state.js +1 -1
- package/src/autonomy/worktree-manager.js +13 -13
- package/src/config/agent-customizer.js +1 -1
- package/src/config/gemini-hooks-generator.js +6 -6
- package/src/config/ide-configs.js +1 -1
- package/src/context/bracket-tracker.js +25 -4
- package/src/context/engine.js +37 -7
- package/src/context/formatter.js +45 -1
- package/src/context/index.js +3 -3
- package/src/dashboard/data-reader.js +7 -7
- package/src/decision/engine.js +2 -2
- package/src/decision/registry-healer.js +1 -1
- package/src/decision/registry-updater.js +2 -2
- package/src/extensions/loader.js +1 -1
- package/src/gates/g1-planning-complete.js +1 -1
- package/src/gates/g2-qa-planning.js +1 -1
- package/src/gates/g3-implementation.js +4 -4
- package/src/gates/g4-qa-implementation.js +2 -2
- package/src/health/auto-fix.js +3 -3
- package/src/health/engine.js +2 -2
- package/src/installer/brownfield-upgrader.js +3 -3
- package/src/installer/manifest.js +1 -1
- package/src/installer/transaction.js +1 -1
- package/src/installer/validator.js +2 -2
- package/src/intelligence/decision-engine.js +1 -1
- package/src/intelligence/memory-manager.js +1 -1
- package/src/intelligence/registry-manager.js +2 -2
- package/src/intelligence/timeline.js +1 -1
- package/src/license/client.js +20 -2
- package/src/license/commands.js +7 -7
- package/src/memory/agent-memory.js +3 -3
- package/src/memory/daily-digest.js +170 -0
- package/src/memory/dream.js +254 -0
- package/src/memory/gotchas.js +2 -2
- package/src/memory/index.js +18 -0
- package/src/memory/magic-docs.js +98 -0
- package/src/memory/memory-extractor.js +163 -0
- package/src/memory/session-digest.js +144 -6
- package/src/merger/yaml-merger.js +1 -1
- package/src/orchestrator/cli.js +980 -0
- package/src/orchestrator/handoff-engine.js +25 -4
- package/src/orchestrator/index.js +9 -0
- package/src/orchestrator/session-manager.js +1 -1
- package/src/preview/detector.js +3 -3
- package/src/preview/launcher.js +2 -2
- package/src/quality/metrics-collector.js +1 -1
- package/src/quality/test-runner.js +2 -4
- package/src/scanning/density-scanner.js +51 -0
- package/src/scanning/env-scanner.js +97 -0
- package/src/scanning/index.js +7 -0
- package/src/scanning/leakage-scanner.js +54 -0
- package/src/scanning/placeholder-scanner.js +40 -0
- package/src/scanning/security-scanner.js +94 -0
- package/src/security/bash-security.js +335 -0
- package/src/security/index.js +9 -0
- package/src/telemetry/config.js +3 -5
- package/src/telemetry/sender.js +19 -17
- package/src/terminal/cli-registry.js +1 -1
- package/src/terminal/cost-tracker.js +1 -2
- package/src/terminal/prompt-builder.js +11 -27
- package/src/terminal/rate-limiter.js +1 -2
- package/src/terminal/run-agent.js +3 -3
- package/src/terminal/spawner.js +41 -4
- package/src/upgrade/checker.js +2 -2
- package/src/upgrade/migrator.js +1 -1
- package/src/utils/config-parser.js +1 -1
- package/src/utils/file-lock.js +3 -3
- package/src/utils/provider-limits.js +35 -4
- package/src/wizard/i18n.js +2 -2
- package/src/wizard/index.js +2 -1
- package/src/wizard/questions.js +4 -2
|
@@ -56,6 +56,144 @@ export function buildSessionDigest(projectDir, sessionState) {
|
|
|
56
56
|
return digest;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Maximum characters for the structured digest (~10K tokens).
|
|
61
|
+
*/
|
|
62
|
+
const MAX_STRUCTURED_CHARS = 40_000;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Section token budgets for structured digest (chars = tokens * 4).
|
|
66
|
+
*/
|
|
67
|
+
const SECTION_BUDGETS = {
|
|
68
|
+
current_agent: 400, // ~100 tokens
|
|
69
|
+
pipeline_position: 400, // ~100 tokens
|
|
70
|
+
active_task: 2000, // ~500 tokens
|
|
71
|
+
files_changed: 4000, // ~1000 tokens
|
|
72
|
+
blockers: 4000, // ~1000 tokens
|
|
73
|
+
decisions_made: 12000, // ~3000 tokens
|
|
74
|
+
gotchas_found: 8000, // ~2000 tokens
|
|
75
|
+
next_steps: 9200, // ~2300 tokens
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Build a structured 8-section session digest with token budgets.
|
|
80
|
+
* Richer than buildSessionDigest — captures decisions, blockers, gotchas,
|
|
81
|
+
* files changed, and next steps for better context recovery after compaction.
|
|
82
|
+
*
|
|
83
|
+
* Inspired by Claude Code's structured session memory (10 sections, 12K tokens).
|
|
84
|
+
*
|
|
85
|
+
* @param {string} projectDir - Project directory
|
|
86
|
+
* @param {object} sessionState - Current session.yaml data
|
|
87
|
+
* @param {object} [enrichment={}] - Additional context from hooks/agents
|
|
88
|
+
* @param {string[]} [enrichment.filesChanged] - Files modified in session
|
|
89
|
+
* @param {string[]} [enrichment.blockers] - Active blockers
|
|
90
|
+
* @param {Array<{what: string, why: string}>} [enrichment.decisions] - Decisions with rationale
|
|
91
|
+
* @param {object[]} [enrichment.gotchas] - Gotchas discovered
|
|
92
|
+
* @param {string[]} [enrichment.nextSteps] - Planned next steps
|
|
93
|
+
* @returns {object} Structured digest with 8 sections
|
|
94
|
+
*/
|
|
95
|
+
export function buildStructuredDigest(projectDir, sessionState, enrichment = {}) {
|
|
96
|
+
const timestamp = new Date().toISOString();
|
|
97
|
+
|
|
98
|
+
const truncate = (text, maxChars) => {
|
|
99
|
+
if (!text) return '';
|
|
100
|
+
const str = String(text);
|
|
101
|
+
return str.length <= maxChars ? str : str.slice(0, maxChars - 3) + '...';
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const truncateArray = (arr, maxChars) => {
|
|
105
|
+
if (!arr || arr.length === 0) return [];
|
|
106
|
+
let total = 0;
|
|
107
|
+
const result = [];
|
|
108
|
+
for (const item of arr) {
|
|
109
|
+
const str = typeof item === 'object' ? JSON.stringify(item) : String(item);
|
|
110
|
+
if (total + str.length > maxChars) break;
|
|
111
|
+
total += str.length;
|
|
112
|
+
result.push(item);
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const digest = {
|
|
118
|
+
version: '2.0',
|
|
119
|
+
timestamp,
|
|
120
|
+
date: timestamp.split('T')[0],
|
|
121
|
+
|
|
122
|
+
// Section 1: Current agent
|
|
123
|
+
current_agent: truncate(
|
|
124
|
+
sessionState.current_agent || sessionState.agents
|
|
125
|
+
? Object.entries(sessionState.agents || {}).find(([, v]) => v.status === 'in_progress')?.[0] || null
|
|
126
|
+
: null,
|
|
127
|
+
SECTION_BUDGETS.current_agent,
|
|
128
|
+
),
|
|
129
|
+
|
|
130
|
+
// Section 2: Pipeline position
|
|
131
|
+
pipeline_position: truncate(
|
|
132
|
+
sessionState.pipeline?.phase || sessionState.project?.state || 'unknown',
|
|
133
|
+
SECTION_BUDGETS.pipeline_position,
|
|
134
|
+
),
|
|
135
|
+
|
|
136
|
+
// Section 3: Active task
|
|
137
|
+
active_task: truncate(
|
|
138
|
+
enrichment.activeTask || null,
|
|
139
|
+
SECTION_BUDGETS.active_task,
|
|
140
|
+
),
|
|
141
|
+
|
|
142
|
+
// Section 4: Files changed
|
|
143
|
+
files_changed: truncateArray(
|
|
144
|
+
enrichment.filesChanged || [],
|
|
145
|
+
SECTION_BUDGETS.files_changed,
|
|
146
|
+
),
|
|
147
|
+
|
|
148
|
+
// Section 5: Blockers
|
|
149
|
+
blockers: truncateArray(
|
|
150
|
+
enrichment.blockers || [],
|
|
151
|
+
SECTION_BUDGETS.blockers,
|
|
152
|
+
),
|
|
153
|
+
|
|
154
|
+
// Section 6: Decisions made
|
|
155
|
+
decisions_made: truncateArray(
|
|
156
|
+
enrichment.decisions || [],
|
|
157
|
+
SECTION_BUDGETS.decisions_made,
|
|
158
|
+
),
|
|
159
|
+
|
|
160
|
+
// Section 7: Gotchas found
|
|
161
|
+
gotchas_found: truncateArray(
|
|
162
|
+
enrichment.gotchas || [],
|
|
163
|
+
SECTION_BUDGETS.gotchas_found,
|
|
164
|
+
),
|
|
165
|
+
|
|
166
|
+
// Section 8: Next steps
|
|
167
|
+
next_steps: truncateArray(
|
|
168
|
+
enrichment.nextSteps || [],
|
|
169
|
+
SECTION_BUDGETS.next_steps,
|
|
170
|
+
),
|
|
171
|
+
|
|
172
|
+
// Preserve base summary for backward compat
|
|
173
|
+
summary: {
|
|
174
|
+
total_agents: Object.keys(sessionState.agents || {}).length,
|
|
175
|
+
completed_agents: Object.values(sessionState.agents || {}).filter(a => a.status === 'completed').length,
|
|
176
|
+
mode: sessionState.mode || 'discover',
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// Final budget enforcement — iterative truncation until within budget
|
|
181
|
+
const truncOrder = ['next_steps', 'gotchas_found', 'files_changed', 'blockers', 'decisions_made'];
|
|
182
|
+
let pass = 0;
|
|
183
|
+
while (JSON.stringify(digest).length > MAX_STRUCTURED_CHARS && pass < truncOrder.length) {
|
|
184
|
+
const field = truncOrder[pass];
|
|
185
|
+
if (Array.isArray(digest[field]) && digest[field].length > 1) {
|
|
186
|
+
digest[field] = digest[field].slice(0, Math.max(1, Math.ceil(digest[field].length / 2)));
|
|
187
|
+
} else {
|
|
188
|
+
pass++;
|
|
189
|
+
}
|
|
190
|
+
// If we halved and still too big, move to next field
|
|
191
|
+
if (JSON.stringify(digest).length > MAX_STRUCTURED_CHARS) pass++;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return digest;
|
|
195
|
+
}
|
|
196
|
+
|
|
59
197
|
/**
|
|
60
198
|
* Save session digest to disk.
|
|
61
199
|
* @param {string} projectDir - Project directory
|
|
@@ -95,7 +233,7 @@ export function saveSessionDigest(projectDir, digest) {
|
|
|
95
233
|
saved: true,
|
|
96
234
|
path: digestPath,
|
|
97
235
|
};
|
|
98
|
-
} catch {
|
|
236
|
+
} catch { /* expected: operation may fail gracefully */
|
|
99
237
|
return {
|
|
100
238
|
saved: false,
|
|
101
239
|
path: digestPath,
|
|
@@ -139,7 +277,7 @@ export function loadLatestDigest(projectDir) {
|
|
|
139
277
|
loaded: true,
|
|
140
278
|
digest,
|
|
141
279
|
};
|
|
142
|
-
} catch {
|
|
280
|
+
} catch { /* expected: file may not exist */
|
|
143
281
|
return {
|
|
144
282
|
loaded: false,
|
|
145
283
|
digest: null,
|
|
@@ -181,7 +319,7 @@ export function listDigests(projectDir) {
|
|
|
181
319
|
completion_rate: digest.summary?.completion_rate || 0,
|
|
182
320
|
total_agents: digest.summary?.total_agents || 0,
|
|
183
321
|
};
|
|
184
|
-
} catch {
|
|
322
|
+
} catch { /* expected: operation may fail gracefully */
|
|
185
323
|
return {
|
|
186
324
|
filename,
|
|
187
325
|
timestamp: null,
|
|
@@ -193,7 +331,7 @@ export function listDigests(projectDir) {
|
|
|
193
331
|
};
|
|
194
332
|
}
|
|
195
333
|
});
|
|
196
|
-
} catch {
|
|
334
|
+
} catch { /* expected: operation may fail gracefully */
|
|
197
335
|
return [];
|
|
198
336
|
}
|
|
199
337
|
}
|
|
@@ -224,7 +362,7 @@ export function pruneDigests(projectDir, keepCount = 10) {
|
|
|
224
362
|
const filepath = join(dir, filename);
|
|
225
363
|
try {
|
|
226
364
|
unlinkSync(filepath);
|
|
227
|
-
} catch {
|
|
365
|
+
} catch { /* expected: file may already be deleted */
|
|
228
366
|
// Ignore delete errors
|
|
229
367
|
}
|
|
230
368
|
});
|
|
@@ -233,7 +371,7 @@ export function pruneDigests(projectDir, keepCount = 10) {
|
|
|
233
371
|
deleted: toDelete.length,
|
|
234
372
|
kept: toKeep.length,
|
|
235
373
|
};
|
|
236
|
-
} catch {
|
|
374
|
+
} catch { /* expected: file may already be deleted */
|
|
237
375
|
return { deleted: 0, kept: 0 };
|
|
238
376
|
}
|
|
239
377
|
}
|
|
@@ -120,7 +120,7 @@ export function mergeYamlFiles(existingContent, newContent) {
|
|
|
120
120
|
const newObj = yaml.load(newContent) || {};
|
|
121
121
|
const merged = deepMergeYaml(existingObj, newObj);
|
|
122
122
|
return yaml.dump(merged, { lineWidth: -1, noRefs: true });
|
|
123
|
-
} catch {
|
|
123
|
+
} catch { /* expected: YAML may be invalid */
|
|
124
124
|
// If YAML parsing fails, return existing content unchanged
|
|
125
125
|
return existingContent;
|
|
126
126
|
}
|