@polymorphism-tech/morph-spec 10.0.0 → 10.2.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/CLAUDE.md +2 -0
- package/README.md +3 -7
- package/bin/morph-spec.js +1 -41
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +27 -38
- package/docs/QUICKSTART.md +31 -191
- package/framework/CLAUDE.md +2 -2
- package/framework/MORPH.md +2 -2
- package/framework/agents.json +0 -1
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-preflight.md +6 -7
- package/framework/docs/CLI.md +24 -21
- package/framework/hooks/README.md +2 -2
- package/framework/hooks/claude-code/core/validate-completion.js +0 -35
- package/framework/hooks/shared/e2e-check.js +44 -7
- package/framework/hooks/shared/heavy-command.js +12 -1
- package/framework/hooks/shared/next-step.js +4 -4
- package/framework/mcp/registry.json +4 -4
- package/framework/phases.json +4 -4
- package/framework/rules/ai-agents-standards.md +1 -1
- package/framework/rules/data-standards.md +1 -1
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-auth-standards.md +1 -1
- package/framework/rules/neon-standards.md +3 -3
- package/framework/schemas/config.schema.json +20 -1
- package/framework/schemas/feature.schema.json +1 -0
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +8 -7
- package/framework/skills/core/morph-apply/references/hotfix.md +1 -1
- package/framework/skills/core/morph-apply/references/review.md +5 -5
- package/framework/skills/core/morph-plan/SKILL.md +22 -16
- package/framework/skills/core/morph-plan/references/critique.md +11 -8
- package/framework/skills/core/morph-plan/references/interview.md +18 -6
- package/framework/skills/core/morph-uiux/SKILL.md +2 -2
- package/framework/standards/STANDARDS.json +20 -24
- package/framework/standards/STANDARDS.meta.json +12 -19
- package/framework/standards/ai-agents/rag-custom-pgvector.md +1 -0
- package/framework/standards/architecture/stack-baseline.md +26 -3
- package/framework/standards/backend/database/postgresql/database.md +22 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +33 -1
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +2 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +10 -9
- package/framework/standards/infrastructure/docker/local-compose-isolation.md +2 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +62 -20
- package/framework/standards/infrastructure/neon/neon-platform.md +61 -0
- package/framework/standards/infrastructure/neon/neon-setup.md +14 -13
- package/framework/standards/integration/mcp/mcp-tools.md +20 -20
- package/framework/templates/docs/plan.md +4 -1
- package/framework/templates/examples/design-system-examples.md +0 -3
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +1 -1
- package/package.json +1 -1
- package/src/commands/project/doctor.js +0 -1
- package/src/commands/state/approve.js +9 -109
- package/src/commands/state/auto-approval.js +1 -1
- package/src/commands/state/create.js +1 -1
- package/src/commands/state/gate-check.js +2 -2
- package/src/commands/state/report-check.js +44 -2
- package/src/commands/validation/verify.js +7 -3
- package/src/core/state/state-manager.js +0 -24
- package/src/lib/detectors/index.js +1 -1
- package/src/lib/heavy/cleanup.js +8 -2
- package/src/lib/installers/memory-installer.js +1 -1
- package/src/lib/plan/checks.js +8 -1
- package/src/lib/plan/critique.js +23 -3
- package/src/lib/review-confirmation.js +67 -0
- package/src/lib/review-reports.js +1 -0
- package/src/lib/risk-detector.js +2 -2
- package/src/lib/tasks/test-nodes.js +5 -0
- package/src/lib/tasks/test-runner.js +37 -43
- package/src/lib/telemetry/telemetry-reader.js +1 -2
- package/src/lib/telemetry/verify-event.js +7 -0
- package/src/lib/validators/verify-cache.js +63 -0
- package/src/lib/validators/verify-runner.js +98 -23
- package/src/utils/agents-installer.js +2 -2
- package/src/utils/hooks-installer.js +0 -20
- package/bin/validate.js +0 -212
- package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
- package/framework/standards/infrastructure/neon/neon-pgrag.md +0 -243
- package/src/commands/observability/telemetry.js +0 -60
- package/src/commands/validation/validate-feature.js +0 -337
package/bin/validate.js
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import { getStackProfile } from '../src/lib/stack/stack-profile.js';
|
|
5
|
-
|
|
6
|
-
async function buildValidators(stack) {
|
|
7
|
-
const validators = {};
|
|
8
|
-
|
|
9
|
-
const isDotnet = !stack || ['dotnet', 'blazor', 'aspnet', 'dotnet-blazor'].includes(stack.toLowerCase());
|
|
10
|
-
const isNextjs = stack && ['nextjs', 'next', 'next.js'].includes(stack.toLowerCase());
|
|
11
|
-
|
|
12
|
-
if (isDotnet) {
|
|
13
|
-
const { validatePackages } = await import('../src/lib/validators/packages/package-validator.js');
|
|
14
|
-
|
|
15
|
-
validators.packages = {
|
|
16
|
-
name: 'Package Compatibility',
|
|
17
|
-
description: 'Validates NuGet package versions against .NET compatibility matrix',
|
|
18
|
-
run: validatePackages
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (isNextjs) {
|
|
23
|
-
const { validateNextComponentFiles } = await import('../src/lib/validators/nextjs/next-component-validator.js');
|
|
24
|
-
validators['next-components'] = {
|
|
25
|
-
name: 'Next.js Components',
|
|
26
|
-
description: "Validates 'use client' directives, hook usage, and file naming conventions",
|
|
27
|
-
run: (projectPath, opts) => validateNextComponentFiles(projectPath, opts)
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return validators;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export async function validateCommand(args = []) {
|
|
35
|
-
const validatorName = args[0] || 'all';
|
|
36
|
-
const options = parseOptions(args);
|
|
37
|
-
|
|
38
|
-
const { stack } = getStackProfile();
|
|
39
|
-
const VALIDATORS = await buildValidators(stack);
|
|
40
|
-
|
|
41
|
-
console.log(chalk.bold.cyan('\n🔍 MORPH-SPEC Validator\n'));
|
|
42
|
-
if (stack) {
|
|
43
|
-
console.log(chalk.gray(`Stack: ${stack}\n`));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (validatorName === 'all') {
|
|
47
|
-
await runAllValidators(VALIDATORS, options);
|
|
48
|
-
} else if (VALIDATORS[validatorName]) {
|
|
49
|
-
await runValidator(VALIDATORS, validatorName, options);
|
|
50
|
-
} else {
|
|
51
|
-
console.error(chalk.red(`❌ Unknown validator: ${validatorName}`));
|
|
52
|
-
console.log(chalk.gray('\nAvailable validators:'));
|
|
53
|
-
for (const [name, validator] of Object.entries(VALIDATORS)) {
|
|
54
|
-
console.log(chalk.gray(` - ${name}: ${validator.description}`));
|
|
55
|
-
}
|
|
56
|
-
console.log(chalk.gray(' - all: Run all validators'));
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async function runAllValidators(VALIDATORS, options) {
|
|
62
|
-
const results = {};
|
|
63
|
-
let totalErrors = 0;
|
|
64
|
-
let totalWarnings = 0;
|
|
65
|
-
|
|
66
|
-
console.log(chalk.gray('Running all validators...\n'));
|
|
67
|
-
|
|
68
|
-
for (const [name, validator] of Object.entries(VALIDATORS)) {
|
|
69
|
-
console.log(chalk.cyan(`\n▸ ${validator.name}`));
|
|
70
|
-
console.log(chalk.gray(` ${validator.description}\n`));
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const result = await validator.run('.', {
|
|
74
|
-
verbose: options.verbose,
|
|
75
|
-
autoFix: options.autoFix
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
results[name] = result;
|
|
79
|
-
|
|
80
|
-
if (result.errors !== undefined) {
|
|
81
|
-
totalErrors += result.errors;
|
|
82
|
-
totalWarnings += result.warnings || 0;
|
|
83
|
-
} else if (result.results) {
|
|
84
|
-
const issues = result.results.flatMap(r => r.issues || []);
|
|
85
|
-
totalErrors += issues.filter(i => i.level === 'error').length;
|
|
86
|
-
totalWarnings += issues.filter(i => i.level === 'warning').length;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
} catch (error) {
|
|
90
|
-
console.error(chalk.red(` ❌ Failed: ${error.message}`));
|
|
91
|
-
results[name] = { status: 'error', error: error.message };
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
console.log(chalk.bold.cyan('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
96
|
-
console.log(chalk.bold('📊 Validation Summary\n'));
|
|
97
|
-
|
|
98
|
-
if (totalErrors === 0 && totalWarnings === 0) {
|
|
99
|
-
console.log(chalk.green('✅ All validations passed!'));
|
|
100
|
-
} else {
|
|
101
|
-
if (totalErrors > 0) {
|
|
102
|
-
console.log(chalk.red(`❌ ${totalErrors} error(s) found`));
|
|
103
|
-
}
|
|
104
|
-
if (totalWarnings > 0) {
|
|
105
|
-
console.log(chalk.yellow(`⚠️ ${totalWarnings} warning(s) found`));
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
console.log('');
|
|
110
|
-
|
|
111
|
-
if (totalErrors > 0 && !options.noFail) {
|
|
112
|
-
process.exit(1);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function runValidator(VALIDATORS, name, options) {
|
|
117
|
-
const validator = VALIDATORS[name];
|
|
118
|
-
|
|
119
|
-
console.log(chalk.cyan(`▸ ${validator.name}`));
|
|
120
|
-
console.log(chalk.gray(` ${validator.description}\n`));
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
const result = await validator.run('.', {
|
|
124
|
-
verbose: true,
|
|
125
|
-
autoFix: options.autoFix
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
if (result.status === 'ok') {
|
|
129
|
-
console.log(chalk.green('\n✅ Validation passed!\n'));
|
|
130
|
-
} else {
|
|
131
|
-
console.log(chalk.yellow('\n⚠️ Validation completed with issues\n'));
|
|
132
|
-
|
|
133
|
-
if (result.errors > 0 && !options.noFail) {
|
|
134
|
-
process.exit(1);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error(chalk.red(`\n❌ Validation failed: ${error.message}\n`));
|
|
140
|
-
if (!options.noFail) {
|
|
141
|
-
process.exit(1);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function parseOptions(args) {
|
|
147
|
-
const options = {
|
|
148
|
-
verbose: false,
|
|
149
|
-
autoFix: false,
|
|
150
|
-
noFail: false
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
for (const arg of args) {
|
|
154
|
-
if (arg === '--verbose' || arg === '-v') {
|
|
155
|
-
options.verbose = true;
|
|
156
|
-
} else if (arg === '--auto-fix' || arg === '--fix') {
|
|
157
|
-
options.autoFix = true;
|
|
158
|
-
} else if (arg === '--no-fail') {
|
|
159
|
-
options.noFail = true;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return options;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export function showValidateHelp() {
|
|
167
|
-
console.log(`
|
|
168
|
-
${chalk.bold.cyan('morph-spec validate')} - Run project validations
|
|
169
|
-
|
|
170
|
-
${chalk.bold('USAGE')}
|
|
171
|
-
morph-spec validate [validator] [options]
|
|
172
|
-
|
|
173
|
-
${chalk.bold('VALIDATORS')}
|
|
174
|
-
all Run all validators (default)
|
|
175
|
-
packages Validate NuGet package compatibility (.NET 10)
|
|
176
|
-
|
|
177
|
-
${chalk.bold('OPTIONS')}
|
|
178
|
-
--verbose, -v Show detailed output
|
|
179
|
-
--auto-fix, --fix Auto-fix issues where possible
|
|
180
|
-
--no-fail Don't exit with error code
|
|
181
|
-
|
|
182
|
-
${chalk.bold('EXAMPLES')}
|
|
183
|
-
morph-spec validate # Run all validators
|
|
184
|
-
morph-spec validate packages --fix # Validate packages and auto-fix
|
|
185
|
-
morph-spec validate all # Run all validators
|
|
186
|
-
|
|
187
|
-
${chalk.bold('EXIT CODES')}
|
|
188
|
-
0 All validations passed
|
|
189
|
-
1 Validation errors found (unless --no-fail)
|
|
190
|
-
|
|
191
|
-
${chalk.bold('INTEGRATION')}
|
|
192
|
-
Add to pre-commit hook:
|
|
193
|
-
morph-spec validate --no-fail
|
|
194
|
-
|
|
195
|
-
CI/CD pipeline:
|
|
196
|
-
morph-spec validate || exit 1
|
|
197
|
-
`);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
201
|
-
const args = process.argv.slice(2);
|
|
202
|
-
|
|
203
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
204
|
-
showValidateHelp();
|
|
205
|
-
process.exit(0);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
validateCommand(args).catch(error => {
|
|
209
|
-
console.error(chalk.red(`\n❌ Error: ${error.message}\n`));
|
|
210
|
-
process.exit(1);
|
|
211
|
-
});
|
|
212
|
-
}
|
|
Binary file
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
# Neon pgrag Standard
|
|
2
|
-
|
|
3
|
-
> **Scope:** neon
|
|
4
|
-
> **Layer:** 2 (on keyword)
|
|
5
|
-
> **Keywords:** pgrag, rag, chunking, embedding, reranking, retrieval
|
|
6
|
-
> **Load When:** pgrag or in-database RAG keywords detected
|
|
7
|
-
|
|
8
|
-
**Verified against:** Neon pgrag (`rag` extension family — experimental, see Core Rules). Last-verified: 2026-05-20.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
Stack: Neon Serverless Postgres
|
|
13
|
-
|
|
14
|
-
## Core Rules
|
|
15
|
-
|
|
16
|
-
- pgrag is EXPERIMENTAL -- API may change between Neon releases
|
|
17
|
-
- ALWAYS enable unstable extensions before installing: `SET neon.allow_unstable_extensions='true'`
|
|
18
|
-
- Use pgrag for simple, self-contained RAG pipelines (PDF ingestion, chunking, search)
|
|
19
|
-
- Use application-level custom RAG (a typed MAF tool over pgvector — see standard ai-agents-rag-custom-pgvector) for complex workflows requiring custom logic, multi-model orchestration, or .NET integration
|
|
20
|
-
- NEVER use pgrag local embeddings (384 dims) when your application uses a different embedding model -- dimension mismatch breaks search
|
|
21
|
-
|
|
22
|
-
## Install pgrag
|
|
23
|
-
|
|
24
|
-
```sql
|
|
25
|
-
-- Enable unstable extensions (required)
|
|
26
|
-
SET neon.allow_unstable_extensions='true';
|
|
27
|
-
|
|
28
|
-
-- Core extension
|
|
29
|
-
CREATE EXTENSION IF NOT EXISTS rag CASCADE;
|
|
30
|
-
|
|
31
|
-
-- Local embedding model (bge-small-en-v1.5, 384 dimensions, 33M params)
|
|
32
|
-
CREATE EXTENSION IF NOT EXISTS rag_bge_small_en_v15 CASCADE;
|
|
33
|
-
|
|
34
|
-
-- Local reranker (jina-reranker-v1-tiny-en, 33M params)
|
|
35
|
-
CREATE EXTENSION IF NOT EXISTS rag_jina_reranker_v1_tiny_en CASCADE;
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Text Extraction
|
|
39
|
-
|
|
40
|
-
Extract text from binary documents directly in SQL:
|
|
41
|
-
|
|
42
|
-
```sql
|
|
43
|
-
-- Extract text from PDF
|
|
44
|
-
SELECT rag.text_from_pdf(pg_read_binary_file('/path/to/document.pdf'));
|
|
45
|
-
|
|
46
|
-
-- Extract text from DOCX
|
|
47
|
-
SELECT rag.text_from_docx(pg_read_binary_file('/path/to/document.docx'));
|
|
48
|
-
|
|
49
|
-
-- Convert HTML to Markdown
|
|
50
|
-
SELECT rag.markdown_from_html('<h1>Title</h1><p>Content here</p>');
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### From a Documents Table
|
|
54
|
-
|
|
55
|
-
```sql
|
|
56
|
-
CREATE TABLE raw_documents (
|
|
57
|
-
id SERIAL PRIMARY KEY,
|
|
58
|
-
filename TEXT NOT NULL,
|
|
59
|
-
content BYTEA NOT NULL,
|
|
60
|
-
extracted_text TEXT
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
-- Extract text on insert
|
|
64
|
-
UPDATE raw_documents
|
|
65
|
-
SET extracted_text = rag.text_from_pdf(content)
|
|
66
|
-
WHERE filename LIKE '%.pdf' AND extracted_text IS NULL;
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Chunking Strategies
|
|
70
|
-
|
|
71
|
-
### Character-Based Chunking
|
|
72
|
-
|
|
73
|
-
Splits text by character count with overlap for context continuity:
|
|
74
|
-
|
|
75
|
-
```sql
|
|
76
|
-
-- Split into chunks of 1000 chars with 200 char overlap
|
|
77
|
-
SELECT unnest(
|
|
78
|
-
rag.chunks_by_character_count(
|
|
79
|
-
'Your long document text here...',
|
|
80
|
-
1000, -- max_chars per chunk
|
|
81
|
-
200 -- overlap chars
|
|
82
|
-
)
|
|
83
|
-
);
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### Token-Based Chunking
|
|
87
|
-
|
|
88
|
-
Uses the bge-small-en-v1.5 tokenizer for model-aware splitting:
|
|
89
|
-
|
|
90
|
-
```sql
|
|
91
|
-
-- Split into chunks of 512 tokens with 50 token overlap
|
|
92
|
-
SELECT unnest(
|
|
93
|
-
rag_bge_small_en_v15.chunks_by_token_count(
|
|
94
|
-
'Your long document text here...',
|
|
95
|
-
512, -- max_tokens per chunk
|
|
96
|
-
50 -- overlap tokens
|
|
97
|
-
)
|
|
98
|
-
);
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## Embedding Generation
|
|
102
|
-
|
|
103
|
-
### Local Embeddings (bge-small-en-v1.5)
|
|
104
|
-
|
|
105
|
-
Zero-cost, runs on the database server. Produces 384-dimension vectors:
|
|
106
|
-
|
|
107
|
-
```sql
|
|
108
|
-
-- Embedding for a document passage (includes "passage:" prefix internally)
|
|
109
|
-
SELECT rag_bge_small_en_v15.embedding_for_passage('Neon is a serverless Postgres platform');
|
|
110
|
-
|
|
111
|
-
-- Embedding for a search query (includes "query:" prefix internally)
|
|
112
|
-
SELECT rag_bge_small_en_v15.embedding_for_query('What is Neon?');
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### OpenAI Embeddings (text-embedding-3-small)
|
|
116
|
-
|
|
117
|
-
Remote API call, produces 1536-dimension vectors:
|
|
118
|
-
|
|
119
|
-
```sql
|
|
120
|
-
-- Set API key (per session)
|
|
121
|
-
SET neon.ai_api_key = 'sk-...';
|
|
122
|
-
|
|
123
|
-
-- Generate embedding via OpenAI
|
|
124
|
-
SELECT rag.openai_text_embedding_3_small('Neon is a serverless Postgres platform');
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Full RAG Pipeline Example
|
|
128
|
-
|
|
129
|
-
```sql
|
|
130
|
-
-- 1. Create chunks table
|
|
131
|
-
CREATE TABLE chunks (
|
|
132
|
-
id SERIAL PRIMARY KEY,
|
|
133
|
-
document_id INT REFERENCES raw_documents(id),
|
|
134
|
-
chunk_text TEXT NOT NULL,
|
|
135
|
-
embedding vector(384)
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
-- 2. Chunk and embed a document
|
|
139
|
-
WITH doc AS (
|
|
140
|
-
SELECT id, rag.text_from_pdf(content) AS full_text
|
|
141
|
-
FROM raw_documents WHERE id = 1
|
|
142
|
-
),
|
|
143
|
-
chunked AS (
|
|
144
|
-
SELECT doc.id AS document_id, unnest(
|
|
145
|
-
rag.chunks_by_character_count(doc.full_text, 1000, 200)
|
|
146
|
-
) AS chunk_text
|
|
147
|
-
FROM doc
|
|
148
|
-
)
|
|
149
|
-
INSERT INTO chunks (document_id, chunk_text, embedding)
|
|
150
|
-
SELECT document_id, chunk_text,
|
|
151
|
-
rag_bge_small_en_v15.embedding_for_passage(chunk_text)
|
|
152
|
-
FROM chunked;
|
|
153
|
-
|
|
154
|
-
-- 3. Create HNSW index
|
|
155
|
-
CREATE INDEX idx_chunks_embedding ON chunks
|
|
156
|
-
USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
## Similarity Search
|
|
160
|
-
|
|
161
|
-
```sql
|
|
162
|
-
-- Find top 10 similar chunks
|
|
163
|
-
SELECT id, chunk_text,
|
|
164
|
-
1 - (embedding <=> rag_bge_small_en_v15.embedding_for_query('What is Neon?')) AS similarity
|
|
165
|
-
FROM chunks
|
|
166
|
-
ORDER BY embedding <=> rag_bge_small_en_v15.embedding_for_query('What is Neon?')
|
|
167
|
-
LIMIT 10;
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## Reranking
|
|
171
|
-
|
|
172
|
-
Reranking refines initial vector search results using a cross-encoder model for higher accuracy:
|
|
173
|
-
|
|
174
|
-
```sql
|
|
175
|
-
-- Rerank top 10 vector results down to top 5
|
|
176
|
-
WITH vector_matches AS (
|
|
177
|
-
SELECT id, chunk_text,
|
|
178
|
-
1 - (embedding <=> rag_bge_small_en_v15.embedding_for_query('What is Neon?')) AS similarity
|
|
179
|
-
FROM chunks
|
|
180
|
-
ORDER BY embedding <=> rag_bge_small_en_v15.embedding_for_query('What is Neon?')
|
|
181
|
-
LIMIT 10
|
|
182
|
-
)
|
|
183
|
-
SELECT id, chunk_text, similarity,
|
|
184
|
-
rag_jina_reranker_v1_tiny_en.rerank_distance('What is Neon?', chunk_text) AS rerank_score
|
|
185
|
-
FROM vector_matches
|
|
186
|
-
ORDER BY rerank_score ASC
|
|
187
|
-
LIMIT 5;
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
## LLM Integration in SQL
|
|
191
|
-
|
|
192
|
-
Send context + question to an LLM directly from SQL:
|
|
193
|
-
|
|
194
|
-
```sql
|
|
195
|
-
-- Set API key
|
|
196
|
-
SET neon.ai_api_key = 'sk-...';
|
|
197
|
-
|
|
198
|
-
-- Ask a question with retrieved context
|
|
199
|
-
WITH context AS (
|
|
200
|
-
SELECT string_agg(chunk_text, E'\n---\n') AS combined_context
|
|
201
|
-
FROM (
|
|
202
|
-
SELECT chunk_text FROM chunks
|
|
203
|
-
ORDER BY embedding <=> rag_bge_small_en_v15.embedding_for_query('What is Neon?')
|
|
204
|
-
LIMIT 5
|
|
205
|
-
) top_chunks
|
|
206
|
-
)
|
|
207
|
-
SELECT rag.openai_chat_completion(
|
|
208
|
-
json_build_object(
|
|
209
|
-
'model', 'gpt-4o',
|
|
210
|
-
'messages', json_build_array(
|
|
211
|
-
json_build_object('role', 'system', 'content',
|
|
212
|
-
'Answer based on the provided context. If unsure, say so.'),
|
|
213
|
-
json_build_object('role', 'user', 'content',
|
|
214
|
-
format('Context:\n%s\n\nQuestion: What is Neon?', context.combined_context))
|
|
215
|
-
)
|
|
216
|
-
)
|
|
217
|
-
) FROM context;
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
## When to Use pgrag vs Application-Level RAG
|
|
221
|
-
|
|
222
|
-
| Factor | pgrag (In-Database) | SK + pgvector (Application) |
|
|
223
|
-
|--------|---------------------|----------------------------|
|
|
224
|
-
| Setup complexity | Minimal -- SQL only | Requires .NET service layer |
|
|
225
|
-
| Embedding model | Local (384d) or OpenAI | Any model via SK connectors |
|
|
226
|
-
| Reranking | Built-in (Jina) | Custom implementation |
|
|
227
|
-
| Custom logic | Limited to SQL | Full C# flexibility |
|
|
228
|
-
| Multi-model orchestration | Not supported | Native SK capability |
|
|
229
|
-
| Production readiness | Experimental | Stable (SK GA) |
|
|
230
|
-
| Cost | Zero for local models | API costs for remote models |
|
|
231
|
-
| Best for | Prototyping, simple RAG, SQL-only environments | Production .NET applications, complex pipelines |
|
|
232
|
-
|
|
233
|
-
**Recommendation:** Start with pgrag for prototyping and validation. Migrate to custom RAG (a typed MAF vector-search tool over pgvector — see ai-agents-rag-custom-pgvector) for production .NET applications that need custom retrieval logic, multi-model orchestration, or MAF agent integration.
|
|
234
|
-
|
|
235
|
-
## Common Mistakes
|
|
236
|
-
|
|
237
|
-
| Wrong | Right | Why |
|
|
238
|
-
|-------|-------|-----|
|
|
239
|
-
| Missing `neon.allow_unstable_extensions` | `SET neon.allow_unstable_extensions='true'` | pgrag won't install without it |
|
|
240
|
-
| Mixing local (384d) and OpenAI (1536d) embeddings | Use one model consistently per column | Dimension mismatch breaks similarity search |
|
|
241
|
-
| No HNSW index on chunks table | Create index after initial data load | Full table scan on every search |
|
|
242
|
-
| Using pgrag in production without testing | Validate with representative data first | Extension is experimental, may change |
|
|
243
|
-
| Storing API key in SQL scripts | Use session variables or environment config | Keys in scripts leak to version control |
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { loadTelemetry, aggregateTelemetry } from '../../lib/telemetry/telemetry-reader.js';
|
|
3
|
-
|
|
4
|
-
// A rule injects its standards' digests through Claude Code's rules system,
|
|
5
|
-
// not the Read tool, so no hook sees them: a low count means "few full-text reads".
|
|
6
|
-
const STANDARDS_CAVEAT =
|
|
7
|
-
'Só reads explícitos (via Read) são observáveis; o resumo que uma rule injeta ao tocar arquivo casado por `paths:` não passa por tool call: não observável aqui.';
|
|
8
|
-
|
|
9
|
-
function section(title, rows, fmt) {
|
|
10
|
-
if (!rows.length) return;
|
|
11
|
-
console.log('\n' + chalk.white.bold(title));
|
|
12
|
-
for (const r of rows) console.log(' ' + fmt(r));
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Read-only and always exits 0: an empty stream is an answer.
|
|
16
|
-
export function telemetryCommand(options = {}) {
|
|
17
|
-
const events = loadTelemetry(options.cwd || process.cwd());
|
|
18
|
-
const summary = aggregateTelemetry(events, { feature: options.feature });
|
|
19
|
-
|
|
20
|
-
if (options.json) {
|
|
21
|
-
console.log(JSON.stringify({ ...summary, notes: { standards: STANDARDS_CAVEAT } }));
|
|
22
|
-
process.exit(0);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (summary.total === 0) {
|
|
26
|
-
const scope = options.feature ? ` para a feature "${options.feature}"` : '';
|
|
27
|
-
console.log(chalk.gray(`Telemetria vazia${scope} — nenhum evento capturado ainda em .morph/logs/events.jsonl.`));
|
|
28
|
-
process.exit(0);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const scope = options.feature ? chalk.cyan(` · feature ${options.feature}`) : '';
|
|
32
|
-
console.log(chalk.white.bold(`Telemetria do harness — ${summary.total} eventos${scope}`));
|
|
33
|
-
if (summary.features.length) {
|
|
34
|
-
console.log(chalk.gray(`Features: ${summary.features.join(', ')}`));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
section('Skills', summary.skills, s => `${chalk.green(s.name.padEnd(24))} ${s.count}`);
|
|
38
|
-
section('Sub-agents despachados', summary.personas, p => `${chalk.magenta(p.name.padEnd(24))} ${p.count}`);
|
|
39
|
-
section('Base de conhecimento (standards lidos explicitamente)', summary.standards,
|
|
40
|
-
s => `${chalk.yellow(String(s.count).padStart(3))}× ${s.path}`);
|
|
41
|
-
if (summary.standards.length || summary.tools.some(t => t.name === 'Read')) {
|
|
42
|
-
console.log(chalk.gray(` ⚠ ${STANDARDS_CAVEAT}`));
|
|
43
|
-
}
|
|
44
|
-
section('Mix de tools', summary.tools, t => `${t.name.padEnd(16)} ${t.count}`);
|
|
45
|
-
|
|
46
|
-
const limit = Number.isInteger(options.limit) && options.limit > 0 ? options.limit : 10;
|
|
47
|
-
const prompts = summary.prompts.slice(-limit);
|
|
48
|
-
if (prompts.length) {
|
|
49
|
-
console.log('\n' + chalk.white.bold(`Prompts (últimos ${prompts.length})`));
|
|
50
|
-
for (const p of prompts) {
|
|
51
|
-
const tag = p.source === 'user' ? chalk.blue('user ') : chalk.magenta('agent');
|
|
52
|
-
const where = p.feature ? chalk.gray(` [${p.feature}/${p.phase || '-'}]`) : '';
|
|
53
|
-
const oneLine = String(p.text || '').replace(/\s+/g, ' ').trim();
|
|
54
|
-
const preview = oneLine.length > 120 ? oneLine.slice(0, 117) + '...' : oneLine;
|
|
55
|
-
console.log(` ${tag}${where} ${preview}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
process.exit(0);
|
|
60
|
-
}
|