@polymorphism-tech/morph-spec 4.15.0 → 4.17.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.
Files changed (100) hide show
  1. package/README.md +2 -2
  2. package/bin/morph-spec.js +4 -0
  3. package/bin/task-manager.js +999 -962
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/agents/ai-agents/vector-search-expert.md +247 -233
  8. package/framework/agents/backend/ms-agent-expert.md +2 -1
  9. package/framework/agents/backend/neon-expert.md +397 -0
  10. package/framework/agents/frontend/gsap-animator.md +131 -0
  11. package/framework/agents.json +1175 -1077
  12. package/framework/commands/morph-preflight.md +200 -192
  13. package/framework/hooks/claude-code/session-start/inject-morph-context.js +1 -1
  14. package/framework/rules/gsap-standards.md +13 -0
  15. package/framework/rules/neon-standards.md +25 -0
  16. package/framework/skills/level-0-meta/mcp-registry.json +1 -1
  17. package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +472 -472
  18. package/framework/skills/level-0-meta/morph-init/SKILL.md +30 -11
  19. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +5 -5
  20. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +5 -5
  21. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +7 -7
  22. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +24 -24
  23. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +12 -12
  24. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +9 -9
  25. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
  26. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
  27. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +143 -143
  28. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +1 -1
  29. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
  30. package/framework/standards/STANDARDS.json +184 -107
  31. package/framework/standards/ai-agents/production.md +1 -0
  32. package/framework/standards/ai-agents/setup.md +3 -2
  33. package/framework/standards/ai-agents/workflows.md +1 -0
  34. package/framework/standards/architecture/vertical-slice/vertical-slice.md +4 -4
  35. package/framework/standards/backend/database/postgresql/database.md +58 -78
  36. package/framework/standards/backend/dotnet/scalar-ui.md +172 -172
  37. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +274 -274
  38. package/framework/standards/backend/integrations/asaas/asaas-api.md +1 -1
  39. package/framework/standards/backend/integrations/clerk/clerk-auth.md +23 -21
  40. package/framework/standards/frontend/design-system/animations.md +9 -0
  41. package/framework/standards/frontend/gsap/gsap-core.md +463 -0
  42. package/framework/standards/frontend/gsap/gsap-plugins.md +294 -0
  43. package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +318 -0
  44. package/framework/standards/frontend/nextjs/motion-patterns.md +1 -0
  45. package/framework/standards/frontend/nextjs/nextjs-patterns.md +47 -37
  46. package/framework/standards/infrastructure/docker/easypanel-deploy.md +4 -8
  47. package/framework/standards/infrastructure/neon/neon-pgrag.md +239 -0
  48. package/framework/standards/infrastructure/neon/neon-pgvector.md +236 -0
  49. package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +310 -0
  50. package/framework/standards/infrastructure/neon/neon-setup.md +186 -0
  51. package/framework/standards/integration/mcp/mcp-tools.md +32 -34
  52. package/framework/templates/README.md +18 -18
  53. package/framework/templates/REGISTRY.json +49 -96
  54. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  55. package/framework/templates/code/dotnet/backend/repository.cs +148 -148
  56. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
  57. package/framework/templates/code/dotnet/database/vector-entity.cs +93 -0
  58. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +1 -1
  59. package/framework/templates/code/sql/{supabase-migration.sql → postgresql-migration.sql} +8 -17
  60. package/framework/templates/code/sql/{supabase-migration.template.sql → postgresql-migration.template.sql} +10 -13
  61. package/framework/templates/code/sql/{rls-policy.sql → postgresql-rls-policy.sql} +8 -8
  62. package/framework/templates/code/typescript/contracts.ts +167 -168
  63. package/framework/templates/context/CONTEXT.md +3 -3
  64. package/framework/templates/docs/proposal.md +6 -6
  65. package/framework/templates/docs/ui-components.md +1 -1
  66. package/framework/templates/feature/decisions.md +121 -121
  67. package/framework/templates/feature/recap.md +145 -145
  68. package/framework/templates/feature/tasks.md +199 -199
  69. package/framework/templates/frontend/nextjs/gsap-scroll-section.tsx.hbs +44 -0
  70. package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -67
  71. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  72. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  73. package/framework/templates/infrastructure/docker/easypanel.template.json +46 -54
  74. package/framework/templates/infrastructure/github/README.md +56 -56
  75. package/framework/templates/integrations/semantic-kernel-neon.cs +125 -0
  76. package/framework/workflows/README.md +1 -1
  77. package/framework/workflows/docs/full-morph.md +69 -69
  78. package/package.json +1 -1
  79. package/src/commands/agents/dispatch-agents.js +19 -15
  80. package/src/commands/project/setup-infra-cmd.js +1 -1
  81. package/src/commands/state/advance-phase.js +5 -0
  82. package/src/commands/tasks/task.js +3 -0
  83. package/src/core/state/state-manager.js +45 -1
  84. package/src/core/templates/template-registry.js +1 -66
  85. package/src/lib/detectors/index.js +2 -2
  86. package/src/lib/generators/architecture-scanner.js +640 -640
  87. package/src/lib/installers/mcp-installer.js +1 -1
  88. package/src/lib/tasks/task-classifier.js +15 -1
  89. package/src/lib/tasks/test-runner.js +5 -1
  90. package/src/lib/validators/content/content-validator.js +9 -10
  91. package/src/lib/validators/validation-runner.js +7 -5
  92. package/src/scripts/setup-infra.js +60 -30
  93. package/framework/agents/backend/supabase-expert.md +0 -256
  94. package/framework/rules/supabase-standards.md +0 -29
  95. package/framework/standards/infrastructure/supabase/mcp-setup.md +0 -142
  96. package/framework/standards/infrastructure/supabase/supabase-auth.md +0 -176
  97. package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +0 -206
  98. package/framework/standards/infrastructure/supabase/supabase-pgvector.md +0 -169
  99. package/framework/standards/infrastructure/supabase/supabase-rls.md +0 -184
  100. package/framework/standards/infrastructure/supabase/supabase-storage.md +0 -153
