chati-dev 4.2.2 → 4.3.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 +80 -53
- package/bin/chati.js +150 -5
- package/framework/agents/build/dev.md +509 -76
- package/framework/agents/deploy/devops.md +40 -25
- package/framework/agents/discover/brief.md +156 -22
- package/framework/agents/discover/brownfield-wu.md +24 -14
- package/framework/agents/discover/greenfield-wu.md +100 -14
- package/framework/agents/plan/architect-data-engineer.md +6 -6
- package/framework/agents/plan/architect-system.md +46 -12
- package/framework/agents/plan/architect.md +40 -20
- package/framework/agents/plan/detail.md +36 -24
- package/framework/agents/plan/phases.md +36 -26
- package/framework/agents/plan/tasks.md +114 -33
- package/framework/agents/plan/ux-brand-architect.md +240 -8
- package/framework/agents/plan/ux-component-engineer.md +28 -13
- package/framework/agents/plan/ux-researcher.md +7 -6
- package/framework/agents/plan/ux.md +55 -22
- package/framework/agents/quality/qa-implementation.md +143 -74
- package/framework/agents/quality/qa-planning.md +115 -42
- package/framework/agents/quality/qa-visual.md +439 -0
- package/framework/agents/shared/visualizer.md +128 -0
- package/framework/config.yaml +7 -6
- package/framework/constitution.md +127 -44
- package/framework/context/governance.md +12 -7
- package/framework/context/quality.md +6 -5
- package/framework/context/root.md +6 -6
- package/framework/data/entity-registry.yaml +377 -4
- package/framework/data/model-limits.json +19 -0
- package/framework/domains/agents/qa-visual.yaml +74 -0
- package/framework/domains/constitution.yaml +46 -2
- package/framework/domains/workflows/greenfield-fullstack.yaml +2 -2
- package/framework/hooks/advance-trigger.js +131 -0
- package/framework/hooks/brief-validator.js +83 -0
- package/framework/hooks/constitution-guard.js +24 -5
- package/framework/hooks/license-guard.js +62 -27
- package/framework/hooks/mode-governance.js +13 -2
- package/framework/hooks/model-governance.js +1 -0
- package/framework/hooks/post-dev.js +207 -0
- package/framework/hooks/prism-engine.js +274 -105
- package/framework/hooks/reasoning-escalator.js +371 -0
- package/framework/hooks/reference-trigger.js +117 -0
- package/framework/hooks/session-digest.js +50 -1
- package/framework/hooks/settings.json +32 -1
- package/framework/hooks/style-guard.js +25 -6
- package/framework/hooks/team-quality-gate.js +19 -12
- package/framework/hooks/undercover-guard.js +4 -2
- package/framework/i18n/en.yaml +3 -3
- package/framework/i18n/es.yaml +3 -3
- package/framework/i18n/fr.yaml +3 -3
- package/framework/i18n/pt.yaml +3 -3
- package/framework/intelligence/context-engine.md +4 -5
- package/framework/intelligence/decision-engine.md +1 -1
- package/framework/intelligence/hooks-performance.md +3 -3
- package/framework/migrations/v1.0-to-v1.1.yaml +1 -1
- package/framework/migrations/v1.4-to-v2.0.yaml +11 -11
- package/framework/migrations/v4.0-to-v4.1.yaml +2 -2
- package/framework/migrations/v4.2-to-v4.3.yaml +29 -0
- package/framework/orchestrator/chati-router.js +387 -0
- package/framework/orchestrator/chati-update.md +40 -40
- package/framework/orchestrator/chati.md +294 -94
- package/framework/scaffold/motion-premium/README.md +89 -0
- package/framework/scaffold/motion-premium/app/globals.css.template +400 -0
- package/framework/scaffold/motion-premium/app/layout.tsx.template +110 -0
- package/framework/scaffold/motion-premium/components/animation/BackgroundCrossfadeProvider.tsx.template +170 -0
- package/framework/scaffold/motion-premium/components/animation/LenisProvider.tsx.template +49 -0
- package/framework/scaffold/motion-premium/components/animation/PageTransitionWrapper.tsx.template +83 -0
- package/framework/scaffold/motion-premium/components/animation/Preloader.tsx.template +171 -0
- package/framework/scaffold/motion-premium/components/ui/Container.tsx.template +69 -0
- package/framework/scaffold/motion-premium/components/ui/PageSection.tsx.template +74 -0
- package/framework/scaffold/motion-premium/lib/animations/gsap.ts.template +112 -0
- package/framework/scaffold/motion-premium/lib/animations/refreshCoordinator.ts.template +75 -0
- package/framework/scaffold/motion-premium/lib/animations/tokens.ts.template +119 -0
- package/framework/scaffold/motion-premium/lib/animations/useGsapContext.ts.template +92 -0
- package/framework/scaffold/motion-premium/lib/animations/useScrollSnapStepper.ts.template +265 -0
- package/framework/scaffold/motion-premium/lib/animations/useSmoothScroll.ts.template +67 -0
- package/framework/scaffold/motion-premium/lib/brand.ts.template +43 -0
- package/framework/scaffold/motion-premium/scaffold.yaml +174 -0
- package/framework/scaffold/motion-premium-3d/README.md +80 -0
- package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/ScrollDemoCanvas.tsx.template +81 -0
- package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/ScrollDemoClient.tsx.template +75 -0
- package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/page.tsx.template +26 -0
- package/framework/scaffold/motion-premium-3d/components/3d/CameraRig.tsx.template +100 -0
- package/framework/scaffold/motion-premium-3d/components/3d/CanvasProvider.tsx.template +85 -0
- package/framework/scaffold/motion-premium-3d/components/3d/InvalidateOnScroll.tsx.template +51 -0
- package/framework/scaffold/motion-premium-3d/components/3d/MeshCrossfade.tsx.template +79 -0
- package/framework/scaffold/motion-premium-3d/components/3d/ScrollCrossfade.tsx.template +88 -0
- package/framework/scaffold/motion-premium-3d/components/3d/ScrollScene.tsx.template +121 -0
- package/framework/scaffold/motion-premium-3d/components/webgl/SceneFallback.tsx.template +65 -0
- package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +68 -0
- package/framework/scaffold/motion-premium-3d/lib/webgl/detect.ts.template +69 -0
- package/framework/scaffold/motion-premium-3d/scaffold.yaml +133 -0
- package/framework/schemas/session.schema.json +109 -21
- package/framework/scripts/reference-capture.js +430 -0
- package/framework/scripts/visual-qa.js +674 -0
- package/framework/tasks/orchestrator-handoff.md +1 -1
- package/framework/tasks/orchestrator-resume.md +1 -1
- package/framework/tasks/orchestrator-route.md +1 -1
- package/framework/tasks/orchestrator-status.md +3 -3
- package/framework/tasks/qa-planning-gate-define.md +1 -1
- package/framework/templates/brandbook-html-tmpl.md +1 -1
- package/framework/templates/brandbook-tmpl.yaml +1 -1
- package/framework/templates/component-spec-tmpl.yaml +1 -1
- package/framework/templates/design-token-tmpl.yaml +1 -1
- package/framework/templates/icon-system-tmpl.yaml +1 -1
- package/framework/templates/team-planning-tasks.yaml +6 -5
- package/framework/workflows/brownfield-discovery.yaml +2 -2
- package/framework/workflows/brownfield-fullstack.yaml +15 -11
- package/framework/workflows/brownfield-service.yaml +14 -10
- package/framework/workflows/brownfield-ui.yaml +15 -11
- package/framework/workflows/greenfield-fullstack.yaml +16 -13
- package/framework/workflows/quick-flow.yaml +3 -3
- package/framework/workflows/standard-flow.yaml +12 -9
- package/package.json +10 -5
- package/src/autonomy/autonomous-gate.js +1 -0
- package/src/autonomy/build-state.js +1 -2
- package/src/autonomy/progress-reporter.js +1 -1
- package/src/config/agent-customizer.js +11 -3
- package/src/config/claude-settings-generator.js +27 -7
- package/src/config/context-file-generator.js +41 -21
- package/src/config/framework-adapter.js +1 -0
- package/src/config/gemini-hooks-generator.js +19 -7
- package/src/config/mcp-configs.js +1 -0
- package/src/context/layers/l1-global.js +2 -1
- package/src/dashboard/data-reader.js +4 -3
- package/src/dashboard/layout.js +2 -1
- package/src/decision/analyzer.js +6 -30
- package/src/decision/engine.js +4 -28
- package/src/decision/registry-healer.js +3 -2
- package/src/decision/registry-updater.js +23 -14
- package/src/extensions/loader.js +2 -8
- package/src/gates/g1-planning-complete.js +2 -1
- package/src/gates/g2-qa-planning.js +2 -1
- package/src/gates/g3-implementation.js +2 -1
- package/src/gates/g4-qa-implementation.js +3 -2
- package/src/gates/g5-deploy-ready.js +2 -1
- package/src/health/engine.js +4 -3
- package/src/installer/core.js +422 -81
- package/src/installer/preflight.js +131 -0
- package/src/installer/provider-overlay.js +3 -3
- package/src/installer/scaffold-applier.js +358 -0
- package/src/installer/templates.js +46 -29
- package/src/installer/validator.js +17 -12
- package/src/intelligence/registry-manager.js +22 -29
- package/src/intelligence/timeline.js +11 -6
- package/src/license/commands.js +1 -1
- package/src/license/wait.js +102 -0
- package/src/memory/agent-memory.js +81 -0
- package/src/memory/dream.js +32 -1
- package/src/merger/replace-merger.js +28 -15
- package/src/orchestrator/agent-selector.js +2 -1
- package/src/orchestrator/cli.js +1869 -71
- package/src/orchestrator/doctor.js +270 -0
- package/src/orchestrator/handoff-engine.js +4 -3
- package/src/orchestrator/index.js +2 -0
- package/src/orchestrator/pipeline-manager.js +306 -15
- package/src/orchestrator/session-manager.js +331 -6
- package/src/tasks/handoff.js +3 -2
- package/src/telemetry/config.js +4 -3
- package/src/telemetry/schema.js +1 -0
- package/src/terminal/collector.js +3 -2
- package/src/terminal/index.js +1 -2
- package/src/terminal/isolation.js +52 -18
- package/src/terminal/prompt-builder.js +42 -25
- package/src/terminal/run-parallel.js +1 -1
- package/src/terminal/run-team.js +3 -3
- package/src/terminal/team-task-list.js +43 -4
- package/src/upgrade/backup.js +3 -2
- package/src/upgrade/checker.js +3 -2
- package/src/upgrade/migrator.js +65 -7
- package/src/upgrade/tracked-files-detector.js +86 -0
- package/src/upgrade/user-messages.js +94 -0
- package/src/utils/config-parser.js +2 -1
- package/src/utils/feature-flags.js +2 -1
- package/src/utils/flatten-entities.js +69 -0
- package/src/utils/framework-dir.js +16 -0
- package/src/utils/model-id.js +85 -0
- package/src/utils/provider-limits.js +84 -23
- package/src/utils/schema-validator.js +1 -1
- package/src/wizard/i18n.js +5 -4
- package/src/wizard/index.js +14 -0
- package/assets/logo - c/303/263pia.png +0 -0
- package/assets/logo.svg +0 -42
- package/assets/logo2.png +0 -0
- package/assets/social-preview.png +0 -0
- package/scripts/bundle-framework.js +0 -69
- package/scripts/changelog-generator.js +0 -222
- package/scripts/codebase-mapper.js +0 -728
- package/scripts/commit-message-generator.js +0 -167
- package/scripts/coverage-analyzer.js +0 -260
- package/scripts/dependency-analyzer.js +0 -280
- package/scripts/doctor/checks/agents.js +0 -77
- package/scripts/doctor/checks/constitution.js +0 -41
- package/scripts/doctor/checks/domain-alignment.js +0 -58
- package/scripts/doctor/checks/prism-layers.js +0 -84
- package/scripts/doctor/checks/registry.js +0 -55
- package/scripts/doctor/checks/schemas.js +0 -61
- package/scripts/doctor/fixes/reference-fix.js +0 -100
- package/scripts/doctor/fixes/registry-fix.js +0 -56
- package/scripts/doctor/index.js +0 -212
- package/scripts/framework-analyzer.js +0 -308
- package/scripts/generate-constitution-domain.js +0 -253
- package/scripts/generate-signing-key.js +0 -33
- package/scripts/health-check.js +0 -481
- package/scripts/ide-sync.js +0 -326
- package/scripts/performance-analyzer.js +0 -325
- package/scripts/plan-tracker.js +0 -278
- package/scripts/populate-entity-registry.js +0 -481
- package/scripts/pr-review.js +0 -317
- package/scripts/rollback-manager.js +0 -310
- package/scripts/semantic-lint.js +0 -328
- package/scripts/sign-manifest.js +0 -53
- package/scripts/stuck-detector.js +0 -343
- package/scripts/test-quality-assessment.js +0 -257
- package/scripts/validate-agents.js +0 -368
- package/scripts/validate-package.js +0 -505
- package/scripts/validate-tasks.js +0 -465
- package/src/autonomy/worktree-manager.js +0 -250
- package/src/intelligence/decision-engine.js +0 -256
- package/src/intelligence/document-sharder.js +0 -221
- package/src/intelligence/elicitation.js +0 -265
|
@@ -6,36 +6,49 @@ import yaml from 'js-yaml';
|
|
|
6
6
|
export function generateSessionYaml(config) {
|
|
7
7
|
const { projectName, projectType, language, selectedIDEs, selectedMCPs, llmProvider, allProviders } = config;
|
|
8
8
|
|
|
9
|
+
// Schema MUST match session-manager.js DEFAULT_SESSION fields,
|
|
10
|
+
// otherwise validateSession() rejects installer-generated sessions.
|
|
9
11
|
const session = {
|
|
10
12
|
schema_version: '1.0',
|
|
13
|
+
version: '1.0',
|
|
14
|
+
mode: 'discover',
|
|
11
15
|
project: {
|
|
12
16
|
name: projectName,
|
|
13
17
|
type: projectType,
|
|
14
18
|
state: 'discover',
|
|
15
19
|
},
|
|
16
|
-
execution_mode: 'interactive',
|
|
17
|
-
execution_profile: 'guided',
|
|
18
|
-
current_agent: '',
|
|
19
20
|
language: language,
|
|
21
|
+
project_type: projectType,
|
|
22
|
+
started_at: new Date().toISOString(),
|
|
23
|
+
current_agent: '',
|
|
24
|
+
pipeline_position: 0,
|
|
25
|
+
execution_mode: 'interactive',
|
|
26
|
+
user_level: 'auto',
|
|
27
|
+
user_level_confidence: 0.0,
|
|
20
28
|
ides: selectedIDEs,
|
|
21
29
|
mcps: selectedMCPs,
|
|
22
30
|
providers_enabled: allProviders && allProviders.length > 0 ? allProviders : [llmProvider || 'claude'],
|
|
23
31
|
primary_provider: llmProvider || 'claude',
|
|
24
32
|
active_provider: llmProvider || 'claude',
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
completed_agents: [],
|
|
34
|
+
agent_results: {},
|
|
35
|
+
mode_transitions: [],
|
|
36
|
+
deviations: [],
|
|
27
37
|
agents: {},
|
|
28
38
|
backlog: [],
|
|
29
39
|
last_handoff: '',
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
// Article XXIII — Reasoning Tier Governance.
|
|
41
|
+
// Tier starts at 'standard' and is promoted by reasoning-escalator.js
|
|
42
|
+
// in response to friction signals. See chati.dev/hooks/reasoning-escalator.js.
|
|
43
|
+
reasoning_tier: 'standard',
|
|
44
|
+
reasoning_escalations: [],
|
|
32
45
|
};
|
|
33
46
|
|
|
34
|
-
// Initialize all
|
|
47
|
+
// Initialize all 13 agent statuses
|
|
35
48
|
const agentNames = [
|
|
36
49
|
'greenfield-wu', 'brownfield-wu', 'brief', 'detail',
|
|
37
50
|
'architect', 'ux', 'phases', 'tasks',
|
|
38
|
-
'qa-planning', 'dev', 'qa-implementation', 'devops',
|
|
51
|
+
'qa-planning', 'dev', 'qa-implementation', 'qa-visual', 'devops',
|
|
39
52
|
];
|
|
40
53
|
|
|
41
54
|
for (const agent of agentNames) {
|
|
@@ -62,7 +75,7 @@ export const PROVIDER_MODEL_MAPS = {
|
|
|
62
75
|
orchestrator: 'sonnet', 'greenfield-wu': 'haiku', 'brownfield-wu': 'opus',
|
|
63
76
|
brief: 'sonnet', detail: 'opus', architect: 'opus', ux: 'sonnet',
|
|
64
77
|
phases: 'sonnet', tasks: 'sonnet', 'qa-planning': 'opus',
|
|
65
|
-
dev: 'opus', 'qa-implementation': 'opus', devops: 'sonnet',
|
|
78
|
+
dev: 'opus', 'qa-implementation': 'opus', 'qa-visual': 'opus', devops: 'sonnet',
|
|
66
79
|
},
|
|
67
80
|
},
|
|
68
81
|
gemini: {
|
|
@@ -71,7 +84,7 @@ export const PROVIDER_MODEL_MAPS = {
|
|
|
71
84
|
orchestrator: 'flash', 'greenfield-wu': 'flash', 'brownfield-wu': 'pro',
|
|
72
85
|
brief: 'flash', detail: 'pro', architect: 'pro', ux: 'flash',
|
|
73
86
|
phases: 'flash', tasks: 'flash', 'qa-planning': 'pro',
|
|
74
|
-
dev: 'pro', 'qa-implementation': 'pro', devops: 'flash',
|
|
87
|
+
dev: 'pro', 'qa-implementation': 'pro', 'qa-visual': 'pro', devops: 'flash',
|
|
75
88
|
},
|
|
76
89
|
},
|
|
77
90
|
codex: {
|
|
@@ -80,7 +93,7 @@ export const PROVIDER_MODEL_MAPS = {
|
|
|
80
93
|
orchestrator: 'mini', 'greenfield-wu': 'mini', 'brownfield-wu': 'codex',
|
|
81
94
|
brief: 'mini', detail: 'codex', architect: 'codex', ux: 'mini',
|
|
82
95
|
phases: 'mini', tasks: 'mini', 'qa-planning': 'codex',
|
|
83
|
-
dev: 'codex', 'qa-implementation': 'codex', devops: 'mini',
|
|
96
|
+
dev: 'codex', 'qa-implementation': 'codex', 'qa-visual': 'codex', devops: 'mini',
|
|
84
97
|
},
|
|
85
98
|
},
|
|
86
99
|
};
|
|
@@ -89,7 +102,8 @@ export const PROVIDER_MODEL_MAPS = {
|
|
|
89
102
|
* Generate config.yaml content
|
|
90
103
|
*/
|
|
91
104
|
export function generateConfigYaml(config) {
|
|
92
|
-
const { version, projectType, language,
|
|
105
|
+
const { version, projectType, language, llmProvider, allProviders } = config;
|
|
106
|
+
const selectedIDEs = config.selectedIDEs || [];
|
|
93
107
|
const provider = llmProvider || 'claude';
|
|
94
108
|
const providers = allProviders && allProviders.length > 0 ? allProviders : [provider];
|
|
95
109
|
|
|
@@ -140,7 +154,7 @@ export function generateConfigYaml(config) {
|
|
|
140
154
|
// pipeline silently. See plan: was previously false for "safe rollout"
|
|
141
155
|
// but the v4.2.0 launch never actually shipped, so this is a fresh start.
|
|
142
156
|
agent_teams: true,
|
|
143
|
-
team_planning_size:
|
|
157
|
+
team_planning_size: 4,
|
|
144
158
|
team_build_size: 2,
|
|
145
159
|
team_echo_threshold: 0.92,
|
|
146
160
|
team_correction_cycles_max: 2,
|
|
@@ -193,7 +207,7 @@ Type \`/chati\` to start.
|
|
|
193
207
|
* Also reads AGENTS.md for project context (auto-generated by context-file-generator).
|
|
194
208
|
*/
|
|
195
209
|
export function generateCodexSkill(options = {}) {
|
|
196
|
-
const orchPath = options.orchestratorPath || 'chati.dev/orchestrator/chati.md';
|
|
210
|
+
const orchPath = options.orchestratorPath || '.chati.dev/orchestrator/chati.md';
|
|
197
211
|
return `---
|
|
198
212
|
name: chati
|
|
199
213
|
description: >
|
|
@@ -231,8 +245,8 @@ Read and execute the full orchestrator at \`${orchPath}\`.
|
|
|
231
245
|
**Context to pass:**
|
|
232
246
|
- \`.chati/session.yaml\` (session state — includes language)
|
|
233
247
|
- \`AGENTS.md\` (project context)
|
|
234
|
-
- \`
|
|
235
|
-
-
|
|
248
|
+
- \`artifacts/handoffs/\` (latest handoff)
|
|
249
|
+
- \`.chati.dev/config.yaml\` (version info)
|
|
236
250
|
|
|
237
251
|
**User input:** $ARGUMENTS
|
|
238
252
|
`;
|
|
@@ -240,12 +254,9 @@ Read and execute the full orchestrator at \`${orchPath}\`.
|
|
|
240
254
|
|
|
241
255
|
/**
|
|
242
256
|
* Generate Gemini CLI TOML command (.gemini/commands/chati.toml)
|
|
243
|
-
*
|
|
244
|
-
* Gemini CLI custom commands use TOML format with `description` and `prompt` fields.
|
|
245
|
-
* Supports {{args}} for user arguments and @{file} for file injection.
|
|
246
257
|
*/
|
|
247
258
|
export function generateGeminiRouter(options = {}) {
|
|
248
|
-
const orchPath = options.orchestratorPath || 'chati.dev/orchestrator/chati.md';
|
|
259
|
+
const orchPath = options.orchestratorPath || '.chati.dev/orchestrator/chati.md';
|
|
249
260
|
return `description = "Activate Chati.dev orchestrator"
|
|
250
261
|
prompt = """
|
|
251
262
|
CRITICAL — Language Override:
|
|
@@ -262,8 +273,8 @@ NEVER create or reference CLAUDE.md, CLAUDE.local.md, or .claude/ directories.
|
|
|
262
273
|
Context to load:
|
|
263
274
|
- .chati/session.yaml (session state — includes language)
|
|
264
275
|
- GEMINI.md (project context)
|
|
265
|
-
-
|
|
266
|
-
- chati.dev/config.yaml (version info)
|
|
276
|
+
- artifacts/handoffs/ (latest handoff)
|
|
277
|
+
- .chati.dev/config.yaml (version info)
|
|
267
278
|
|
|
268
279
|
User input: {{args}}
|
|
269
280
|
"""
|
|
@@ -276,15 +287,17 @@ User input: {{args}}
|
|
|
276
287
|
export function generateClaudeLocalMd() {
|
|
277
288
|
return `# Chati.dev Runtime State
|
|
278
289
|
|
|
290
|
+
<!-- chati-lock:start -->
|
|
279
291
|
## Session Lock
|
|
280
292
|
**Status: INACTIVE** — Type \`/chati\` to activate.
|
|
293
|
+
<!-- chati-lock:end -->
|
|
281
294
|
|
|
282
|
-
<!--
|
|
283
|
-
|
|
295
|
+
<!-- chati-state:start -->
|
|
284
296
|
## Current State
|
|
285
297
|
- **Agent**: None (ready to start)
|
|
286
298
|
- **Pipeline**: Pre-start
|
|
287
299
|
- **Mode**: interactive
|
|
300
|
+
<!-- chati-state:end -->
|
|
288
301
|
|
|
289
302
|
## Recent Decisions
|
|
290
303
|
_No decisions yet. Start with /chati._
|
|
@@ -301,15 +314,17 @@ _Auto-updated by Chati.dev orchestrator_
|
|
|
301
314
|
export function generateGeminiSessionLock() {
|
|
302
315
|
return `# Chati.dev Runtime State
|
|
303
316
|
|
|
317
|
+
<!-- chati-lock:start -->
|
|
304
318
|
## Session Lock
|
|
305
319
|
**Status: INACTIVE** — Type \`/chati\` to activate.
|
|
320
|
+
<!-- chati-lock:end -->
|
|
306
321
|
|
|
307
|
-
<!--
|
|
308
|
-
|
|
322
|
+
<!-- chati-state:start -->
|
|
309
323
|
## Current State
|
|
310
324
|
- **Agent**: None (ready to start)
|
|
311
325
|
- **Pipeline**: Pre-start
|
|
312
326
|
- **Mode**: interactive
|
|
327
|
+
<!-- chati-state:end -->
|
|
313
328
|
|
|
314
329
|
## Recent Decisions
|
|
315
330
|
_No decisions yet. Start with /chati._
|
|
@@ -326,15 +341,17 @@ _Auto-updated by Chati.dev orchestrator_
|
|
|
326
341
|
export function generateAgentsOverrideMd() {
|
|
327
342
|
return `# Chati.dev Runtime State
|
|
328
343
|
|
|
344
|
+
<!-- chati-lock:start -->
|
|
329
345
|
## Session Lock
|
|
330
346
|
**Status: INACTIVE** — Type \`$chati\` to activate.
|
|
347
|
+
<!-- chati-lock:end -->
|
|
331
348
|
|
|
332
|
-
<!--
|
|
333
|
-
|
|
349
|
+
<!-- chati-state:start -->
|
|
334
350
|
## Current State
|
|
335
351
|
- **Agent**: None (ready to start)
|
|
336
352
|
- **Pipeline**: Pre-start
|
|
337
353
|
- **Mode**: interactive
|
|
354
|
+
<!-- chati-state:end -->
|
|
338
355
|
|
|
339
356
|
## Recent Decisions
|
|
340
357
|
_No decisions yet. Start with $chati._
|
|
@@ -6,6 +6,11 @@ import { loadManifest } from './manifest.js';
|
|
|
6
6
|
import { ADAPTABLE_FILES } from '../config/framework-adapter.js';
|
|
7
7
|
import { validateSchema, CONFIG_SCHEMA } from '../utils/schema-validator.js';
|
|
8
8
|
|
|
9
|
+
function resolveFrameworkDir(targetDir) {
|
|
10
|
+
if (existsSync(join(targetDir, '.chati.dev'))) return '.chati.dev';
|
|
11
|
+
return 'chati.dev';
|
|
12
|
+
}
|
|
13
|
+
|
|
9
14
|
/**
|
|
10
15
|
* Validate chati.dev installation
|
|
11
16
|
* Checks all primary agents, constitution, session, schemas, etc.
|
|
@@ -47,7 +52,7 @@ export async function validateInstallation(targetDir) {
|
|
|
47
52
|
|
|
48
53
|
let agentCount = 0;
|
|
49
54
|
for (const file of agentFiles) {
|
|
50
|
-
const filePath = join(targetDir,
|
|
55
|
+
const filePath = join(targetDir, resolveFrameworkDir(targetDir), file);
|
|
51
56
|
if (existsSync(filePath)) {
|
|
52
57
|
const content = readFileSync(filePath, 'utf-8');
|
|
53
58
|
// Check that agent implements protocols (look for key protocol references)
|
|
@@ -63,11 +68,11 @@ export async function validateInstallation(targetDir) {
|
|
|
63
68
|
if (results.agents.pass) results.passed += 1;
|
|
64
69
|
|
|
65
70
|
// Check constitution
|
|
66
|
-
const constitutionPath = join(targetDir,
|
|
71
|
+
const constitutionPath = join(targetDir, resolveFrameworkDir(targetDir), 'constitution.md');
|
|
67
72
|
if (existsSync(constitutionPath)) {
|
|
68
73
|
const content = readFileSync(constitutionPath, 'utf-8');
|
|
69
74
|
const articleCount = (content.match(/^## Article/gm) || []).length;
|
|
70
|
-
results.constitution.pass = articleCount >=
|
|
75
|
+
results.constitution.pass = articleCount >= 22;
|
|
71
76
|
results.constitution.details.push({ articleCount });
|
|
72
77
|
}
|
|
73
78
|
results.total += 1;
|
|
@@ -86,7 +91,7 @@ export async function validateInstallation(targetDir) {
|
|
|
86
91
|
];
|
|
87
92
|
let schemaCount = 0;
|
|
88
93
|
for (const file of schemaFiles) {
|
|
89
|
-
if (existsSync(join(targetDir,
|
|
94
|
+
if (existsSync(join(targetDir, resolveFrameworkDir(targetDir), 'schemas', file))) {
|
|
90
95
|
schemaCount++;
|
|
91
96
|
}
|
|
92
97
|
}
|
|
@@ -102,7 +107,7 @@ export async function validateInstallation(targetDir) {
|
|
|
102
107
|
];
|
|
103
108
|
let workflowCount = 0;
|
|
104
109
|
for (const file of workflowFiles) {
|
|
105
|
-
if (existsSync(join(targetDir,
|
|
110
|
+
if (existsSync(join(targetDir, resolveFrameworkDir(targetDir), 'workflows', file))) {
|
|
106
111
|
workflowCount++;
|
|
107
112
|
}
|
|
108
113
|
}
|
|
@@ -118,7 +123,7 @@ export async function validateInstallation(targetDir) {
|
|
|
118
123
|
];
|
|
119
124
|
let templateCount = 0;
|
|
120
125
|
for (const file of templateFiles) {
|
|
121
|
-
if (existsSync(join(targetDir,
|
|
126
|
+
if (existsSync(join(targetDir, resolveFrameworkDir(targetDir), 'templates', file))) {
|
|
122
127
|
templateCount++;
|
|
123
128
|
}
|
|
124
129
|
}
|
|
@@ -137,7 +142,7 @@ export async function validateInstallation(targetDir) {
|
|
|
137
142
|
];
|
|
138
143
|
let intelCount = 0;
|
|
139
144
|
for (const file of intelligenceFiles) {
|
|
140
|
-
if (existsSync(join(targetDir,
|
|
145
|
+
if (existsSync(join(targetDir, resolveFrameworkDir(targetDir), file))) {
|
|
141
146
|
intelCount++;
|
|
142
147
|
}
|
|
143
148
|
}
|
|
@@ -147,7 +152,7 @@ export async function validateInstallation(targetDir) {
|
|
|
147
152
|
if (results.intelligence.pass) results.passed += 1;
|
|
148
153
|
|
|
149
154
|
// Check entity registry
|
|
150
|
-
const registryPath = join(targetDir,
|
|
155
|
+
const registryPath = join(targetDir, resolveFrameworkDir(targetDir), 'data', 'entity-registry.yaml');
|
|
151
156
|
results.registry.pass = existsSync(registryPath);
|
|
152
157
|
results.total += 1;
|
|
153
158
|
if (results.registry.pass) results.passed += 1;
|
|
@@ -164,7 +169,7 @@ export async function validateInstallation(targetDir) {
|
|
|
164
169
|
let contextCount = 0;
|
|
165
170
|
for (const file of contextFileNames) {
|
|
166
171
|
// Check source (chati.dev/context/) OR deployed (.claude/rules/chati/)
|
|
167
|
-
const inSource = existsSync(join(targetDir,
|
|
172
|
+
const inSource = existsSync(join(targetDir, resolveFrameworkDir(targetDir), 'context', file));
|
|
168
173
|
const inRules = existsSync(join(targetDir, '.claude', 'rules', 'chati', file));
|
|
169
174
|
if (inSource || inRules) {
|
|
170
175
|
contextCount++;
|
|
@@ -176,7 +181,7 @@ export async function validateInstallation(targetDir) {
|
|
|
176
181
|
if (results.context.pass) results.passed += 1;
|
|
177
182
|
|
|
178
183
|
// Check provider overlays (if multiple providers configured)
|
|
179
|
-
const configYamlPath = join(targetDir,
|
|
184
|
+
const configYamlPath = join(targetDir, resolveFrameworkDir(targetDir), 'config.yaml');
|
|
180
185
|
if (existsSync(configYamlPath)) {
|
|
181
186
|
try {
|
|
182
187
|
const configContent = readFileSync(configYamlPath, 'utf-8');
|
|
@@ -208,7 +213,7 @@ export async function validateInstallation(targetDir) {
|
|
|
208
213
|
const missingFiles = [];
|
|
209
214
|
|
|
210
215
|
for (const file of ADAPTABLE_FILES) {
|
|
211
|
-
const overlayPath = join(targetDir,
|
|
216
|
+
const overlayPath = join(targetDir, resolveFrameworkDir(targetDir), '.adapted', provider, file);
|
|
212
217
|
if (existsSync(overlayPath)) {
|
|
213
218
|
present++;
|
|
214
219
|
} else {
|
|
@@ -253,7 +258,7 @@ export async function validateInstallation(targetDir) {
|
|
|
253
258
|
const mismatches = [];
|
|
254
259
|
|
|
255
260
|
for (const [relPath, entry] of Object.entries(manifest.files)) {
|
|
256
|
-
const absPath = join(targetDir,
|
|
261
|
+
const absPath = join(targetDir, resolveFrameworkDir(targetDir), relPath);
|
|
257
262
|
if (existsSync(absPath)) {
|
|
258
263
|
const currentHash = hashFile(absPath);
|
|
259
264
|
if (currentHash === entry.hash) {
|
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import yaml from 'js-yaml';
|
|
4
|
+
import { resolveFrameworkDir } from '../utils/framework-dir.js';
|
|
5
|
+
import { flattenEntitiesMap } from '../utils/flatten-entities.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get the entity registry path for a given project.
|
|
9
|
+
* Supports both .chati.dev/ and chati.dev/ structures.
|
|
10
|
+
* @param {string} targetDir
|
|
11
|
+
* @returns {string} Relative path from project root
|
|
12
|
+
*/
|
|
13
|
+
function getRegistryPath(targetDir) {
|
|
14
|
+
return `${resolveFrameworkDir(targetDir)}/data/entity-registry.yaml`;
|
|
15
|
+
}
|
|
4
16
|
|
|
5
|
-
const REGISTRY_PATH = 'chati.dev/data/entity-registry.yaml';
|
|
6
17
|
|
|
7
18
|
/**
|
|
8
19
|
* Check registry integrity against filesystem
|
|
9
20
|
*/
|
|
10
21
|
export function checkRegistry(targetDir) {
|
|
11
|
-
const registryPath = join(targetDir,
|
|
22
|
+
const registryPath = join(targetDir, getRegistryPath(targetDir));
|
|
12
23
|
if (!existsSync(registryPath)) {
|
|
13
24
|
return { valid: false, error: 'Entity registry not found', missing: [], orphaned: [] };
|
|
14
25
|
}
|
|
@@ -22,7 +33,7 @@ export function checkRegistry(targetDir) {
|
|
|
22
33
|
const found = [];
|
|
23
34
|
|
|
24
35
|
// Check each registered entity exists on disk
|
|
25
|
-
const entities =
|
|
36
|
+
const entities = flattenEntitiesMap(registry.entities);
|
|
26
37
|
for (const entity of entities) {
|
|
27
38
|
const filePath = join(targetDir, entity.path);
|
|
28
39
|
if (existsSync(filePath)) {
|
|
@@ -44,7 +55,7 @@ export function checkRegistry(targetDir) {
|
|
|
44
55
|
* Get registry statistics
|
|
45
56
|
*/
|
|
46
57
|
export function getRegistryStats(targetDir) {
|
|
47
|
-
const registryPath = join(targetDir,
|
|
58
|
+
const registryPath = join(targetDir, getRegistryPath(targetDir));
|
|
48
59
|
if (!existsSync(registryPath)) {
|
|
49
60
|
return { exists: false, totalEntities: 0, byType: {} };
|
|
50
61
|
}
|
|
@@ -54,7 +65,7 @@ export function getRegistryStats(targetDir) {
|
|
|
54
65
|
return { exists: true, totalEntities: 0, byType: {}, error: 'Parse error' };
|
|
55
66
|
}
|
|
56
67
|
|
|
57
|
-
const entities =
|
|
68
|
+
const entities = flattenEntitiesMap(registry.entities);
|
|
58
69
|
const byType = {};
|
|
59
70
|
for (const entity of entities) {
|
|
60
71
|
const type = entity.type || 'unknown';
|
|
@@ -111,9 +122,10 @@ export function runHealthCheck(targetDir) {
|
|
|
111
122
|
'session.schema.json', 'config.schema.json', 'task.schema.json',
|
|
112
123
|
'context.schema.json', 'memory.schema.json',
|
|
113
124
|
];
|
|
125
|
+
const fwDir = resolveFrameworkDir(targetDir);
|
|
114
126
|
let validSchemas = 0;
|
|
115
127
|
for (const file of schemaFiles) {
|
|
116
|
-
const schemaPath = join(targetDir,
|
|
128
|
+
const schemaPath = join(targetDir, fwDir, 'schemas', file);
|
|
117
129
|
if (existsSync(schemaPath)) {
|
|
118
130
|
try {
|
|
119
131
|
JSON.parse(readFileSync(schemaPath, 'utf-8'));
|
|
@@ -127,12 +139,12 @@ export function runHealthCheck(targetDir) {
|
|
|
127
139
|
checks.schemas.details = `${validSchemas}/${schemaFiles.length} valid`;
|
|
128
140
|
|
|
129
141
|
// 3. Constitution check
|
|
130
|
-
const constitutionPath = join(targetDir,
|
|
142
|
+
const constitutionPath = join(targetDir, fwDir, 'constitution.md');
|
|
131
143
|
if (existsSync(constitutionPath)) {
|
|
132
144
|
const content = readFileSync(constitutionPath, 'utf-8');
|
|
133
145
|
const articleCount = (content.match(/^## Article/gm) || []).length;
|
|
134
|
-
checks.constitution.pass = articleCount >=
|
|
135
|
-
checks.constitution.details = `${articleCount}/
|
|
146
|
+
checks.constitution.pass = articleCount >= 22;
|
|
147
|
+
checks.constitution.details = `${articleCount}/23 articles`;
|
|
136
148
|
} else {
|
|
137
149
|
checks.constitution.details = 'Not found';
|
|
138
150
|
}
|
|
@@ -149,7 +161,7 @@ export function runHealthCheck(targetDir) {
|
|
|
149
161
|
];
|
|
150
162
|
let foundAgents = 0;
|
|
151
163
|
for (const p of agentPaths) {
|
|
152
|
-
if (existsSync(join(targetDir,
|
|
164
|
+
if (existsSync(join(targetDir, fwDir, p))) foundAgents++;
|
|
153
165
|
}
|
|
154
166
|
checks.agents.pass = foundAgents === 13;
|
|
155
167
|
checks.agents.details = `${foundAgents}/13 present`;
|
|
@@ -181,22 +193,3 @@ function loadRegistry(registryPath) {
|
|
|
181
193
|
}
|
|
182
194
|
}
|
|
183
195
|
|
|
184
|
-
/**
|
|
185
|
-
* Flatten nested entity categories into flat array
|
|
186
|
-
*/
|
|
187
|
-
function flattenEntities(entities) {
|
|
188
|
-
if (!entities) return [];
|
|
189
|
-
const flat = [];
|
|
190
|
-
|
|
191
|
-
for (const [, items] of Object.entries(entities)) {
|
|
192
|
-
if (typeof items === 'object' && items !== null) {
|
|
193
|
-
for (const [name, entity] of Object.entries(items)) {
|
|
194
|
-
if (entity && entity.path) {
|
|
195
|
-
flat.push({ name, ...entity });
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return flat;
|
|
202
|
-
}
|
|
@@ -6,11 +6,6 @@
|
|
|
6
6
|
* results, handoffs, and deviations.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* @deprecated Not currently imported by any production module.
|
|
11
|
-
* Retained for potential future integration. Review for removal or integration by v5.0.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
9
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
15
10
|
import { join, dirname } from 'path';
|
|
16
11
|
|
|
@@ -38,6 +33,8 @@ export const EventType = {
|
|
|
38
33
|
PROVIDER_SELECTED: 'provider_selected',
|
|
39
34
|
SESSION_STARTED: 'session_started',
|
|
40
35
|
SESSION_ENDED: 'session_ended',
|
|
36
|
+
CORRECTION_TRIGGERED: 'correction_triggered',
|
|
37
|
+
CORRECTION_ESCALATED: 'correction_escalated',
|
|
41
38
|
};
|
|
42
39
|
|
|
43
40
|
const TIMELINE_FILE = '.chati/timeline.json';
|
|
@@ -82,8 +79,16 @@ function saveTimeline(projectDir, timeline) {
|
|
|
82
79
|
*/
|
|
83
80
|
export function recordEvent(projectDir, type, agent, data = {}) {
|
|
84
81
|
const timeline = loadTimeline(projectDir);
|
|
85
|
-
_counter++;
|
|
86
82
|
|
|
83
|
+
// Dedup: skip if the last event of this type already targets the same agent.
|
|
84
|
+
// Prevents duplicate AGENT_ACTIVATED entries when handleAdvance chains into
|
|
85
|
+
// handleNext while the caller also invokes next separately.
|
|
86
|
+
const last = timeline[timeline.length - 1];
|
|
87
|
+
if (last && last.type === type && last.agent === agent) {
|
|
88
|
+
return last;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
_counter++;
|
|
87
92
|
const event = {
|
|
88
93
|
id: `evt-${Date.now()}-${_counter}`,
|
|
89
94
|
type,
|
package/src/license/commands.js
CHANGED
|
@@ -37,7 +37,7 @@ export async function runActivate(projectDir, keyArg) {
|
|
|
37
37
|
const plan = result.plan ?? 'trial';
|
|
38
38
|
const days = result.days_remaining ?? '?';
|
|
39
39
|
console.log(`\n✓ Activated! Plan: ${plan} — ${days} day(s) remaining.`);
|
|
40
|
-
console.log(` Key saved to chati
|
|
40
|
+
console.log(` Key saved to ~/.chati-dev/license.yaml`);
|
|
41
41
|
console.log(` Run /chati to start.\n`);
|
|
42
42
|
} catch (err) {
|
|
43
43
|
console.error(`\n✗ Activation failed: ${err.message}`);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Poll ~/.chati-dev/license.yaml until status=VALID.
|
|
3
|
+
*
|
|
4
|
+
* Part of the autonomous-completion workstream (ADR-AUTO-01): when a /chati
|
|
5
|
+
* session boots with no VALID license, the orchestrator tells the user to
|
|
6
|
+
* run `npx chati-dev activate --key=X` in another terminal, then calls
|
|
7
|
+
* this util to block until the file appears with VALID status.
|
|
8
|
+
*
|
|
9
|
+
* Design invariants:
|
|
10
|
+
* - Never echo the license key to chat (the whole reason we don't use
|
|
11
|
+
* chat-paste). We only check status.
|
|
12
|
+
* - Bounded wait: default 60s timeout, 3s poll interval.
|
|
13
|
+
* - Fast-fail on EXPIRED / INVALID / REVOKED — those need a NEW key,
|
|
14
|
+
* polling won't resolve them.
|
|
15
|
+
* - Pure function on time: cancellation via AbortSignal supported so
|
|
16
|
+
* callers (tests, interactive CLI) can stop the loop.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { existsSync, readFileSync } from 'fs';
|
|
20
|
+
import { join } from 'path';
|
|
21
|
+
|
|
22
|
+
export const DEFAULT_POLL_INTERVAL_MS = 3_000;
|
|
23
|
+
export const DEFAULT_TIMEOUT_MS = 60_000;
|
|
24
|
+
|
|
25
|
+
const TERMINAL_BAD_STATUSES = new Set(['EXPIRED', 'INVALID', 'REVOKED']);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Read ~/.chati-dev/license.yaml and extract the status field.
|
|
29
|
+
* Pure + synchronous — safe to call from poll loop.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} home
|
|
32
|
+
* @returns {{ present: boolean, status: string|null, reason: string|null, error?: string }}
|
|
33
|
+
*/
|
|
34
|
+
export function readLicenseStatus(home) {
|
|
35
|
+
const licensePath = join(home, '.chati-dev', 'license.yaml');
|
|
36
|
+
if (!existsSync(licensePath)) {
|
|
37
|
+
return { present: false, status: null, reason: null };
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const raw = readFileSync(licensePath, 'utf-8');
|
|
41
|
+
const statusMatch = raw.match(/^\s*status:\s*["']?(\w+)/m);
|
|
42
|
+
const reasonMatch = raw.match(/^\s*reason:\s*["']?([^"'\n]+)/m);
|
|
43
|
+
return {
|
|
44
|
+
present: true,
|
|
45
|
+
status: statusMatch ? statusMatch[1].toUpperCase() : null,
|
|
46
|
+
reason: reasonMatch ? reasonMatch[1].trim() : null,
|
|
47
|
+
};
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return { present: true, status: null, reason: null, error: err.message };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Poll for a VALID license. Resolves when:
|
|
55
|
+
* - Status becomes VALID → { valid: true, status: 'VALID' }
|
|
56
|
+
* - Status is terminal-bad (EXPIRED|INVALID|REVOKED) → { valid: false, status, reason }
|
|
57
|
+
* - Timeout elapses → { valid: false, status: 'TIMEOUT', reason: 'No valid license detected within N seconds' }
|
|
58
|
+
* - Caller aborts → { valid: false, status: 'ABORTED' }
|
|
59
|
+
*
|
|
60
|
+
* @param {object} [opts]
|
|
61
|
+
* @param {string} [opts.home] Override HOME (default: process.env.HOME).
|
|
62
|
+
* @param {number} [opts.intervalMs] Poll period (default 3000).
|
|
63
|
+
* @param {number} [opts.timeoutMs] Total wait (default 60_000).
|
|
64
|
+
* @param {AbortSignal} [opts.signal] Cancel the loop externally.
|
|
65
|
+
* @param {(ms:number)=>Promise<void>} [opts.sleep] Injectable sleep (tests).
|
|
66
|
+
* @returns {Promise<{ valid: boolean, status: string|null, reason: string|null, waitedMs: number }>}
|
|
67
|
+
*/
|
|
68
|
+
export async function waitForLicense(opts = {}) {
|
|
69
|
+
const home = opts.home || process.env.HOME || '';
|
|
70
|
+
const intervalMs = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
71
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
72
|
+
const signal = opts.signal;
|
|
73
|
+
const sleep = opts.sleep || ((ms) => new Promise(r => setTimeout(r, ms)));
|
|
74
|
+
|
|
75
|
+
const startedAt = Date.now();
|
|
76
|
+
|
|
77
|
+
for (;;) {
|
|
78
|
+
if (signal?.aborted) {
|
|
79
|
+
return { valid: false, status: 'ABORTED', reason: 'Poll aborted by caller', waitedMs: Date.now() - startedAt };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const snap = readLicenseStatus(home);
|
|
83
|
+
if (snap.status === 'VALID') {
|
|
84
|
+
return { valid: true, status: 'VALID', reason: null, waitedMs: Date.now() - startedAt };
|
|
85
|
+
}
|
|
86
|
+
if (snap.status && TERMINAL_BAD_STATUSES.has(snap.status)) {
|
|
87
|
+
return { valid: false, status: snap.status, reason: snap.reason || `License ${snap.status}`, waitedMs: Date.now() - startedAt };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const elapsed = Date.now() - startedAt;
|
|
91
|
+
if (elapsed >= timeoutMs) {
|
|
92
|
+
return {
|
|
93
|
+
valid: false,
|
|
94
|
+
status: 'TIMEOUT',
|
|
95
|
+
reason: `No VALID license detected at ~/.chati-dev/license.yaml within ${Math.round(timeoutMs / 1000)}s.`,
|
|
96
|
+
waitedMs: elapsed,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await sleep(Math.min(intervalMs, timeoutMs - elapsed));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -167,6 +167,87 @@ export function writeAgentMemory(projectDir, agentName, entry) {
|
|
|
167
167
|
return { saved: true };
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Write a minimal MEMORY.md for an agent that has none, derived from a
|
|
172
|
+
* session digest (memory-bootstrap-v1, F-A5-006).
|
|
173
|
+
*
|
|
174
|
+
* Rules:
|
|
175
|
+
* - Only writes when the target MEMORY.md does not exist or is empty
|
|
176
|
+
* (idempotent; second call on the same agent is a no-op).
|
|
177
|
+
* - Entries are tagged `source: bootstrap` and confidence `low` so
|
|
178
|
+
* dream.js can safely re-classify / deduplicate on a later full run.
|
|
179
|
+
* - The produced file carries a `<!-- bootstrap-write: true -->` HTML
|
|
180
|
+
* comment header for auditability.
|
|
181
|
+
*
|
|
182
|
+
* @param {string} projectDir
|
|
183
|
+
* @param {string} agentName
|
|
184
|
+
* @param {object} digest - Session digest shape from session-digest.js
|
|
185
|
+
* @returns {{ written: boolean, reason?: string, path?: string }}
|
|
186
|
+
*/
|
|
187
|
+
export function writeBootstrapMemory(projectDir, agentName, digest) {
|
|
188
|
+
if (!agentName || agentName === 'none') {
|
|
189
|
+
return { written: false, reason: 'no-agent' };
|
|
190
|
+
}
|
|
191
|
+
const memoryPath = getAgentMemoryPath(projectDir, agentName);
|
|
192
|
+
|
|
193
|
+
// Idempotency: skip if a non-empty MEMORY.md already exists.
|
|
194
|
+
if (existsSync(memoryPath)) {
|
|
195
|
+
try {
|
|
196
|
+
const existing = readFileSync(memoryPath, 'utf-8').trim();
|
|
197
|
+
if (existing.length > 0) {
|
|
198
|
+
return { written: false, reason: 'already-populated' };
|
|
199
|
+
}
|
|
200
|
+
} catch { /* fall through to write */ }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const dir = dirname(memoryPath);
|
|
204
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
205
|
+
|
|
206
|
+
const header = `<!-- bootstrap-write: true -->\n<!-- source: session-digest bootstrap (memory-bootstrap-v1) -->\n`;
|
|
207
|
+
const entries = [];
|
|
208
|
+
|
|
209
|
+
// Seed Episodic with the current pipeline context.
|
|
210
|
+
if (digest?.mode && digest?.pipelinePosition) {
|
|
211
|
+
entries.push({
|
|
212
|
+
category: 'Episodic',
|
|
213
|
+
content: `Resumed at mode=${digest.mode}, pipeline_position=${digest.pipelinePosition}, workflow=${digest.workflow || 'unknown'}.`,
|
|
214
|
+
confidence: 'low',
|
|
215
|
+
tags: ['source:bootstrap'],
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Seed Semantic from active_task (if digest has one).
|
|
220
|
+
if (digest?.active_task) {
|
|
221
|
+
entries.push({
|
|
222
|
+
category: 'Semantic',
|
|
223
|
+
content: `Active task on resume: ${String(digest.active_task).slice(0, 500)}`,
|
|
224
|
+
confidence: 'low',
|
|
225
|
+
tags: ['source:bootstrap'],
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Seed Procedural from gotchas_found (capped).
|
|
230
|
+
if (Array.isArray(digest?.gotchas_found) && digest.gotchas_found.length > 0) {
|
|
231
|
+
for (const g of digest.gotchas_found.slice(0, 5)) {
|
|
232
|
+
if (!g?.message) continue;
|
|
233
|
+
entries.push({
|
|
234
|
+
category: 'Procedural',
|
|
235
|
+
content: `Known gotcha [${g.severity || 'info'}] ${g.id || ''}: ${String(g.message).slice(0, 200)}`,
|
|
236
|
+
confidence: 'low',
|
|
237
|
+
tags: ['source:bootstrap', `severity:${g.severity || 'info'}`],
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (entries.length === 0) {
|
|
243
|
+
return { written: false, reason: 'no-content' };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const body = formatMemoryMarkdown(entries);
|
|
247
|
+
writeFileSync(memoryPath, header + '\n' + body, 'utf-8');
|
|
248
|
+
return { written: true, path: memoryPath };
|
|
249
|
+
}
|
|
250
|
+
|
|
170
251
|
/**
|
|
171
252
|
* Search across all agent memories.
|
|
172
253
|
* @param {string} projectDir - Project directory
|