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
|
@@ -10,13 +10,21 @@
|
|
|
10
10
|
import { existsSync, readFileSync } from 'fs';
|
|
11
11
|
import { join } from 'path';
|
|
12
12
|
import yaml from 'js-yaml';
|
|
13
|
+
import { resolveFrameworkDir } from '../utils/framework-dir.js';
|
|
13
14
|
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
15
16
|
// Constants
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
17
18
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Get the customize directory for a given project.
|
|
21
|
+
* Supports both .chati.dev/ and chati.dev/ structures.
|
|
22
|
+
* @param {string} projectDir
|
|
23
|
+
* @returns {string} Relative path from project root
|
|
24
|
+
*/
|
|
25
|
+
function getCustomizeDir(projectDir) {
|
|
26
|
+
return `${resolveFrameworkDir(projectDir)}/customize`;
|
|
27
|
+
}
|
|
20
28
|
|
|
21
29
|
/** Valid override fields that customizations can set. */
|
|
22
30
|
export const VALID_OVERRIDE_FIELDS = [
|
|
@@ -57,7 +65,7 @@ export function loadCustomization(projectDir, agentName) {
|
|
|
57
65
|
return { found: false, overrides: {} };
|
|
58
66
|
}
|
|
59
67
|
|
|
60
|
-
const customizePath = join(projectDir,
|
|
68
|
+
const customizePath = join(projectDir, getCustomizeDir(projectDir), `${agentName}.yaml`);
|
|
61
69
|
|
|
62
70
|
if (!existsSync(customizePath)) {
|
|
63
71
|
return { found: false, overrides: {} };
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Claude Code settings.json generator.
|
|
3
3
|
*
|
|
4
|
-
* Produces .claude/settings.json content that wires the
|
|
4
|
+
* Produces .claude/settings.json content that wires the 14 chati.dev hooks
|
|
5
5
|
* into Claude Code's hook system and applies a baseline permission policy.
|
|
6
6
|
*
|
|
7
7
|
* Background: prior to v4.2.2, the installer wrote hooks to
|
|
8
|
-
* chati.dev/hooks/settings.json — but Claude Code reads .claude/settings.json,
|
|
8
|
+
* .chati.dev/hooks/settings.json — but Claude Code reads .claude/settings.json,
|
|
9
9
|
* not the framework's bundled file. As a result, every hook was dormant for
|
|
10
10
|
* every Claude Code user since the project started. This generator is the fix:
|
|
11
|
-
* it produces the file Claude Code actually loads, registers all
|
|
11
|
+
* it produces the file Claude Code actually loads, registers all 14 hooks at
|
|
12
12
|
* the canonical events, and applies a generic permission policy modeled on
|
|
13
13
|
* production Claude Code setups.
|
|
14
14
|
*
|
|
@@ -20,16 +20,23 @@
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Hook registration map. Each entry is one hook script and the events it
|
|
23
|
-
* should fire on. Source of truth lives in chati.dev/hooks/.
|
|
23
|
+
* should fire on. Source of truth lives in .chati.dev/hooks/.
|
|
24
24
|
*/
|
|
25
25
|
const HOOK_REGISTRY = [
|
|
26
26
|
// UserPromptSubmit — fires on every user message turn.
|
|
27
|
-
// license-guard
|
|
28
|
-
|
|
27
|
+
// NOTE: license-guard is NOT here. UserPromptSubmit blocks are SILENT (prompt
|
|
28
|
+
// is dropped, no message shown to user). License enforcement is done via:
|
|
29
|
+
// 1. Slash command STEP 0 (visible — Claude runs checkLicense() via Bash)
|
|
30
|
+
// 2. PreToolUse catch-all below (backup — Claude sees deny reason, reports it)
|
|
29
31
|
{ name: 'prism-engine', event: 'UserPromptSubmit', matcher: '.*' },
|
|
30
32
|
{ name: 'model-governance', event: 'UserPromptSubmit', matcher: '.*' },
|
|
31
33
|
|
|
32
34
|
// PreToolUse — fires before each tool call. Matcher targets specific tools.
|
|
35
|
+
// license-guard is first on catch-all ".*" — blocks ALL tool calls when
|
|
36
|
+
// license is expired. Claude sees the deny reason and can report it to user.
|
|
37
|
+
// This is the backup defense: even if someone bypasses the slash command,
|
|
38
|
+
// no tool call succeeds with an expired license.
|
|
39
|
+
{ name: 'license-guard', event: 'PreToolUse', matcher: '.*' },
|
|
33
40
|
{ name: 'read-protection', event: 'PreToolUse', matcher: 'Read' },
|
|
34
41
|
{ name: 'constitution-guard',event: 'PreToolUse', matcher: 'Bash|Write|Edit' },
|
|
35
42
|
{ name: 'mode-governance', event: 'PreToolUse', matcher: 'Write|Edit' },
|
|
@@ -37,6 +44,16 @@ const HOOK_REGISTRY = [
|
|
|
37
44
|
{ name: 'undercover-guard', event: 'PreToolUse', matcher: 'Write|Edit|Bash' },
|
|
38
45
|
{ name: 'team-quality-gate', event: 'PreToolUse', matcher: 'Write|Edit' },
|
|
39
46
|
|
|
47
|
+
// PostToolUse — fires after each tool call. Used to auto-advance the pipeline
|
|
48
|
+
// when an agent finishes writing its handoff. Hook inspects the written path
|
|
49
|
+
// and triggers `chati-router.js advance` if it matches an agent handoff file.
|
|
50
|
+
{ name: 'advance-trigger', event: 'PostToolUse', matcher: 'Write' },
|
|
51
|
+
{ name: 'reference-trigger', event: 'PostToolUse', matcher: 'Write' },
|
|
52
|
+
{ name: 'brief-validator', event: 'PostToolUse', matcher: 'Write' },
|
|
53
|
+
// Autonomous-completion v1 (ADR-AUTO-06): spawns visual-qa.js on
|
|
54
|
+
// dev-handoff write so the next /chati turn already has report.json.
|
|
55
|
+
{ name: 'post-dev', event: 'PostToolUse', matcher: 'Write' },
|
|
56
|
+
|
|
40
57
|
// PreCompact — fires before context compaction (observational).
|
|
41
58
|
{ name: 'session-digest', event: 'PreCompact', matcher: '' },
|
|
42
59
|
];
|
|
@@ -64,6 +81,9 @@ const PERMISSIONS_ALLOW = [
|
|
|
64
81
|
'EnterPlanMode',
|
|
65
82
|
'ExitPlanMode',
|
|
66
83
|
'KillShell',
|
|
84
|
+
'Bash(npx playwright:*)',
|
|
85
|
+
'Bash(node .chati.dev/scripts/*)',
|
|
86
|
+
'Bash(node chati.dev/scripts/*)',
|
|
67
87
|
];
|
|
68
88
|
|
|
69
89
|
/**
|
|
@@ -164,7 +184,7 @@ function buildHooksObject() {
|
|
|
164
184
|
}
|
|
165
185
|
matcherGroups.get(entry.matcher).push({
|
|
166
186
|
type: 'command',
|
|
167
|
-
command: `node chati.dev/hooks/${entry.name}.js`,
|
|
187
|
+
command: `node .chati.dev/hooks/${entry.name}.js`,
|
|
168
188
|
});
|
|
169
189
|
}
|
|
170
190
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
14
14
|
import { join } from 'path';
|
|
15
15
|
import { getEnabledNonClaudeProviders } from '../utils/config-parser.js';
|
|
16
|
+
import { resolveFrameworkDir } from '../utils/framework-dir.js';
|
|
16
17
|
|
|
17
18
|
// ---------------------------------------------------------------------------
|
|
18
19
|
// Replacement Maps
|
|
@@ -48,23 +49,34 @@ const CODEX_STRIP_PATTERNS = [
|
|
|
48
49
|
];
|
|
49
50
|
|
|
50
51
|
/**
|
|
51
|
-
*
|
|
52
|
-
* Note: Codex has no commands/ or rules/ directories — references point to
|
|
52
|
+
* Build text replacements for Codex CLI context file.
|
|
53
|
+
* Note: Codex has no commands/ or rules/ directories — references point to the framework dir.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} [frameworkDir='chati.dev'] - Resolved framework directory name (e.g. 'chati.dev' or '.chati.dev')
|
|
56
|
+
* @returns {Array<[string, string]>} Replacement pairs
|
|
53
57
|
*/
|
|
54
|
-
|
|
55
|
-
[
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
]
|
|
58
|
+
function buildCodexReplacements(frameworkDir = 'chati.dev') {
|
|
59
|
+
return [
|
|
60
|
+
['Claude Code', 'Codex CLI'],
|
|
61
|
+
['claude code', 'Codex CLI'],
|
|
62
|
+
['CLAUDE.md', 'AGENTS.md'],
|
|
63
|
+
['Claude.md', 'AGENTS.md'],
|
|
64
|
+
['.claude/commands/', `${frameworkDir}/orchestrator/`],
|
|
65
|
+
['.claude/rules/chati/', `${frameworkDir}/context/`],
|
|
66
|
+
['.claude/rules/', `${frameworkDir}/context/`],
|
|
67
|
+
['.claude/mcp.json', '.codex/config.toml'],
|
|
68
|
+
['claude --print', 'codex exec'],
|
|
69
|
+
['claude -p', 'codex exec'],
|
|
70
|
+
['CLAUDE.local.md', 'AGENTS.override.md'],
|
|
71
|
+
['/chati', '$chati'],
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Text replacements for Codex CLI context file (legacy constant using default 'chati.dev/' prefix).
|
|
77
|
+
* @deprecated Use buildCodexReplacements(frameworkDir) for correct path resolution.
|
|
78
|
+
*/
|
|
79
|
+
const CODEX_REPLACEMENTS = buildCodexReplacements();
|
|
68
80
|
|
|
69
81
|
// ---------------------------------------------------------------------------
|
|
70
82
|
// Generators
|
|
@@ -122,10 +134,12 @@ export function generateGeminiMd(content) {
|
|
|
122
134
|
*
|
|
123
135
|
* @param {string} content - Raw CLAUDE.md content
|
|
124
136
|
* @param {object} [contextFiles] - Optional inline context { root, governance, protocols, quality }
|
|
137
|
+
* @param {string} [frameworkDir='chati.dev'] - Resolved framework directory name
|
|
125
138
|
* @returns {string} Adapted content for AGENTS.md
|
|
126
139
|
*/
|
|
127
|
-
export function generateAgentsMd(content, contextFiles = null) {
|
|
140
|
+
export function generateAgentsMd(content, contextFiles = null, frameworkDir = 'chati.dev') {
|
|
128
141
|
let result = content;
|
|
142
|
+
const replacements = buildCodexReplacements(frameworkDir);
|
|
129
143
|
|
|
130
144
|
// Strip hook-related sections (Codex has no hooks support)
|
|
131
145
|
for (const pattern of CODEX_STRIP_PATTERNS) {
|
|
@@ -133,7 +147,7 @@ export function generateAgentsMd(content, contextFiles = null) {
|
|
|
133
147
|
}
|
|
134
148
|
|
|
135
149
|
// Apply text replacements
|
|
136
|
-
for (const [search, replace] of
|
|
150
|
+
for (const [search, replace] of replacements) {
|
|
137
151
|
result = result.replaceAll(search, replace);
|
|
138
152
|
}
|
|
139
153
|
|
|
@@ -155,7 +169,7 @@ export function generateAgentsMd(content, contextFiles = null) {
|
|
|
155
169
|
if (fileContent) {
|
|
156
170
|
// Adapt context for Codex (replace Claude-specific refs)
|
|
157
171
|
let adapted = fileContent;
|
|
158
|
-
for (const [search, replace] of
|
|
172
|
+
for (const [search, replace] of replacements) {
|
|
159
173
|
adapted = adapted.replaceAll(search, replace);
|
|
160
174
|
}
|
|
161
175
|
sections.push(adapted.trim());
|
|
@@ -213,6 +227,9 @@ export function generateContextFiles(projectDir, baseContent = null) {
|
|
|
213
227
|
// Read context files for inline injection (Codex)
|
|
214
228
|
const contextFiles = readContextFilesFromDisk(projectDir);
|
|
215
229
|
|
|
230
|
+
// Resolve framework directory for dynamic path generation
|
|
231
|
+
const fwDir = resolveFrameworkDir(projectDir);
|
|
232
|
+
|
|
216
233
|
// Provider-to-generator mapping
|
|
217
234
|
const generators = {
|
|
218
235
|
gemini: {
|
|
@@ -221,7 +238,7 @@ export function generateContextFiles(projectDir, baseContent = null) {
|
|
|
221
238
|
},
|
|
222
239
|
codex: {
|
|
223
240
|
filename: 'AGENTS.md',
|
|
224
|
-
generate: (content) => generateAgentsMd(content, contextFiles),
|
|
241
|
+
generate: (content) => generateAgentsMd(content, contextFiles, fwDir),
|
|
225
242
|
},
|
|
226
243
|
};
|
|
227
244
|
|
|
@@ -247,7 +264,10 @@ export function generateContextFiles(projectDir, baseContent = null) {
|
|
|
247
264
|
* @returns {object|null} { root, governance, protocols, quality } or null
|
|
248
265
|
*/
|
|
249
266
|
function readContextFilesFromDisk(projectDir) {
|
|
250
|
-
|
|
267
|
+
// Support both new (.chati.dev/) and legacy (chati.dev/) structures
|
|
268
|
+
const newDir = join(projectDir, '.chati.dev', 'context');
|
|
269
|
+
const legacyDir = join(projectDir, 'chati.dev', 'context');
|
|
270
|
+
const contextDir = existsSync(newDir) ? newDir : legacyDir;
|
|
251
271
|
if (!existsSync(contextDir)) return null;
|
|
252
272
|
|
|
253
273
|
const files = {};
|
|
@@ -31,6 +31,7 @@ export const ADAPTABLE_FILES = new Set([
|
|
|
31
31
|
'agents/plan/tasks.md',
|
|
32
32
|
'agents/quality/qa-planning.md',
|
|
33
33
|
'agents/quality/qa-implementation.md',
|
|
34
|
+
'agents/quality/qa-visual.md',
|
|
34
35
|
'agents/build/dev.md',
|
|
35
36
|
'agents/deploy/devops.md',
|
|
36
37
|
'context/governance.md',
|
|
@@ -34,6 +34,7 @@ export const HOOK_MAP = {
|
|
|
34
34
|
'session-digest': { event: 'PreCompress', description: 'Save session state before context compression' },
|
|
35
35
|
'undercover-guard': { event: 'BeforeTool', description: 'Advisory: sanitize internal framework references from deliverables' },
|
|
36
36
|
'style-guard': { event: 'BeforeTool', description: 'Advisory: enforce em-dash and emoji standards' },
|
|
37
|
+
'team-quality-gate': { event: 'BeforeTool', description: 'Validate team task completion and quality scores before handoff (Article XXI)' },
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
// ---------------------------------------------------------------------------
|
|
@@ -57,6 +58,16 @@ import { fileURLToPath } from 'url';
|
|
|
57
58
|
const __filename = fileURLToPath(import.meta.url);
|
|
58
59
|
const __dirname = join(__filename, '..');
|
|
59
60
|
const projectRoot = join(__dirname, '..', '..');
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolve framework directory (supports .chati.dev/ and legacy chati.dev/).
|
|
64
|
+
* @param {string} cwd - Project root directory
|
|
65
|
+
*/
|
|
66
|
+
function resolveFrameworkDir(cwd) {
|
|
67
|
+
const newPath = join(cwd, '.chati.dev');
|
|
68
|
+
if (existsSync(newPath)) return '.chati.dev';
|
|
69
|
+
return 'chati.dev';
|
|
70
|
+
}
|
|
60
71
|
`;
|
|
61
72
|
|
|
62
73
|
/**
|
|
@@ -86,7 +97,7 @@ async function main() {
|
|
|
86
97
|
}
|
|
87
98
|
|
|
88
99
|
// Delegate to shared PRISM engine logic
|
|
89
|
-
const hookPath = join(cwd,
|
|
100
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'prism-engine.js');
|
|
90
101
|
if (existsSync(hookPath)) {
|
|
91
102
|
const mod = await import(hookPath);
|
|
92
103
|
if (typeof mod.buildPrismContext === 'function') {
|
|
@@ -141,7 +152,7 @@ async function main() {
|
|
|
141
152
|
|
|
142
153
|
if (agent) {
|
|
143
154
|
// Delegate to shared model governance logic
|
|
144
|
-
const hookPath = join(cwd,
|
|
155
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'model-governance.js');
|
|
145
156
|
if (existsSync(hookPath)) {
|
|
146
157
|
const mod = await import(hookPath);
|
|
147
158
|
if (typeof mod.getRecommendedModel === 'function') {
|
|
@@ -175,8 +186,9 @@ function generateModeGovernance() {
|
|
|
175
186
|
* Blocks write operations outside current mode scope.
|
|
176
187
|
* Constitution Article XI enforcement.
|
|
177
188
|
*/
|
|
189
|
+
const fwDir = existsSync(join(process.cwd(), '.chati.dev')) ? '.chati.dev' : 'chati.dev';
|
|
178
190
|
const MODE_SCOPES = {
|
|
179
|
-
planning: { allowed: ['
|
|
191
|
+
planning: { allowed: [fwDir + '/', '.chati/', 'artifacts/'] },
|
|
180
192
|
build: { allowed: ['*'] },
|
|
181
193
|
deploy: { allowed: ['*'] },
|
|
182
194
|
};
|
|
@@ -447,7 +459,7 @@ async function main() {
|
|
|
447
459
|
const cwd = event.cwd || process.cwd();
|
|
448
460
|
|
|
449
461
|
// Delegate to shared session digest logic
|
|
450
|
-
const hookPath = join(cwd,
|
|
462
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'session-digest.js');
|
|
451
463
|
if (existsSync(hookPath)) {
|
|
452
464
|
const mod = await import(hookPath);
|
|
453
465
|
if (typeof mod.saveDigest === 'function') {
|
|
@@ -495,7 +507,7 @@ async function main() {
|
|
|
495
507
|
}
|
|
496
508
|
|
|
497
509
|
// Delegate to shared undercover guard logic
|
|
498
|
-
const hookPath = join(cwd,
|
|
510
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'undercover-guard.js');
|
|
499
511
|
if (!existsSync(hookPath)) {
|
|
500
512
|
console.log(JSON.stringify({}));
|
|
501
513
|
return;
|
|
@@ -565,7 +577,7 @@ async function main() {
|
|
|
565
577
|
}
|
|
566
578
|
|
|
567
579
|
// Delegate to shared style guard logic
|
|
568
|
-
const hookPath = join(cwd,
|
|
580
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'style-guard.js');
|
|
569
581
|
if (!existsSync(hookPath)) {
|
|
570
582
|
console.log(JSON.stringify({}));
|
|
571
583
|
return;
|
|
@@ -648,7 +660,7 @@ async function main() {
|
|
|
648
660
|
const cwd = event.cwd || process.cwd();
|
|
649
661
|
|
|
650
662
|
// Delegate to canonical license check
|
|
651
|
-
const hookPath = join(cwd,
|
|
663
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'license-guard.js');
|
|
652
664
|
if (!existsSync(hookPath)) {
|
|
653
665
|
// No canonical hook present — fail open
|
|
654
666
|
console.log(JSON.stringify({}));
|
|
@@ -42,6 +42,7 @@ export const AGENT_MCP_DEPS = {
|
|
|
42
42
|
'qa-planning': { required: [], optional: [] },
|
|
43
43
|
dev: { required: ['context7'], optional: ['browser'] },
|
|
44
44
|
'qa-implementation': { required: [], optional: ['browser'] },
|
|
45
|
+
'qa-visual': { required: [], optional: ['browser'] },
|
|
45
46
|
devops: { required: [], optional: [] },
|
|
46
47
|
};
|
|
47
48
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { loadGlobalDomain, extractRules } from '../domain-loader.js';
|
|
9
|
+
import { resolveFrameworkDir } from '../../utils/framework-dir.js';
|
|
9
10
|
|
|
10
11
|
// Maps session states to governance modes for domain lookup
|
|
11
12
|
const STATE_TO_GOVERNANCE_MODE = {
|
|
@@ -40,7 +41,7 @@ export function processL1(ctx) {
|
|
|
40
41
|
rules,
|
|
41
42
|
mode: sessionMode,
|
|
42
43
|
modeRules: {
|
|
43
|
-
writeScope: modeRules.writeScope || '
|
|
44
|
+
writeScope: modeRules.writeScope || resolveFrameworkDir(ctx.projectDir || process.cwd()) + '/',
|
|
44
45
|
allowedActions: modeRules.allowedActions || [],
|
|
45
46
|
blockedActions: modeRules.blockedActions || [],
|
|
46
47
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync, existsSync, readdirSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import yaml from 'js-yaml';
|
|
4
|
+
import { resolveFrameworkDir } from '../utils/framework-dir.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Read all dashboard data from project directory
|
|
@@ -31,7 +32,7 @@ export async function readDashboardData(targetDir) {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
// Read config.yaml
|
|
34
|
-
const configPath = join(targetDir,
|
|
35
|
+
const configPath = join(targetDir, resolveFrameworkDir(targetDir), 'config.yaml');
|
|
35
36
|
if (existsSync(configPath)) {
|
|
36
37
|
try {
|
|
37
38
|
data.config = yaml.load(readFileSync(configPath, 'utf-8'));
|
|
@@ -59,7 +60,7 @@ export async function readDashboardData(targetDir) {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
// Read task progress from artifacts
|
|
62
|
-
const tasksDir = join(targetDir, '
|
|
63
|
+
const tasksDir = join(targetDir, 'artifacts', '6-Tasks');
|
|
63
64
|
if (existsSync(tasksDir)) {
|
|
64
65
|
try {
|
|
65
66
|
const files = readdirSync(tasksDir).filter(f => f.endsWith('.yaml') || f.endsWith('.md'));
|
|
@@ -70,7 +71,7 @@ export async function readDashboardData(targetDir) {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
// Read gotchas
|
|
73
|
-
const gotchasPath = join(targetDir,
|
|
74
|
+
const gotchasPath = join(targetDir, resolveFrameworkDir(targetDir), 'intelligence', 'gotchas.yaml');
|
|
74
75
|
if (existsSync(gotchasPath)) {
|
|
75
76
|
try {
|
|
76
77
|
const gotchasData = yaml.load(readFileSync(gotchasPath, 'utf-8'));
|
package/src/dashboard/layout.js
CHANGED
|
@@ -62,7 +62,8 @@ export function buildProjectInfo(data) {
|
|
|
62
62
|
const name = project.name || 'unknown';
|
|
63
63
|
const type = project.type === 'brownfield' ? 'Brownfield' : 'Greenfield';
|
|
64
64
|
const state = formatState(project.state);
|
|
65
|
-
const
|
|
65
|
+
const rawMode = session.execution_mode;
|
|
66
|
+
const mode = formatMode(rawMode && typeof rawMode === 'object' ? rawMode.mode : rawMode);
|
|
66
67
|
const lang = session.language || 'en';
|
|
67
68
|
// Show the provider-specific IDE (last in the list) rather than always claude-code
|
|
68
69
|
const ides = session.ides || [];
|
package/src/decision/analyzer.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
import { readFileSync, existsSync } from 'fs';
|
|
9
9
|
import { join } from 'path';
|
|
10
10
|
import yaml from 'js-yaml';
|
|
11
|
+
import { resolveFrameworkDir } from '../utils/framework-dir.js';
|
|
12
|
+
import { flattenEntitiesFromRegistry } from '../utils/flatten-entities.js';
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Analyze impact of changing an entity.
|
|
@@ -38,12 +40,12 @@ export function analyzeImpact(projectDir, entityPath) {
|
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
// Load entity details
|
|
41
|
-
const registryPath = join(projectDir,
|
|
43
|
+
const registryPath = join(projectDir, resolveFrameworkDir(projectDir), 'entity-registry.yaml');
|
|
42
44
|
const registry = existsSync(registryPath)
|
|
43
45
|
? yaml.load(readFileSync(registryPath, 'utf8'))
|
|
44
46
|
: { entities: {} };
|
|
45
47
|
|
|
46
|
-
const allEntities =
|
|
48
|
+
const allEntities = flattenEntitiesFromRegistry(registry);
|
|
47
49
|
const entityMap = new Map(allEntities.map(e => [e.path, e]));
|
|
48
50
|
|
|
49
51
|
const affectedEntities = affectedPaths.map(path => {
|
|
@@ -68,7 +70,7 @@ export function analyzeImpact(projectDir, entityPath) {
|
|
|
68
70
|
* @returns {Map<string, string[]>} Map of entityPath -> [dependentPaths]
|
|
69
71
|
*/
|
|
70
72
|
export function buildDependencyGraph(projectDir) {
|
|
71
|
-
const registryPath = join(projectDir,
|
|
73
|
+
const registryPath = join(projectDir, resolveFrameworkDir(projectDir), 'entity-registry.yaml');
|
|
72
74
|
const graph = new Map();
|
|
73
75
|
|
|
74
76
|
if (!existsSync(registryPath)) {
|
|
@@ -76,7 +78,7 @@ export function buildDependencyGraph(projectDir) {
|
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
const registry = yaml.load(readFileSync(registryPath, 'utf8'));
|
|
79
|
-
const entities =
|
|
81
|
+
const entities = flattenEntitiesFromRegistry(registry);
|
|
80
82
|
|
|
81
83
|
// Initialize graph with all entities
|
|
82
84
|
entities.forEach(entity => {
|
|
@@ -287,29 +289,3 @@ function tokenize(text) {
|
|
|
287
289
|
.filter(w => w.length > 2 && !stopwords.has(w));
|
|
288
290
|
}
|
|
289
291
|
|
|
290
|
-
/**
|
|
291
|
-
* Flatten entity registry into array of entities.
|
|
292
|
-
* @private
|
|
293
|
-
* @param {object} registry
|
|
294
|
-
* @returns {object[]}
|
|
295
|
-
*/
|
|
296
|
-
function flattenEntities(registry) {
|
|
297
|
-
const entities = [];
|
|
298
|
-
|
|
299
|
-
if (!registry.entities) {
|
|
300
|
-
return entities;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
Object.entries(registry.entities).forEach(([type, typeEntities]) => {
|
|
304
|
-
if (Array.isArray(typeEntities)) {
|
|
305
|
-
typeEntities.forEach(entity => {
|
|
306
|
-
entities.push({
|
|
307
|
-
...entity,
|
|
308
|
-
type
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
return entities;
|
|
315
|
-
}
|
package/src/decision/engine.js
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
13
13
|
import { join } from 'path';
|
|
14
14
|
import yaml from 'js-yaml';
|
|
15
|
+
import { resolveFrameworkDir } from '../utils/framework-dir.js';
|
|
16
|
+
import { flattenEntitiesFromRegistry } from '../utils/flatten-entities.js';
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Analyze a request against existing entities and recommend action.
|
|
@@ -22,7 +24,7 @@ import yaml from 'js-yaml';
|
|
|
22
24
|
* @returns {{ recommendation: 'REUSE'|'ADAPT'|'CREATE', score: number, matches: object[], reasoning: string }}
|
|
23
25
|
*/
|
|
24
26
|
export function analyzeRequest(projectDir, request) {
|
|
25
|
-
const registryPath = join(projectDir,
|
|
27
|
+
const registryPath = join(projectDir, resolveFrameworkDir(projectDir), 'entity-registry.yaml');
|
|
26
28
|
|
|
27
29
|
if (!existsSync(registryPath)) {
|
|
28
30
|
return {
|
|
@@ -34,7 +36,7 @@ export function analyzeRequest(projectDir, request) {
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
const registry = yaml.load(readFileSync(registryPath, 'utf8'));
|
|
37
|
-
const entities =
|
|
39
|
+
const entities = flattenEntitiesFromRegistry(registry);
|
|
38
40
|
|
|
39
41
|
// Calculate similarity scores for all entities
|
|
40
42
|
const scoredMatches = entities
|
|
@@ -246,29 +248,3 @@ export function getEngineStats(projectDir) {
|
|
|
246
248
|
return stats;
|
|
247
249
|
}
|
|
248
250
|
|
|
249
|
-
/**
|
|
250
|
-
* Flatten entity registry into array of entities.
|
|
251
|
-
* @private
|
|
252
|
-
* @param {object} registry
|
|
253
|
-
* @returns {object[]}
|
|
254
|
-
*/
|
|
255
|
-
function flattenEntities(registry) {
|
|
256
|
-
const entities = [];
|
|
257
|
-
|
|
258
|
-
if (!registry.entities) {
|
|
259
|
-
return entities;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
Object.entries(registry.entities).forEach(([type, typeEntities]) => {
|
|
263
|
-
if (Array.isArray(typeEntities)) {
|
|
264
|
-
typeEntities.forEach(entity => {
|
|
265
|
-
entities.push({
|
|
266
|
-
...entity,
|
|
267
|
-
type
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
return entities;
|
|
274
|
-
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { readFileSync, writeFileSync, existsSync, statSync, copyFileSync, mkdirSync } from 'fs';
|
|
9
9
|
import { join, isAbsolute } from 'path';
|
|
10
10
|
import yaml from 'js-yaml';
|
|
11
|
+
import { resolveFrameworkDir } from '../utils/framework-dir.js';
|
|
11
12
|
import { detectNewEntities, detectRemovedEntities, generateEntityMeta } from './registry-updater.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -16,7 +17,7 @@ import { detectNewEntities, detectRemovedEntities, generateEntityMeta } from './
|
|
|
16
17
|
* @returns {{ registry: object|null, registryPath: string }}
|
|
17
18
|
*/
|
|
18
19
|
function loadRegistry(projectDir) {
|
|
19
|
-
const registryPath = join(projectDir,
|
|
20
|
+
const registryPath = join(projectDir, resolveFrameworkDir(projectDir), 'entity-registry.yaml');
|
|
20
21
|
if (!existsSync(registryPath)) {
|
|
21
22
|
return { registry: null, registryPath };
|
|
22
23
|
}
|
|
@@ -347,7 +348,7 @@ export function detectCountMismatch(projectDir, preloaded) {
|
|
|
347
348
|
* @returns {{ applied: number, backup: string }}
|
|
348
349
|
*/
|
|
349
350
|
export function applyFixes(projectDir, fixes) {
|
|
350
|
-
const registryPath = join(projectDir,
|
|
351
|
+
const registryPath = join(projectDir, resolveFrameworkDir(projectDir), 'entity-registry.yaml');
|
|
351
352
|
|
|
352
353
|
if (!existsSync(registryPath)) {
|
|
353
354
|
return { applied: 0, backup: null };
|
|
@@ -8,17 +8,26 @@
|
|
|
8
8
|
import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from 'fs';
|
|
9
9
|
import { join, basename, relative } from 'path';
|
|
10
10
|
import yaml from 'js-yaml';
|
|
11
|
+
import { resolveFrameworkDir } from '../utils/framework-dir.js';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Build KNOWN_DIRS using resolveFrameworkDir so paths are correct
|
|
15
|
+
* for both chati.dev/ and .chati.dev/ installations.
|
|
16
|
+
* @param {string} projectDir
|
|
17
|
+
* @returns {{ path: string, type: string }[]}
|
|
18
|
+
*/
|
|
19
|
+
function buildKnownDirs(projectDir) {
|
|
20
|
+
const fw = resolveFrameworkDir(projectDir);
|
|
21
|
+
return [
|
|
22
|
+
{ path: `${fw}/agents`, type: 'agent' },
|
|
23
|
+
{ path: `${fw}/tasks`, type: 'task' },
|
|
24
|
+
{ path: `${fw}/schemas`, type: 'schema' },
|
|
25
|
+
{ path: `${fw}/domains`, type: 'domain' },
|
|
26
|
+
{ path: `${fw}/hooks`, type: 'hook' },
|
|
27
|
+
{ path: `${fw}/workflows`, type: 'workflow' },
|
|
28
|
+
{ path: `${fw}/templates`, type: 'template' },
|
|
29
|
+
];
|
|
30
|
+
}
|
|
22
31
|
|
|
23
32
|
/**
|
|
24
33
|
* Scan project for new/changed files and update registry.
|
|
@@ -28,7 +37,7 @@ const KNOWN_DIRS = [
|
|
|
28
37
|
*/
|
|
29
38
|
export function updateRegistry(projectDir, options = {}) {
|
|
30
39
|
const { dryRun = false } = options;
|
|
31
|
-
const registryPath = join(projectDir,
|
|
40
|
+
const registryPath = join(projectDir, resolveFrameworkDir(projectDir), 'entity-registry.yaml');
|
|
32
41
|
|
|
33
42
|
let registry = {
|
|
34
43
|
metadata: {
|
|
@@ -157,7 +166,7 @@ export function updateRegistry(projectDir, options = {}) {
|
|
|
157
166
|
* @returns {object[]} New entity candidates
|
|
158
167
|
*/
|
|
159
168
|
export function detectNewEntities(projectDir) {
|
|
160
|
-
const registryPath = join(projectDir,
|
|
169
|
+
const registryPath = join(projectDir, resolveFrameworkDir(projectDir), 'entity-registry.yaml');
|
|
161
170
|
const existingPaths = new Set();
|
|
162
171
|
|
|
163
172
|
if (existsSync(registryPath)) {
|
|
@@ -175,7 +184,7 @@ export function detectNewEntities(projectDir) {
|
|
|
175
184
|
|
|
176
185
|
const newEntities = [];
|
|
177
186
|
|
|
178
|
-
|
|
187
|
+
buildKnownDirs(projectDir).forEach(({ path: dirPath, type }) => {
|
|
179
188
|
const fullPath = join(projectDir, dirPath);
|
|
180
189
|
if (!existsSync(fullPath)) {
|
|
181
190
|
return;
|
|
@@ -204,7 +213,7 @@ export function detectNewEntities(projectDir) {
|
|
|
204
213
|
* @returns {object[]} Removed entity candidates
|
|
205
214
|
*/
|
|
206
215
|
export function detectRemovedEntities(projectDir) {
|
|
207
|
-
const registryPath = join(projectDir,
|
|
216
|
+
const registryPath = join(projectDir, resolveFrameworkDir(projectDir), 'entity-registry.yaml');
|
|
208
217
|
if (!existsSync(registryPath)) {
|
|
209
218
|
return [];
|
|
210
219
|
}
|
package/src/extensions/loader.js
CHANGED
|
@@ -11,13 +11,7 @@
|
|
|
11
11
|
import { existsSync, readdirSync } from 'fs';
|
|
12
12
|
import { join, extname, basename } from 'path';
|
|
13
13
|
import { registerExtension, EXTENSION_POINTS } from './registry.js';
|
|
14
|
-
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
// Constants
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
|
|
19
|
-
/** Directory relative to project root where extensions are stored. */
|
|
20
|
-
const EXTENSIONS_DIR = 'chati.dev/extensions';
|
|
14
|
+
import { resolveFrameworkDir } from '../utils/framework-dir.js';
|
|
21
15
|
|
|
22
16
|
/** Required fields in an extension module's default export. */
|
|
23
17
|
const REQUIRED_FIELDS = ['name', 'point', 'handler'];
|
|
@@ -79,7 +73,7 @@ export function discoverExtensions(projectDir) {
|
|
|
79
73
|
return { files: [], dir: '' };
|
|
80
74
|
}
|
|
81
75
|
|
|
82
|
-
const extDir = join(projectDir,
|
|
76
|
+
const extDir = join(projectDir, resolveFrameworkDir(projectDir), 'extensions');
|
|
83
77
|
|
|
84
78
|
if (!existsSync(extDir)) {
|
|
85
79
|
return { files: [], dir: extDir };
|