@@ -26,7 +26,7 @@ export function loadMcpRegistry() {
26
26
  /**
27
27
  * Filter MCPs by stack relevance
28
28
  * @param {Object} registry - Full MCP registry
29
- * @param {string} stack - Detected project stack (e.g., 'nextjs-supabase', 'blazor')
29
+ * @param {string} stack - Detected project stack (e.g., 'nextjs-neon', 'blazor')
30
30
  * @returns {Object} Filtered map of mcpName → mcpEntry
31
31
  */
32
32
  export function filterByStack(registry, stack) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Task classifier — identifies test/validation tasks by title.
2
+ * Task classifier — identifies test/validation and infrastructure tasks by title.
3
3
  */
4
4
 
5
5
  const TEST_TASK_PATTERN = /\b(tests?|testing|specs?|coverage|e2e|unit|integration|integra[cç][aã]o|valida[cç][aã]o|cobertura|testes?)\b/i;
@@ -14,3 +14,17 @@ export function isTestTask(taskTitle) {
14
14
  if (!taskTitle) return false;
15
15
  return TEST_TASK_PATTERN.test(taskTitle);
16
16
  }
17
+
18
+ const INFRA_TASK_PATTERN = /\b(infrastructure|infra|cleanup|migration|migra[cç][aã]o|config|setup|ci\/cd|devops|deploy|docker|packages?|dependencies|tags)\b/i;
19
+
20
+ /**
21
+ * Returns true if the task title indicates an infrastructure/cleanup/migration task.
22
+ * Used to skip contract validation and suppress breaking change warnings.
23
+ *
24
+ * @param {string} taskTitle
25
+ * @returns {boolean}
26
+ */
27
+ export function isInfrastructureTask(taskTitle) {
28
+ if (!taskTitle) return false;
29
+ return INFRA_TASK_PATTERN.test(taskTitle);
30
+ }
@@ -68,9 +68,13 @@ export function runTestSuite(projectPath = '.', opts = {}) {
68
68
  });
69
69
  return { passed: true, output: output ?? '', exitCode: 0 };
