@ismaelsoilet/synapsejs 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +206 -0
- package/bin/synapse.ts +919 -0
- package/package.json +70 -0
- package/src/client/components.tsx +508 -0
- package/src/client/context.tsx +62 -0
- package/src/client/hooks.ts +152 -0
- package/src/client/i18n.ts +40 -0
- package/src/client.ts +66 -0
- package/src/compiler/agent-diagnostic-json.ts +113 -0
- package/src/compiler/ast-daemon-compressor.ts +190 -0
- package/src/compiler/client-bundler.ts +356 -0
- package/src/compiler/context-bench.ts +191 -0
- package/src/compiler/contract.ts +260 -0
- package/src/compiler/db-schema-generator.ts +306 -0
- package/src/compiler/fields-parser.ts +267 -0
- package/src/compiler/impact-analyzer.ts +279 -0
- package/src/compiler/migration-runner.ts +555 -0
- package/src/compiler/oracle-runner.ts +293 -0
- package/src/compiler/scaffolder.ts +528 -0
- package/src/compiler/schema-dag.ts +137 -0
- package/src/compiler/schema-drift.ts +184 -0
- package/src/compiler/slice-discovery.ts +154 -0
- package/src/compiler/slice-splitter.ts +838 -0
- package/src/compiler/slice-templates.ts +1527 -0
- package/src/compiler/standalone-builder.ts +117 -0
- package/src/core/action-context.ts +152 -0
- package/src/core/config.ts +77 -0
- package/src/core/database-client.ts +246 -0
- package/src/core/database-factory.ts +46 -0
- package/src/core/image-optimizer.ts +83 -0
- package/src/core/index.ts +14 -0
- package/src/core/jobs.ts +53 -0
- package/src/core/machine-types.ts +75 -0
- package/src/core/oracle.ts +20 -0
- package/src/core/postgres-client.ts +164 -0
- package/src/core/postgres-queue.ts +288 -0
- package/src/core/query-builder.ts +343 -0
- package/src/core/rpc-client.ts +70 -0
- package/src/core/session-context.ts +87 -0
- package/src/core/session-cookie.ts +81 -0
- package/src/core/session-token.ts +170 -0
- package/src/core/sqlite-client.ts +201 -0
- package/src/core/storage.ts +317 -0
- package/src/index.ts +133 -0
- package/src/mcp/server.ts +553 -0
- package/src/runtime/client-entry.ts +65 -0
- package/src/runtime/discovery-rules.ts +63 -0
- package/src/runtime/event-hub.ts +97 -0
- package/src/runtime/postgres-event-hub.ts +148 -0
- package/src/runtime/queue-engine.ts +255 -0
- package/src/runtime/rate-limiter.ts +152 -0
- package/src/runtime/server.ts +1550 -0
- package/src/runtime/uploads.ts +192 -0
- package/templates/starter/.dockerignore +7 -0
- package/templates/starter/Dockerfile +40 -0
- package/templates/starter/README.md +40 -0
- package/templates/starter/gitignore +6 -0
- package/templates/starter/package.json +34 -0
- package/templates/starter/src/slices/welcome/hello-world.slice.tsx +172 -0
- package/templates/starter/tsconfig.json +21 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SynapseJS - The Machine-Centric AI-Native Fullstack Engine
|
|
3
|
+
*
|
|
4
|
+
* Official Public Package Entry Point
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// 2. Convenience Re-exports for JIT Validation & PBT
|
|
8
|
+
export { type Static, type TSchema, Type } from '@sinclair/typebox';
|
|
9
|
+
export { Value } from '@sinclair/typebox/value';
|
|
10
|
+
// 1. Kernel Types & Functional Error Handling
|
|
11
|
+
export * from './core/index';
|
|
12
|
+
export { type RpcTransportError, rpcCall, rpcTransportFailure } from './core/rpc-client';
|
|
13
|
+
|
|
14
|
+
import * as fc from 'fast-check';
|
|
15
|
+
|
|
16
|
+
export * from './client';
|
|
17
|
+
export { type DiagnosticIssue, type DiagnosticReport, runMachineVerifications } from './compiler/agent-diagnostic-json';
|
|
18
|
+
export { compressRepositoryAST } from './compiler/ast-daemon-compressor';
|
|
19
|
+
export {
|
|
20
|
+
buildClientBundle,
|
|
21
|
+
buildVendorBundle,
|
|
22
|
+
VENDOR_BUNDLE_NAME,
|
|
23
|
+
VENDOR_BUNDLE_URL
|
|
24
|
+
} from './compiler/client-bundler';
|
|
25
|
+
export {
|
|
26
|
+
type MachineContract,
|
|
27
|
+
machineContract,
|
|
28
|
+
renderContractJson,
|
|
29
|
+
renderContractMarkdown
|
|
30
|
+
} from './compiler/contract';
|
|
31
|
+
export {
|
|
32
|
+
type ColumnDefinition,
|
|
33
|
+
type DatabaseCatalog,
|
|
34
|
+
generateDatabaseSchemaCatalog,
|
|
35
|
+
mapSqlTypeToTs,
|
|
36
|
+
parseDdlToCatalog,
|
|
37
|
+
type TableDefinition,
|
|
38
|
+
toPascalCase
|
|
39
|
+
} from './compiler/db-schema-generator';
|
|
40
|
+
export { type ParsedField, parseFields } from './compiler/fields-parser';
|
|
41
|
+
export {
|
|
42
|
+
analyzeImpact,
|
|
43
|
+
type ImpactAnalysisReport,
|
|
44
|
+
type ImpactedSlice,
|
|
45
|
+
type ImpactReason
|
|
46
|
+
} from './compiler/impact-analyzer';
|
|
47
|
+
export {
|
|
48
|
+
type BidirectionalDdl,
|
|
49
|
+
type MigrationReport,
|
|
50
|
+
type MigrationResult,
|
|
51
|
+
parseBidirectionalDdl,
|
|
52
|
+
type RollbackOptions,
|
|
53
|
+
type RollbackReport,
|
|
54
|
+
type RollbackResult,
|
|
55
|
+
rollbackSliceMigrations,
|
|
56
|
+
runSliceMigrations
|
|
57
|
+
} from './compiler/migration-runner';
|
|
58
|
+
export {
|
|
59
|
+
ORACLE_DIR_NAME,
|
|
60
|
+
type OracleCaseResult,
|
|
61
|
+
type OracleErrorCode,
|
|
62
|
+
type OracleReport,
|
|
63
|
+
type OracleSliceResult,
|
|
64
|
+
oracleWrapperSource,
|
|
65
|
+
parseOracleJunit,
|
|
66
|
+
runSliceOracles
|
|
67
|
+
} from './compiler/oracle-runner';
|
|
68
|
+
export { type ScaffoldError, type ScaffoldErrorCode, scaffoldCrud, scaffoldSlice } from './compiler/scaffolder';
|
|
69
|
+
export { orderSlicesByDag, parseTableDependencies } from './compiler/schema-dag';
|
|
70
|
+
export {
|
|
71
|
+
checkSchemaDrift,
|
|
72
|
+
type MissingColumnInfo,
|
|
73
|
+
type MissingTableInfo,
|
|
74
|
+
type SchemaDriftReport
|
|
75
|
+
} from './compiler/schema-drift';
|
|
76
|
+
export {
|
|
77
|
+
findSliceFiles,
|
|
78
|
+
resolveSlicesDir,
|
|
79
|
+
SLICE_EXTENSION,
|
|
80
|
+
type SlicesDirError,
|
|
81
|
+
type SlicesDirErrorCode,
|
|
82
|
+
type SlicesResolution
|
|
83
|
+
} from './compiler/slice-discovery';
|
|
84
|
+
export {
|
|
85
|
+
artifactDirectory,
|
|
86
|
+
type SplitArtifact,
|
|
87
|
+
type SplitDiagnostic,
|
|
88
|
+
type SplitResult,
|
|
89
|
+
type SplitVerification,
|
|
90
|
+
splitSlice,
|
|
91
|
+
verifySplit,
|
|
92
|
+
writeSplitArtifacts
|
|
93
|
+
} from './compiler/slice-splitter';
|
|
94
|
+
export { buildStandalone, type StandaloneBuildResult } from './compiler/standalone-builder';
|
|
95
|
+
export type { SliceOracle, SliceOracleCase } from './core/oracle';
|
|
96
|
+
export {
|
|
97
|
+
clearSession,
|
|
98
|
+
currentRoles,
|
|
99
|
+
DEFAULT_SESSION_MAX_AGE_SECONDS,
|
|
100
|
+
ROLES_COOKIE,
|
|
101
|
+
SESSION_COOKIE,
|
|
102
|
+
sessionCookie,
|
|
103
|
+
storeSession
|
|
104
|
+
} from './core/session-cookie';
|
|
105
|
+
// 5. Model Context Protocol (MCP) Server
|
|
106
|
+
export { SynapseMcpServer } from './mcp/server';
|
|
107
|
+
export * from './runtime/discovery-rules';
|
|
108
|
+
// 4. Runtime, Queue & Zero-Wiring HTTP Server
|
|
109
|
+
export {
|
|
110
|
+
EventHub,
|
|
111
|
+
type EventListener,
|
|
112
|
+
getEventHub,
|
|
113
|
+
resetEventHub,
|
|
114
|
+
type UnsubscribeFn
|
|
115
|
+
} from './runtime/event-hub';
|
|
116
|
+
export {
|
|
117
|
+
PostgresEventHub,
|
|
118
|
+
type PostgresEventHubOptions
|
|
119
|
+
} from './runtime/postgres-event-hub';
|
|
120
|
+
export { type JobRecord, QueueEngine } from './runtime/queue-engine';
|
|
121
|
+
export {
|
|
122
|
+
type RateLimitOptions,
|
|
123
|
+
type RateLimitResult,
|
|
124
|
+
TokenBucketRateLimiter
|
|
125
|
+
} from './runtime/rate-limiter';
|
|
126
|
+
export {
|
|
127
|
+
type DiscoveredSlice,
|
|
128
|
+
type SliceLoadError,
|
|
129
|
+
type SliceLoaderContext,
|
|
130
|
+
type SliceMetadata,
|
|
131
|
+
SynapseServer
|
|
132
|
+
} from './runtime/server';
|
|
133
|
+
export { fc };
|
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SynapseJS - Native Model Context Protocol (MCP) Server
|
|
3
|
+
*
|
|
4
|
+
* Exposes SynapseJS machine capabilities (repo-map, db-schema, diagnostics,
|
|
5
|
+
* split verification, PBT, migrations, scaffolding, authoring contract)
|
|
6
|
+
* directly to AI Agents (Cursor, Claude Code, Windsurf, Antigravity) via JSON-RPC 2.0 over stdio.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import * as readline from 'readline';
|
|
12
|
+
import { runMachineVerifications } from '../compiler/agent-diagnostic-json';
|
|
13
|
+
import { machineContract } from '../compiler/contract';
|
|
14
|
+
import { analyzeImpact } from '../compiler/impact-analyzer';
|
|
15
|
+
import { rollbackSliceMigrations, runSliceMigrations } from '../compiler/migration-runner';
|
|
16
|
+
import { runSliceOracles } from '../compiler/oracle-runner';
|
|
17
|
+
import { type SliceTemplate, scaffoldCrud, scaffoldSlice } from '../compiler/scaffolder';
|
|
18
|
+
import { checkSchemaDrift } from '../compiler/schema-drift';
|
|
19
|
+
import { findSliceFiles, resolveSlicesDir } from '../compiler/slice-discovery';
|
|
20
|
+
import { artifactDirectory, splitSlice, verifySplit, writeSplitArtifacts } from '../compiler/slice-splitter';
|
|
21
|
+
|
|
22
|
+
interface JsonRpcRequest {
|
|
23
|
+
jsonrpc: string;
|
|
24
|
+
id?: string | number | null;
|
|
25
|
+
method: string;
|
|
26
|
+
// biome-ignore lint/suspicious/noExplicitAny: JSON-RPC parameter boundary
|
|
27
|
+
params?: any;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class SynapseMcpServer {
|
|
31
|
+
private root: string;
|
|
32
|
+
|
|
33
|
+
constructor(root: string = process.cwd()) {
|
|
34
|
+
this.root = root;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
start() {
|
|
38
|
+
const rl = readline.createInterface({
|
|
39
|
+
input: process.stdin,
|
|
40
|
+
output: process.stdout,
|
|
41
|
+
terminal: false
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
rl.on('line', async (line) => {
|
|
45
|
+
const trimmed = line.trim();
|
|
46
|
+
if (!trimmed) return;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const request: JsonRpcRequest = JSON.parse(trimmed);
|
|
50
|
+
const response = await this.handleRequest(request);
|
|
51
|
+
if (response) {
|
|
52
|
+
process.stdout.write(`${JSON.stringify(response)}\n`);
|
|
53
|
+
}
|
|
54
|
+
// biome-ignore lint/suspicious/noExplicitAny: error message catch
|
|
55
|
+
} catch (err: any) {
|
|
56
|
+
process.stdout.write(
|
|
57
|
+
`${JSON.stringify({
|
|
58
|
+
jsonrpc: '2.0',
|
|
59
|
+
id: null,
|
|
60
|
+
error: { code: -32700, message: `Parse error: ${err.message}` }
|
|
61
|
+
})}\n`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// biome-ignore lint/suspicious/noExplicitAny: JSON-RPC response boundary
|
|
68
|
+
public async handleRequest(req: JsonRpcRequest): Promise<any> {
|
|
69
|
+
const { id, method, params } = req;
|
|
70
|
+
|
|
71
|
+
switch (method) {
|
|
72
|
+
case 'initialize': {
|
|
73
|
+
return {
|
|
74
|
+
jsonrpc: '2.0',
|
|
75
|
+
id,
|
|
76
|
+
result: {
|
|
77
|
+
protocolVersion: '2024-11-05',
|
|
78
|
+
capabilities: {
|
|
79
|
+
tools: {}
|
|
80
|
+
},
|
|
81
|
+
serverInfo: {
|
|
82
|
+
name: 'synapse-mcp',
|
|
83
|
+
version: '1.1.0'
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
case 'notifications/initialized': {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
case 'tools/list': {
|
|
94
|
+
return {
|
|
95
|
+
jsonrpc: '2.0',
|
|
96
|
+
id,
|
|
97
|
+
result: {
|
|
98
|
+
tools: [
|
|
99
|
+
{
|
|
100
|
+
name: 'synapse_get_repo_map',
|
|
101
|
+
description:
|
|
102
|
+
'Get compressed codebase skeleton map (.codebase/repo-map.d.ts) for AI context (<3000 tokens)',
|
|
103
|
+
inputSchema: { type: 'object', properties: {} }
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'synapse_get_db_schema',
|
|
107
|
+
description:
|
|
108
|
+
'Get the centralized database schema catalog (.codebase/db-schema.d.ts) for AI context and type-safe relational queries',
|
|
109
|
+
inputSchema: { type: 'object', properties: {} }
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'synapse_check',
|
|
113
|
+
description:
|
|
114
|
+
'Run machine-centric compiler diagnostics, returning exact JSON coordinates (file, line, col, message)',
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
targetFile: { type: 'string', description: 'Optional specific file to check' }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'synapse_split',
|
|
124
|
+
description:
|
|
125
|
+
'Run slice splitter and leak verification gates to enforce server/client isolation and zero leakage',
|
|
126
|
+
inputSchema: {
|
|
127
|
+
type: 'object',
|
|
128
|
+
properties: {
|
|
129
|
+
targetSlice: { type: 'string', description: 'Optional specific slice name to split' }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'synapse_run_pbt',
|
|
135
|
+
description: 'Execute Fast-Check Property-Based Testing (PBT) invariant test suite across all slices',
|
|
136
|
+
inputSchema: { type: 'object', properties: {} }
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'synapse_scaffold_slice',
|
|
140
|
+
description:
|
|
141
|
+
'Scaffold a new fullstack atomic vertical slice with TypeBox, Result, Action, React UI, and PBT oracles',
|
|
142
|
+
inputSchema: {
|
|
143
|
+
type: 'object',
|
|
144
|
+
required: ['domain', 'name'],
|
|
145
|
+
properties: {
|
|
146
|
+
domain: { type: 'string', description: 'Domain name (e.g. billing, customers, orders)' },
|
|
147
|
+
name: { type: 'string', description: 'Slice name in kebab-case (e.g. cancel-subscription)' },
|
|
148
|
+
template: {
|
|
149
|
+
type: 'string',
|
|
150
|
+
enum: ['create', 'list', 'update', 'delete', 'login', 'oauth-github', 'crud'],
|
|
151
|
+
description: 'Optional slice template shape (default: create)'
|
|
152
|
+
},
|
|
153
|
+
fields: {
|
|
154
|
+
type: 'string',
|
|
155
|
+
description:
|
|
156
|
+
'Optional fields grammar, e.g. "name:string,email:string,status:enum(ACTIVE|INACTIVE),price:number"'
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'synapse_contract',
|
|
163
|
+
description:
|
|
164
|
+
'Return the machine contract: slice exports and suffixes, Result and HTTP semantics, session rules, addressing, SSR behavior and the gates to run',
|
|
165
|
+
inputSchema: { type: 'object', properties: {} }
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'synapse_migrate',
|
|
169
|
+
description:
|
|
170
|
+
'Auto-discover and apply sliceSchema DDL declarations across slices into the active database',
|
|
171
|
+
inputSchema: { type: 'object', properties: {} }
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'synapse_check_db_drift',
|
|
175
|
+
description:
|
|
176
|
+
'Inspects live database catalog against sliceSchema DDL contracts, identifying missing tables, missing columns, and orphan tables',
|
|
177
|
+
inputSchema: { type: 'object', properties: {} }
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'synapse_diff_impact',
|
|
181
|
+
description:
|
|
182
|
+
'Analyzes cross-slice dependencies (Foreign Keys, shared module imports, and table references) to report all slices impacted by a file or table change',
|
|
183
|
+
inputSchema: {
|
|
184
|
+
type: 'object',
|
|
185
|
+
required: ['target'],
|
|
186
|
+
properties: {
|
|
187
|
+
target: {
|
|
188
|
+
type: 'string',
|
|
189
|
+
description: 'Slice file path, slice name, shared module path, or table name'
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'synapse_rollback',
|
|
196
|
+
description: 'Roll back applied sliceSchema migrations by executing their -- down: statements',
|
|
197
|
+
inputSchema: {
|
|
198
|
+
type: 'object',
|
|
199
|
+
properties: {
|
|
200
|
+
targetSlice: { type: 'string', description: 'Optional target slice to rollback' },
|
|
201
|
+
steps: { type: 'number', description: 'Number of recent migrations to rollback (default: 1)' }
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
case 'tools/call': {
|
|
211
|
+
const toolName = params?.name;
|
|
212
|
+
const args = params?.arguments || {};
|
|
213
|
+
|
|
214
|
+
let contentText = '';
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
if (toolName === 'synapse_get_repo_map') {
|
|
218
|
+
const repoMapPath = path.join(this.root, '.codebase/repo-map.d.ts');
|
|
219
|
+
|
|
220
|
+
if (!fs.existsSync(repoMapPath)) {
|
|
221
|
+
return {
|
|
222
|
+
jsonrpc: '2.0',
|
|
223
|
+
id,
|
|
224
|
+
result: {
|
|
225
|
+
isError: true,
|
|
226
|
+
content: [
|
|
227
|
+
{
|
|
228
|
+
type: 'text',
|
|
229
|
+
text: JSON.stringify(
|
|
230
|
+
{
|
|
231
|
+
status: 'FAIL',
|
|
232
|
+
code: 'REPO_MAP_MISSING',
|
|
233
|
+
message: `repo-map.d.ts não encontrado. Gere com 'synapse skeleton'.`,
|
|
234
|
+
repoMapPath
|
|
235
|
+
},
|
|
236
|
+
null,
|
|
237
|
+
2
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const content = fs.readFileSync(repoMapPath, 'utf-8');
|
|
246
|
+
contentText = JSON.stringify(
|
|
247
|
+
{ status: 'PASS', repoMapPath: path.relative(this.root, repoMapPath), repoMap: content },
|
|
248
|
+
null,
|
|
249
|
+
2
|
|
250
|
+
);
|
|
251
|
+
} else if (toolName === 'synapse_get_db_schema') {
|
|
252
|
+
const schemaPath = path.join(this.root, '.codebase/db-schema.d.ts');
|
|
253
|
+
|
|
254
|
+
if (!fs.existsSync(schemaPath)) {
|
|
255
|
+
return {
|
|
256
|
+
jsonrpc: '2.0',
|
|
257
|
+
id,
|
|
258
|
+
result: {
|
|
259
|
+
isError: true,
|
|
260
|
+
content: [
|
|
261
|
+
{
|
|
262
|
+
type: 'text',
|
|
263
|
+
text: JSON.stringify(
|
|
264
|
+
{
|
|
265
|
+
status: 'FAIL',
|
|
266
|
+
code: 'DB_SCHEMA_MISSING',
|
|
267
|
+
message: `db-schema.d.ts não encontrado. Gere com 'synapse skeleton'.`,
|
|
268
|
+
schemaPath
|
|
269
|
+
},
|
|
270
|
+
null,
|
|
271
|
+
2
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
]
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const content = fs.readFileSync(schemaPath, 'utf-8');
|
|
280
|
+
contentText = JSON.stringify(
|
|
281
|
+
{ status: 'PASS', schemaPath: path.relative(this.root, schemaPath), schema: content },
|
|
282
|
+
null,
|
|
283
|
+
2
|
|
284
|
+
);
|
|
285
|
+
} else if (toolName === 'synapse_check') {
|
|
286
|
+
const report = runMachineVerifications(this.root, args.targetFile);
|
|
287
|
+
contentText = JSON.stringify(report, null, 2);
|
|
288
|
+
} else if (toolName === 'synapse_split') {
|
|
289
|
+
const resolution = resolveSlicesDir(this.root);
|
|
290
|
+
if (!resolution.ok) {
|
|
291
|
+
return {
|
|
292
|
+
jsonrpc: '2.0',
|
|
293
|
+
id,
|
|
294
|
+
result: {
|
|
295
|
+
isError: true,
|
|
296
|
+
content: [
|
|
297
|
+
{
|
|
298
|
+
type: 'text',
|
|
299
|
+
text: JSON.stringify(
|
|
300
|
+
{
|
|
301
|
+
status: 'FAIL',
|
|
302
|
+
operation: 'SLICE_SPLIT',
|
|
303
|
+
code: resolution.error.code,
|
|
304
|
+
message: resolution.error.message,
|
|
305
|
+
candidates: resolution.error.candidates
|
|
306
|
+
},
|
|
307
|
+
null,
|
|
308
|
+
2
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
]
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
let sliceFiles = findSliceFiles(resolution.value.slicesDir);
|
|
317
|
+
if (args.targetSlice) {
|
|
318
|
+
sliceFiles = sliceFiles.filter((f) => path.basename(f, '.slice.tsx') === args.targetSlice);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const slices = [];
|
|
322
|
+
let failed = false;
|
|
323
|
+
|
|
324
|
+
for (const file of sliceFiles) {
|
|
325
|
+
const split = splitSlice(file, this.root);
|
|
326
|
+
|
|
327
|
+
if (!split.ok) {
|
|
328
|
+
failed = true;
|
|
329
|
+
slices.push({
|
|
330
|
+
slice: path.basename(file, '.slice.tsx'),
|
|
331
|
+
status: 'FAIL',
|
|
332
|
+
code: split.error.code,
|
|
333
|
+
message: split.error.message
|
|
334
|
+
});
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const outDir = artifactDirectory(this.root, split.value.sliceName);
|
|
339
|
+
const written = writeSplitArtifacts(split.value, outDir);
|
|
340
|
+
const verification = verifySplit(split.value, outDir);
|
|
341
|
+
|
|
342
|
+
if (verification.status === 'FAIL') {
|
|
343
|
+
failed = true;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
slices.push({
|
|
347
|
+
slice: split.value.sliceName,
|
|
348
|
+
status: verification.status,
|
|
349
|
+
outDir: path.relative(this.root, outDir),
|
|
350
|
+
artifacts: written.map((target) => path.relative(this.root, target)),
|
|
351
|
+
diagnostics: verification.diagnostics,
|
|
352
|
+
leaks: verification.leaks
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const splitStatus = failed || slices.length === 0 ? 'FAIL' : 'PASS';
|
|
357
|
+
contentText = JSON.stringify(
|
|
358
|
+
{
|
|
359
|
+
status: splitStatus,
|
|
360
|
+
operation: 'SLICE_SPLIT',
|
|
361
|
+
slicesDir: path.relative(this.root, resolution.value.slicesDir),
|
|
362
|
+
totalProcessed: slices.length,
|
|
363
|
+
slices
|
|
364
|
+
},
|
|
365
|
+
null,
|
|
366
|
+
2
|
|
367
|
+
);
|
|
368
|
+
} else if (toolName === 'synapse_run_pbt') {
|
|
369
|
+
const report = await runSliceOracles(this.root);
|
|
370
|
+
|
|
371
|
+
if (
|
|
372
|
+
report.status === 'FAIL' &&
|
|
373
|
+
(report.code === 'NO_SLICES_DIR' || report.code === 'AMBIGUOUS_SLICES_DIR')
|
|
374
|
+
) {
|
|
375
|
+
return {
|
|
376
|
+
jsonrpc: '2.0',
|
|
377
|
+
id,
|
|
378
|
+
result: {
|
|
379
|
+
isError: true,
|
|
380
|
+
content: [{ type: 'text', text: JSON.stringify(report, null, 2) }]
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
contentText = JSON.stringify(report, null, 2);
|
|
386
|
+
} else if (toolName === 'synapse_scaffold_slice') {
|
|
387
|
+
if (args.template === 'crud') {
|
|
388
|
+
const crud = scaffoldCrud(args.domain, args.name, this.root, args.fields);
|
|
389
|
+
|
|
390
|
+
if (!crud.ok) {
|
|
391
|
+
return {
|
|
392
|
+
jsonrpc: '2.0',
|
|
393
|
+
id,
|
|
394
|
+
result: {
|
|
395
|
+
isError: true,
|
|
396
|
+
content: [
|
|
397
|
+
{
|
|
398
|
+
type: 'text',
|
|
399
|
+
text: JSON.stringify(
|
|
400
|
+
{
|
|
401
|
+
status: 'FAIL',
|
|
402
|
+
code: crud.error.code,
|
|
403
|
+
message: crud.error.message,
|
|
404
|
+
candidates: crud.error.candidates
|
|
405
|
+
},
|
|
406
|
+
null,
|
|
407
|
+
2
|
|
408
|
+
)
|
|
409
|
+
}
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
contentText = JSON.stringify(
|
|
416
|
+
{
|
|
417
|
+
status: 'PASS',
|
|
418
|
+
operation: 'SCAFFOLD_CRUD',
|
|
419
|
+
domain: args.domain,
|
|
420
|
+
resource: args.name,
|
|
421
|
+
createdPaths: crud.value.map((file) => path.relative(this.root, file))
|
|
422
|
+
},
|
|
423
|
+
null,
|
|
424
|
+
2
|
|
425
|
+
);
|
|
426
|
+
} else {
|
|
427
|
+
const created = scaffoldSlice(
|
|
428
|
+
args.domain,
|
|
429
|
+
args.name,
|
|
430
|
+
this.root,
|
|
431
|
+
(args.template || 'create') as SliceTemplate,
|
|
432
|
+
args.fields
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
if (!created.ok) {
|
|
436
|
+
return {
|
|
437
|
+
jsonrpc: '2.0',
|
|
438
|
+
id,
|
|
439
|
+
result: {
|
|
440
|
+
isError: true,
|
|
441
|
+
content: [
|
|
442
|
+
{
|
|
443
|
+
type: 'text',
|
|
444
|
+
text: JSON.stringify(
|
|
445
|
+
{
|
|
446
|
+
status: 'FAIL',
|
|
447
|
+
code: created.error.code,
|
|
448
|
+
message: created.error.message,
|
|
449
|
+
candidates: created.error.candidates
|
|
450
|
+
},
|
|
451
|
+
null,
|
|
452
|
+
2
|
|
453
|
+
)
|
|
454
|
+
}
|
|
455
|
+
]
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
contentText = JSON.stringify(
|
|
461
|
+
{ status: 'PASS', createdPath: path.relative(this.root, created.value) },
|
|
462
|
+
null,
|
|
463
|
+
2
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
} else if (toolName === 'synapse_contract') {
|
|
467
|
+
contentText = JSON.stringify(machineContract(), null, 2);
|
|
468
|
+
} else if (toolName === 'synapse_migrate') {
|
|
469
|
+
const report = await runSliceMigrations(this.root);
|
|
470
|
+
contentText = JSON.stringify(report, null, 2);
|
|
471
|
+
} else if (toolName === 'synapse_check_db_drift') {
|
|
472
|
+
const report = await checkSchemaDrift(this.root);
|
|
473
|
+
contentText = JSON.stringify(report, null, 2);
|
|
474
|
+
} else if (toolName === 'synapse_diff_impact') {
|
|
475
|
+
if (!args.target) {
|
|
476
|
+
return {
|
|
477
|
+
jsonrpc: '2.0',
|
|
478
|
+
id,
|
|
479
|
+
result: {
|
|
480
|
+
isError: true,
|
|
481
|
+
content: [
|
|
482
|
+
{
|
|
483
|
+
type: 'text',
|
|
484
|
+
text: JSON.stringify(
|
|
485
|
+
{
|
|
486
|
+
status: 'FAIL',
|
|
487
|
+
code: 'MISSING_TARGET',
|
|
488
|
+
message: 'Argumento target obrigatório'
|
|
489
|
+
},
|
|
490
|
+
null,
|
|
491
|
+
2
|
|
492
|
+
)
|
|
493
|
+
}
|
|
494
|
+
]
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
const report = analyzeImpact(args.target, this.root);
|
|
499
|
+
contentText = JSON.stringify(report, null, 2);
|
|
500
|
+
} else if (toolName === 'synapse_rollback') {
|
|
501
|
+
const report = await rollbackSliceMigrations(this.root, undefined, {
|
|
502
|
+
targetSlice: args.targetSlice,
|
|
503
|
+
steps: args.steps
|
|
504
|
+
});
|
|
505
|
+
contentText = JSON.stringify(report, null, 2);
|
|
506
|
+
} else {
|
|
507
|
+
return {
|
|
508
|
+
jsonrpc: '2.0',
|
|
509
|
+
id,
|
|
510
|
+
error: { code: -32601, message: `Unknown tool: ${toolName}` }
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return {
|
|
515
|
+
jsonrpc: '2.0',
|
|
516
|
+
id,
|
|
517
|
+
result: {
|
|
518
|
+
content: [
|
|
519
|
+
{
|
|
520
|
+
type: 'text',
|
|
521
|
+
text: contentText
|
|
522
|
+
}
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
} catch (err: unknown) {
|
|
527
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
528
|
+
return {
|
|
529
|
+
jsonrpc: '2.0',
|
|
530
|
+
id,
|
|
531
|
+
result: {
|
|
532
|
+
isError: true,
|
|
533
|
+
content: [
|
|
534
|
+
{
|
|
535
|
+
type: 'text',
|
|
536
|
+
text: JSON.stringify({ status: 'ERROR', error: message }, null, 2)
|
|
537
|
+
}
|
|
538
|
+
]
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
default: {
|
|
545
|
+
return {
|
|
546
|
+
jsonrpc: '2.0',
|
|
547
|
+
id,
|
|
548
|
+
error: { code: -32601, message: `Method not found: ${method}` }
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|