chati-dev 1.4.0 → 2.0.1
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 +3 -3
- package/framework/agents/build/dev.md +343 -0
- package/framework/agents/clarity/architect.md +112 -0
- package/framework/agents/clarity/brief.md +182 -0
- package/framework/agents/clarity/brownfield-wu.md +181 -0
- package/framework/agents/clarity/detail.md +110 -0
- package/framework/agents/clarity/greenfield-wu.md +153 -0
- package/framework/agents/clarity/ux.md +112 -0
- package/framework/config.yaml +3 -3
- package/framework/constitution.md +31 -1
- package/framework/context/governance.md +37 -0
- package/framework/context/protocols.md +34 -0
- package/framework/context/quality.md +27 -0
- package/framework/context/root.md +24 -0
- package/framework/domains/agents/architect.yaml +51 -0
- package/framework/domains/agents/brief.yaml +47 -0
- package/framework/domains/agents/brownfield-wu.yaml +49 -0
- package/framework/domains/agents/detail.yaml +47 -0
- package/framework/domains/agents/dev.yaml +49 -0
- package/framework/domains/agents/devops.yaml +43 -0
- package/framework/domains/agents/greenfield-wu.yaml +47 -0
- package/framework/domains/agents/orchestrator.yaml +49 -0
- package/framework/domains/agents/phases.yaml +47 -0
- package/framework/domains/agents/qa-implementation.yaml +43 -0
- package/framework/domains/agents/qa-planning.yaml +44 -0
- package/framework/domains/agents/tasks.yaml +48 -0
- package/framework/domains/agents/ux.yaml +50 -0
- package/framework/domains/constitution.yaml +77 -0
- package/framework/domains/global.yaml +64 -0
- package/framework/domains/workflows/brownfield-discovery.yaml +16 -0
- package/framework/domains/workflows/brownfield-fullstack.yaml +26 -0
- package/framework/domains/workflows/brownfield-service.yaml +22 -0
- package/framework/domains/workflows/brownfield-ui.yaml +22 -0
- package/framework/domains/workflows/greenfield-fullstack.yaml +26 -0
- package/framework/hooks/constitution-guard.js +101 -0
- package/framework/hooks/mode-governance.js +92 -0
- package/framework/hooks/model-governance.js +76 -0
- package/framework/hooks/prism-engine.js +89 -0
- package/framework/hooks/session-digest.js +60 -0
- package/framework/hooks/settings.json +44 -0
- package/framework/migrations/v1.4-to-v2.0.yaml +167 -0
- package/framework/migrations/v2.0-to-v2.0.1.yaml +132 -0
- package/framework/orchestrator/chati.md +284 -6
- package/framework/tasks/architect-api-design.md +63 -0
- package/framework/tasks/architect-consolidate.md +47 -0
- package/framework/tasks/architect-db-design.md +73 -0
- package/framework/tasks/architect-design.md +95 -0
- package/framework/tasks/architect-security-review.md +62 -0
- package/framework/tasks/architect-stack-selection.md +53 -0
- package/framework/tasks/brief-consolidate.md +249 -0
- package/framework/tasks/brief-constraint-identify.md +277 -0
- package/framework/tasks/brief-extract-requirements.md +339 -0
- package/framework/tasks/brief-stakeholder-map.md +176 -0
- package/framework/tasks/brief-validate-completeness.md +121 -0
- package/framework/tasks/brownfield-wu-architecture-map.md +394 -0
- package/framework/tasks/brownfield-wu-deep-discovery.md +312 -0
- package/framework/tasks/brownfield-wu-dependency-scan.md +359 -0
- package/framework/tasks/brownfield-wu-migration-plan.md +483 -0
- package/framework/tasks/brownfield-wu-report.md +325 -0
- package/framework/tasks/brownfield-wu-risk-assess.md +424 -0
- package/framework/tasks/detail-acceptance-criteria.md +372 -0
- package/framework/tasks/detail-consolidate.md +138 -0
- package/framework/tasks/detail-edge-case-analysis.md +300 -0
- package/framework/tasks/detail-expand-prd.md +389 -0
- package/framework/tasks/detail-nfr-extraction.md +223 -0
- package/framework/tasks/dev-code-review.md +404 -0
- package/framework/tasks/dev-consolidate.md +543 -0
- package/framework/tasks/dev-debug.md +322 -0
- package/framework/tasks/dev-implement.md +252 -0
- package/framework/tasks/dev-iterate.md +411 -0
- package/framework/tasks/dev-pr-prepare.md +497 -0
- package/framework/tasks/dev-refactor.md +342 -0
- package/framework/tasks/dev-test-write.md +306 -0
- package/framework/tasks/devops-ci-setup.md +412 -0
- package/framework/tasks/devops-consolidate.md +712 -0
- package/framework/tasks/devops-deploy-config.md +598 -0
- package/framework/tasks/devops-monitoring-setup.md +658 -0
- package/framework/tasks/devops-release-prepare.md +673 -0
- package/framework/tasks/greenfield-wu-analyze-empty.md +169 -0
- package/framework/tasks/greenfield-wu-report.md +266 -0
- package/framework/tasks/greenfield-wu-scaffold-detection.md +203 -0
- package/framework/tasks/greenfield-wu-tech-stack-assess.md +255 -0
- package/framework/tasks/orchestrator-deviation.md +260 -0
- package/framework/tasks/orchestrator-escalate.md +276 -0
- package/framework/tasks/orchestrator-handoff.md +243 -0
- package/framework/tasks/orchestrator-health.md +372 -0
- package/framework/tasks/orchestrator-mode-switch.md +262 -0
- package/framework/tasks/orchestrator-resume.md +189 -0
- package/framework/tasks/orchestrator-route.md +169 -0
- package/framework/tasks/orchestrator-spawn-terminal.md +358 -0
- package/framework/tasks/orchestrator-status.md +260 -0
- package/framework/tasks/orchestrator-suggest-mode.md +372 -0
- package/framework/tasks/phases-breakdown.md +91 -0
- package/framework/tasks/phases-dependency-mapping.md +67 -0
- package/framework/tasks/phases-mvp-scoping.md +94 -0
- package/framework/tasks/qa-impl-consolidate.md +522 -0
- package/framework/tasks/qa-impl-performance-test.md +487 -0
- package/framework/tasks/qa-impl-regression-check.md +413 -0
- package/framework/tasks/qa-impl-sast-scan.md +402 -0
- package/framework/tasks/qa-impl-test-execute.md +344 -0
- package/framework/tasks/qa-impl-verdict.md +339 -0
- package/framework/tasks/qa-planning-consolidate.md +309 -0
- package/framework/tasks/qa-planning-coverage-plan.md +338 -0
- package/framework/tasks/qa-planning-gate-define.md +339 -0
- package/framework/tasks/qa-planning-risk-matrix.md +631 -0
- package/framework/tasks/qa-planning-test-strategy.md +217 -0
- package/framework/tasks/tasks-acceptance-write.md +75 -0
- package/framework/tasks/tasks-consolidate.md +57 -0
- package/framework/tasks/tasks-decompose.md +80 -0
- package/framework/tasks/tasks-estimate.md +66 -0
- package/framework/tasks/ux-a11y-check.md +49 -0
- package/framework/tasks/ux-component-map.md +55 -0
- package/framework/tasks/ux-consolidate.md +46 -0
- package/framework/tasks/ux-user-flow.md +46 -0
- package/framework/tasks/ux-wireframe.md +76 -0
- package/package.json +1 -1
- package/scripts/bundle-framework.js +2 -0
- package/scripts/changelog-generator.js +222 -0
- package/scripts/codebase-mapper.js +728 -0
- package/scripts/commit-message-generator.js +167 -0
- package/scripts/coverage-analyzer.js +260 -0
- package/scripts/dependency-analyzer.js +280 -0
- package/scripts/framework-analyzer.js +308 -0
- package/scripts/generate-constitution-domain.js +253 -0
- package/scripts/health-check.js +481 -0
- package/scripts/ide-sync.js +327 -0
- package/scripts/performance-analyzer.js +325 -0
- package/scripts/plan-tracker.js +278 -0
- package/scripts/populate-entity-registry.js +481 -0
- package/scripts/pr-review.js +317 -0
- package/scripts/rollback-manager.js +310 -0
- package/scripts/stuck-detector.js +343 -0
- package/scripts/test-quality-assessment.js +257 -0
- package/scripts/validate-agents.js +367 -0
- package/scripts/validate-tasks.js +465 -0
- package/src/autonomy/autonomous-gate.js +293 -0
- package/src/autonomy/index.js +51 -0
- package/src/autonomy/mode-manager.js +225 -0
- package/src/autonomy/mode-suggester.js +283 -0
- package/src/autonomy/progress-reporter.js +268 -0
- package/src/autonomy/safety-net.js +320 -0
- package/src/context/bracket-tracker.js +79 -0
- package/src/context/domain-loader.js +107 -0
- package/src/context/engine.js +144 -0
- package/src/context/formatter.js +184 -0
- package/src/context/index.js +4 -0
- package/src/context/layers/l0-constitution.js +28 -0
- package/src/context/layers/l1-global.js +37 -0
- package/src/context/layers/l2-agent.js +39 -0
- package/src/context/layers/l3-workflow.js +42 -0
- package/src/context/layers/l4-task.js +24 -0
- package/src/decision/analyzer.js +167 -0
- package/src/decision/engine.js +270 -0
- package/src/decision/index.js +38 -0
- package/src/decision/registry-healer.js +450 -0
- package/src/decision/registry-updater.js +330 -0
- package/src/gates/circuit-breaker.js +119 -0
- package/src/gates/g1-planning-complete.js +153 -0
- package/src/gates/g2-qa-planning.js +153 -0
- package/src/gates/g3-implementation.js +188 -0
- package/src/gates/g4-qa-implementation.js +207 -0
- package/src/gates/g5-deploy-ready.js +180 -0
- package/src/gates/gate-base.js +144 -0
- package/src/gates/index.js +46 -0
- package/src/installer/brownfield-upgrader.js +249 -0
- package/src/installer/core.js +55 -3
- package/src/installer/file-hasher.js +51 -0
- package/src/installer/manifest.js +117 -0
- package/src/installer/templates.js +17 -15
- package/src/installer/transaction.js +229 -0
- package/src/installer/validator.js +18 -1
- package/src/memory/agent-memory.js +255 -0
- package/src/memory/gotchas-injector.js +72 -0
- package/src/memory/gotchas.js +361 -0
- package/src/memory/index.js +35 -0
- package/src/memory/search.js +233 -0
- package/src/memory/session-digest.js +239 -0
- package/src/merger/env-merger.js +112 -0
- package/src/merger/index.js +56 -0
- package/src/merger/replace-merger.js +51 -0
- package/src/merger/yaml-merger.js +127 -0
- package/src/orchestrator/agent-selector.js +285 -0
- package/src/orchestrator/deviation-handler.js +350 -0
- package/src/orchestrator/handoff-engine.js +271 -0
- package/src/orchestrator/index.js +67 -0
- package/src/orchestrator/intent-classifier.js +264 -0
- package/src/orchestrator/pipeline-manager.js +492 -0
- package/src/orchestrator/pipeline-state.js +223 -0
- package/src/orchestrator/session-manager.js +409 -0
- package/src/tasks/executor.js +195 -0
- package/src/tasks/handoff.js +226 -0
- package/src/tasks/index.js +4 -0
- package/src/tasks/loader.js +210 -0
- package/src/tasks/router.js +182 -0
- package/src/terminal/collector.js +216 -0
- package/src/terminal/index.js +30 -0
- package/src/terminal/isolation.js +129 -0
- package/src/terminal/monitor.js +277 -0
- package/src/terminal/spawner.js +269 -0
- package/src/upgrade/checker.js +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Formatter — Produces structured XML for agent prompt injection.
|
|
3
|
+
*
|
|
4
|
+
* Output format: <chati-context bracket="..."> with nested sections.
|
|
5
|
+
* Truncates intelligently based on token budget via priority-based pruning.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Format PRISM pipeline results into XML context block.
|
|
10
|
+
* @param {object} options
|
|
11
|
+
* @param {string} options.bracket - Current bracket name
|
|
12
|
+
* @param {number} options.tokenBudget - Max approximate token count
|
|
13
|
+
* @param {object} options.l0 - L0 Constitution result
|
|
14
|
+
* @param {object} [options.l1] - L1 Global result
|
|
15
|
+
* @param {object} [options.l2] - L2 Agent result
|
|
16
|
+
* @param {object} [options.l3] - L3 Workflow result
|
|
17
|
+
* @param {object} [options.l4] - L4 Task result
|
|
18
|
+
* @returns {string} XML context block
|
|
19
|
+
*/
|
|
20
|
+
export function formatContext(options) {
|
|
21
|
+
const { bracket, tokenBudget, l0, l1, l2, l3, l4 } = options;
|
|
22
|
+
const sections = [];
|
|
23
|
+
|
|
24
|
+
// L0 — Constitution (always present)
|
|
25
|
+
if (l0) {
|
|
26
|
+
sections.push(formatConstitution(l0));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// L1 — Mode + Global
|
|
30
|
+
if (l1) {
|
|
31
|
+
sections.push(formatGlobal(l1));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// L2 — Agent
|
|
35
|
+
if (l2 && l2.agent) {
|
|
36
|
+
sections.push(formatAgent(l2));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// L3 — Pipeline/Workflow
|
|
40
|
+
if (l3 && l3.workflow) {
|
|
41
|
+
sections.push(formatWorkflow(l3));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// L4 — Task
|
|
45
|
+
if (l4 && l4.taskId) {
|
|
46
|
+
sections.push(formatTask(l4));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let body = sections.join('\n\n');
|
|
50
|
+
|
|
51
|
+
// Truncate if over budget (rough estimate: 1 token ≈ 4 chars)
|
|
52
|
+
const maxChars = tokenBudget * 4;
|
|
53
|
+
if (body.length > maxChars) {
|
|
54
|
+
body = truncateByPriority(sections, maxChars);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return `<chati-context bracket="${bracket}">\n${body}\n</chati-context>`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function formatConstitution(l0) {
|
|
61
|
+
const rulesText = l0.rules.length > 0
|
|
62
|
+
? l0.rules.map(r => ` <rule id="${esc(r.id)}" priority="${esc(r.priority)}">${esc(r.text)}</rule>`).join('\n')
|
|
63
|
+
: ` <summary>${esc(l0.summary)}</summary>`;
|
|
64
|
+
|
|
65
|
+
return ` <constitution articles="${l0.articleCount}">\n${rulesText}\n </constitution>`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function formatGlobal(l1) {
|
|
69
|
+
const lines = [];
|
|
70
|
+
lines.push(` <mode name="${esc(l1.mode)}">`);
|
|
71
|
+
if (l1.modeRules.writeScope) {
|
|
72
|
+
lines.push(` <write-scope>${esc(l1.modeRules.writeScope)}</write-scope>`);
|
|
73
|
+
}
|
|
74
|
+
if (l1.modeRules.allowedActions.length > 0) {
|
|
75
|
+
lines.push(` <allowed>${l1.modeRules.allowedActions.map(esc).join(', ')}</allowed>`);
|
|
76
|
+
}
|
|
77
|
+
if (l1.modeRules.blockedActions.length > 0) {
|
|
78
|
+
lines.push(` <blocked>${l1.modeRules.blockedActions.map(esc).join(', ')}</blocked>`);
|
|
79
|
+
}
|
|
80
|
+
lines.push(` </mode>`);
|
|
81
|
+
|
|
82
|
+
if (l1.rules.length > 0) {
|
|
83
|
+
lines.push(` <global-rules>`);
|
|
84
|
+
for (const r of l1.rules) {
|
|
85
|
+
lines.push(` <rule id="${esc(r.id)}">${esc(r.text)}</rule>`);
|
|
86
|
+
}
|
|
87
|
+
lines.push(` </global-rules>`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return lines.join('\n');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function formatAgent(l2) {
|
|
94
|
+
const lines = [];
|
|
95
|
+
lines.push(` <agent name="${esc(l2.agent)}">`);
|
|
96
|
+
if (l2.mission) {
|
|
97
|
+
lines.push(` <mission>${esc(l2.mission)}</mission>`);
|
|
98
|
+
}
|
|
99
|
+
if (l2.authority.exclusive.length > 0) {
|
|
100
|
+
lines.push(` <exclusive>${l2.authority.exclusive.map(esc).join(', ')}</exclusive>`);
|
|
101
|
+
}
|
|
102
|
+
if (l2.authority.blocked.length > 0) {
|
|
103
|
+
lines.push(` <blocked>${l2.authority.blocked.map(esc).join(', ')}</blocked>`);
|
|
104
|
+
}
|
|
105
|
+
if (l2.outputs.length > 0) {
|
|
106
|
+
lines.push(` <outputs>${l2.outputs.map(esc).join(', ')}</outputs>`);
|
|
107
|
+
}
|
|
108
|
+
if (l2.rules.length > 0) {
|
|
109
|
+
for (const r of l2.rules) {
|
|
110
|
+
lines.push(` <rule id="${esc(r.id)}">${esc(r.text)}</rule>`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
lines.push(` </agent>`);
|
|
114
|
+
return lines.join('\n');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function formatWorkflow(l3) {
|
|
118
|
+
const p = l3.pipelineContext;
|
|
119
|
+
const lines = [];
|
|
120
|
+
lines.push(` <pipeline workflow="${esc(l3.workflow)}" progress="${p.progress}%">`);
|
|
121
|
+
if (p.previousStep) lines.push(` <previous>${esc(p.previousStep)}</previous>`);
|
|
122
|
+
if (p.currentStep) lines.push(` <current>${esc(p.currentStep)}</current>`);
|
|
123
|
+
if (p.nextStep) lines.push(` <next>${esc(p.nextStep)}</next>`);
|
|
124
|
+
if (l3.rules.length > 0) {
|
|
125
|
+
for (const r of l3.rules) {
|
|
126
|
+
lines.push(` <rule id="${esc(r.id)}">${esc(r.text)}</rule>`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
lines.push(` </pipeline>`);
|
|
130
|
+
return lines.join('\n');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function formatTask(l4) {
|
|
134
|
+
const lines = [];
|
|
135
|
+
lines.push(` <task id="${esc(l4.taskId)}">`);
|
|
136
|
+
if (l4.criteria.length > 0) {
|
|
137
|
+
lines.push(` <criteria>`);
|
|
138
|
+
for (const c of l4.criteria) {
|
|
139
|
+
lines.push(` <criterion>${esc(c)}</criterion>`);
|
|
140
|
+
}
|
|
141
|
+
lines.push(` </criteria>`);
|
|
142
|
+
}
|
|
143
|
+
if (l4.artifacts.length > 0) {
|
|
144
|
+
lines.push(` <artifacts>${l4.artifacts.map(esc).join(', ')}</artifacts>`);
|
|
145
|
+
}
|
|
146
|
+
if (Object.keys(l4.handoff).length > 0) {
|
|
147
|
+
lines.push(` <handoff>`);
|
|
148
|
+
for (const [key, val] of Object.entries(l4.handoff)) {
|
|
149
|
+
lines.push(` <${esc(key)}>${esc(String(val))}</${esc(key)}>`);
|
|
150
|
+
}
|
|
151
|
+
lines.push(` </handoff>`);
|
|
152
|
+
}
|
|
153
|
+
lines.push(` </task>`);
|
|
154
|
+
return lines.join('\n');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Truncate sections by removing lower-priority layers first (L4 → L3 → L2).
|
|
159
|
+
* L0 and L1 are never truncated.
|
|
160
|
+
*/
|
|
161
|
+
function truncateByPriority(sections, maxChars) {
|
|
162
|
+
let result = sections.join('\n\n');
|
|
163
|
+
// Remove from end (lowest priority) until within budget
|
|
164
|
+
const trimmed = [...sections];
|
|
165
|
+
while (trimmed.length > 2 && result.length > maxChars) {
|
|
166
|
+
trimmed.pop();
|
|
167
|
+
result = trimmed.join('\n\n');
|
|
168
|
+
}
|
|
169
|
+
// If still over, hard truncate
|
|
170
|
+
if (result.length > maxChars) {
|
|
171
|
+
result = result.slice(0, maxChars) + '\n <!-- truncated -->';
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Escape XML special characters */
|
|
177
|
+
function esc(str) {
|
|
178
|
+
if (!str) return '';
|
|
179
|
+
return String(str)
|
|
180
|
+
.replace(/&/g, '&')
|
|
181
|
+
.replace(/</g, '<')
|
|
182
|
+
.replace(/>/g, '>')
|
|
183
|
+
.replace(/"/g, '"');
|
|
184
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { runPrism, getPrismInfo } from './engine.js';
|
|
2
|
+
export { calculateBracket, estimateRemaining, isLayerActive, getBracketDefinitions } from './bracket-tracker.js';
|
|
3
|
+
export { formatContext } from './formatter.js';
|
|
4
|
+
export { loadDomainFile, loadAgentDomains, loadWorkflowDomains, extractRules } from './domain-loader.js';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L0 Constitution Layer — ALWAYS active, non-negotiable.
|
|
3
|
+
*
|
|
4
|
+
* Loads constitution rules from domain file. These are the fundamental
|
|
5
|
+
* governance rules that every agent must follow at all times.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { loadConstitutionDomain, extractRules } from '../domain-loader.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Process L0: load constitution governance rules.
|
|
12
|
+
* @param {object} ctx - Pipeline context
|
|
13
|
+
* @param {string} ctx.domainsDir - Path to chati.dev/domains/
|
|
14
|
+
* @returns {{ layer: string, rules: Array, summary: string }}
|
|
15
|
+
*/
|
|
16
|
+
export function processL0(ctx) {
|
|
17
|
+
const domain = loadConstitutionDomain(ctx.domainsDir);
|
|
18
|
+
const rules = extractRules(domain);
|
|
19
|
+
|
|
20
|
+
const summary = domain?.summary || 'Constitution governance: self-validation, quality >= 95%, guided options, persistent state, two-layer handoff, language protocol, deviation protocol, mode governance.';
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
layer: 'L0',
|
|
24
|
+
rules,
|
|
25
|
+
summary,
|
|
26
|
+
articleCount: domain?.articleCount || rules.length,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L1 Global Layer — ALWAYS active.
|
|
3
|
+
*
|
|
4
|
+
* Loads global rules: coding standards, bracket-specific behavioral rules,
|
|
5
|
+
* mode governance constraints (clarity/build/deploy).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { loadGlobalDomain, extractRules } from '../domain-loader.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Process L1: load global rules + mode governance.
|
|
12
|
+
* @param {object} ctx - Pipeline context
|
|
13
|
+
* @param {string} ctx.domainsDir - Path to chati.dev/domains/
|
|
14
|
+
* @param {string} ctx.mode - Current mode (clarity, build, deploy)
|
|
15
|
+
* @param {string} ctx.bracket - Current bracket (FRESH, MODERATE, etc.)
|
|
16
|
+
* @returns {{ layer: string, rules: Array, mode: string, modeRules: object }}
|
|
17
|
+
*/
|
|
18
|
+
export function processL1(ctx) {
|
|
19
|
+
const domain = loadGlobalDomain(ctx.domainsDir);
|
|
20
|
+
const rules = extractRules(domain);
|
|
21
|
+
const mode = ctx.mode || 'clarity';
|
|
22
|
+
|
|
23
|
+
const modeRules = domain?.modes?.[mode] || {};
|
|
24
|
+
const bracketRules = domain?.brackets?.[ctx.bracket] || {};
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
layer: 'L1',
|
|
28
|
+
rules,
|
|
29
|
+
mode,
|
|
30
|
+
modeRules: {
|
|
31
|
+
writeScope: modeRules.writeScope || 'chati.dev/',
|
|
32
|
+
allowedActions: modeRules.allowedActions || [],
|
|
33
|
+
blockedActions: modeRules.blockedActions || [],
|
|
34
|
+
},
|
|
35
|
+
bracketBehavior: bracketRules.behavior || 'normal',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L2 Agent Layer — Active when an agent is assigned.
|
|
3
|
+
*
|
|
4
|
+
* Loads agent-specific domain rules: authority boundaries,
|
|
5
|
+
* behavioral rules, and task registry.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { loadAgentDomains, extractRules } from '../domain-loader.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Process L2: load agent domain rules.
|
|
12
|
+
* @param {object} ctx - Pipeline context
|
|
13
|
+
* @param {string} ctx.domainsDir - Path to chati.dev/domains/
|
|
14
|
+
* @param {string} ctx.agent - Active agent name (e.g. 'brief', 'dev', 'orchestrator')
|
|
15
|
+
* @returns {{ layer: string, agent: string, rules: Array, authority: object }}
|
|
16
|
+
*/
|
|
17
|
+
export function processL2(ctx) {
|
|
18
|
+
if (!ctx.agent) {
|
|
19
|
+
return { layer: 'L2', agent: null, rules: [], authority: {} };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const allDomains = loadAgentDomains(ctx.domainsDir);
|
|
23
|
+
const domain = allDomains.get(ctx.agent) || null;
|
|
24
|
+
const rules = extractRules(domain);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
layer: 'L2',
|
|
28
|
+
agent: ctx.agent,
|
|
29
|
+
rules,
|
|
30
|
+
authority: {
|
|
31
|
+
exclusive: domain?.authority?.exclusive || [],
|
|
32
|
+
allowed: domain?.authority?.allowed || [],
|
|
33
|
+
blocked: domain?.authority?.blocked || [],
|
|
34
|
+
redirectMessage: domain?.authority?.redirectMessage || null,
|
|
35
|
+
},
|
|
36
|
+
mission: domain?.mission || '',
|
|
37
|
+
outputs: domain?.outputs || [],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L3 Workflow/Pipeline Layer — Active when session has pipeline state.
|
|
3
|
+
*
|
|
4
|
+
* Loads workflow rules with phase awareness: what came before,
|
|
5
|
+
* what comes next, and constraints for the current pipeline position.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { loadWorkflowDomains, extractRules } from '../domain-loader.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Process L3: load workflow rules with pipeline awareness.
|
|
12
|
+
* @param {object} ctx - Pipeline context
|
|
13
|
+
* @param {string} ctx.domainsDir - Path to chati.dev/domains/
|
|
14
|
+
* @param {string} ctx.workflow - Active workflow name (e.g. 'greenfield-fullstack')
|
|
15
|
+
* @param {string} ctx.pipelinePosition - Current position in pipeline (e.g. 'brief')
|
|
16
|
+
* @returns {{ layer: string, workflow: string, rules: Array, pipelineContext: object }}
|
|
17
|
+
*/
|
|
18
|
+
export function processL3(ctx) {
|
|
19
|
+
if (!ctx.workflow) {
|
|
20
|
+
return { layer: 'L3', workflow: null, rules: [], pipelineContext: {} };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const allDomains = loadWorkflowDomains(ctx.domainsDir);
|
|
24
|
+
const domain = allDomains.get(ctx.workflow) || null;
|
|
25
|
+
const rules = extractRules(domain);
|
|
26
|
+
|
|
27
|
+
const steps = domain?.steps || [];
|
|
28
|
+
const currentIdx = steps.indexOf(ctx.pipelinePosition);
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
layer: 'L3',
|
|
32
|
+
workflow: ctx.workflow,
|
|
33
|
+
rules,
|
|
34
|
+
pipelineContext: {
|
|
35
|
+
currentStep: ctx.pipelinePosition || null,
|
|
36
|
+
previousStep: currentIdx > 0 ? steps[currentIdx - 1] : null,
|
|
37
|
+
nextStep: currentIdx >= 0 && currentIdx < steps.length - 1 ? steps[currentIdx + 1] : null,
|
|
38
|
+
totalSteps: steps.length,
|
|
39
|
+
progress: steps.length > 0 ? Math.round(((currentIdx + 1) / steps.length) * 100) : 0,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L4 Task Layer — Active when a specific task is running.
|
|
3
|
+
*
|
|
4
|
+
* Injects task-specific context: the active task definition,
|
|
5
|
+
* handoff data from the previous agent, and relevant artifacts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Process L4: inject task context and handoff data.
|
|
10
|
+
* @param {object} ctx - Pipeline context
|
|
11
|
+
* @param {string} [ctx.taskId] - Active task ID (e.g. 'brief-extract')
|
|
12
|
+
* @param {object} [ctx.handoff] - Handoff data from previous agent
|
|
13
|
+
* @param {string[]} [ctx.artifacts] - List of artifact paths relevant to current task
|
|
14
|
+
* @returns {{ layer: string, taskId: string|null, handoff: object, artifacts: string[] }}
|
|
15
|
+
*/
|
|
16
|
+
export function processL4(ctx) {
|
|
17
|
+
return {
|
|
18
|
+
layer: 'L4',
|
|
19
|
+
taskId: ctx.taskId || null,
|
|
20
|
+
handoff: ctx.handoff || {},
|
|
21
|
+
artifacts: ctx.artifacts || [],
|
|
22
|
+
criteria: ctx.taskCriteria || [],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Impact Analyzer - Dependency analysis for chati.dev
|
|
3
|
+
* Analyzes impact of changes via dependency graph traversal
|
|
4
|
+
*
|
|
5
|
+
* @module decision/analyzer
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, existsSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import yaml from 'js-yaml';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Analyze impact of changing an entity.
|
|
14
|
+
* Builds dependency graph from entity-registry and calculates affected entities.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} projectDir
|
|
17
|
+
* @param {string} entityPath - Path of entity being changed
|
|
18
|
+
* @returns {{ impactLevel: 'low'|'medium'|'high'|'critical', affectedEntities: object[], dependencyChain: string[] }}
|
|
19
|
+
*/
|
|
20
|
+
export function analyzeImpact(projectDir, entityPath) {
|
|
21
|
+
const graph = buildDependencyGraph(projectDir);
|
|
22
|
+
const affectedPaths = getTransitiveDependents(graph, entityPath);
|
|
23
|
+
|
|
24
|
+
// Determine impact level based on affected entity count
|
|
25
|
+
let impactLevel;
|
|
26
|
+
const affectedCount = affectedPaths.length;
|
|
27
|
+
|
|
28
|
+
if (affectedCount === 0) {
|
|
29
|
+
impactLevel = 'low';
|
|
30
|
+
} else if (affectedCount <= 2) {
|
|
31
|
+
impactLevel = 'low';
|
|
32
|
+
} else if (affectedCount <= 5) {
|
|
33
|
+
impactLevel = 'medium';
|
|
34
|
+
} else if (affectedCount <= 10) {
|
|
35
|
+
impactLevel = 'high';
|
|
36
|
+
} else {
|
|
37
|
+
impactLevel = 'critical';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Load entity details
|
|
41
|
+
const registryPath = join(projectDir, 'chati.dev', 'entity-registry.yaml');
|
|
42
|
+
const registry = existsSync(registryPath)
|
|
43
|
+
? yaml.load(readFileSync(registryPath, 'utf8'))
|
|
44
|
+
: { entities: {} };
|
|
45
|
+
|
|
46
|
+
const allEntities = flattenEntities(registry);
|
|
47
|
+
const entityMap = new Map(allEntities.map(e => [e.path, e]));
|
|
48
|
+
|
|
49
|
+
const affectedEntities = affectedPaths.map(path => {
|
|
50
|
+
const entity = entityMap.get(path);
|
|
51
|
+
return {
|
|
52
|
+
path,
|
|
53
|
+
type: entity?.type || 'unknown',
|
|
54
|
+
name: entity?.name || path.split('/').pop()
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
impactLevel,
|
|
60
|
+
affectedEntities,
|
|
61
|
+
dependencyChain: affectedPaths
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build dependency graph from entity registry.
|
|
67
|
+
* @param {string} projectDir
|
|
68
|
+
* @returns {Map<string, string[]>} Map of entityPath -> [dependentPaths]
|
|
69
|
+
*/
|
|
70
|
+
export function buildDependencyGraph(projectDir) {
|
|
71
|
+
const registryPath = join(projectDir, 'chati.dev', 'entity-registry.yaml');
|
|
72
|
+
const graph = new Map();
|
|
73
|
+
|
|
74
|
+
if (!existsSync(registryPath)) {
|
|
75
|
+
return graph;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const registry = yaml.load(readFileSync(registryPath, 'utf8'));
|
|
79
|
+
const entities = flattenEntities(registry);
|
|
80
|
+
|
|
81
|
+
// Initialize graph with all entities
|
|
82
|
+
entities.forEach(entity => {
|
|
83
|
+
if (entity.path) {
|
|
84
|
+
graph.set(entity.path, []);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Build reverse dependency map (who depends on whom)
|
|
89
|
+
entities.forEach(entity => {
|
|
90
|
+
if (entity.dependencies && Array.isArray(entity.dependencies)) {
|
|
91
|
+
entity.dependencies.forEach(depPath => {
|
|
92
|
+
if (!graph.has(depPath)) {
|
|
93
|
+
graph.set(depPath, []);
|
|
94
|
+
}
|
|
95
|
+
// Add entity.path as dependent of depPath
|
|
96
|
+
const dependents = graph.get(depPath);
|
|
97
|
+
if (!dependents.includes(entity.path)) {
|
|
98
|
+
dependents.push(entity.path);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return graph;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get all entities that depend on a given entity (transitive).
|
|
109
|
+
* Uses BFS traversal.
|
|
110
|
+
* @param {Map<string, string[]>} graph
|
|
111
|
+
* @param {string} entityPath
|
|
112
|
+
* @returns {string[]} All affected paths
|
|
113
|
+
*/
|
|
114
|
+
export function getTransitiveDependents(graph, entityPath) {
|
|
115
|
+
const visited = new Set();
|
|
116
|
+
const queue = [entityPath];
|
|
117
|
+
const result = [];
|
|
118
|
+
|
|
119
|
+
while (queue.length > 0) {
|
|
120
|
+
const current = queue.shift();
|
|
121
|
+
|
|
122
|
+
if (visited.has(current)) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
visited.add(current);
|
|
127
|
+
|
|
128
|
+
// Get direct dependents
|
|
129
|
+
const dependents = graph.get(current) || [];
|
|
130
|
+
|
|
131
|
+
dependents.forEach(dependent => {
|
|
132
|
+
if (!visited.has(dependent)) {
|
|
133
|
+
queue.push(dependent);
|
|
134
|
+
result.push(dependent);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Flatten entity registry into array of entities.
|
|
144
|
+
* @private
|
|
145
|
+
* @param {object} registry
|
|
146
|
+
* @returns {object[]}
|
|
147
|
+
*/
|
|
148
|
+
function flattenEntities(registry) {
|
|
149
|
+
const entities = [];
|
|
150
|
+
|
|
151
|
+
if (!registry.entities) {
|
|
152
|
+
return entities;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
Object.entries(registry.entities).forEach(([type, typeEntities]) => {
|
|
156
|
+
if (Array.isArray(typeEntities)) {
|
|
157
|
+
typeEntities.forEach(entity => {
|
|
158
|
+
entities.push({
|
|
159
|
+
...entity,
|
|
160
|
+
type
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return entities;
|
|
167
|
+
}
|