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
|
@@ -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: {} };
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code settings.json generator.
|
|
3
|
+
*
|
|
4
|
+
* Produces .claude/settings.json content that wires the 14 chati.dev hooks
|
|
5
|
+
* into Claude Code's hook system and applies a baseline permission policy.
|
|
6
|
+
*
|
|
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,
|
|
9
|
+
* not the framework's bundled file. As a result, every hook was dormant for
|
|
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 14 hooks at
|
|
12
|
+
* the canonical events, and applies a generic permission policy modeled on
|
|
13
|
+
* production Claude Code setups.
|
|
14
|
+
*
|
|
15
|
+
* Hook output schema (v4.2.2): all PreToolUse hooks now use the canonical
|
|
16
|
+
* `hookSpecificOutput.permissionDecision` field per Anthropic docs at
|
|
17
|
+
* https://code.claude.com/docs/en/hooks.md. The previous `decision` field
|
|
18
|
+
* was silently no-op'd by Claude Code.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
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/.
|
|
24
|
+
*/
|
|
25
|
+
const HOOK_REGISTRY = [
|
|
26
|
+
// UserPromptSubmit — fires on every user message turn.
|
|
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)
|
|
31
|
+
{ name: 'prism-engine', event: 'UserPromptSubmit', matcher: '.*' },
|
|
32
|
+
{ name: 'model-governance', event: 'UserPromptSubmit', matcher: '.*' },
|
|
33
|
+
|
|
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: '.*' },
|
|
40
|
+
{ name: 'read-protection', event: 'PreToolUse', matcher: 'Read' },
|
|
41
|
+
{ name: 'constitution-guard',event: 'PreToolUse', matcher: 'Bash|Write|Edit' },
|
|
42
|
+
{ name: 'mode-governance', event: 'PreToolUse', matcher: 'Write|Edit' },
|
|
43
|
+
{ name: 'style-guard', event: 'PreToolUse', matcher: 'Write|Edit|Bash' },
|
|
44
|
+
{ name: 'undercover-guard', event: 'PreToolUse', matcher: 'Write|Edit|Bash' },
|
|
45
|
+
{ name: 'team-quality-gate', event: 'PreToolUse', matcher: 'Write|Edit' },
|
|
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
|
+
|
|
57
|
+
// PreCompact — fires before context compaction (observational).
|
|
58
|
+
{ name: 'session-digest', event: 'PreCompact', matcher: '' },
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Baseline permission allow list.
|
|
63
|
+
* Modeled on production Claude Code setups: lets the assistant work freely
|
|
64
|
+
* inside the project without prompting for every tool call. Personal MCP
|
|
65
|
+
* permissions are intentionally NOT included — those are user-specific.
|
|
66
|
+
*/
|
|
67
|
+
const PERMISSIONS_ALLOW = [
|
|
68
|
+
'Read(**/*)',
|
|
69
|
+
'Write(**/*)',
|
|
70
|
+
'Edit(**/*)',
|
|
71
|
+
'Bash',
|
|
72
|
+
'WebFetch',
|
|
73
|
+
'WebSearch',
|
|
74
|
+
'Task',
|
|
75
|
+
'Glob',
|
|
76
|
+
'Grep',
|
|
77
|
+
'NotebookEdit',
|
|
78
|
+
'Skill(*)',
|
|
79
|
+
'TodoWrite',
|
|
80
|
+
'AskUserQuestion',
|
|
81
|
+
'EnterPlanMode',
|
|
82
|
+
'ExitPlanMode',
|
|
83
|
+
'KillShell',
|
|
84
|
+
'Bash(npx playwright:*)',
|
|
85
|
+
'Bash(node .chati.dev/scripts/*)',
|
|
86
|
+
'Bash(node chati.dev/scripts/*)',
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Baseline permission deny list.
|
|
91
|
+
*
|
|
92
|
+
* Categories:
|
|
93
|
+
* - Catastrophic filesystem: deletes that hit root, mass-format, recursive chmod
|
|
94
|
+
* - Git destructive: force push, hard reset, .git removal, hook bypass
|
|
95
|
+
* - System path writes: /etc, /usr, /System, /bin, /sbin (note `//` prefix
|
|
96
|
+
* means absolute filesystem path, not project-relative)
|
|
97
|
+
* - Secret reads: SSH keys, AWS/GCP credentials, npm tokens, GitHub tokens
|
|
98
|
+
* - Accidental publish: npm/yarn/pnpm/pip/cargo publish commands
|
|
99
|
+
*
|
|
100
|
+
* Path syntax (from Claude Code docs):
|
|
101
|
+
* - `Bash(cmd:*)` matches "cmd" with any args
|
|
102
|
+
* - `~/path/**` expands ~ to home dir, ** matches recursive
|
|
103
|
+
* - `//etc/**` double-slash means absolute filesystem path
|
|
104
|
+
* - `/path/**` single-slash is project-root-relative (NOT absolute)
|
|
105
|
+
*/
|
|
106
|
+
const PERMISSIONS_DENY = [
|
|
107
|
+
// Catastrophic filesystem
|
|
108
|
+
'Bash(rm -rf /)',
|
|
109
|
+
'Bash(rm -rf /*)',
|
|
110
|
+
'Bash(rm -rf ~)',
|
|
111
|
+
'Bash(rm -rf ~/*)',
|
|
112
|
+
'Bash(rm -rf $HOME)',
|
|
113
|
+
'Bash(rm -rf $HOME/*)',
|
|
114
|
+
'Bash(sudo rm -rf:*)',
|
|
115
|
+
'Bash(mkfs:*)',
|
|
116
|
+
'Bash(dd if=/dev/zero:*)',
|
|
117
|
+
'Bash(dd if=/dev/random:*)',
|
|
118
|
+
'Bash(chmod -R 777 /)',
|
|
119
|
+
'Bash(chown -R:* /)',
|
|
120
|
+
|
|
121
|
+
// Git destructive
|
|
122
|
+
'Bash(git push --force:*)',
|
|
123
|
+
'Bash(git push -f:*)',
|
|
124
|
+
'Bash(git push --force-with-lease:*)',
|
|
125
|
+
'Bash(git reset --hard:*)',
|
|
126
|
+
'Bash(git clean -fd:*)',
|
|
127
|
+
'Bash(git clean -fdx:*)',
|
|
128
|
+
'Bash(git clean -fx:*)',
|
|
129
|
+
'Bash(rm -rf .git)',
|
|
130
|
+
'Bash(rm -rf .git/*)',
|
|
131
|
+
|
|
132
|
+
// Bypass hooks (always suspect)
|
|
133
|
+
'Bash(git commit --no-verify:*)',
|
|
134
|
+
'Bash(git commit -n:*)',
|
|
135
|
+
'Bash(git commit --no-gpg-sign:*)',
|
|
136
|
+
'Bash(git rebase --no-verify:*)',
|
|
137
|
+
|
|
138
|
+
// System path writes — `//` prefix = absolute filesystem
|
|
139
|
+
'Write(//etc/**)',
|
|
140
|
+
'Write(//usr/**)',
|
|
141
|
+
'Write(//System/**)',
|
|
142
|
+
'Write(//bin/**)',
|
|
143
|
+
'Write(//sbin/**)',
|
|
144
|
+
'Write(//Library/**)',
|
|
145
|
+
'Edit(//etc/**)',
|
|
146
|
+
'Edit(//usr/**)',
|
|
147
|
+
'Edit(//System/**)',
|
|
148
|
+
'Edit(//bin/**)',
|
|
149
|
+
'Edit(//sbin/**)',
|
|
150
|
+
'Edit(//Library/**)',
|
|
151
|
+
|
|
152
|
+
// Credentials and secrets
|
|
153
|
+
'Read(~/.ssh/**)',
|
|
154
|
+
'Read(~/.aws/credentials)',
|
|
155
|
+
'Read(~/.aws/config)',
|
|
156
|
+
'Read(~/.gnupg/**)',
|
|
157
|
+
'Read(~/.config/gh/hosts.yml)',
|
|
158
|
+
'Read(~/.docker/config.json)',
|
|
159
|
+
'Read(~/.npmrc)',
|
|
160
|
+
'Read(~/.pypirc)',
|
|
161
|
+
'Read(~/.netrc)',
|
|
162
|
+
|
|
163
|
+
// Accidental publish
|
|
164
|
+
'Bash(npm publish:*)',
|
|
165
|
+
'Bash(yarn publish:*)',
|
|
166
|
+
'Bash(pnpm publish:*)',
|
|
167
|
+
'Bash(pip upload:*)',
|
|
168
|
+
'Bash(twine upload:*)',
|
|
169
|
+
'Bash(cargo publish:*)',
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Group HOOK_REGISTRY entries by event, then by matcher within each event.
|
|
174
|
+
* Returns the shape Claude Code expects under the top-level "hooks" key.
|
|
175
|
+
*/
|
|
176
|
+
function buildHooksObject() {
|
|
177
|
+
const events = {};
|
|
178
|
+
|
|
179
|
+
for (const entry of HOOK_REGISTRY) {
|
|
180
|
+
if (!events[entry.event]) events[entry.event] = new Map();
|
|
181
|
+
const matcherGroups = events[entry.event];
|
|
182
|
+
if (!matcherGroups.has(entry.matcher)) {
|
|
183
|
+
matcherGroups.set(entry.matcher, []);
|
|
184
|
+
}
|
|
185
|
+
matcherGroups.get(entry.matcher).push({
|
|
186
|
+
type: 'command',
|
|
187
|
+
command: `node .chati.dev/hooks/${entry.name}.js`,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const result = {};
|
|
192
|
+
for (const [event, matcherGroups] of Object.entries(events)) {
|
|
193
|
+
result[event] = [];
|
|
194
|
+
for (const [matcher, hooks] of matcherGroups.entries()) {
|
|
195
|
+
const group = { hooks };
|
|
196
|
+
if (matcher) group.matcher = matcher;
|
|
197
|
+
result[event].push(group);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Generate the full settings.json content.
|
|
205
|
+
*
|
|
206
|
+
* @returns {string} JSON string with trailing newline, ready to write to disk.
|
|
207
|
+
*/
|
|
208
|
+
export function generateClaudeSettings() {
|
|
209
|
+
const settings = {
|
|
210
|
+
$schema: 'https://json.schemastore.org/claude-code-settings.json',
|
|
211
|
+
permissions: {
|
|
212
|
+
allow: PERMISSIONS_ALLOW,
|
|
213
|
+
deny: PERMISSIONS_DENY,
|
|
214
|
+
defaultMode: 'default',
|
|
215
|
+
},
|
|
216
|
+
hooks: buildHooksObject(),
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
return JSON.stringify(settings, null, 2) + '\n';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export {
|
|
223
|
+
HOOK_REGISTRY,
|
|
224
|
+
PERMISSIONS_ALLOW,
|
|
225
|
+
PERMISSIONS_DENY,
|
|
226
|
+
};
|
|
@@ -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',
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
* Maps each chati.dev hook to its Gemini CLI equivalent event.
|
|
23
23
|
*/
|
|
24
24
|
export const HOOK_MAP = {
|
|
25
|
+
// license-guard MUST be first on BeforeModel so license check runs before
|
|
26
|
+
// any model call. Without this, an expired trial could keep using Gemini
|
|
27
|
+
// indefinitely from a long-running session.
|
|
28
|
+
'license-guard': { event: 'BeforeModel', description: 'Validate license on every turn (chati.dev governance)' },
|
|
25
29
|
'prism-engine': { event: 'BeforeModel', description: 'Inject PRISM context into model prompt' },
|
|
26
30
|
'model-governance': { event: 'BeforeModel', description: 'Advisory: recommended model per agent' },
|
|
27
31
|
'mode-governance': { event: 'BeforeTool', description: 'Block writes outside current mode scope' },
|
|
@@ -30,6 +34,7 @@ export const HOOK_MAP = {
|
|
|
30
34
|
'session-digest': { event: 'PreCompress', description: 'Save session state before context compression' },
|
|
31
35
|
'undercover-guard': { event: 'BeforeTool', description: 'Advisory: sanitize internal framework references from deliverables' },
|
|
32
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)' },
|
|
33
38
|
};
|
|
34
39
|
|
|
35
40
|
// ---------------------------------------------------------------------------
|
|
@@ -53,6 +58,16 @@ import { fileURLToPath } from 'url';
|
|
|
53
58
|
const __filename = fileURLToPath(import.meta.url);
|
|
54
59
|
const __dirname = join(__filename, '..');
|
|
55
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
|
+
}
|
|
56
71
|
`;
|
|
57
72
|
|
|
58
73
|
/**
|
|
@@ -82,7 +97,7 @@ async function main() {
|
|
|
82
97
|
}
|
|
83
98
|
|
|
84
99
|
// Delegate to shared PRISM engine logic
|
|
85
|
-
const hookPath = join(cwd,
|
|
100
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'prism-engine.js');
|
|
86
101
|
if (existsSync(hookPath)) {
|
|
87
102
|
const mod = await import(hookPath);
|
|
88
103
|
if (typeof mod.buildPrismContext === 'function') {
|
|
@@ -137,7 +152,7 @@ async function main() {
|
|
|
137
152
|
|
|
138
153
|
if (agent) {
|
|
139
154
|
// Delegate to shared model governance logic
|
|
140
|
-
const hookPath = join(cwd,
|
|
155
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'model-governance.js');
|
|
141
156
|
if (existsSync(hookPath)) {
|
|
142
157
|
const mod = await import(hookPath);
|
|
143
158
|
if (typeof mod.getRecommendedModel === 'function') {
|
|
@@ -171,8 +186,9 @@ function generateModeGovernance() {
|
|
|
171
186
|
* Blocks write operations outside current mode scope.
|
|
172
187
|
* Constitution Article XI enforcement.
|
|
173
188
|
*/
|
|
189
|
+
const fwDir = existsSync(join(process.cwd(), '.chati.dev')) ? '.chati.dev' : 'chati.dev';
|
|
174
190
|
const MODE_SCOPES = {
|
|
175
|
-
planning: { allowed: ['
|
|
191
|
+
planning: { allowed: [fwDir + '/', '.chati/', 'artifacts/'] },
|
|
176
192
|
build: { allowed: ['*'] },
|
|
177
193
|
deploy: { allowed: ['*'] },
|
|
178
194
|
};
|
|
@@ -443,7 +459,7 @@ async function main() {
|
|
|
443
459
|
const cwd = event.cwd || process.cwd();
|
|
444
460
|
|
|
445
461
|
// Delegate to shared session digest logic
|
|
446
|
-
const hookPath = join(cwd,
|
|
462
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'session-digest.js');
|
|
447
463
|
if (existsSync(hookPath)) {
|
|
448
464
|
const mod = await import(hookPath);
|
|
449
465
|
if (typeof mod.saveDigest === 'function') {
|
|
@@ -491,7 +507,7 @@ async function main() {
|
|
|
491
507
|
}
|
|
492
508
|
|
|
493
509
|
// Delegate to shared undercover guard logic
|
|
494
|
-
const hookPath = join(cwd,
|
|
510
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'undercover-guard.js');
|
|
495
511
|
if (!existsSync(hookPath)) {
|
|
496
512
|
console.log(JSON.stringify({}));
|
|
497
513
|
return;
|
|
@@ -561,7 +577,7 @@ async function main() {
|
|
|
561
577
|
}
|
|
562
578
|
|
|
563
579
|
// Delegate to shared style guard logic
|
|
564
|
-
const hookPath = join(cwd,
|
|
580
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'style-guard.js');
|
|
565
581
|
if (!existsSync(hookPath)) {
|
|
566
582
|
console.log(JSON.stringify({}));
|
|
567
583
|
return;
|
|
@@ -611,6 +627,7 @@ main();
|
|
|
611
627
|
*/
|
|
612
628
|
export function generateAllGeminiHooks() {
|
|
613
629
|
return {
|
|
630
|
+
'license-guard.js': generateLicenseGuard(),
|
|
614
631
|
'prism-engine.js': generatePrismEngine(),
|
|
615
632
|
'model-governance.js': generateModelGovernance(),
|
|
616
633
|
'mode-governance.js': generateModeGovernance(),
|
|
@@ -622,6 +639,59 @@ export function generateAllGeminiHooks() {
|
|
|
622
639
|
};
|
|
623
640
|
}
|
|
624
641
|
|
|
642
|
+
/**
|
|
643
|
+
* Generate the license guard hook for Gemini CLI.
|
|
644
|
+
* BeforeModel event — validates license on every turn.
|
|
645
|
+
*
|
|
646
|
+
* Delegates to chati.dev/hooks/license-guard.js (canonical implementation)
|
|
647
|
+
* via dynamic import of checkLicense(). Translates the verdict object to
|
|
648
|
+
* Gemini's blocking schema (exit code 2 + stderr is the universal block).
|
|
649
|
+
*/
|
|
650
|
+
function generateLicenseGuard() {
|
|
651
|
+
return `${HOOK_HEADER}
|
|
652
|
+
async function main() {
|
|
653
|
+
let input = '';
|
|
654
|
+
for await (const chunk of process.stdin) {
|
|
655
|
+
input += chunk;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
try {
|
|
659
|
+
const event = JSON.parse(input);
|
|
660
|
+
const cwd = event.cwd || process.cwd();
|
|
661
|
+
|
|
662
|
+
// Delegate to canonical license check
|
|
663
|
+
const hookPath = join(cwd, resolveFrameworkDir(cwd), 'hooks', 'license-guard.js');
|
|
664
|
+
if (!existsSync(hookPath)) {
|
|
665
|
+
// No canonical hook present — fail open
|
|
666
|
+
console.log(JSON.stringify({}));
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const mod = await import(hookPath);
|
|
671
|
+
if (typeof mod.checkLicense !== 'function') {
|
|
672
|
+
console.log(JSON.stringify({}));
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const result = await mod.checkLicense();
|
|
677
|
+
if (result.valid) {
|
|
678
|
+
console.log(JSON.stringify({}));
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Block via exit code 2 (universal Gemini block) + stderr message.
|
|
683
|
+
process.stderr.write(result.reason || 'License invalid');
|
|
684
|
+
process.exit(2);
|
|
685
|
+
} catch {
|
|
686
|
+
// Fail open on any parse/import error — never block on bugs.
|
|
687
|
+
console.log(JSON.stringify({}));
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
main();
|
|
692
|
+
`;
|
|
693
|
+
}
|
|
694
|
+
|
|
625
695
|
/**
|
|
626
696
|
* Generate .gemini/settings.json content with hook configuration.
|
|
627
697
|
*
|
|
@@ -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 || [];
|