@devran-ai/kit 4.1.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/.agent/CheatSheet.md +350 -0
- package/.agent/README.md +76 -0
- package/.agent/agents/README.md +155 -0
- package/.agent/agents/architect.md +185 -0
- package/.agent/agents/backend-specialist.md +276 -0
- package/.agent/agents/build-error-resolver.md +207 -0
- package/.agent/agents/code-reviewer.md +162 -0
- package/.agent/agents/database-architect.md +138 -0
- package/.agent/agents/devops-engineer.md +144 -0
- package/.agent/agents/doc-updater.md +229 -0
- package/.agent/agents/e2e-runner.md +145 -0
- package/.agent/agents/explorer-agent.md +143 -0
- package/.agent/agents/frontend-specialist.md +144 -0
- package/.agent/agents/go-reviewer.md +128 -0
- package/.agent/agents/knowledge-agent.md +197 -0
- package/.agent/agents/mobile-developer.md +150 -0
- package/.agent/agents/performance-optimizer.md +175 -0
- package/.agent/agents/planner.md +133 -0
- package/.agent/agents/pr-reviewer.md +148 -0
- package/.agent/agents/python-reviewer.md +123 -0
- package/.agent/agents/refactor-cleaner.md +201 -0
- package/.agent/agents/reliability-engineer.md +156 -0
- package/.agent/agents/security-reviewer.md +141 -0
- package/.agent/agents/sprint-orchestrator.md +124 -0
- package/.agent/agents/tdd-guide.md +179 -0
- package/.agent/agents/typescript-reviewer.md +110 -0
- package/.agent/checklists/README.md +102 -0
- package/.agent/checklists/pre-commit.md +93 -0
- package/.agent/checklists/session-end.md +99 -0
- package/.agent/checklists/session-start.md +102 -0
- package/.agent/checklists/task-complete.md +81 -0
- package/.agent/commands/README.md +130 -0
- package/.agent/commands/adr.md +29 -0
- package/.agent/commands/ask.md +28 -0
- package/.agent/commands/build.md +30 -0
- package/.agent/commands/changelog.md +40 -0
- package/.agent/commands/checkpoint.md +28 -0
- package/.agent/commands/code-review.md +65 -0
- package/.agent/commands/compact.md +28 -0
- package/.agent/commands/cook.md +30 -0
- package/.agent/commands/db.md +30 -0
- package/.agent/commands/debug.md +31 -0
- package/.agent/commands/deploy.md +37 -0
- package/.agent/commands/design.md +29 -0
- package/.agent/commands/doc.md +30 -0
- package/.agent/commands/eval.md +30 -0
- package/.agent/commands/fix.md +32 -0
- package/.agent/commands/git.md +32 -0
- package/.agent/commands/help.md +273 -0
- package/.agent/commands/implement.md +30 -0
- package/.agent/commands/integrate.md +32 -0
- package/.agent/commands/learn.md +29 -0
- package/.agent/commands/perf.md +31 -0
- package/.agent/commands/plan.md +56 -0
- package/.agent/commands/pr-describe.md +65 -0
- package/.agent/commands/pr-fix.md +45 -0
- package/.agent/commands/pr-merge.md +45 -0
- package/.agent/commands/pr-review.md +50 -0
- package/.agent/commands/pr-split.md +54 -0
- package/.agent/commands/pr-status.md +56 -0
- package/.agent/commands/pr.md +58 -0
- package/.agent/commands/refactor.md +32 -0
- package/.agent/commands/research.md +28 -0
- package/.agent/commands/scout.md +30 -0
- package/.agent/commands/security-scan.md +33 -0
- package/.agent/commands/setup.md +31 -0
- package/.agent/commands/status.md +59 -0
- package/.agent/commands/tdd.md +73 -0
- package/.agent/commands/verify.md +58 -0
- package/.agent/contexts/brainstorm.md +26 -0
- package/.agent/contexts/debug.md +28 -0
- package/.agent/contexts/implement.md +29 -0
- package/.agent/contexts/plan-quality-log.md +30 -0
- package/.agent/contexts/review.md +27 -0
- package/.agent/contexts/ship.md +28 -0
- package/.agent/decisions/001-trust-grade-governance.md +46 -0
- package/.agent/decisions/002-cross-ide-generation.md +15 -0
- package/.agent/engine/identity.json +4 -0
- package/.agent/engine/loading-rules.json +193 -0
- package/.agent/engine/marketplace-index.json +29 -0
- package/.agent/engine/mcp-servers/filesystem.json +9 -0
- package/.agent/engine/mcp-servers/github.json +11 -0
- package/.agent/engine/mcp-servers/postgres.json +11 -0
- package/.agent/engine/mcp-servers/supabase.json +11 -0
- package/.agent/engine/mcp-servers/vercel.json +11 -0
- package/.agent/engine/reliability-config.json +14 -0
- package/.agent/engine/sdlc-map.json +50 -0
- package/.agent/engine/workflow-state.json +167 -0
- package/.agent/hooks/README.md +101 -0
- package/.agent/hooks/hooks.json +104 -0
- package/.agent/hooks/templates/session-end.md +110 -0
- package/.agent/hooks/templates/session-start.md +95 -0
- package/.agent/manifest.json +466 -0
- package/.agent/rules/agent-upgrade-policy.md +56 -0
- package/.agent/rules/architecture.md +111 -0
- package/.agent/rules/coding-style.md +75 -0
- package/.agent/rules/documentation.md +74 -0
- package/.agent/rules/git-workflow.md +140 -0
- package/.agent/rules/quality-gate.md +117 -0
- package/.agent/rules/security.md +67 -0
- package/.agent/rules/sprint-tracking.md +103 -0
- package/.agent/rules/testing.md +80 -0
- package/.agent/rules/workflow-standards.md +30 -0
- package/.agent/rules.md +293 -0
- package/.agent/session-context.md +69 -0
- package/.agent/session-state.json +27 -0
- package/.agent/skills/README.md +135 -0
- package/.agent/skills/api-patterns/SKILL.md +117 -0
- package/.agent/skills/app-builder/SKILL.md +202 -0
- package/.agent/skills/architecture/SKILL.md +101 -0
- package/.agent/skills/behavioral-modes/SKILL.md +295 -0
- package/.agent/skills/brainstorming/SKILL.md +156 -0
- package/.agent/skills/clean-code/SKILL.md +142 -0
- package/.agent/skills/context-budget/SKILL.md +78 -0
- package/.agent/skills/continuous-learning/SKILL.md +145 -0
- package/.agent/skills/database-design/SKILL.md +303 -0
- package/.agent/skills/debugging-strategies/SKILL.md +158 -0
- package/.agent/skills/deployment-procedures/SKILL.md +191 -0
- package/.agent/skills/docker-patterns/SKILL.md +161 -0
- package/.agent/skills/eval-harness/SKILL.md +89 -0
- package/.agent/skills/frontend-patterns/SKILL.md +141 -0
- package/.agent/skills/git-workflow/SKILL.md +159 -0
- package/.agent/skills/i18n-localization/SKILL.md +191 -0
- package/.agent/skills/intelligent-routing/SKILL.md +180 -0
- package/.agent/skills/mcp-integration/SKILL.md +240 -0
- package/.agent/skills/mobile-design/SKILL.md +191 -0
- package/.agent/skills/nodejs-patterns/SKILL.md +164 -0
- package/.agent/skills/parallel-agents/SKILL.md +200 -0
- package/.agent/skills/performance-profiling/SKILL.md +134 -0
- package/.agent/skills/plan-validation/SKILL.md +192 -0
- package/.agent/skills/plan-writing/SKILL.md +183 -0
- package/.agent/skills/plan-writing/domain-enhancers.md +184 -0
- package/.agent/skills/plan-writing/plan-retrospective.md +116 -0
- package/.agent/skills/plan-writing/plan-schema.md +119 -0
- package/.agent/skills/pr-toolkit/SKILL.md +174 -0
- package/.agent/skills/production-readiness/SKILL.md +126 -0
- package/.agent/skills/security-practices/SKILL.md +109 -0
- package/.agent/skills/shell-conventions/SKILL.md +92 -0
- package/.agent/skills/strategic-compact/SKILL.md +62 -0
- package/.agent/skills/testing-patterns/SKILL.md +141 -0
- package/.agent/skills/typescript-expert/SKILL.md +160 -0
- package/.agent/skills/ui-ux-pro-max/SKILL.md +137 -0
- package/.agent/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.agent/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.agent/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.agent/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.agent/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.agent/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.agent/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.agent/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.agent/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.agent/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.agent/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.agent/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.agent/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.agent/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.agent/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.agent/skills/verification-loop/SKILL.md +89 -0
- package/.agent/skills/webapp-testing/SKILL.md +175 -0
- package/.agent/templates/adr-template.md +32 -0
- package/.agent/templates/bug-report.md +37 -0
- package/.agent/templates/feature-request.md +32 -0
- package/.agent/workflows/README.md +101 -0
- package/.agent/workflows/brainstorm.md +86 -0
- package/.agent/workflows/create.md +85 -0
- package/.agent/workflows/debug.md +83 -0
- package/.agent/workflows/deploy.md +114 -0
- package/.agent/workflows/enhance.md +85 -0
- package/.agent/workflows/orchestrate.md +106 -0
- package/.agent/workflows/plan.md +105 -0
- package/.agent/workflows/pr-fix.md +163 -0
- package/.agent/workflows/pr-merge.md +117 -0
- package/.agent/workflows/pr-review.md +178 -0
- package/.agent/workflows/pr-split.md +118 -0
- package/.agent/workflows/pr.md +184 -0
- package/.agent/workflows/preflight.md +107 -0
- package/.agent/workflows/preview.md +95 -0
- package/.agent/workflows/quality-gate.md +103 -0
- package/.agent/workflows/retrospective.md +100 -0
- package/.agent/workflows/review.md +104 -0
- package/.agent/workflows/status.md +89 -0
- package/.agent/workflows/test.md +98 -0
- package/.agent/workflows/ui-ux-pro-max.md +93 -0
- package/.agent/workflows/upgrade.md +97 -0
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/bin/kit.js +773 -0
- package/lib/agent-registry.js +228 -0
- package/lib/agent-reputation.js +343 -0
- package/lib/circuit-breaker.js +195 -0
- package/lib/cli-commands.js +322 -0
- package/lib/config-validator.js +274 -0
- package/lib/conflict-detector.js +252 -0
- package/lib/constants.js +47 -0
- package/lib/engineering-manager.js +336 -0
- package/lib/error-budget.js +370 -0
- package/lib/hook-system.js +256 -0
- package/lib/ide-generator.js +434 -0
- package/lib/identity.js +240 -0
- package/lib/io.js +146 -0
- package/lib/learning-engine.js +163 -0
- package/lib/loading-engine.js +421 -0
- package/lib/logger.js +118 -0
- package/lib/marketplace.js +321 -0
- package/lib/plugin-system.js +604 -0
- package/lib/plugin-verifier.js +197 -0
- package/lib/rate-limiter.js +113 -0
- package/lib/security-scanner.js +312 -0
- package/lib/self-healing.js +468 -0
- package/lib/session-manager.js +264 -0
- package/lib/skill-sandbox.js +244 -0
- package/lib/task-governance.js +522 -0
- package/lib/task-model.js +332 -0
- package/lib/updater.js +240 -0
- package/lib/verify.js +279 -0
- package/lib/workflow-engine.js +373 -0
- package/lib/workflow-events.js +166 -0
- package/lib/workflow-persistence.js +160 -0
- package/package.json +57 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devran AI Kit — Hook Trigger System
|
|
3
|
+
*
|
|
4
|
+
* Event-driven lifecycle hook execution based on hooks.json.
|
|
5
|
+
* Evaluates hook actions and reports results with severity awareness.
|
|
6
|
+
*
|
|
7
|
+
* @module lib/hook-system
|
|
8
|
+
* @author Emre Dursun
|
|
9
|
+
* @since v3.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const { AGENT_DIR, ENGINE_DIR, HOOKS_DIR } = require('./constants');
|
|
18
|
+
|
|
19
|
+
const HOOKS_FILE = 'hooks.json';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {object} ActionResult
|
|
23
|
+
* @property {string} action - Action description
|
|
24
|
+
* @property {'critical' | 'high' | 'medium' | 'low'} severity - Action severity
|
|
25
|
+
* @property {'block' | 'warn' | 'log'} onFailure - Failure behavior
|
|
26
|
+
* @property {'pass' | 'fail' | 'skip'} status - Evaluation result
|
|
27
|
+
* @property {string} reason - Reason for status
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {object} HookEvaluation
|
|
32
|
+
* @property {string} event - Hook event name
|
|
33
|
+
* @property {boolean} blocked - Whether any blocking action failed
|
|
34
|
+
* @property {number} passed - Count of passed actions
|
|
35
|
+
* @property {number} failed - Count of failed actions
|
|
36
|
+
* @property {number} skipped - Count of skipped actions
|
|
37
|
+
* @property {ActionResult[]} results - Individual action results
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Loads hooks definition from hooks.json.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} projectRoot - Root directory of the project
|
|
44
|
+
* @returns {object} Parsed hooks config
|
|
45
|
+
*/
|
|
46
|
+
function loadHooks(projectRoot) {
|
|
47
|
+
const hooksPath = path.join(projectRoot, AGENT_DIR, HOOKS_DIR, HOOKS_FILE);
|
|
48
|
+
|
|
49
|
+
if (!fs.existsSync(hooksPath)) {
|
|
50
|
+
return { hooks: [] };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(fs.readFileSync(hooksPath, 'utf-8'));
|
|
55
|
+
} catch {
|
|
56
|
+
return { hooks: [] };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Gets the action definitions for a specific event.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} eventName - Hook event name (e.g., 'session-start')
|
|
64
|
+
* @param {string} projectRoot - Root directory of the project
|
|
65
|
+
* @returns {object[]} Action definitions for this event
|
|
66
|
+
*/
|
|
67
|
+
function getHookActions(eventName, projectRoot) {
|
|
68
|
+
const config = loadHooks(projectRoot);
|
|
69
|
+
const hook = (config.hooks || []).find((h) => h.event === eventName);
|
|
70
|
+
|
|
71
|
+
if (!hook) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return hook.actions || [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Evaluates a hook event against the current project state.
|
|
80
|
+
*
|
|
81
|
+
* This performs a "check" pass — it determines which actions would
|
|
82
|
+
* pass or fail without actually executing them. Actions are evaluated
|
|
83
|
+
* based on the existence of required files and configurations.
|
|
84
|
+
*
|
|
85
|
+
* @param {string} eventName - Hook event name
|
|
86
|
+
* @param {object} context - Evaluation context
|
|
87
|
+
* @param {string} context.projectRoot - Root directory of the project
|
|
88
|
+
* @param {boolean} [context.gitClean] - Whether git status is clean
|
|
89
|
+
* @param {boolean} [context.testsPass] - Whether tests pass
|
|
90
|
+
* @param {boolean} [context.buildPass] - Whether build passes
|
|
91
|
+
* @param {boolean} [context.lintPass] - Whether lint passes
|
|
92
|
+
* @returns {HookEvaluation}
|
|
93
|
+
*/
|
|
94
|
+
function evaluateHook(eventName, context) {
|
|
95
|
+
const projectRoot = context.projectRoot;
|
|
96
|
+
const actions = getHookActions(eventName, projectRoot);
|
|
97
|
+
|
|
98
|
+
if (actions.length === 0) {
|
|
99
|
+
return { event: eventName, blocked: false, passed: 0, failed: 0, skipped: 0, results: [] };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** @type {ActionResult[]} */
|
|
103
|
+
const results = [];
|
|
104
|
+
let blocked = false;
|
|
105
|
+
|
|
106
|
+
for (const action of actions) {
|
|
107
|
+
const result = evaluateAction(action, context);
|
|
108
|
+
results.push(result);
|
|
109
|
+
|
|
110
|
+
if (result.status === 'fail' && action.onFailure === 'block') {
|
|
111
|
+
blocked = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
event: eventName,
|
|
117
|
+
blocked,
|
|
118
|
+
passed: results.filter((r) => r.status === 'pass').length,
|
|
119
|
+
failed: results.filter((r) => r.status === 'fail').length,
|
|
120
|
+
skipped: results.filter((r) => r.status === 'skip').length,
|
|
121
|
+
results,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Evaluates a single action based on context.
|
|
127
|
+
*
|
|
128
|
+
* @param {object} action - Action definition from hooks.json
|
|
129
|
+
* @param {object} context - Evaluation context
|
|
130
|
+
* @returns {ActionResult}
|
|
131
|
+
*/
|
|
132
|
+
function evaluateAction(action, context) {
|
|
133
|
+
const actionLower = action.action.toLowerCase();
|
|
134
|
+
const projectRoot = context.projectRoot;
|
|
135
|
+
|
|
136
|
+
// File existence checks
|
|
137
|
+
if (actionLower.includes('session-context.md') || actionLower.includes('session-state.json')) {
|
|
138
|
+
const targetFile = actionLower.includes('session-context.md') ? 'session-context.md' : 'session-state.json';
|
|
139
|
+
const filePath = path.join(projectRoot, AGENT_DIR, targetFile);
|
|
140
|
+
const exists = fs.existsSync(filePath);
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
action: action.action,
|
|
144
|
+
severity: action.severity,
|
|
145
|
+
onFailure: action.onFailure,
|
|
146
|
+
status: exists ? 'pass' : 'fail',
|
|
147
|
+
reason: exists ? `${targetFile} exists` : `${targetFile} not found`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (actionLower.includes('loading-rules.json') || actionLower.includes('workflow-state.json')) {
|
|
152
|
+
const targetFile = actionLower.includes('loading-rules.json') ? 'loading-rules.json' : 'workflow-state.json';
|
|
153
|
+
const filePath = path.join(projectRoot, AGENT_DIR, ENGINE_DIR, targetFile);
|
|
154
|
+
const exists = fs.existsSync(filePath);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
action: action.action,
|
|
158
|
+
severity: action.severity,
|
|
159
|
+
onFailure: action.onFailure,
|
|
160
|
+
status: exists ? 'pass' : 'fail',
|
|
161
|
+
reason: exists ? `${targetFile} exists and readable` : `${targetFile} not found`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Git status check
|
|
166
|
+
if (actionLower.includes('git status')) {
|
|
167
|
+
const gitClean = context.gitClean !== undefined ? context.gitClean : true;
|
|
168
|
+
return {
|
|
169
|
+
action: action.action,
|
|
170
|
+
severity: action.severity,
|
|
171
|
+
onFailure: action.onFailure,
|
|
172
|
+
status: gitClean ? 'pass' : 'fail',
|
|
173
|
+
reason: gitClean ? 'Git status clean' : 'Git has uncommitted changes',
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Build/test/lint checks
|
|
178
|
+
if (actionLower.includes('build passes') || actionLower.includes('npm run build')) {
|
|
179
|
+
return {
|
|
180
|
+
action: action.action,
|
|
181
|
+
severity: action.severity,
|
|
182
|
+
onFailure: action.onFailure,
|
|
183
|
+
status: context.buildPass ? 'pass' : context.buildPass === false ? 'fail' : 'skip',
|
|
184
|
+
reason: context.buildPass ? 'Build passes' : context.buildPass === false ? 'Build failed' : 'Build not checked',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (actionLower.includes('tests pass') || actionLower.includes('npm test')) {
|
|
189
|
+
return {
|
|
190
|
+
action: action.action,
|
|
191
|
+
severity: action.severity,
|
|
192
|
+
onFailure: action.onFailure,
|
|
193
|
+
status: context.testsPass ? 'pass' : context.testsPass === false ? 'fail' : 'skip',
|
|
194
|
+
reason: context.testsPass ? 'Tests pass' : context.testsPass === false ? 'Tests failed' : 'Tests not checked',
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (actionLower.includes('lint passes') || actionLower.includes('npm run lint')) {
|
|
199
|
+
return {
|
|
200
|
+
action: action.action,
|
|
201
|
+
severity: action.severity,
|
|
202
|
+
onFailure: action.onFailure,
|
|
203
|
+
status: context.lintPass ? 'pass' : context.lintPass === false ? 'fail' : 'skip',
|
|
204
|
+
reason: context.lintPass ? 'Lint passes' : context.lintPass === false ? 'Lint failed' : 'Lint not checked',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Default: skip actions we can't evaluate programmatically
|
|
209
|
+
return {
|
|
210
|
+
action: action.action,
|
|
211
|
+
severity: action.severity,
|
|
212
|
+
onFailure: action.onFailure,
|
|
213
|
+
status: 'skip',
|
|
214
|
+
reason: 'Cannot evaluate programmatically — requires agent execution',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Lists all available hook events.
|
|
220
|
+
*
|
|
221
|
+
* @param {string} projectRoot - Root directory of the project
|
|
222
|
+
* @returns {Array<{ event: string, description: string, actionCount: number }>}
|
|
223
|
+
*/
|
|
224
|
+
function listEvents(projectRoot) {
|
|
225
|
+
const config = loadHooks(projectRoot);
|
|
226
|
+
|
|
227
|
+
return (config.hooks || []).map((hook) => ({
|
|
228
|
+
event: hook.event,
|
|
229
|
+
description: hook.description,
|
|
230
|
+
actionCount: (hook.actions || []).length,
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Generates a full hook readiness report for all events.
|
|
236
|
+
*
|
|
237
|
+
* @param {string} projectRoot - Root directory of the project
|
|
238
|
+
* @returns {{ events: HookEvaluation[], totalActions: number, readyCount: number }}
|
|
239
|
+
*/
|
|
240
|
+
function getHookReport(projectRoot) {
|
|
241
|
+
const events = listEvents(projectRoot);
|
|
242
|
+
const context = { projectRoot };
|
|
243
|
+
|
|
244
|
+
const evaluations = events.map((e) => evaluateHook(e.event, context));
|
|
245
|
+
const totalActions = evaluations.reduce((sum, e) => sum + e.passed + e.failed + e.skipped, 0);
|
|
246
|
+
const readyCount = evaluations.filter((e) => !e.blocked).length;
|
|
247
|
+
|
|
248
|
+
return { events: evaluations, totalActions, readyCount };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
module.exports = {
|
|
252
|
+
getHookActions,
|
|
253
|
+
evaluateHook,
|
|
254
|
+
listEvents,
|
|
255
|
+
getHookReport,
|
|
256
|
+
};
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devran AI Kit — IDE Configuration Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates IDE-specific configuration files from manifest.json
|
|
5
|
+
* for Cursor, OpenCode, and Codex. Single source of truth ensures
|
|
6
|
+
* all IDE configs stay in sync with the agent manifest.
|
|
7
|
+
*
|
|
8
|
+
* @module lib/ide-generator
|
|
9
|
+
* @since v4.1.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { CURSOR_DIR, OPENCODE_DIR, CODEX_DIR } = require('./constants');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {object} IdeFile
|
|
20
|
+
* @property {string} path - Relative path from project root
|
|
21
|
+
* @property {string} content - File content to write
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {object} IdeConfig
|
|
26
|
+
* @property {IdeFile[]} files - Array of files to generate
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {object} WriteResult
|
|
31
|
+
* @property {string[]} written - Files that were written
|
|
32
|
+
* @property {string[]} skipped - Files that were skipped (already exist)
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Condenses rules.md content to fit under 4K characters.
|
|
37
|
+
* Extracts operating constraints, coding style, and security requirements.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} rulesContent - Full rules.md content
|
|
40
|
+
* @returns {string} Condensed rules content
|
|
41
|
+
*/
|
|
42
|
+
function condenseRules(rulesContent) {
|
|
43
|
+
const sections = [];
|
|
44
|
+
|
|
45
|
+
sections.push('# Devran AI Kit Governance\n');
|
|
46
|
+
|
|
47
|
+
// Extract operating constraints table
|
|
48
|
+
const constraintsMatch = rulesContent.match(
|
|
49
|
+
/### Operating Constraints[\s\S]*?\n\n/
|
|
50
|
+
);
|
|
51
|
+
if (constraintsMatch) {
|
|
52
|
+
sections.push(constraintsMatch[0].trim());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Extract key protocols by matching the heading format generically
|
|
56
|
+
const protocolRegex = /### [A-Z]\. [\s\S]*?(?=###|$)/g;
|
|
57
|
+
let protocolMatch;
|
|
58
|
+
while ((protocolMatch = protocolRegex.exec(rulesContent)) !== null) {
|
|
59
|
+
const trimmed = protocolMatch[0].trim().split('\n').slice(0, 5).join('\n');
|
|
60
|
+
sections.push(trimmed);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Extract technical standards
|
|
64
|
+
const techMatch = rulesContent.match(
|
|
65
|
+
/## .* TECHNICAL STANDARDS[\s\S]*?(?=## |$)/
|
|
66
|
+
);
|
|
67
|
+
if (techMatch) {
|
|
68
|
+
sections.push(techMatch[0].trim().split('\n').slice(0, 12).join('\n'));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const result = sections.join('\n\n');
|
|
72
|
+
// Enforce 4K limit
|
|
73
|
+
return result.length > 4000 ? result.slice(0, 3997) + '...' : result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Generates Cursor IDE configuration with governance rules.
|
|
78
|
+
*
|
|
79
|
+
* @param {object} _manifest - Parsed manifest.json
|
|
80
|
+
* @param {string} rulesContent - Raw rules.md content
|
|
81
|
+
* @returns {IdeConfig}
|
|
82
|
+
*/
|
|
83
|
+
function generateCursorConfig(_manifest, rulesContent) {
|
|
84
|
+
const condensed = condenseRules(rulesContent);
|
|
85
|
+
const mdcContent = [
|
|
86
|
+
'---',
|
|
87
|
+
'description: "Devran AI Kit - Trust-Grade AI governance and coding standards"',
|
|
88
|
+
'alwaysApply: true',
|
|
89
|
+
'---',
|
|
90
|
+
'',
|
|
91
|
+
condensed,
|
|
92
|
+
'',
|
|
93
|
+
].join('\n');
|
|
94
|
+
|
|
95
|
+
return Object.freeze({
|
|
96
|
+
files: Object.freeze([
|
|
97
|
+
Object.freeze({
|
|
98
|
+
path: `${CURSOR_DIR}/rules/kit-governance.mdc`,
|
|
99
|
+
content: mdcContent,
|
|
100
|
+
}),
|
|
101
|
+
]),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Extracts agent descriptions from manifest for IDE config.
|
|
107
|
+
*
|
|
108
|
+
* @param {object} manifest - Parsed manifest.json
|
|
109
|
+
* @returns {{ planner: string, reviewer: string }}
|
|
110
|
+
*/
|
|
111
|
+
function extractAgentDescriptions(manifest) {
|
|
112
|
+
const agents = manifest?.capabilities?.agents?.items || [];
|
|
113
|
+
const plannerAgent = agents.find((a) => a.name === 'planner');
|
|
114
|
+
const reviewerAgent = agents.find((a) => a.name === 'code-reviewer');
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
planner: plannerAgent?.domain || 'Implementation planning specialist',
|
|
118
|
+
reviewer: reviewerAgent?.domain || 'Code review and security analysis',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Generates OpenCode IDE configuration.
|
|
124
|
+
*
|
|
125
|
+
* @param {object} manifest - Parsed manifest.json
|
|
126
|
+
* @returns {IdeConfig}
|
|
127
|
+
*/
|
|
128
|
+
function generateOpenCodeConfig(manifest) {
|
|
129
|
+
const descriptions = extractAgentDescriptions(manifest);
|
|
130
|
+
|
|
131
|
+
const config = {
|
|
132
|
+
$schema: 'https://opencode.ai/config.json',
|
|
133
|
+
model: 'anthropic/claude-sonnet-4-5',
|
|
134
|
+
small_model: 'anthropic/claude-haiku-4-5',
|
|
135
|
+
instructions: ['.agent/rules.md'],
|
|
136
|
+
agent: {
|
|
137
|
+
build: {
|
|
138
|
+
description: 'Primary development agent with Devran AI Kit governance',
|
|
139
|
+
model: 'anthropic/claude-sonnet-4-5',
|
|
140
|
+
},
|
|
141
|
+
planner: {
|
|
142
|
+
description: descriptions.planner,
|
|
143
|
+
model: 'anthropic/claude-sonnet-4-5',
|
|
144
|
+
},
|
|
145
|
+
reviewer: {
|
|
146
|
+
description: descriptions.reviewer,
|
|
147
|
+
model: 'anthropic/claude-sonnet-4-5',
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return Object.freeze({
|
|
153
|
+
files: Object.freeze([
|
|
154
|
+
Object.freeze({
|
|
155
|
+
path: `${OPENCODE_DIR}/opencode.json`,
|
|
156
|
+
content: JSON.stringify(config, null, 2) + '\n',
|
|
157
|
+
}),
|
|
158
|
+
]),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Serializes a value to TOML format.
|
|
164
|
+
*
|
|
165
|
+
* @param {string|number|boolean|string[]} value - Value to serialize
|
|
166
|
+
* @returns {string} TOML-formatted value
|
|
167
|
+
*/
|
|
168
|
+
function serializeTomlValue(value) {
|
|
169
|
+
if (typeof value === 'string') {
|
|
170
|
+
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
171
|
+
}
|
|
172
|
+
if (typeof value === 'boolean' || typeof value === 'number') {
|
|
173
|
+
return String(value);
|
|
174
|
+
}
|
|
175
|
+
if (Array.isArray(value)) {
|
|
176
|
+
const items = value.map(v => serializeTomlValue(v));
|
|
177
|
+
return `[${items.join(', ')}]`;
|
|
178
|
+
}
|
|
179
|
+
return `"${String(value)}"`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Minimal TOML serializer for IDE config generation.
|
|
184
|
+
*
|
|
185
|
+
* Intentionally limited to the subset needed by Codex config:
|
|
186
|
+
* strings, numbers, booleans, arrays, and single-level nested objects.
|
|
187
|
+
* Full TOML spec (inline tables, arrays of tables, multi-line strings)
|
|
188
|
+
* is not supported. This is a zero-dependency design decision — we avoid
|
|
189
|
+
* adding @iarna/toml to maintain the zero-dependency guarantee.
|
|
190
|
+
*
|
|
191
|
+
* @param {object} obj - Object to serialize
|
|
192
|
+
* @returns {string} TOML-formatted string
|
|
193
|
+
*/
|
|
194
|
+
function serializeToml(obj) {
|
|
195
|
+
const lines = [];
|
|
196
|
+
const sections = [];
|
|
197
|
+
|
|
198
|
+
// First pass: top-level scalars
|
|
199
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
200
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
201
|
+
sections.push([key, value]);
|
|
202
|
+
} else {
|
|
203
|
+
lines.push(`${key} = ${serializeTomlValue(value)}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Second pass: nested sections
|
|
208
|
+
for (const [sectionKey, sectionValue] of sections) {
|
|
209
|
+
lines.push('');
|
|
210
|
+
|
|
211
|
+
// Check if section contains further nested objects
|
|
212
|
+
const hasNestedObjects = Object.values(sectionValue).some(
|
|
213
|
+
(v) => v !== null && typeof v === 'object' && !Array.isArray(v)
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
if (hasNestedObjects) {
|
|
217
|
+
// Emit section header once for top-level scalars
|
|
218
|
+
const scalars = Object.entries(sectionValue).filter(
|
|
219
|
+
([, v]) => v === null || typeof v !== 'object' || Array.isArray(v)
|
|
220
|
+
);
|
|
221
|
+
if (scalars.length > 0) {
|
|
222
|
+
lines.push(`[${sectionKey}]`);
|
|
223
|
+
for (const [subKey, subValue] of scalars) {
|
|
224
|
+
lines.push(`${subKey} = ${serializeTomlValue(subValue)}`);
|
|
225
|
+
}
|
|
226
|
+
lines.push('');
|
|
227
|
+
}
|
|
228
|
+
// Then nested subsections
|
|
229
|
+
for (const [subKey, subValue] of Object.entries(sectionValue)) {
|
|
230
|
+
if (subValue !== null && typeof subValue === 'object' && !Array.isArray(subValue)) {
|
|
231
|
+
lines.push(`[${sectionKey}.${subKey}]`);
|
|
232
|
+
for (const [innerKey, innerValue] of Object.entries(subValue)) {
|
|
233
|
+
lines.push(`${innerKey} = ${serializeTomlValue(innerValue)}`);
|
|
234
|
+
}
|
|
235
|
+
lines.push('');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
} else {
|
|
239
|
+
lines.push(`[${sectionKey}]`);
|
|
240
|
+
for (const [innerKey, innerValue] of Object.entries(sectionValue)) {
|
|
241
|
+
lines.push(`${innerKey} = ${serializeTomlValue(innerValue)}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return lines.join('\n') + '\n';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Generates Codex IDE configuration.
|
|
251
|
+
*
|
|
252
|
+
* @param {object} manifest - Parsed manifest.json
|
|
253
|
+
* @returns {IdeConfig}
|
|
254
|
+
*/
|
|
255
|
+
function generateCodexConfig(manifest) {
|
|
256
|
+
const descriptions = extractAgentDescriptions(manifest);
|
|
257
|
+
|
|
258
|
+
const config = {
|
|
259
|
+
approval_policy: 'suggest',
|
|
260
|
+
sandbox_mode: 'workspace-write',
|
|
261
|
+
model: {
|
|
262
|
+
default: 'anthropic/claude-sonnet-4-5',
|
|
263
|
+
},
|
|
264
|
+
agents: {
|
|
265
|
+
planner: {
|
|
266
|
+
description: descriptions.planner,
|
|
267
|
+
model: 'anthropic/claude-sonnet-4-5',
|
|
268
|
+
},
|
|
269
|
+
reviewer: {
|
|
270
|
+
description: descriptions.reviewer,
|
|
271
|
+
model: 'anthropic/claude-sonnet-4-5',
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const tomlHeader = [
|
|
277
|
+
'# Devran AI Kit — Codex Configuration',
|
|
278
|
+
'# Generated by kit init — do not edit manually',
|
|
279
|
+
'',
|
|
280
|
+
'',
|
|
281
|
+
].join('\n');
|
|
282
|
+
|
|
283
|
+
return Object.freeze({
|
|
284
|
+
files: Object.freeze([
|
|
285
|
+
Object.freeze({
|
|
286
|
+
path: `${CODEX_DIR}/config.toml`,
|
|
287
|
+
content: tomlHeader + serializeToml(config),
|
|
288
|
+
}),
|
|
289
|
+
]),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Validates that a file path resolves under the project root.
|
|
295
|
+
* Prevents path traversal attacks.
|
|
296
|
+
*
|
|
297
|
+
* @param {string} projectRoot - Absolute path to project root
|
|
298
|
+
* @param {string} filePath - Relative file path to validate
|
|
299
|
+
* @returns {string} Resolved absolute path
|
|
300
|
+
* @throws {Error} If path resolves outside project root
|
|
301
|
+
*/
|
|
302
|
+
function validatePath(projectRoot, filePath) {
|
|
303
|
+
if (typeof filePath !== 'string' || filePath.includes('\0')) {
|
|
304
|
+
throw new Error(`Invalid file path: ${filePath}`);
|
|
305
|
+
}
|
|
306
|
+
const resolved = path.resolve(projectRoot, filePath);
|
|
307
|
+
const normalizedRoot = path.resolve(projectRoot) + path.sep;
|
|
308
|
+
|
|
309
|
+
if (!resolved.startsWith(normalizedRoot)) {
|
|
310
|
+
throw new Error(`Path traversal detected: ${filePath}`);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return resolved;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Writes a file atomically using temp-file-then-rename pattern.
|
|
318
|
+
* Falls back to direct write on Windows EPERM/EACCES errors.
|
|
319
|
+
*
|
|
320
|
+
* @param {string} absolutePath - Absolute destination path
|
|
321
|
+
* @param {string} content - File content
|
|
322
|
+
*/
|
|
323
|
+
function atomicWriteFile(absolutePath, content) {
|
|
324
|
+
const dir = path.dirname(absolutePath);
|
|
325
|
+
const tempPath = `${absolutePath}.tmp`;
|
|
326
|
+
|
|
327
|
+
if (!fs.existsSync(dir)) {
|
|
328
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
try {
|
|
332
|
+
fs.writeFileSync(tempPath, content, 'utf-8');
|
|
333
|
+
fs.renameSync(tempPath, absolutePath);
|
|
334
|
+
} catch (renameErr) {
|
|
335
|
+
const isTransient = renameErr.code === 'EPERM' || renameErr.code === 'EACCES';
|
|
336
|
+
if (isTransient) {
|
|
337
|
+
fs.writeFileSync(absolutePath, content, 'utf-8');
|
|
338
|
+
} else {
|
|
339
|
+
throw renameErr;
|
|
340
|
+
}
|
|
341
|
+
} finally {
|
|
342
|
+
// Ensure temp file is always cleaned up
|
|
343
|
+
try {
|
|
344
|
+
if (fs.existsSync(tempPath)) fs.unlinkSync(tempPath);
|
|
345
|
+
} catch { /* non-critical */ }
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Rolls back previously written files during transactional failure.
|
|
351
|
+
*
|
|
352
|
+
* @param {string} projectRoot - Absolute project root
|
|
353
|
+
* @param {string[]} writtenPaths - Relative paths of written files
|
|
354
|
+
*/
|
|
355
|
+
function rollbackWrittenFiles(projectRoot, writtenPaths) {
|
|
356
|
+
for (const writtenPath of writtenPaths) {
|
|
357
|
+
try {
|
|
358
|
+
const abs = path.resolve(projectRoot, writtenPath);
|
|
359
|
+
if (fs.existsSync(abs)) fs.unlinkSync(abs);
|
|
360
|
+
} catch { /* rollback failure is non-critical */ }
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Writes IDE configuration files to the project directory.
|
|
366
|
+
* Uses atomic write pattern (write to .tmp, then rename).
|
|
367
|
+
* Transactional: rolls back all written files if any write fails.
|
|
368
|
+
*
|
|
369
|
+
* @param {string} projectRoot - Absolute path to project root
|
|
370
|
+
* @param {IdeConfig[]} configs - Array of IDE configurations
|
|
371
|
+
* @param {object} [options={}] - Write options
|
|
372
|
+
* @param {boolean} [options.force=false] - Overwrite existing files
|
|
373
|
+
* @param {boolean} [options.skipExisting=false] - Silently skip existing
|
|
374
|
+
* @returns {WriteResult}
|
|
375
|
+
*/
|
|
376
|
+
function writeIdeConfigs(projectRoot, configs, options = {}) {
|
|
377
|
+
if (typeof projectRoot !== 'string' || !path.isAbsolute(projectRoot)) {
|
|
378
|
+
throw new TypeError('projectRoot must be an absolute path string');
|
|
379
|
+
}
|
|
380
|
+
if (!Array.isArray(configs)) {
|
|
381
|
+
throw new TypeError('configs must be an array');
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const { force = false, skipExisting = false } = options;
|
|
385
|
+
const written = [];
|
|
386
|
+
const skipped = [];
|
|
387
|
+
|
|
388
|
+
// Collect all files from all configs
|
|
389
|
+
const allFiles = configs.flatMap((config) => config.files || []);
|
|
390
|
+
|
|
391
|
+
for (const file of allFiles) {
|
|
392
|
+
const absolutePath = validatePath(projectRoot, file.path);
|
|
393
|
+
|
|
394
|
+
if (fs.existsSync(absolutePath) && !force) {
|
|
395
|
+
skipped.push(file.path);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
try {
|
|
400
|
+
atomicWriteFile(absolutePath, file.content);
|
|
401
|
+
written.push(file.path);
|
|
402
|
+
} catch (err) {
|
|
403
|
+
rollbackWrittenFiles(projectRoot, written);
|
|
404
|
+
throw new Error(`Failed to write ${file.path}: ${err.message}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return Object.freeze({ written: Object.freeze([...written]), skipped: Object.freeze([...skipped]) });
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Generates all IDE configurations from manifest and rules.
|
|
413
|
+
*
|
|
414
|
+
* @param {object} manifest - Parsed manifest.json
|
|
415
|
+
* @param {string} rulesContent - Raw rules.md content
|
|
416
|
+
* @returns {IdeConfig[]}
|
|
417
|
+
*/
|
|
418
|
+
function generateAllIdeConfigs(manifest, rulesContent) {
|
|
419
|
+
return [
|
|
420
|
+
generateCursorConfig(manifest, rulesContent),
|
|
421
|
+
generateOpenCodeConfig(manifest),
|
|
422
|
+
generateCodexConfig(manifest),
|
|
423
|
+
];
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
module.exports = Object.freeze({
|
|
427
|
+
generateCursorConfig,
|
|
428
|
+
generateOpenCodeConfig,
|
|
429
|
+
generateCodexConfig,
|
|
430
|
+
generateAllIdeConfigs,
|
|
431
|
+
writeIdeConfigs,
|
|
432
|
+
serializeToml,
|
|
433
|
+
condenseRules,
|
|
434
|
+
});
|