70
70
  } catch (err) {
71
+ const output = (err.stdout || '') + (err.stderr || '');
72
+ const BUILD_FAIL = /Build FAILED|error MSB\d+|error CS\d+|Could not find.*\.csproj/i;
73
+ const buildFailed = BUILD_FAIL.test(output);
71
74
  return {
72
75
  passed: false,
73
- output: (err.stdout || '') + (err.stderr || ''),
76
+ buildFailed,
77
+ output,
74
78
  exitCode: err.status ?? 1
75
79
  };
76
80
  }
@@ -23,18 +23,17 @@ export function validateSpecContent(specPath) {
23
23
 
24
24
  const content = readFileSync(specPath, 'utf8');
25
25
 
26
- // Required sections for a complete spec (prefix-match to handle variants like
27
- // "## Functional Requirements" and "## Technical Architecture")
26
+ // Required sections (flexible regex handles numbered variants like '## 1. Overview')
28
27
  const requiredSections = [
29
- '## Overview',
30
- '## Functional Requirements',
31
- '## Technical',
32
- '## Data Model',
28
+ { label: '## Overview', pattern: /^##\s+(?:\d+\.?\s+)?Overview/i },
29
+ { label: '## Functional Requirements', pattern: /^##\s+(?:\d+\.?\s+)?Functional Requirements/i },
30
+ { label: '## Technical', pattern: /^##\s+(?:\d+\.?\s+)?Technical/i },
31
+ { label: '## Data Model', pattern: /^##\s+(?:\d+\.?\s+)?Data Model/i },
33
32
  ];
34
33
 
35
34
  const lines = content.split('\n');
36
- const missing = requiredSections.filter(section =>
37
- !lines.some(line => line.startsWith(section))
35
+ const missing = requiredSections.filter(s =>
36
+ !lines.some(line => s.pattern.test(line))
38
37
  );
39
38
 
40
39
  // Additional quality checks
@@ -44,8 +43,8 @@ export function validateSpecContent(specPath) {
44
43
  // Missing sections are warnings, not errors — not every spec needs all sections
45
44
  // (e.g., frontend-only features may not have Data Model, CLI tools may not have Technical Architecture)
46
45
  if (missing.length > 0) {
47
- missing.forEach(section => {
48
- warnings.push(`Missing recommended section: ${section}`);
46
+ missing.forEach(s => {
47
+ warnings.push(`Missing recommended section: ${s.label}`);
49
48
  });
50
49
  }
51
50
 
@@ -119,11 +119,13 @@ export async function runValidation(projectPath, featureName, options = {}) {
119
119
  }
120
120
 
121
121
  // Always run contract compliance if the contracts file exists (stack-aware)
122
- const _profileForContracts = getStackProfile(projectPath);
123
- const contractsType = resolveContractsOutputType(_profileForContracts.stack);
124
- const contractsPath = getAbsoluteOutputPath(projectPath, featureName, contractsType);
125
- if (existsSync(contractsPath) && !_profileForContracts.isNextjs) {
126
- validatorIds.push('contract-compliance');
122
+ if (!options.skipContracts) {
123
+ const _profileForContracts = getStackProfile(projectPath);
124
+ const contractsType = resolveContractsOutputType(_profileForContracts.stack);
125
+ const contractsPath = getAbsoluteOutputPath(projectPath, featureName, contractsType);
126
+ if (existsSync(contractsPath) && !_profileForContracts.isNextjs) {
127
+ validatorIds.push('contract-compliance');
128
+ }
127
129
  }
128
130
 
129
131
  // Deduplicate
@@ -79,19 +79,75 @@ export function installRulesForStack(rulesSrc, rulesDest, projectTags) {
79
79
  }
80
80
  }
81
81
 
82
+ /**
83
+ * Re-install only stack-aware assets (rules, skills, agents) using current config.json.
84
+ * Skips infrastructure steps (dirs, framework copy, hooks).
85
+ *
86
+ * @param {string} targetPath - Absolute path to the target project directory
87
+ * @param {Function} log - Logging function
88
+ * @returns {{ agentCounts: object, domainCounts: object, projectTags: string[] }}
89
+ */
90
+ async function installStackAwareAssets(targetPath, log) {
91
+ const configPath = join(targetPath, '.morph', 'config', 'config.json');
92
+ let projectTags = [];
93
+ if (await pathExists(configPath)) {
94
+ try {
95
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
96
+ projectTags = getProjectTags(config);
97
+ } catch { /* ignore malformed config */ }
98
+ }
99
+
100
+ log('Installing stack-filtered rules...');
101
+ const claudeDest = join(targetPath, '.claude');
102
+ const rulesSrc = join(FRAMEWORK_DIR, 'rules');
103
+ const rulesDest = join(claudeDest, 'rules');
104
+ if (await pathExists(rulesSrc)) {
105
+ await ensureDir(rulesDest);
106
+ installRulesForStack(rulesSrc, rulesDest, projectTags);
107
+ }
108
+
109
+ log('Installing skills...');
110
+ await installSkills(targetPath, { projectTags });
111
+
112
+ log('Installing agents...');
113
+ const agentCounts = await installAgents(targetPath, FRAMEWORK_DIR, { projectTags });
114
+
115
+ log('Installing domain agents...');
116
+ const domainCounts = await installDomainAgents(targetPath, FRAMEWORK_DIR, { projectTags });
117
+
118
+ return { agentCounts, domainCounts, projectTags };
119
+ }
120
+
82
121
  /**
83
122
  * Installs MORPH-SPEC infrastructure into the target project directory.
84
123
  * Headless — no prompts, no spinner (suppressed when MORPH_QUIET=1), no stack detection.
85
124
  *
86
125
  * @param {string} targetPath - Absolute path to the target project directory
126
+ * @param {object} options
127
+ * @param {Function} options._exec - exec function (for testing)
128
+ * @param {boolean} options.reinstall - If true, only re-install stack-aware assets (rules, skills, agents)
87
129
  */
88
- export async function setupInfra(targetPath, { _exec = execSync } = {}) {
130
+ export async function setupInfra(targetPath, { _exec = execSync, reinstall = false } = {}) {
89
131
  const quiet = process.env.MORPH_QUIET === '1';
90
132
 
91
133
  function log(msg) {
92
134
  if (!quiet) process.stdout.write(msg + '\n');
93
135
  }
94
136
 
137
+ // --- Reinstall mode: only re-install stack-aware assets ---
138
+ if (reinstall) {
139
+ log('setup-infra (reinstall): re-installing stack-aware assets...');
140
+ const { agentCounts, domainCounts } = await installStackAwareAssets(targetPath, log);
141
+ log('setup-infra (reinstall): complete.');
142
+ return {
143
+ agents: {
144
+ tier1: agentCounts?.tier1 ?? 0,
145
+ tier2: agentCounts?.tier2 ?? 0,
146
+ specialists: domainCounts?.specialists ?? 0,
147
+ },
148
+ };
149
+ }
150
+
95
151
  // --- 0. Install required Claude Code plugins ---
96
152
  await installRequiredPlugins(log, _exec);
97
153
 
@@ -188,35 +244,9 @@ export async function setupInfra(targetPath, { _exec = execSync } = {}) {
188
244
  await copyDirectory(commandsSrc, commandsDest);
189
245
  }
190
246
 
191
- // --- Compute project tags for stack-aware installation ---
192
- let projectTags = [];
193
- if (await pathExists(configPath)) {
194
- try {
195
- const config = JSON.parse(readFileSync(configPath, 'utf-8'));
196
- projectTags = getProjectTags(config);
197
- } catch { /* ignore malformed config */ }
198
- }
199
-
200
- // --- 10. Copy framework/rules → .claude/rules (filtered by stack) ---
201
- log('Step 10: Installing path-scoped rules...');
202
- const rulesSrc = join(FRAMEWORK_DIR, 'rules');
203
- const rulesDest = join(claudeDest, 'rules');
204
- if (await pathExists(rulesSrc)) {
205
- await ensureDir(rulesDest);
206
- installRulesForStack(rulesSrc, rulesDest, projectTags);
207
- }
208
-
209
- // --- 11. installSkills (filtered by stack) ---
210
- log('Step 11: Installing skills...');
211
- await installSkills(targetPath, { projectTags });
212
-
213
- // --- 12. installAgents ---
214
- log('Step 12: Installing agents...');
215
- const agentCounts = await installAgents(targetPath, FRAMEWORK_DIR, { projectTags });
216
-
217
- // --- 13. installDomainAgents (filtered by stack) ---
218
- log('Step 13: Installing domain agents...');
219
- const domainCounts = await installDomainAgents(targetPath, FRAMEWORK_DIR, { projectTags });
247
+ // --- 10-13. Install stack-aware assets (rules, skills, agents) ---
248
+ log('Steps 10-13: Installing stack-aware assets...');
249
+ const { agentCounts, domainCounts } = await installStackAwareAssets(targetPath, log);
220
250
 
221
251
  // --- 14. Copy framework/CLAUDE.md → .claude/CLAUDE.md (morph-owned, gitignored) ---
222
252
  log('Step 14: Installing .claude/CLAUDE.md...');
@@ -1,256 +0,0 @@
1
- ---
2
- name: supabase-expert
3
- description: Supabase & PostgreSQL specialist for RLS policies, auth integration, pgvector, Realtime subscriptions, Edge Functions, and migration management. Use when designing database schemas, implementing Row Level Security, setting up auth flows, or working with pgvector embeddings.
4
- allowed-tools: Read, Write, Edit, Bash, Glob, Grep
5
- stacks:
6
- - supabase
7
- color: teal
8
- effort: medium
9
- ---
10
-
11
- # Supabase & PostgreSQL Specialist
12
-
13
- Expert in Supabase auth, Row Level Security, Realtime, Edge Functions, and pgvector.
14
-
15
- > **Ref:** `framework/standards/infrastructure/supabase/` — RLS, auth, storage, pgvector standards
16
-
17
- ## Connection Pooling
18
-
19
- ```
20
- # Direct connection (migrations only)
21
- postgresql://postgres:[password]@db.[project].supabase.co:5432/postgres
22
-
23
- # PgBouncer (application — ALWAYS use this)
24
- postgresql://postgres.[project]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
25
- ```
26
-
27
- **Rule:** Application code ALWAYS connects via PgBouncer (port 6543). Direct connections (port 5432) only for migrations and schema changes.
28
-
29
- ---
30
-
31
- ## Row Level Security (RLS)
32
-
33
- ### Enable RLS on every table
34
-
35
- ```sql
36
- ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY;
37
- ```
38
-
39
- ### Common RLS Patterns
40
-
41
- ```sql
42
- -- User owns the row
43
- CREATE POLICY "Users can view own documents"
44
- ON public.documents FOR SELECT
45
- USING (auth.uid() = user_id);
46
-
47
- -- User can insert own rows
48
- CREATE POLICY "Users can insert own documents"
49
- ON public.documents FOR INSERT
50
- WITH CHECK (auth.uid() = user_id);
51
-
52
- -- User can update own rows
53
- CREATE POLICY "Users can update own documents"
54
- ON public.documents FOR UPDATE
55
- USING (auth.uid() = user_id)
56
- WITH CHECK (auth.uid() = user_id);
57
-
58
- -- User can delete own rows
59
- CREATE POLICY "Users can delete own documents"
60
- ON public.documents FOR DELETE
61
- USING (auth.uid() = user_id);
62
-
63
- -- Service role bypass (for backend API)
64
- CREATE POLICY "Service role full access"
65
- ON public.documents FOR ALL
66
- USING (auth.role() = 'service_role');
67
-
68
- -- Team-based access
69
- CREATE POLICY "Team members can view"
70
- ON public.documents FOR SELECT
71
- USING (
72
- team_id IN (
73
- SELECT team_id FROM public.team_members
74
- WHERE user_id = auth.uid()
75
- )
76
- );
77
- ```
78
-
79
- ### RLS with Clerk JWT (custom claims)
80
-
81
- ```sql
82
- -- Extract Clerk user ID from JWT
83
- CREATE OR REPLACE FUNCTION auth.clerk_user_id()
84
- RETURNS TEXT AS $$
85
- SELECT coalesce(
86
- current_setting('request.jwt.claims', true)::json->>'sub',
87
- ''
88
- );
89
- $$ LANGUAGE sql STABLE;
90
-
91
- -- Use in RLS policies
92
- CREATE POLICY "Clerk users own rows"
93
- ON public.documents FOR ALL
94
- USING (clerk_user_id = auth.clerk_user_id());
95
- ```
96
-
97
- ---
98
-
99
- ## Migrations
100
-
101
- ```bash
102
- # Create new migration
103
- npx supabase migration new create_documents_table
104
-
105
- # Apply locally
106
- npx supabase db reset
107
-
108
- # Push to remote
109
- npx supabase db push
110
-
111
- # Diff local vs remote
112
- npx supabase db diff --use-migra
113
-
114
- # Lint for common issues
115
- npx supabase db lint --level warning
116
- ```
117
-
118
- ### Migration file structure
119
-
120
- ```sql
121
- -- supabase/migrations/20240101000000_create_documents_table.sql
122
-
123
- CREATE TABLE public.documents (
124
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
125
- user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
126
- title TEXT NOT NULL,
127
- content TEXT,
128
- created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
129
- updated_at TIMESTAMPTZ DEFAULT now() NOT NULL
130
- );
131
-
132
- -- Always enable RLS
133
- ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY;
134
-
135
- -- Always add indexes for filtered columns
136
- CREATE INDEX idx_documents_user_id ON public.documents(user_id);
137
- CREATE INDEX idx_documents_created_at ON public.documents(created_at DESC);
138
-
139
- -- RLS policies
140
- CREATE POLICY "Users manage own documents"
141
- ON public.documents FOR ALL
142
- USING (auth.uid() = user_id)
143
- WITH CHECK (auth.uid() = user_id);
144
- ```
145
-
146
- ---
147
-
148
- ## pgvector (Embeddings)
149
-
150
- ### Setup
151
-
152
- ```sql
153
- CREATE EXTENSION IF NOT EXISTS vector;
154
-
155
- CREATE TABLE public.embeddings (
156
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
157
- document_id UUID REFERENCES public.documents(id) ON DELETE CASCADE,
158
- content TEXT NOT NULL,
159
- embedding vector(1536) NOT NULL, -- OpenAI text-embedding-3-small
160
- metadata JSONB DEFAULT '{}'::jsonb,
161
- created_at TIMESTAMPTZ DEFAULT now() NOT NULL
162
- );
163
-
164
- -- HNSW index for fast similarity search (cosine distance)
165
- CREATE INDEX idx_embeddings_vector ON public.embeddings
166
- USING hnsw (embedding vector_cosine_ops)
167
- WITH (m = 16, ef_construction = 64);
168
- ```
169
-
170
- ### Similarity search function
171
-
172
- ```sql
173
- CREATE OR REPLACE FUNCTION match_embeddings(
174
- query_embedding vector(1536),
175
- match_threshold FLOAT DEFAULT 0.78,
176
- match_count INT DEFAULT 5
177
- )
178
- RETURNS TABLE (
179
- id UUID,
180
- content TEXT,
181
- metadata JSONB,
182
- similarity FLOAT
183
- )
184
- LANGUAGE plpgsql AS $$
185
- BEGIN
186
- RETURN QUERY
187
- SELECT
188
- e.id,
189
- e.content,
190
- e.metadata,
191
- 1 - (e.embedding <=> query_embedding) AS similarity
192
- FROM public.embeddings e
193
- WHERE 1 - (e.embedding <=> query_embedding) > match_threshold
194
- ORDER BY e.embedding <=> query_embedding
195
- LIMIT match_count;
196
- END;
197
- $$;
198
- ```
199
-
200
- ---
201
-
202
- ## Realtime Subscriptions
203
-
204
- ```typescript
205
- // Subscribe to changes on a table
206
- const channel = supabase
207
- .channel('documents-changes')
208
- .on('postgres_changes',
209
- { event: '*', schema: 'public', table: 'documents', filter: `user_id=eq.${userId}` },
210
- (payload) => {
211
- console.log('Change:', payload.eventType, payload.new);
212
- }
213
- )
214
- .subscribe();
215
-
216
- // Cleanup
217
- channel.unsubscribe();
218
- ```
219
-
220
- **Rule:** Always filter subscriptions by user/team — never subscribe to full table changes.
221
-
222
- ---
223
-
224
- ## Storage (File Uploads)
225
-
226
- ```sql
227
- -- Create bucket
228
- INSERT INTO storage.buckets (id, name, public)
229
- VALUES ('avatars', 'avatars', true);
230
-
231
- -- RLS for storage
232
- CREATE POLICY "Users upload own avatar"
233
- ON storage.objects FOR INSERT
234
- WITH CHECK (
235
- bucket_id = 'avatars' AND
236
- auth.uid()::text = (storage.foldername(name))[1]
237
- );
238
- ```
239
-
240
- ---
241
-
242
- ## Checklist
243
-
244
- - [ ] PgBouncer (port 6543) used for all application connections
245
- - [ ] RLS enabled on ALL public tables
246
- - [ ] RLS policies cover SELECT, INSERT, UPDATE, DELETE
247
- - [ ] Service role policy exists for backend API access
248
- - [ ] Migrations use `npx supabase migration new` (not manual SQL)
249
- - [ ] Indexes on all frequently filtered columns
250
- - [ ] pgvector: HNSW index with appropriate m/ef_construction values
251
- - [ ] Realtime subscriptions filtered by user/team
252
- - [ ] Storage buckets have RLS policies
253
-
254
- ---
255
-
256
- *MORPH-SPEC by Polymorphism Tech*
@@ -1,29 +0,0 @@
1
- ---
2
- paths:
3
- - "**/*.sql"
4
- - "**/supabase/**"
5
- - "**/migrations/**"
6
- stacks:
7
- - supabase
8
- ---
9
-
10
- # Supabase & PostgreSQL Standards
11
-
12
- @.morph/framework/standards/infrastructure/supabase/supabase-auth.md
13
- @.morph/framework/standards/infrastructure/supabase/supabase-rls.md
14
- @.morph/framework/standards/infrastructure/supabase/supabase-storage.md
15
- @.morph/framework/standards/infrastructure/supabase/supabase-pgvector.md
16
- @.morph/framework/standards/infrastructure/supabase/mcp-setup.md
17
- @.morph/framework/standards/infrastructure/supabase/supabase-custom-jwt.md
18
- @.morph/framework/standards/backend/database/postgresql/database.md
19
-
20
- ## SQL & Migration Templates
21
-
22
- | Template | Path | Purpose |
23
- |----------|------|---------|
24
- | Migration (full) | `.morph/framework/templates/code/sql/supabase-migration.template.sql` | RLS + indexes |
25
- | Migration (simple) | `.morph/framework/templates/code/sql/supabase-migration.sql` | Basic migration |
26
- | RLS Policy | `.morph/framework/templates/code/sql/rls-policy.sql` | Row Level Security |
27
- | TS Types | `.morph/framework/templates/code/typescript/contracts.ts` | Supabase DB types |
28
-
29
- Read the template to see the exact pattern for migrations, RLS policies, and type generation.
@@ -1,142 +0,0 @@
1
- # Supabase Local Development Guide
2
-
3
- > **Scope:** nextjs-supabase
4
- > **Layer:** 2 (on keyword)
5
- > **Keywords:** supabase, cli, local, docker, setup, migrations, psql
6
- > **Load When:** supabase setup keywords detected
7
-
8
- Local-first database management via Supabase CLI and psql — always targeting the local Docker instance.
9
-
10
- ## CRITICAL WARNING
11
-
12
- ```
13
- ==========================================================
14
- NEVER connect to a PRODUCTION Supabase project.
15
-
16
- All database operations MUST target the local Docker
17
- instance started via `npx supabase start`.
18
-
19
- Production schema may diverge from local (renamed tables,
20
- columns, migrations). Local is the single source of truth
21
- during development.
22
- ==========================================================
23
- ```
24
-
25
- ## Setup
26
-
27
- ### 1. Install Supabase CLI
28
-
29
- ```bash
30
- npm install -g supabase
31
- # or use npx (no global install needed)
32
- npx supabase --version
33
- ```
34
-
35
- ### 2. Start Local Instance
36
-
37
- ```bash
38
- npx supabase start
39
- ```
40
-
41
- This starts PostgreSQL, GoTrue (auth), Storage, and other services in Docker containers.
42
-
43
- ### 3. Get Connection Details
44
-
45
- ```bash
46
- npx supabase status
47
- ```
48
-
49
- Output includes:
50
- - **DB URL**: `postgresql://postgres:postgres@127.0.0.1:54322/postgres` (port may vary)
51
- - **API URL**: `http://127.0.0.1:54321`
52
- - **anon key** and **service_role key** for local testing
53
-
54
- > **Important:** The port (54322) may vary when running multiple Supabase instances. Always use `npx supabase status` to get the current DB URL — never hardcode it.
55
-
56
- ## Common CLI Commands
57
-
58
- ### Schema Inspection
59
-
60
- ```bash
61
- # Full schema dump
62
- npx supabase db dump --local --schema public
63
-
64
- # List tables (from dump output)
65
- npx supabase db dump --local --schema public 2>/dev/null | grep "CREATE TABLE"
66
-
67
- # Detailed table schema via psql
68
- psql $DB_URL -c "SELECT column_name, data_type, is_nullable, column_default \
69
- FROM information_schema.columns WHERE table_name = 'TABLE_NAME' ORDER BY ordinal_position"
70
-
71
- # Foreign key relationships
72
- psql $DB_URL -c "SELECT tc.table_name, kcu.column_name, ccu.table_name AS foreign_table, \
73
- ccu.column_name AS foreign_column FROM information_schema.table_constraints tc \
74
- JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name \
75
- JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name \
76
- WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name = 'TABLE_NAME'"
77
-
78
- # RLS policies
79
- psql $DB_URL -c "SELECT tablename, policyname, cmd, qual FROM pg_policies WHERE schemaname = 'public'"
80
-
81
- # Indexes
82
- psql $DB_URL -c "SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'TABLE_NAME'"
83
- ```
84
-
85
- ### Migrations
86
-
87
- ```bash
88
- # Create a new migration
89
- npx supabase migration new add_status_to_leads
90
-
91
- # Apply pending migrations to local
92
- npx supabase migration up --local
93
-
94
- # List applied migrations
95
- npx supabase migration list --local
96
-
97
- # Reset local database (destructive — re-applies all migrations)
98
- npx supabase db reset
99
- ```
100
-
101
- ### Data Operations
102
-
103
- ```bash
104
- # Run arbitrary SQL against local
105
- psql $DB_URL -c "SELECT * FROM leads LIMIT 5"
106
-
107
- # Seed data
108
- npx supabase db seed --local
109
- ```
110
-
111
- ## Usage with MORPH-SPEC Workflow
112
-
113
- During MORPH-SPEC phases, the Supabase CLI is used for:
114
-
115
- | Phase | CLI Usage | Commands |
116
- |-------|-----------|----------|
117
- | **Phase 2 (Design)** | Inspect existing schema to inform spec | `npx supabase status` → `psql` queries |
118
- | **Phase 4 (Tasks)** | Verify migration dependencies | `npx supabase migration list --local` |
119
- | **Phase 5 (Implement)** | Apply migrations, verify RLS, test queries | `npx supabase migration up --local` + `psql` |
120
- | **Post-Implementation** | Verify RLS policies are correct | `psql` pg_policies queries |
121
-
122
- ### Example Workflow
123
-
124
- ```
125
- 1. Design phase: `npx supabase status` → `psql $DB_URL` to inspect schema
126
- 2. Write migration SQL in supabase/migrations/
127
- 3. Apply: `npx supabase migration up --local`
128
- 4. Verify: `psql $DB_URL -c "SELECT policyname, cmd FROM pg_policies WHERE tablename = 'search_logs'"`
129
- 5. Test: `psql $DB_URL -c "SELECT * FROM search_logs LIMIT 5"`
130
- ```
131
-
132
- ## Best Practices
133
-
134
- 1. **Always use local Supabase** (`npx supabase start`) — never connect to remote/production
135
- 2. **Use `npx supabase status`** to detect the DB URL dynamically — port may vary
136
- 3. **Write migrations as SQL files** in `supabase/migrations/` — apply via CLI
137
- 4. **Verify RLS after every table change** — missing policies are a CRITICAL security issue
138
- 5. **Reset local DB when needed** — `npx supabase db reset` re-applies all migrations cleanly
139
-
140
- ---
141
-
142
- *MORPH-SPEC by Polymorphism Tech*