chati-dev 4.2.1 → 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 +8 -7
- package/framework/constitution.md +127 -43
- 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 +91 -36
- package/framework/hooks/license-guard.js +129 -190
- package/framework/hooks/mode-governance.js +68 -16
- package/framework/hooks/model-governance.js +19 -8
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/post-dev.js +207 -0
- package/framework/hooks/prism-engine.js +288 -105
- package/framework/hooks/read-protection.js +37 -9
- package/framework/hooks/reasoning-escalator.js +371 -0
- package/framework/hooks/reference-trigger.js +117 -0
- package/framework/hooks/session-digest.js +94 -20
- package/framework/hooks/settings.json +32 -1
- package/framework/hooks/style-guard.js +52 -13
- package/framework/hooks/team-quality-gate.js +58 -25
- package/framework/hooks/undercover-guard.js +34 -13
- 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 +318 -115
- 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 +226 -0
- package/src/config/context-file-generator.js +41 -21
- package/src/config/framework-adapter.js +1 -0
- package/src/config/gemini-hooks-generator.js +76 -6
- 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 +557 -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 +52 -31
- 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 +1908 -80
- 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 -367
- 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
|
|
|
@@ -134,9 +148,13 @@ export function generateConfigYaml(config) {
|
|
|
134
148
|
model_fallback: true,
|
|
135
149
|
frustration_detection: true,
|
|
136
150
|
bash_security_checks: true,
|
|
137
|
-
// Agent Teams (v4.2.
|
|
138
|
-
|
|
139
|
-
|
|
151
|
+
// Agent Teams (Article XXI) — default ON in v4.2.2.
|
|
152
|
+
// Only effective when provider === 'claude' (gated in cli.js
|
|
153
|
+
// isAgentTeamsEnabled). Gemini and Codex always fall back to sequential
|
|
154
|
+
// pipeline silently. See plan: was previously false for "safe rollout"
|
|
155
|
+
// but the v4.2.0 launch never actually shipped, so this is a fresh start.
|
|
156
|
+
agent_teams: true,
|
|
157
|
+
team_planning_size: 4,
|
|
140
158
|
team_build_size: 2,
|
|
141
159
|
team_echo_threshold: 0.92,
|
|
142
160
|
team_correction_cycles_max: 2,
|
|
@@ -189,7 +207,7 @@ Type \`/chati\` to start.
|
|
|
189
207
|
* Also reads AGENTS.md for project context (auto-generated by context-file-generator).
|
|
190
208
|
*/
|
|
191
209
|
export function generateCodexSkill(options = {}) {
|
|
192
|
-
const orchPath = options.orchestratorPath || 'chati.dev/orchestrator/chati.md';
|
|
210
|
+
const orchPath = options.orchestratorPath || '.chati.dev/orchestrator/chati.md';
|
|
193
211
|
return `---
|
|
194
212
|
name: chati
|
|
195
213
|
description: >
|
|
@@ -227,8 +245,8 @@ Read and execute the full orchestrator at \`${orchPath}\`.
|
|
|
227
245
|
**Context to pass:**
|
|
228
246
|
- \`.chati/session.yaml\` (session state — includes language)
|
|
229
247
|
- \`AGENTS.md\` (project context)
|
|
230
|
-
- \`
|
|
231
|
-
-
|
|
248
|
+
- \`artifacts/handoffs/\` (latest handoff)
|
|
249
|
+
- \`.chati.dev/config.yaml\` (version info)
|
|
232
250
|
|
|
233
251
|
**User input:** $ARGUMENTS
|
|
234
252
|
`;
|
|
@@ -236,12 +254,9 @@ Read and execute the full orchestrator at \`${orchPath}\`.
|
|
|
236
254
|
|
|
237
255
|
/**
|
|
238
256
|
* Generate Gemini CLI TOML command (.gemini/commands/chati.toml)
|
|
239
|
-
*
|
|
240
|
-
* Gemini CLI custom commands use TOML format with `description` and `prompt` fields.
|
|
241
|
-
* Supports {{args}} for user arguments and @{file} for file injection.
|
|
242
257
|
*/
|
|
243
258
|
export function generateGeminiRouter(options = {}) {
|
|
244
|
-
const orchPath = options.orchestratorPath || 'chati.dev/orchestrator/chati.md';
|
|
259
|
+
const orchPath = options.orchestratorPath || '.chati.dev/orchestrator/chati.md';
|
|
245
260
|
return `description = "Activate Chati.dev orchestrator"
|
|
246
261
|
prompt = """
|
|
247
262
|
CRITICAL — Language Override:
|
|
@@ -258,8 +273,8 @@ NEVER create or reference CLAUDE.md, CLAUDE.local.md, or .claude/ directories.
|
|
|
258
273
|
Context to load:
|
|
259
274
|
- .chati/session.yaml (session state — includes language)
|
|
260
275
|
- GEMINI.md (project context)
|
|
261
|
-
-
|
|
262
|
-
- chati.dev/config.yaml (version info)
|
|
276
|
+
- artifacts/handoffs/ (latest handoff)
|
|
277
|
+
- .chati.dev/config.yaml (version info)
|
|
263
278
|
|
|
264
279
|
User input: {{args}}
|
|
265
280
|
"""
|
|
@@ -272,15 +287,17 @@ User input: {{args}}
|
|
|
272
287
|
export function generateClaudeLocalMd() {
|
|
273
288
|
return `# Chati.dev Runtime State
|
|
274
289
|
|
|
290
|
+
<!-- chati-lock:start -->
|
|
275
291
|
## Session Lock
|
|
276
292
|
**Status: INACTIVE** — Type \`/chati\` to activate.
|
|
293
|
+
<!-- chati-lock:end -->
|
|
277
294
|
|
|
278
|
-
<!--
|
|
279
|
-
|
|
295
|
+
<!-- chati-state:start -->
|
|
280
296
|
## Current State
|
|
281
297
|
- **Agent**: None (ready to start)
|
|
282
298
|
- **Pipeline**: Pre-start
|
|
283
299
|
- **Mode**: interactive
|
|
300
|
+
<!-- chati-state:end -->
|
|
284
301
|
|
|
285
302
|
## Recent Decisions
|
|
286
303
|
_No decisions yet. Start with /chati._
|
|
@@ -297,15 +314,17 @@ _Auto-updated by Chati.dev orchestrator_
|
|
|
297
314
|
export function generateGeminiSessionLock() {
|
|
298
315
|
return `# Chati.dev Runtime State
|
|
299
316
|
|
|
317
|
+
<!-- chati-lock:start -->
|
|
300
318
|
## Session Lock
|
|
301
319
|
**Status: INACTIVE** — Type \`/chati\` to activate.
|
|
320
|
+
<!-- chati-lock:end -->
|
|
302
321
|
|
|
303
|
-
<!--
|
|
304
|
-
|
|
322
|
+
<!-- chati-state:start -->
|
|
305
323
|
## Current State
|
|
306
324
|
- **Agent**: None (ready to start)
|
|
307
325
|
- **Pipeline**: Pre-start
|
|
308
326
|
- **Mode**: interactive
|
|
327
|
+
<!-- chati-state:end -->
|
|
309
328
|
|
|
310
329
|
## Recent Decisions
|
|
311
330
|
_No decisions yet. Start with /chati._
|
|
@@ -322,15 +341,17 @@ _Auto-updated by Chati.dev orchestrator_
|
|
|
322
341
|
export function generateAgentsOverrideMd() {
|
|
323
342
|
return `# Chati.dev Runtime State
|
|
324
343
|
|
|
344
|
+
<!-- chati-lock:start -->
|
|
325
345
|
## Session Lock
|
|
326
346
|
**Status: INACTIVE** — Type \`$chati\` to activate.
|
|
347
|
+
<!-- chati-lock:end -->
|
|
327
348
|
|
|
328
|
-
<!--
|
|
329
|
-
|
|
349
|
+
<!-- chati-state:start -->
|
|
330
350
|
## Current State
|
|
331
351
|
- **Agent**: None (ready to start)
|
|
332
352
|
- **Pipeline**: Pre-start
|
|
333
353
|
- **Mode**: interactive
|
|
354
|
+
<!-- chati-state:end -->
|
|
334
355
|
|
|
335
356
|
## Recent Decisions
|
|
336
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
|