@rigstate/rules-engine 0.6.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 ADDED
@@ -0,0 +1,38 @@
1
+ # @rigstate/rules-engine
2
+
3
+ The universal rules generation engine for Rigstate. This package centralizes the logic for creating context-aware rules for various AI-powered IDEs (Cursor, Windsurf, VS Code, etc.).
4
+
5
+ ## 🚀 Features
6
+
7
+ - **Multi-Agent Identity**: Generates instructions for specialized AI agents (The Nordic Trinity).
8
+ - **Guardian Rules**: Enforces architectural constraints like file size limits and type safety.
9
+ - **Roadmap Injection**: Dynamically injects the current active roadmap step into rules.
10
+ - **Stack DNA**: Applies tech-stack specific constraints.
11
+ - **Multi-IDE Support**: Supports different rule formats and filenames (.cursorrules, .windsurfrules, etc.).
12
+ - **Strict Tool Ownership**: Binds MCP tools to specific Agent IDs for persona consistency.
13
+
14
+ ## 📦 Usage
15
+
16
+ ```typescript
17
+ import { generateRuleContent, fetchLegacyStats, fetchActiveAgents } from '@rigstate/rules-engine';
18
+
19
+ const rules = generateRuleContent(
20
+ project,
21
+ techStackList,
22
+ roadmapChunks,
23
+ 'cursor', // preference
24
+ legacyStats,
25
+ activeAgents
26
+ );
27
+ ```
28
+
29
+ ## 🏗️ Architecture
30
+
31
+ This package is designed to be environment-agnostic. It can be used in:
32
+ 1. **Next.js Web App**: For rule previews and GitHub synchronization.
33
+ 2. **MCP Server**: For real-time rule delivery directly to the IDE.
34
+ 3. **CLI**: for local rule validation.
35
+
36
+ ## 🛡️ License
37
+
38
+ MIT
@@ -0,0 +1,38 @@
1
+ import { RuleGenerationContext, IDEProvider, MultiFileRuleResult, TableMetadata } from './types';
2
+ export type { RuleGenerationContext, IDEProvider, ProjectSettings, RuleFile, MultiFileRuleResult, AgentSkill } from './types';
3
+ export { IDE_FILE_NAMES } from './types';
4
+ export { generateAvailableSkillsSection, generateSkillFileContent, getRigstateStandardSkills } from './sections/skills';
5
+ /**
6
+ * Generate modular IDE rule content optimized for AI coding assistants.
7
+ * v2.4.0: Added Agent IDs, Tool Binding, and Strict Ownership.
8
+ * Refactored into sub-modules for Guardian Compliance (400-line limit).
9
+ */
10
+ /**
11
+ * Generate modular IDE rule content optimized for AI coding assistants.
12
+ * v2.4.0: Added Agent IDs, Tool Binding, and Strict Ownership.
13
+ * Refactored into sub-modules for Guardian Compliance (400-line limit).
14
+ */
15
+ export declare function generateRuleContent(project: RuleGenerationContext["project"], stack: string[], roadmap: RuleGenerationContext["roadmap"], ide?: IDEProvider, legacyStats?: RuleGenerationContext["legacyStats"], activeAgents?: RuleGenerationContext["activeAgents"], lean?: boolean): string;
16
+ /**
17
+ * Generate multiple modular rule files optimized for modern Cursor (.mdc)
18
+ * and universal AGENTS.md format.
19
+ */
20
+ export declare function generateRuleFiles(project: RuleGenerationContext["project"], stack: string[], roadmap: RuleGenerationContext["roadmap"], ide?: IDEProvider, legacyStats?: RuleGenerationContext["legacyStats"], activeAgents?: RuleGenerationContext["activeAgents"], databaseMetadata?: TableMetadata[]): MultiFileRuleResult;
21
+ export declare function fetchLegacyStats(supabase: any, projectId: string): Promise<RuleGenerationContext["legacyStats"]>;
22
+ /**
23
+ * Fetch active agents sorted by authority level
24
+ * Note: Pass in a SupabaseClient instance (browser or server)
25
+ */
26
+ export declare function fetchActiveAgents(supabase: any): Promise<RuleGenerationContext["activeAgents"]>;
27
+ export declare function mergeRuleContent(existingContent: string, newRules: string): string;
28
+ /**
29
+ * Fetch project tech stack from database.
30
+ * Falls back to common defaults if no tags are defined.
31
+ * Note: Pass in a SupabaseClient instance (browser or server)
32
+ */
33
+ export declare function fetchProjectTechStack(supabase: any, projectId: string, fallbackStack?: string[]): Promise<string[]>;
34
+ /**
35
+ * Get the appropriate filename for a given IDE provider.
36
+ * Convenience wrapper around IDE_FILE_NAMES constant.
37
+ */
38
+ export declare function getFileNameForIDE(ide: IDEProvider): string;
package/dist/index.js ADDED
@@ -0,0 +1,347 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRigstateStandardSkills = exports.generateSkillFileContent = exports.generateAvailableSkillsSection = exports.IDE_FILE_NAMES = void 0;
4
+ exports.generateRuleContent = generateRuleContent;
5
+ exports.generateRuleFiles = generateRuleFiles;
6
+ exports.fetchLegacyStats = fetchLegacyStats;
7
+ exports.fetchActiveAgents = fetchActiveAgents;
8
+ exports.mergeRuleContent = mergeRuleContent;
9
+ exports.fetchProjectTechStack = fetchProjectTechStack;
10
+ exports.getFileNameForIDE = getFileNameForIDE;
11
+ const types_1 = require("./types");
12
+ const identity_1 = require("./sections/identity");
13
+ const stack_dna_1 = require("./sections/stack-dna");
14
+ const current_step_1 = require("./sections/current-step");
15
+ const workflow_1 = require("./sections/workflow");
16
+ const tooling_1 = require("./sections/tooling");
17
+ const skills_1 = require("./sections/skills");
18
+ const mdc_1 = require("./utils/mdc");
19
+ var types_2 = require("./types");
20
+ Object.defineProperty(exports, "IDE_FILE_NAMES", { enumerable: true, get: function () { return types_2.IDE_FILE_NAMES; } });
21
+ var skills_2 = require("./sections/skills");
22
+ Object.defineProperty(exports, "generateAvailableSkillsSection", { enumerable: true, get: function () { return skills_2.generateAvailableSkillsSection; } });
23
+ Object.defineProperty(exports, "generateSkillFileContent", { enumerable: true, get: function () { return skills_2.generateSkillFileContent; } });
24
+ Object.defineProperty(exports, "getRigstateStandardSkills", { enumerable: true, get: function () { return skills_2.getRigstateStandardSkills; } });
25
+ const RIGSTATE_START = "RIGSTATE_START";
26
+ const RIGSTATE_END = "RIGSTATE_END";
27
+ const ENGINE_VERSION = "3.0.0";
28
+ /**
29
+ * Generate modular IDE rule content optimized for AI coding assistants.
30
+ * v2.4.0: Added Agent IDs, Tool Binding, and Strict Ownership.
31
+ * Refactored into sub-modules for Guardian Compliance (400-line limit).
32
+ */
33
+ /**
34
+ * Generate modular IDE rule content optimized for AI coding assistants.
35
+ * v2.4.0: Added Agent IDs, Tool Binding, and Strict Ownership.
36
+ * Refactored into sub-modules for Guardian Compliance (400-line limit).
37
+ */
38
+ function generateRuleContent(project, stack, roadmap, ide = 'cursor', legacyStats, activeAgents, lean = false) {
39
+ const sections = [];
40
+ sections.push('# 🚀 Rigstate Supervisor v2.4 (Context-Aware)');
41
+ // SECTION 0: HIERARCHY (Top Priority)
42
+ sections.push(`IMPORTANT: Internal agent coordination must always use the provided Agent IDs. Display names are for user-facing chat only. When invoking tools or referencing hierarchy, use the ID as the primary key.
43
+
44
+ ## ⚖️ AGENT HIERARCHY & AUTHORITY
45
+ You must defer to the instructions of agents with higher Authority Levels (10 being highest).
46
+ Security and Architecture (Levels 8-10) always override creative or implementation suggestions (Levels 1-5).`);
47
+ // SECTION 1: IDENTITY (Multi-Agent Ecosystem)
48
+ // Always keep Identity in root for instant persona recognition
49
+ const identitySection = (0, identity_1.generateIdentitySection)(project, ide, activeAgents);
50
+ sections.push(identitySection);
51
+ // SECTION 1.5: SKILLS (Discovery Layer)
52
+ const skills = (0, skills_1.getRigstateStandardSkills)();
53
+ const skillsSection = (0, skills_1.generateAvailableSkillsSection)(skills);
54
+ if (skillsSection) {
55
+ sections.push(skillsSection);
56
+ }
57
+ // SECTION 2: STACK DNA + GUARDIAN RULES
58
+ // In Lean mode (Cursor context), we trust rigstate-guardian.mdc to handle this.
59
+ if (!lean) {
60
+ const stackDnaSection = (0, stack_dna_1.generateStackDnaSection)(project, stack, legacyStats);
61
+ sections.push(stackDnaSection);
62
+ }
63
+ // SECTION 3: CURRENT STEP (Active Focus Injection)
64
+ // In Lean mode, rigstate-roadmap.mdc handles this dynamically.
65
+ if (!lean) {
66
+ const currentStepSection = (0, current_step_1.generateCurrentStepSection)(roadmap);
67
+ if (currentStepSection) {
68
+ sections.push(currentStepSection);
69
+ }
70
+ }
71
+ // SECTION 4: WORKFLOW (How to Parse Roadmap Steps)
72
+ // Frank's "Supervisor Mode" logic.
73
+ // We KEEP this in Lean mode because it defines the core behavior of the agent globally.
74
+ // However, if it's identical to rigstate-workflow.mdc, we might consider linking.
75
+ // But Frank's protocol is the "Operating System" of the session.
76
+ const workflowSection = (0, workflow_1.generateWorkflowSection)(ide);
77
+ sections.push(workflowSection);
78
+ // SECTION 5: TOOLING (CLI + MCP Integration)
79
+ // In Lean mode, rigstate-workflow.mdc has the specific CLI commands and tools.
80
+ if (!lean) {
81
+ const toolingSection = (0, tooling_1.generateToolingSection)(activeAgents);
82
+ sections.push(toolingSection);
83
+ }
84
+ else {
85
+ // Add a pointer in Lean mode
86
+ sections.push(`## 🔧 TOOLING & SPECIFIC RULES
87
+ > **OPTIMIZED MODE:** Detailed technical rules, CLI commands, and tech stack constraints are loaded dynamically from \`.cursor/rules/*.mdc\` based on the files you interact with.
88
+ > - **Stack & Guardian:** See \`rigstate-guardian.mdc\`
89
+ > - **Roadmap & Tasks:** See \`rigstate-roadmap.mdc\`
90
+ > - **Tools & Workflow:** See \`rigstate-workflow.mdc\``);
91
+ }
92
+ // Generate IDE-specific header
93
+ const headerMap = {
94
+ cursor: `# Cursor Project Rules: ${project.name}`,
95
+ antigravity: `# Antigravity Project Rules: ${project.name}`,
96
+ windsurf: `# Windsurf Project Rules: ${project.name}`,
97
+ vscode: `# VS Code Project Rules: ${project.name}`,
98
+ copilot: `# GitHub Copilot Instructions: ${project.name}`,
99
+ generic: `# Project Conventions: ${project.name}`
100
+ };
101
+ const header = headerMap[ide] || `# Project Rules: ${project.name}`;
102
+ return `${RIGSTATE_START}
103
+ ${header}
104
+ > Generated by Rigstate v2.5.0 | Project ID: ${project.id} | Last synced: ${new Date().toISOString()}
105
+ > ${lean ? '⚡ LEAN MODE ACTIVE: Redundant context offloaded to .cursor/rules/*.mdc' : '📦 FULL MODE ACTIVE'}
106
+
107
+ ⚠️ **SYSTEM NOTE:** Changes made to this Guardian template propagate to ALL Rigstate projects on next sync.
108
+ 🛡️ **Guardian v2.5 Upgrade Applied:** IMPACT_GUARD + BUILD_INTEGRITY now active globally.
109
+
110
+ ${sections.join('\n\n---\n\n')}
111
+ ${RIGSTATE_END}`;
112
+ }
113
+ /**
114
+ * Generate multiple modular rule files optimized for modern Cursor (.mdc)
115
+ * and universal AGENTS.md format.
116
+ */
117
+ function generateRuleFiles(project, stack, roadmap, ide = 'cursor', legacyStats, activeAgents, databaseMetadata) {
118
+ const files = [];
119
+ // 1. GENERATE AGENTS.MD (Universal Root File - Context Reference)
120
+ const agentTable = activeAgents?.map((a) => `| **${a.name}** | \`${a.key}\` | ${a.job_title} | ${a.primary_mission || 'Specialist'} |`).join('\n') || '| - | - | - | No agents configured |';
121
+ const agentsMdContent = `# 🤖 AI Agent Context: ${project.name}
122
+ > **Rigstate v${ENGINE_VERSION}** | Project ID: \`${project.id}\`
123
+
124
+ This file describes the **specialist personas** available in this project.
125
+ These are **context providers**, not active controllers. The IDE agent (you) remains in full control of code execution.
126
+
127
+ ## 📋 Available Specialists
128
+ | Name | Key | Role | Specialty |
129
+ |:--- |:--- |:--- |:--- |
130
+ ${agentTable}
131
+
132
+ ## 🔍 How to Use This Context
133
+ 1. **Read their expertise**: Each specialist has a defined area of knowledge (architecture, documentation, history).
134
+ 2. **Adopt their perspective**: When working in their domain, consider their guidelines.
135
+ 3. **Call MCP tools if needed**: Some specialists have associated tools (e.g., \`generate_professional_pdf\` for The Scribe).
136
+
137
+ ## ⚠️ Important
138
+ - These personas do **NOT** execute code or override your decisions.
139
+ - They provide **context and guidelines** that you apply at your discretion.
140
+ - Authority levels indicate priority of guidelines when they conflict (higher = stronger recommendation).
141
+
142
+ ---
143
+ *Generated by Rigstate. Run \`rigstate sync\` to refresh.*`;
144
+ files.push({
145
+ path: 'AGENTS.md',
146
+ content: agentsMdContent,
147
+ metadata: { description: "Project hierarchy and agent identities" }
148
+ });
149
+ // 2. GENERATE SYSTEM-SPECIFIC MONO-FILE (Fallback/Baseline)
150
+ // If IDE is Cursor, we use LEAN mode for the root .cursorrules file
151
+ // because we are generating specific .mdc files below that cover the details.
152
+ const isLean = ide === 'cursor';
153
+ const masterFileName = getFileNameForIDE(ide);
154
+ const monoContent = generateRuleContent(project, stack, roadmap, ide, legacyStats, activeAgents, isLean);
155
+ files.push({
156
+ path: masterFileName,
157
+ content: monoContent,
158
+ metadata: { description: `Master rules file for ${ide}` }
159
+ });
160
+ // 3. GENERATE MODULAR (.mdc) RULES (Cursor, Antigravity, VS Code)
161
+ if (ide === 'cursor' || ide === 'antigravity' || ide === 'vscode') {
162
+ // Identity & Context
163
+ files.push({
164
+ path: '.cursor/rules/rigstate-identity.mdc',
165
+ content: (0, mdc_1.wrapMdc)((0, identity_1.generateIdentitySection)(project, ide, activeAgents), {
166
+ description: "Project context and specialist personas",
167
+ alwaysApply: true
168
+ })
169
+ });
170
+ // Guardian & Stack DNA
171
+ files.push({
172
+ path: '.cursor/rules/rigstate-guardian.mdc',
173
+ content: (0, mdc_1.wrapMdc)((0, stack_dna_1.generateStackDnaSection)(project, stack, legacyStats), {
174
+ description: "Governance rules, tech stack constraints, and file size limits",
175
+ globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.sql"],
176
+ alwaysApply: true
177
+ })
178
+ });
179
+ // Current Focus (Roadmap)
180
+ const currentStep = (0, current_step_1.generateCurrentStepSection)(roadmap);
181
+ if (currentStep) {
182
+ files.push({
183
+ path: '.cursor/rules/rigstate-roadmap.mdc',
184
+ content: (0, mdc_1.wrapMdc)(currentStep, {
185
+ description: "Active sprint focus and current roadmap step details",
186
+ alwaysApply: true
187
+ })
188
+ });
189
+ }
190
+ // Workflow & Tooling
191
+ files.push({
192
+ path: '.cursor/rules/rigstate-workflow.mdc',
193
+ content: (0, mdc_1.wrapMdc)((0, workflow_1.generateWorkflowSection)(ide) + '\n\n' + (0, tooling_1.generateToolingSection)(activeAgents), {
194
+ description: "Coding workflows, CLI usage, and tool binding rules",
195
+ alwaysApply: true
196
+ })
197
+ });
198
+ // Database Specific (scoped)
199
+ let dbContent = "## 🗄️ Database Standards\n- Always verify RLS policies for new tables.\n- Use `supabase/migrations` for DDL changes.\n- Reference `types/supabase.ts` for strictly typed queries.";
200
+ if (databaseMetadata && databaseMetadata.length > 0) {
201
+ const securedCount = databaseMetadata.filter(t => t.rls_enabled).length;
202
+ const unsecuredCount = databaseMetadata.length - securedCount;
203
+ const unsecuredTables = databaseMetadata.filter(t => !t.rls_enabled).map(t => t.table_name);
204
+ dbContent = `## 🗄️ Database Context: ${databaseMetadata.length} Tables
205
+ > **Security Check:** ${securedCount} Secured | ${unsecuredCount} Unsecured
206
+
207
+ ### ⚠️ Security Attention Required
208
+ ${unsecuredTables.length > 0
209
+ ? unsecuredTables.map(t => `- 🔴 **${t}**: RLS Disabled`).join('\n')
210
+ : "- ✅ All tables have Row Level Security enabled."}
211
+
212
+ ### 📋 Schema Reference
213
+ | Table | RLS | Policies | Cols | Key Features |
214
+ | :--- | :---: | :---: | :---: | :--- |
215
+ ${databaseMetadata.map(t => {
216
+ const features = [];
217
+ if (t.has_user_id)
218
+ features.push('User-Scoped');
219
+ if (t.has_created_at)
220
+ features.push('Timestamps');
221
+ return `| \`${t.table_name}\` | ${t.rls_enabled ? '✅' : '❌'} | ${t.policy_count} | ${t.column_count} | ${features.join(', ') || '-'} |`;
222
+ }).join('\n')}
223
+
224
+ ### 🛡️ Development Rules
225
+ 1. **RLS is MANDATORY:** All tables containing user data must have RLS enabled.
226
+ 2. **Use RPCs for Complex Logic:** Do not put complex business logic in client-side queries.
227
+ 3. **Migrations:** Always use \`supabase/migrations\` for schema changes.`;
228
+ }
229
+ files.push({
230
+ path: '.cursor/rules/rigstate-database.mdc',
231
+ content: (0, mdc_1.wrapMdc)(dbContent, {
232
+ description: "Live database schema, RLS status, and table metadata",
233
+ globs: ["supabase/**/*", "**/*.sql", "**/lib/supabase/**"],
234
+ alwaysApply: databaseMetadata && databaseMetadata.length > 0 ? true : false
235
+ })
236
+ });
237
+ // 4. GENERATE AGENT SKILLS (.agent/skills/<name>/SKILL.md)
238
+ const rigstateSkills = (0, skills_1.getRigstateStandardSkills)();
239
+ for (const skill of rigstateSkills) {
240
+ files.push({
241
+ path: `.agent/skills/${skill.name}/SKILL.md`,
242
+ content: (0, skills_1.generateSkillFileContent)(skill),
243
+ metadata: { description: skill.description }
244
+ });
245
+ }
246
+ }
247
+ return {
248
+ files,
249
+ suggestedIde: ide,
250
+ version: ENGINE_VERSION
251
+ };
252
+ }
253
+ async function fetchLegacyStats(supabase, projectId) {
254
+ const { data: chunks } = await supabase
255
+ .from('roadmap_chunks')
256
+ .select('is_legacy')
257
+ .eq('project_id', projectId);
258
+ if (!chunks)
259
+ return { total: 0, legacyCount: 0, activeCount: 0 };
260
+ const legacyCount = (chunks || []).filter((c) => c.is_legacy === true).length;
261
+ const activeCount = (chunks || []).filter((c) => c.is_legacy !== true).length;
262
+ return {
263
+ total: (chunks || []).length,
264
+ legacyCount,
265
+ activeCount
266
+ };
267
+ }
268
+ /**
269
+ * Fetch active agents sorted by authority level
270
+ * Note: Pass in a SupabaseClient instance (browser or server)
271
+ */
272
+ async function fetchActiveAgents(supabase) {
273
+ const { data: prompts } = await supabase
274
+ .from('system_prompts')
275
+ .select('id, key, content, name, display_name, job_title, authority_level, primary_mission, trigger_keywords')
276
+ .eq('include_in_rules', true)
277
+ .eq('is_active', true)
278
+ .order('authority_level', { ascending: false });
279
+ if (!prompts)
280
+ return [];
281
+ return (prompts || []).map((p) => ({
282
+ id: p.id,
283
+ key: p.key,
284
+ name: p.display_name || p.name || p.key,
285
+ job_title: p.job_title || 'Specialist Agent',
286
+ content: p.content,
287
+ authority_level: (() => {
288
+ if (p.authority_level === null || p.authority_level === undefined) {
289
+ throw new Error(`Agent ${p.key} is missing authority_level. Update via CMS.`);
290
+ }
291
+ return p.authority_level;
292
+ })(),
293
+ primary_mission: p.primary_mission || undefined,
294
+ trigger_keywords: p.trigger_keywords || undefined
295
+ }));
296
+ }
297
+ function mergeRuleContent(existingContent, newRules) {
298
+ const startIndex = existingContent.indexOf(RIGSTATE_START);
299
+ const endIndex = existingContent.indexOf(RIGSTATE_END);
300
+ if (startIndex !== -1 && endIndex !== -1) {
301
+ const before = existingContent.substring(0, startIndex);
302
+ const after = existingContent.substring(endIndex + RIGSTATE_END.length);
303
+ return before + newRules + after;
304
+ }
305
+ else {
306
+ return existingContent + "\n\n" + newRules;
307
+ }
308
+ }
309
+ /**
310
+ * Fetch project tech stack from database.
311
+ * Falls back to common defaults if no tags are defined.
312
+ * Note: Pass in a SupabaseClient instance (browser or server)
313
+ */
314
+ async function fetchProjectTechStack(supabase, projectId, fallbackStack = ['Next.js', 'TypeScript', 'Supabase', 'Tailwind CSS']) {
315
+ try {
316
+ // Try to fetch from project_tech_tags table
317
+ const { data: tags, error } = await supabase
318
+ .from('project_tech_tags')
319
+ .select('name')
320
+ .eq('project_id', projectId);
321
+ if (error || !tags || tags.length === 0) {
322
+ // Fallback: Check project metadata for stack info
323
+ const { data: project } = await supabase
324
+ .from('projects')
325
+ .select('functional_spec')
326
+ .eq('id', projectId)
327
+ .single();
328
+ // Extract tech from functional_spec if available
329
+ if (project?.functional_spec?.techStack) {
330
+ return project.functional_spec.techStack;
331
+ }
332
+ return fallbackStack;
333
+ }
334
+ return tags.map((t) => t.name);
335
+ }
336
+ catch (error) {
337
+ console.warn('fetchProjectTechStack: Failed to fetch, using fallback', error);
338
+ return fallbackStack;
339
+ }
340
+ }
341
+ /**
342
+ * Get the appropriate filename for a given IDE provider.
343
+ * Convenience wrapper around IDE_FILE_NAMES constant.
344
+ */
345
+ function getFileNameForIDE(ide) {
346
+ return types_1.IDE_FILE_NAMES[ide] || types_1.IDE_FILE_NAMES.cursor;
347
+ }
@@ -0,0 +1,2 @@
1
+ import { RuleGenerationContext } from '../types';
2
+ export declare function generateCurrentStepSection(roadmap: RuleGenerationContext["roadmap"]): string | null;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateCurrentStepSection = generateCurrentStepSection;
4
+ function generateCurrentStepSection(roadmap) {
5
+ const activeRoadmap = roadmap.filter((r) => r.is_legacy !== true);
6
+ const activeSteps = activeRoadmap
7
+ .filter((r) => r.status === 'ACTIVE')
8
+ .sort((a, b) => a.step_number - b.step_number);
9
+ if (activeSteps.length === 0) {
10
+ const nextSteps = activeRoadmap
11
+ .filter((r) => r.status === 'LOCKED')
12
+ .sort((a, b) => a.step_number - b.step_number)
13
+ .slice(0, 1);
14
+ if (nextSteps.length === 0)
15
+ return null;
16
+ return `## 🎯 CURRENT FOCUS
17
+
18
+ > **No active task.** The next step in the backlog is:
19
+ >
20
+ > **Step ${nextSteps[0].step_number}: ${nextSteps[0].title}**`;
21
+ }
22
+ const currentStep = activeSteps[0];
23
+ let objectiveText = currentStep.title;
24
+ let constraintsText = '';
25
+ let dodText = '';
26
+ if (currentStep.prompt_content) {
27
+ const content = currentStep.prompt_content;
28
+ const objectiveMatch = content.match(/###\s*🎯\s*Objective\s*\n([\s\S]*?)(?=###|$)/i);
29
+ if (objectiveMatch)
30
+ objectiveText = objectiveMatch[1].trim();
31
+ const constraintsMatch = content.match(/###\s*⚠️\s*Constraints\s*\n([\s\S]*?)(?=###|$)/i);
32
+ if (constraintsMatch)
33
+ constraintsText = constraintsMatch[1].trim();
34
+ const dodMatch = content.match(/###\s*✅\s*Definition of Done\s*\n([\s\S]*?)(?=###|$)/i);
35
+ if (dodMatch)
36
+ dodText = dodMatch[1].trim();
37
+ }
38
+ let section = `## 🎯 CURRENT FOCUS
39
+
40
+ **Active Step ${currentStep.step_number}: ${currentStep.title}**
41
+ ${currentStep.sprint_focus ? `*Sprint: ${currentStep.sprint_focus}*` : ''}
42
+
43
+ ### Objective
44
+ ${objectiveText}`;
45
+ if (constraintsText) {
46
+ section += `\n\n### Task-Specific Constraints\n${constraintsText}`;
47
+ }
48
+ if (dodText) {
49
+ section += `\n\n### Definition of Done\n${dodText}`;
50
+ }
51
+ return section;
52
+ }
@@ -0,0 +1,2 @@
1
+ import { RuleGenerationContext, IDEProvider } from '../types';
2
+ export declare function generateIdentitySection(project: RuleGenerationContext["project"], ide: IDEProvider, activeAgents?: RuleGenerationContext["activeAgents"]): string;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateIdentitySection = generateIdentitySection;
4
+ function generateIdentitySection(project, ide, activeAgents) {
5
+ const mission = project.functional_spec?.projectDescription
6
+ || project.description
7
+ || `Build a ${project.ambition_level || 'professional'} application.`;
8
+ const audienceInfo = project.functional_spec?.targetAudience
9
+ ? `\n- **Target Users:** ${project.functional_spec.targetAudience}`
10
+ : '';
11
+ const problemInfo = project.functional_spec?.coreProblem
12
+ ? `\n- **Problem Being Solved:** ${project.functional_spec.coreProblem}`
13
+ : '';
14
+ // Build specialist context list
15
+ const specialistList = activeAgents?.map((a) => `- **${a.name}** (\`${a.key}\`, Lvl ${a.authority_level}): ${a.primary_mission || extractFirstSentence(a.content)}`).join('\n') || '- No specialists configured.';
16
+ return `## 🧠 PROJECT CONTEXT
17
+
18
+ **Project:** ${project.name}
19
+ **ID:** \`${project.id}\`
20
+ **Mission:** ${mission}${audienceInfo}${problemInfo}
21
+
22
+ ---
23
+
24
+ ## 🤖 SPECIALIST PERSONAS
25
+
26
+ The following personas represent areas of expertise. Reference their guidelines when working in their domain.
27
+
28
+ ${specialistList}
29
+
30
+ ### How to Use Specialists
31
+ 1. **Architecture & Governance** → Follow Frank's guidelines for code structure and security.
32
+ 2. **Documentation & Reports** → Use The Scribe's patterns for markdown and PDFs.
33
+ 3. **Historical Context** → Consult The Librarian for legacy feature discovery.
34
+
35
+ > **Note:** These are informational contexts, not active agents. You (the IDE agent) execute all code.
36
+
37
+ ---
38
+
39
+ ## 🎯 CODING PRINCIPLES
40
+ - **CONCISE:** No filler words. Get to the point.
41
+ - **PRECISE:** Give specific answers with file paths and code.
42
+ - **PRACTICAL:** Focus on what ships, not theory.
43
+ - **GUARDIAN-AWARE:** Respect architectural constraints in the Guardian rules.`;
44
+ }
45
+ function extractFirstSentence(text) {
46
+ const match = text.match(/^[^.!?]*[.!?]/);
47
+ return match ? match[0].trim() : text.slice(0, 100) + '...';
48
+ }
@@ -0,0 +1,13 @@
1
+ import { AgentSkill } from '../types';
2
+ /**
3
+ * Generate the <available_skills> XML block for the root .cursorrules file.
4
+ */
5
+ export declare function generateAvailableSkillsSection(skills: AgentSkill[]): string;
6
+ /**
7
+ * Generate the content for a specific SKILL.md file.
8
+ */
9
+ export declare function generateSkillFileContent(skill: AgentSkill): string;
10
+ /**
11
+ * Get the standard Rigstate library of skills.
12
+ */
13
+ export declare function getRigstateStandardSkills(): AgentSkill[];