@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 +38 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +347 -0
- package/dist/sections/current-step.d.ts +2 -0
- package/dist/sections/current-step.js +52 -0
- package/dist/sections/identity.d.ts +2 -0
- package/dist/sections/identity.js +48 -0
- package/dist/sections/skills.d.ts +13 -0
- package/dist/sections/skills.js +130 -0
- package/dist/sections/stack-dna.d.ts +2 -0
- package/dist/sections/stack-dna.js +89 -0
- package/dist/sections/tooling.d.ts +2 -0
- package/dist/sections/tooling.js +67 -0
- package/dist/sections/workflow.d.ts +2 -0
- package/dist/sections/workflow.js +281 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.js +15 -0
- package/dist/utils/mdc.d.ts +8 -0
- package/dist/utils/mdc.js +23 -0
- package/package.json +16 -0
- package/src/index.ts +416 -0
- package/src/sections/current-step.ts +62 -0
- package/src/sections/identity.ts +58 -0
- package/src/sections/skills.ts +130 -0
- package/src/sections/stack-dna.ts +100 -0
- package/src/sections/tooling.ts +72 -0
- package/src/sections/workflow.ts +281 -0
- package/src/skills/rigstate-integrity-gate/content.md +34 -0
- package/src/types.ts +122 -0
- package/src/utils/mdc.ts +33 -0
- package/tsconfig.json +19 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported IDE providers for rules file generation.
|
|
3
|
+
* - cursor: .cursorrules + .cursor/rules/*.mdc
|
|
4
|
+
* - antigravity: .cursorrules + .cursor/rules/*.mdc (Cursor-compatible)
|
|
5
|
+
* - windsurf: .windsurfrules
|
|
6
|
+
* - vscode: .cursorrules + .cursor/rules/*.mdc
|
|
7
|
+
* - copilot: .github/copilot-instructions.md
|
|
8
|
+
* - generic: CONVENTIONS.md (universal markdown format)
|
|
9
|
+
*/
|
|
10
|
+
export type IDEProvider = 'cursor' | 'antigravity' | 'windsurf' | 'vscode' | 'copilot' | 'generic';
|
|
11
|
+
/**
|
|
12
|
+
* Mapping of IDE providers to their respective rules file names.
|
|
13
|
+
* Centralized here to ensure consistency across all consumers.
|
|
14
|
+
*/
|
|
15
|
+
export declare const IDE_FILE_NAMES: Record<IDEProvider, string>;
|
|
16
|
+
export interface ProjectSettings {
|
|
17
|
+
lmax: number;
|
|
18
|
+
lmax_ui: number;
|
|
19
|
+
security_level: string;
|
|
20
|
+
}
|
|
21
|
+
export interface RuleFile {
|
|
22
|
+
path: string;
|
|
23
|
+
content: string;
|
|
24
|
+
metadata?: {
|
|
25
|
+
description?: string;
|
|
26
|
+
globs?: string[];
|
|
27
|
+
alwaysApply?: boolean;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface MultiFileRuleResult {
|
|
31
|
+
files: RuleFile[];
|
|
32
|
+
suggestedIde: IDEProvider;
|
|
33
|
+
version: string;
|
|
34
|
+
}
|
|
35
|
+
export interface RuleGenerationContext {
|
|
36
|
+
project: {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
description?: string;
|
|
40
|
+
functional_spec?: {
|
|
41
|
+
projectDescription?: string;
|
|
42
|
+
coreProblem?: string;
|
|
43
|
+
targetAudience?: string;
|
|
44
|
+
featureList?: Array<{
|
|
45
|
+
name: string;
|
|
46
|
+
description: string;
|
|
47
|
+
priority: string;
|
|
48
|
+
}>;
|
|
49
|
+
keyInsights?: string[];
|
|
50
|
+
};
|
|
51
|
+
ambition_level?: string;
|
|
52
|
+
tenancy?: 'SINGLE' | 'MULTI_ORG';
|
|
53
|
+
monetization?: 'FREE' | 'SAAS';
|
|
54
|
+
compliance?: 'NONE' | 'GDPR' | 'HIPAA';
|
|
55
|
+
vibe?: 'CORPORATE' | 'CREATIVE' | 'BRUTALIST';
|
|
56
|
+
settings?: ProjectSettings;
|
|
57
|
+
};
|
|
58
|
+
stack: string[];
|
|
59
|
+
roadmap: Array<{
|
|
60
|
+
step_number: number;
|
|
61
|
+
title: string;
|
|
62
|
+
status: string;
|
|
63
|
+
sprint_focus?: string;
|
|
64
|
+
prompt_content?: string;
|
|
65
|
+
is_legacy?: boolean;
|
|
66
|
+
}>;
|
|
67
|
+
ide: IDEProvider;
|
|
68
|
+
legacyStats?: {
|
|
69
|
+
total: number;
|
|
70
|
+
legacyCount: number;
|
|
71
|
+
activeCount: number;
|
|
72
|
+
};
|
|
73
|
+
activeAgents?: Array<{
|
|
74
|
+
id: string;
|
|
75
|
+
key: string;
|
|
76
|
+
name: string;
|
|
77
|
+
job_title?: string;
|
|
78
|
+
content: string;
|
|
79
|
+
authority_level: number;
|
|
80
|
+
primary_mission?: string;
|
|
81
|
+
trigger_keywords?: string;
|
|
82
|
+
}>;
|
|
83
|
+
databaseMetadata?: TableMetadata[];
|
|
84
|
+
}
|
|
85
|
+
export interface TableMetadata {
|
|
86
|
+
table_name: string;
|
|
87
|
+
rls_enabled: boolean;
|
|
88
|
+
policy_count: number;
|
|
89
|
+
column_count: number;
|
|
90
|
+
has_id: boolean;
|
|
91
|
+
has_created_at: boolean;
|
|
92
|
+
has_updated_at: boolean;
|
|
93
|
+
has_user_id: boolean;
|
|
94
|
+
indexed_columns: string[];
|
|
95
|
+
foreign_keys: {
|
|
96
|
+
column: string;
|
|
97
|
+
target_table: string;
|
|
98
|
+
}[];
|
|
99
|
+
}
|
|
100
|
+
export interface AgentSkill {
|
|
101
|
+
name: string;
|
|
102
|
+
description: string;
|
|
103
|
+
specialist: string;
|
|
104
|
+
version: string;
|
|
105
|
+
governance: 'OPEN' | 'SOFT_LOCK' | 'HARD_LOCK';
|
|
106
|
+
content: string;
|
|
107
|
+
activation?: {
|
|
108
|
+
imports?: string[];
|
|
109
|
+
content?: string[];
|
|
110
|
+
files?: string[];
|
|
111
|
+
violation_id?: string;
|
|
112
|
+
metric_threshold?: number;
|
|
113
|
+
};
|
|
114
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IDE_FILE_NAMES = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Mapping of IDE providers to their respective rules file names.
|
|
6
|
+
* Centralized here to ensure consistency across all consumers.
|
|
7
|
+
*/
|
|
8
|
+
exports.IDE_FILE_NAMES = {
|
|
9
|
+
cursor: '.cursorrules',
|
|
10
|
+
antigravity: '.cursorrules',
|
|
11
|
+
windsurf: '.windsurfrules',
|
|
12
|
+
vscode: '.cursorrules',
|
|
13
|
+
copilot: '.github/copilot-instructions.md',
|
|
14
|
+
generic: 'CONVENTIONS.md'
|
|
15
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapMdc = wrapMdc;
|
|
4
|
+
/**
|
|
5
|
+
* Utility to wrap markdown content with MDC (Modern Cursor) frontmatter.
|
|
6
|
+
*/
|
|
7
|
+
function wrapMdc(content, metadata) {
|
|
8
|
+
const yaml = ['---'];
|
|
9
|
+
if (metadata.description) {
|
|
10
|
+
yaml.push(`description: "${metadata.description.replace(/"/g, '\\"')}"`);
|
|
11
|
+
}
|
|
12
|
+
if (metadata.globs && metadata.globs.length > 0) {
|
|
13
|
+
yaml.push('globs:');
|
|
14
|
+
for (const glob of metadata.globs) {
|
|
15
|
+
yaml.push(` - "${glob}"`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (metadata.alwaysApply !== undefined) {
|
|
19
|
+
yaml.push(`alwaysApply: ${metadata.alwaysApply}`);
|
|
20
|
+
}
|
|
21
|
+
yaml.push('---');
|
|
22
|
+
return `${yaml.join('\n')}\n${content}`;
|
|
23
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rigstate/rules-engine",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"dev": "tsc -w"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"zod": "^3.22.4"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"typescript": "^5.3.3"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RuleGenerationContext,
|
|
3
|
+
IDEProvider,
|
|
4
|
+
IDE_FILE_NAMES,
|
|
5
|
+
RuleFile,
|
|
6
|
+
MultiFileRuleResult,
|
|
7
|
+
TableMetadata
|
|
8
|
+
} from './types';
|
|
9
|
+
import { generateIdentitySection } from './sections/identity';
|
|
10
|
+
import { generateStackDnaSection } from './sections/stack-dna';
|
|
11
|
+
import { generateCurrentStepSection } from './sections/current-step';
|
|
12
|
+
import { generateWorkflowSection } from './sections/workflow';
|
|
13
|
+
import { generateToolingSection } from './sections/tooling';
|
|
14
|
+
import { generateAvailableSkillsSection, generateSkillFileContent, getRigstateStandardSkills } from './sections/skills';
|
|
15
|
+
import { wrapMdc } from './utils/mdc';
|
|
16
|
+
|
|
17
|
+
// Re-export types and constants for consumers
|
|
18
|
+
export type {
|
|
19
|
+
RuleGenerationContext,
|
|
20
|
+
IDEProvider,
|
|
21
|
+
ProjectSettings,
|
|
22
|
+
RuleFile,
|
|
23
|
+
MultiFileRuleResult,
|
|
24
|
+
AgentSkill
|
|
25
|
+
} from './types';
|
|
26
|
+
export { IDE_FILE_NAMES } from './types';
|
|
27
|
+
export { generateAvailableSkillsSection, generateSkillFileContent, getRigstateStandardSkills } from './sections/skills';
|
|
28
|
+
|
|
29
|
+
const RIGSTATE_START = "RIGSTATE_START";
|
|
30
|
+
const RIGSTATE_END = "RIGSTATE_END";
|
|
31
|
+
const ENGINE_VERSION = "3.0.0";
|
|
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
|
+
/**
|
|
39
|
+
* Generate modular IDE rule content optimized for AI coding assistants.
|
|
40
|
+
* v2.4.0: Added Agent IDs, Tool Binding, and Strict Ownership.
|
|
41
|
+
* Refactored into sub-modules for Guardian Compliance (400-line limit).
|
|
42
|
+
*/
|
|
43
|
+
export function generateRuleContent(
|
|
44
|
+
project: RuleGenerationContext["project"],
|
|
45
|
+
stack: string[],
|
|
46
|
+
roadmap: RuleGenerationContext["roadmap"],
|
|
47
|
+
ide: IDEProvider = 'cursor',
|
|
48
|
+
legacyStats?: RuleGenerationContext["legacyStats"],
|
|
49
|
+
activeAgents?: RuleGenerationContext["activeAgents"],
|
|
50
|
+
lean: boolean = false
|
|
51
|
+
): string {
|
|
52
|
+
const sections: string[] = [];
|
|
53
|
+
sections.push('# 🚀 Rigstate Supervisor v2.4 (Context-Aware)');
|
|
54
|
+
|
|
55
|
+
// SECTION 0: HIERARCHY (Top Priority)
|
|
56
|
+
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.
|
|
57
|
+
|
|
58
|
+
## ⚖️ AGENT HIERARCHY & AUTHORITY
|
|
59
|
+
You must defer to the instructions of agents with higher Authority Levels (10 being highest).
|
|
60
|
+
Security and Architecture (Levels 8-10) always override creative or implementation suggestions (Levels 1-5).`);
|
|
61
|
+
|
|
62
|
+
// SECTION 1: IDENTITY (Multi-Agent Ecosystem)
|
|
63
|
+
// Always keep Identity in root for instant persona recognition
|
|
64
|
+
const identitySection = generateIdentitySection(project, ide, activeAgents);
|
|
65
|
+
sections.push(identitySection);
|
|
66
|
+
|
|
67
|
+
// SECTION 1.5: SKILLS (Discovery Layer)
|
|
68
|
+
const skills = getRigstateStandardSkills();
|
|
69
|
+
const skillsSection = generateAvailableSkillsSection(skills);
|
|
70
|
+
if (skillsSection) {
|
|
71
|
+
sections.push(skillsSection);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// SECTION 2: STACK DNA + GUARDIAN RULES
|
|
75
|
+
// In Lean mode (Cursor context), we trust rigstate-guardian.mdc to handle this.
|
|
76
|
+
if (!lean) {
|
|
77
|
+
const stackDnaSection = generateStackDnaSection(project, stack, legacyStats);
|
|
78
|
+
sections.push(stackDnaSection);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// SECTION 3: CURRENT STEP (Active Focus Injection)
|
|
82
|
+
// In Lean mode, rigstate-roadmap.mdc handles this dynamically.
|
|
83
|
+
if (!lean) {
|
|
84
|
+
const currentStepSection = generateCurrentStepSection(roadmap);
|
|
85
|
+
if (currentStepSection) {
|
|
86
|
+
sections.push(currentStepSection);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// SECTION 4: WORKFLOW (How to Parse Roadmap Steps)
|
|
91
|
+
// Frank's "Supervisor Mode" logic.
|
|
92
|
+
// We KEEP this in Lean mode because it defines the core behavior of the agent globally.
|
|
93
|
+
// However, if it's identical to rigstate-workflow.mdc, we might consider linking.
|
|
94
|
+
// But Frank's protocol is the "Operating System" of the session.
|
|
95
|
+
const workflowSection = generateWorkflowSection(ide);
|
|
96
|
+
sections.push(workflowSection);
|
|
97
|
+
|
|
98
|
+
// SECTION 5: TOOLING (CLI + MCP Integration)
|
|
99
|
+
// In Lean mode, rigstate-workflow.mdc has the specific CLI commands and tools.
|
|
100
|
+
if (!lean) {
|
|
101
|
+
const toolingSection = generateToolingSection(activeAgents);
|
|
102
|
+
sections.push(toolingSection);
|
|
103
|
+
} else {
|
|
104
|
+
// Add a pointer in Lean mode
|
|
105
|
+
sections.push(`## 🔧 TOOLING & SPECIFIC RULES
|
|
106
|
+
> **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.
|
|
107
|
+
> - **Stack & Guardian:** See \`rigstate-guardian.mdc\`
|
|
108
|
+
> - **Roadmap & Tasks:** See \`rigstate-roadmap.mdc\`
|
|
109
|
+
> - **Tools & Workflow:** See \`rigstate-workflow.mdc\``);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Generate IDE-specific header
|
|
113
|
+
const headerMap: Record<IDEProvider, string> = {
|
|
114
|
+
cursor: `# Cursor Project Rules: ${project.name}`,
|
|
115
|
+
antigravity: `# Antigravity Project Rules: ${project.name}`,
|
|
116
|
+
windsurf: `# Windsurf Project Rules: ${project.name}`,
|
|
117
|
+
vscode: `# VS Code Project Rules: ${project.name}`,
|
|
118
|
+
copilot: `# GitHub Copilot Instructions: ${project.name}`,
|
|
119
|
+
generic: `# Project Conventions: ${project.name}`
|
|
120
|
+
};
|
|
121
|
+
const header = headerMap[ide] || `# Project Rules: ${project.name}`;
|
|
122
|
+
|
|
123
|
+
return `${RIGSTATE_START}
|
|
124
|
+
${header}
|
|
125
|
+
> Generated by Rigstate v2.5.0 | Project ID: ${project.id} | Last synced: ${new Date().toISOString()}
|
|
126
|
+
> ${lean ? '⚡ LEAN MODE ACTIVE: Redundant context offloaded to .cursor/rules/*.mdc' : '📦 FULL MODE ACTIVE'}
|
|
127
|
+
|
|
128
|
+
⚠️ **SYSTEM NOTE:** Changes made to this Guardian template propagate to ALL Rigstate projects on next sync.
|
|
129
|
+
🛡️ **Guardian v2.5 Upgrade Applied:** IMPACT_GUARD + BUILD_INTEGRITY now active globally.
|
|
130
|
+
|
|
131
|
+
${sections.join('\n\n---\n\n')}
|
|
132
|
+
${RIGSTATE_END}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Generate multiple modular rule files optimized for modern Cursor (.mdc)
|
|
137
|
+
* and universal AGENTS.md format.
|
|
138
|
+
*/
|
|
139
|
+
export function generateRuleFiles(
|
|
140
|
+
project: RuleGenerationContext["project"],
|
|
141
|
+
stack: string[],
|
|
142
|
+
roadmap: RuleGenerationContext["roadmap"],
|
|
143
|
+
ide: IDEProvider = 'cursor',
|
|
144
|
+
legacyStats?: RuleGenerationContext["legacyStats"],
|
|
145
|
+
activeAgents?: RuleGenerationContext["activeAgents"],
|
|
146
|
+
databaseMetadata?: TableMetadata[]
|
|
147
|
+
): MultiFileRuleResult {
|
|
148
|
+
const files: RuleFile[] = [];
|
|
149
|
+
|
|
150
|
+
// 1. GENERATE AGENTS.MD (Universal Root File - Context Reference)
|
|
151
|
+
const agentTable = activeAgents?.map((a: any) =>
|
|
152
|
+
`| **${a.name}** | \`${a.key}\` | ${a.job_title} | ${a.primary_mission || 'Specialist'} |`
|
|
153
|
+
).join('\n') || '| - | - | - | No agents configured |';
|
|
154
|
+
|
|
155
|
+
const agentsMdContent = `# 🤖 AI Agent Context: ${project.name}
|
|
156
|
+
> **Rigstate v${ENGINE_VERSION}** | Project ID: \`${project.id}\`
|
|
157
|
+
|
|
158
|
+
This file describes the **specialist personas** available in this project.
|
|
159
|
+
These are **context providers**, not active controllers. The IDE agent (you) remains in full control of code execution.
|
|
160
|
+
|
|
161
|
+
## 📋 Available Specialists
|
|
162
|
+
| Name | Key | Role | Specialty |
|
|
163
|
+
|:--- |:--- |:--- |:--- |
|
|
164
|
+
${agentTable}
|
|
165
|
+
|
|
166
|
+
## 🔍 How to Use This Context
|
|
167
|
+
1. **Read their expertise**: Each specialist has a defined area of knowledge (architecture, documentation, history).
|
|
168
|
+
2. **Adopt their perspective**: When working in their domain, consider their guidelines.
|
|
169
|
+
3. **Call MCP tools if needed**: Some specialists have associated tools (e.g., \`generate_professional_pdf\` for The Scribe).
|
|
170
|
+
|
|
171
|
+
## ⚠️ Important
|
|
172
|
+
- These personas do **NOT** execute code or override your decisions.
|
|
173
|
+
- They provide **context and guidelines** that you apply at your discretion.
|
|
174
|
+
- Authority levels indicate priority of guidelines when they conflict (higher = stronger recommendation).
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
*Generated by Rigstate. Run \`rigstate sync\` to refresh.*`;
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
files.push({
|
|
181
|
+
path: 'AGENTS.md',
|
|
182
|
+
content: agentsMdContent,
|
|
183
|
+
metadata: { description: "Project hierarchy and agent identities" }
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// 2. GENERATE SYSTEM-SPECIFIC MONO-FILE (Fallback/Baseline)
|
|
187
|
+
// If IDE is Cursor, we use LEAN mode for the root .cursorrules file
|
|
188
|
+
// because we are generating specific .mdc files below that cover the details.
|
|
189
|
+
const isLean = ide === 'cursor';
|
|
190
|
+
const masterFileName = getFileNameForIDE(ide);
|
|
191
|
+
const monoContent = generateRuleContent(project, stack, roadmap, ide, legacyStats, activeAgents, isLean);
|
|
192
|
+
|
|
193
|
+
files.push({
|
|
194
|
+
path: masterFileName,
|
|
195
|
+
content: monoContent,
|
|
196
|
+
metadata: { description: `Master rules file for ${ide}` }
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
// 3. GENERATE MODULAR (.mdc) RULES (Cursor, Antigravity, VS Code)
|
|
201
|
+
if (ide === 'cursor' || ide === 'antigravity' || ide === 'vscode') {
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
// Identity & Context
|
|
205
|
+
files.push({
|
|
206
|
+
path: '.cursor/rules/rigstate-identity.mdc',
|
|
207
|
+
content: wrapMdc(generateIdentitySection(project, ide, activeAgents), {
|
|
208
|
+
description: "Project context and specialist personas",
|
|
209
|
+
alwaysApply: true
|
|
210
|
+
})
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
// Guardian & Stack DNA
|
|
216
|
+
files.push({
|
|
217
|
+
path: '.cursor/rules/rigstate-guardian.mdc',
|
|
218
|
+
content: wrapMdc(generateStackDnaSection(project, stack, legacyStats), {
|
|
219
|
+
description: "Governance rules, tech stack constraints, and file size limits",
|
|
220
|
+
globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.sql"],
|
|
221
|
+
alwaysApply: true
|
|
222
|
+
})
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Current Focus (Roadmap)
|
|
226
|
+
const currentStep = generateCurrentStepSection(roadmap);
|
|
227
|
+
if (currentStep) {
|
|
228
|
+
files.push({
|
|
229
|
+
path: '.cursor/rules/rigstate-roadmap.mdc',
|
|
230
|
+
content: wrapMdc(currentStep, {
|
|
231
|
+
description: "Active sprint focus and current roadmap step details",
|
|
232
|
+
alwaysApply: true
|
|
233
|
+
})
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Workflow & Tooling
|
|
238
|
+
files.push({
|
|
239
|
+
path: '.cursor/rules/rigstate-workflow.mdc',
|
|
240
|
+
content: wrapMdc(generateWorkflowSection(ide) + '\n\n' + generateToolingSection(activeAgents), {
|
|
241
|
+
description: "Coding workflows, CLI usage, and tool binding rules",
|
|
242
|
+
alwaysApply: true
|
|
243
|
+
})
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Database Specific (scoped)
|
|
247
|
+
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.";
|
|
248
|
+
|
|
249
|
+
if (databaseMetadata && databaseMetadata.length > 0) {
|
|
250
|
+
const securedCount = databaseMetadata.filter(t => t.rls_enabled).length;
|
|
251
|
+
const unsecuredCount = databaseMetadata.length - securedCount;
|
|
252
|
+
const unsecuredTables = databaseMetadata.filter(t => !t.rls_enabled).map(t => t.table_name);
|
|
253
|
+
|
|
254
|
+
dbContent = `## 🗄️ Database Context: ${databaseMetadata.length} Tables
|
|
255
|
+
> **Security Check:** ${securedCount} Secured | ${unsecuredCount} Unsecured
|
|
256
|
+
|
|
257
|
+
### ⚠️ Security Attention Required
|
|
258
|
+
${unsecuredTables.length > 0
|
|
259
|
+
? unsecuredTables.map(t => `- 🔴 **${t}**: RLS Disabled`).join('\n')
|
|
260
|
+
: "- ✅ All tables have Row Level Security enabled."}
|
|
261
|
+
|
|
262
|
+
### 📋 Schema Reference
|
|
263
|
+
| Table | RLS | Policies | Cols | Key Features |
|
|
264
|
+
| :--- | :---: | :---: | :---: | :--- |
|
|
265
|
+
${databaseMetadata.map(t => {
|
|
266
|
+
const features = [];
|
|
267
|
+
if (t.has_user_id) features.push('User-Scoped');
|
|
268
|
+
if (t.has_created_at) features.push('Timestamps');
|
|
269
|
+
return `| \`${t.table_name}\` | ${t.rls_enabled ? '✅' : '❌'} | ${t.policy_count} | ${t.column_count} | ${features.join(', ') || '-'} |`;
|
|
270
|
+
}).join('\n')}
|
|
271
|
+
|
|
272
|
+
### 🛡️ Development Rules
|
|
273
|
+
1. **RLS is MANDATORY:** All tables containing user data must have RLS enabled.
|
|
274
|
+
2. **Use RPCs for Complex Logic:** Do not put complex business logic in client-side queries.
|
|
275
|
+
3. **Migrations:** Always use \`supabase/migrations\` for schema changes.`;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
files.push({
|
|
279
|
+
path: '.cursor/rules/rigstate-database.mdc',
|
|
280
|
+
content: wrapMdc(dbContent, {
|
|
281
|
+
description: "Live database schema, RLS status, and table metadata",
|
|
282
|
+
globs: ["supabase/**/*", "**/*.sql", "**/lib/supabase/**"],
|
|
283
|
+
alwaysApply: databaseMetadata && databaseMetadata.length > 0 ? true : false
|
|
284
|
+
})
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// 4. GENERATE AGENT SKILLS (.agent/skills/<name>/SKILL.md)
|
|
288
|
+
const rigstateSkills = getRigstateStandardSkills();
|
|
289
|
+
for (const skill of rigstateSkills) {
|
|
290
|
+
files.push({
|
|
291
|
+
path: `.agent/skills/${skill.name}/SKILL.md`,
|
|
292
|
+
content: generateSkillFileContent(skill),
|
|
293
|
+
metadata: { description: skill.description }
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
files,
|
|
302
|
+
suggestedIde: ide,
|
|
303
|
+
version: ENGINE_VERSION
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export async function fetchLegacyStats(supabase: any, projectId: string): Promise<RuleGenerationContext["legacyStats"]> {
|
|
308
|
+
const { data: chunks } = await supabase
|
|
309
|
+
.from('roadmap_chunks')
|
|
310
|
+
.select('is_legacy')
|
|
311
|
+
.eq('project_id', projectId);
|
|
312
|
+
|
|
313
|
+
if (!chunks) return { total: 0, legacyCount: 0, activeCount: 0 };
|
|
314
|
+
|
|
315
|
+
const legacyCount = (chunks || []).filter((c: any) => c.is_legacy === true).length;
|
|
316
|
+
const activeCount = (chunks || []).filter((c: any) => c.is_legacy !== true).length;
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
total: (chunks || []).length,
|
|
320
|
+
legacyCount,
|
|
321
|
+
activeCount
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Fetch active agents sorted by authority level
|
|
327
|
+
* Note: Pass in a SupabaseClient instance (browser or server)
|
|
328
|
+
*/
|
|
329
|
+
export async function fetchActiveAgents(supabase: any): Promise<RuleGenerationContext["activeAgents"]> {
|
|
330
|
+
const { data: prompts } = await supabase
|
|
331
|
+
.from('system_prompts')
|
|
332
|
+
.select('id, key, content, name, display_name, job_title, authority_level, primary_mission, trigger_keywords')
|
|
333
|
+
.eq('include_in_rules', true)
|
|
334
|
+
.eq('is_active', true)
|
|
335
|
+
.order('authority_level', { ascending: false });
|
|
336
|
+
|
|
337
|
+
if (!prompts) return [];
|
|
338
|
+
|
|
339
|
+
return (prompts || []).map((p: any) => ({
|
|
340
|
+
id: p.id,
|
|
341
|
+
key: p.key,
|
|
342
|
+
name: p.display_name || p.name || p.key,
|
|
343
|
+
job_title: p.job_title || 'Specialist Agent',
|
|
344
|
+
content: p.content,
|
|
345
|
+
authority_level: (() => {
|
|
346
|
+
if (p.authority_level === null || p.authority_level === undefined) {
|
|
347
|
+
throw new Error(`Agent ${p.key} is missing authority_level. Update via CMS.`);
|
|
348
|
+
}
|
|
349
|
+
return p.authority_level;
|
|
350
|
+
})(),
|
|
351
|
+
primary_mission: p.primary_mission || undefined,
|
|
352
|
+
trigger_keywords: p.trigger_keywords || undefined
|
|
353
|
+
}));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function mergeRuleContent(existingContent: string, newRules: string): string {
|
|
357
|
+
const startIndex = existingContent.indexOf(RIGSTATE_START);
|
|
358
|
+
const endIndex = existingContent.indexOf(RIGSTATE_END);
|
|
359
|
+
|
|
360
|
+
if (startIndex !== -1 && endIndex !== -1) {
|
|
361
|
+
const before = existingContent.substring(0, startIndex);
|
|
362
|
+
const after = existingContent.substring(endIndex + RIGSTATE_END.length);
|
|
363
|
+
return before + newRules + after;
|
|
364
|
+
} else {
|
|
365
|
+
return existingContent + "\n\n" + newRules;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Fetch project tech stack from database.
|
|
371
|
+
* Falls back to common defaults if no tags are defined.
|
|
372
|
+
* Note: Pass in a SupabaseClient instance (browser or server)
|
|
373
|
+
*/
|
|
374
|
+
export async function fetchProjectTechStack(
|
|
375
|
+
supabase: any,
|
|
376
|
+
projectId: string,
|
|
377
|
+
fallbackStack: string[] = ['Next.js', 'TypeScript', 'Supabase', 'Tailwind CSS']
|
|
378
|
+
): Promise<string[]> {
|
|
379
|
+
try {
|
|
380
|
+
// Try to fetch from project_tech_tags table
|
|
381
|
+
const { data: tags, error } = await supabase
|
|
382
|
+
.from('project_tech_tags')
|
|
383
|
+
.select('name')
|
|
384
|
+
.eq('project_id', projectId);
|
|
385
|
+
|
|
386
|
+
if (error || !tags || tags.length === 0) {
|
|
387
|
+
// Fallback: Check project metadata for stack info
|
|
388
|
+
const { data: project } = await supabase
|
|
389
|
+
.from('projects')
|
|
390
|
+
.select('functional_spec')
|
|
391
|
+
.eq('id', projectId)
|
|
392
|
+
.single();
|
|
393
|
+
|
|
394
|
+
// Extract tech from functional_spec if available
|
|
395
|
+
if (project?.functional_spec?.techStack) {
|
|
396
|
+
return project.functional_spec.techStack;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return fallbackStack;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return tags.map((t: any) => t.name);
|
|
403
|
+
} catch (error) {
|
|
404
|
+
console.warn('fetchProjectTechStack: Failed to fetch, using fallback', error);
|
|
405
|
+
return fallbackStack;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Get the appropriate filename for a given IDE provider.
|
|
411
|
+
* Convenience wrapper around IDE_FILE_NAMES constant.
|
|
412
|
+
*/
|
|
413
|
+
export function getFileNameForIDE(ide: IDEProvider): string {
|
|
414
|
+
return IDE_FILE_NAMES[ide] || IDE_FILE_NAMES.cursor;
|
|
415
|
+
}
|
|
416
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { RuleGenerationContext } from '../types';
|
|
2
|
+
|
|
3
|
+
export function generateCurrentStepSection(
|
|
4
|
+
roadmap: RuleGenerationContext["roadmap"]
|
|
5
|
+
): string | null {
|
|
6
|
+
const activeRoadmap = roadmap.filter((r: any) => r.is_legacy !== true);
|
|
7
|
+
|
|
8
|
+
const activeSteps = activeRoadmap
|
|
9
|
+
.filter((r: any) => r.status === 'ACTIVE')
|
|
10
|
+
.sort((a: any, b: any) => a.step_number - b.step_number);
|
|
11
|
+
|
|
12
|
+
if (activeSteps.length === 0) {
|
|
13
|
+
const nextSteps = activeRoadmap
|
|
14
|
+
.filter((r: any) => r.status === 'LOCKED')
|
|
15
|
+
.sort((a: any, b: any) => a.step_number - b.step_number)
|
|
16
|
+
.slice(0, 1);
|
|
17
|
+
|
|
18
|
+
if (nextSteps.length === 0) return null;
|
|
19
|
+
|
|
20
|
+
return `## 🎯 CURRENT FOCUS
|
|
21
|
+
|
|
22
|
+
> **No active task.** The next step in the backlog is:
|
|
23
|
+
>
|
|
24
|
+
> **Step ${nextSteps[0].step_number}: ${nextSteps[0].title}**`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const currentStep = activeSteps[0];
|
|
28
|
+
let objectiveText = currentStep.title;
|
|
29
|
+
let constraintsText = '';
|
|
30
|
+
let dodText = '';
|
|
31
|
+
|
|
32
|
+
if (currentStep.prompt_content) {
|
|
33
|
+
const content = currentStep.prompt_content;
|
|
34
|
+
|
|
35
|
+
const objectiveMatch = content.match(/###\s*🎯\s*Objective\s*\n([\s\S]*?)(?=###|$)/i);
|
|
36
|
+
if (objectiveMatch) objectiveText = objectiveMatch[1].trim();
|
|
37
|
+
|
|
38
|
+
const constraintsMatch = content.match(/###\s*⚠️\s*Constraints\s*\n([\s\S]*?)(?=###|$)/i);
|
|
39
|
+
if (constraintsMatch) constraintsText = constraintsMatch[1].trim();
|
|
40
|
+
|
|
41
|
+
const dodMatch = content.match(/###\s*✅\s*Definition of Done\s*\n([\s\S]*?)(?=###|$)/i);
|
|
42
|
+
if (dodMatch) dodText = dodMatch[1].trim();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let section = `## 🎯 CURRENT FOCUS
|
|
46
|
+
|
|
47
|
+
**Active Step ${currentStep.step_number}: ${currentStep.title}**
|
|
48
|
+
${currentStep.sprint_focus ? `*Sprint: ${currentStep.sprint_focus}*` : ''}
|
|
49
|
+
|
|
50
|
+
### Objective
|
|
51
|
+
${objectiveText}`;
|
|
52
|
+
|
|
53
|
+
if (constraintsText) {
|
|
54
|
+
section += `\n\n### Task-Specific Constraints\n${constraintsText}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (dodText) {
|
|
58
|
+
section += `\n\n### Definition of Done\n${dodText}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return section;
|
|
62
|
+
}
|