@oleksandr.rudnychenko/sync_loop 0.2.2 → 0.2.4
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 +246 -124
- package/bin/cli.js +131 -77
- package/package.json +3 -4
- package/src/init.js +569 -365
- package/src/server.js +289 -208
- package/{template → src/template}/README.md +3 -3
- /package/{template → src/template}/.agent-loop/README.md +0 -0
- /package/{template → src/template}/.agent-loop/feedback.md +0 -0
- /package/{template → src/template}/.agent-loop/glossary.md +0 -0
- /package/{template → src/template}/.agent-loop/patterns/api-standards.md +0 -0
- /package/{template → src/template}/.agent-loop/patterns/code-patterns.md +0 -0
- /package/{template → src/template}/.agent-loop/patterns/refactoring-workflow.md +0 -0
- /package/{template → src/template}/.agent-loop/patterns/testing-guide.md +0 -0
- /package/{template → src/template}/.agent-loop/patterns.md +0 -0
- /package/{template → src/template}/.agent-loop/reasoning-kernel.md +0 -0
- /package/{template → src/template}/.agent-loop/validate-env.md +0 -0
- /package/{template → src/template}/.agent-loop/validate-n.md +0 -0
- /package/{template → src/template}/AGENTS.md +0 -0
- /package/{template → src/template}/bootstrap-prompt.md +0 -0
- /package/{template → src/template}/protocol-summary.md +0 -0
package/src/server.js
CHANGED
|
@@ -1,208 +1,289 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { readFileSync } from "node:fs";
|
|
5
|
-
import { join, dirname } from "node:path";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { init } from "./init.js";
|
|
8
|
-
|
|
9
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const TEMPLATE_DIR = join(__dirname, "
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
path: ".agent-loop/
|
|
57
|
-
name: "
|
|
58
|
-
description: "
|
|
59
|
-
},
|
|
60
|
-
"
|
|
61
|
-
path: ".
|
|
62
|
-
name: "
|
|
63
|
-
description: "
|
|
64
|
-
},
|
|
65
|
-
"
|
|
66
|
-
path: "
|
|
67
|
-
name: "
|
|
68
|
-
description: "
|
|
69
|
-
},
|
|
70
|
-
"
|
|
71
|
-
path: ".agent-loop/
|
|
72
|
-
name: "
|
|
73
|
-
description: "
|
|
74
|
-
},
|
|
75
|
-
"
|
|
76
|
-
path: ".agent-loop/
|
|
77
|
-
name: "
|
|
78
|
-
description: "
|
|
79
|
-
},
|
|
80
|
-
"
|
|
81
|
-
path: ".agent-loop/
|
|
82
|
-
name: "
|
|
83
|
-
description: "
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
path
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
),
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
server.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { join, dirname, resolve, posix } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { init, detectStacks } from "./init.js";
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const TEMPLATE_DIR = join(__dirname, "template");
|
|
11
|
+
const PACKAGE_JSON = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
12
|
+
|
|
13
|
+
function readTemplate(relativePath) {
|
|
14
|
+
return readFileSync(join(TEMPLATE_DIR, relativePath), "utf-8");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function pathDir(value) {
|
|
18
|
+
const dir = posix.dirname(value);
|
|
19
|
+
return dir === "." ? "" : dir;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function splitHash(link) {
|
|
23
|
+
const idx = link.indexOf("#");
|
|
24
|
+
if (idx === -1) return { pathPart: link, hash: "" };
|
|
25
|
+
return {
|
|
26
|
+
pathPart: link.slice(0, idx),
|
|
27
|
+
hash: link.slice(idx),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isExternalLink(link) {
|
|
32
|
+
return /^([a-z]+:|#)/i.test(link);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function rewriteMarkdownLinks(content, transform) {
|
|
36
|
+
let inFence = false;
|
|
37
|
+
return content
|
|
38
|
+
.split("\n")
|
|
39
|
+
.map((line) => {
|
|
40
|
+
if (line.trimStart().startsWith("```")) {
|
|
41
|
+
inFence = !inFence;
|
|
42
|
+
return line;
|
|
43
|
+
}
|
|
44
|
+
if (inFence) return line;
|
|
45
|
+
return line.replace(/\]\(([^)]+)\)/g, (_match, linkPath) => `](${transform(linkPath)})`);
|
|
46
|
+
})
|
|
47
|
+
.join("\n");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Doc registry
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
const DOCS = {
|
|
55
|
+
"overview": {
|
|
56
|
+
path: ".agent-loop/README.md",
|
|
57
|
+
name: "Protocol Overview",
|
|
58
|
+
description: "SyncLoop file index and framework overview",
|
|
59
|
+
},
|
|
60
|
+
"agents-md": {
|
|
61
|
+
path: "AGENTS.md",
|
|
62
|
+
name: "AGENTS.md Template",
|
|
63
|
+
description: "Root entrypoint template for AI agents - project identity, protocol, guardrails",
|
|
64
|
+
},
|
|
65
|
+
"protocol-summary": {
|
|
66
|
+
path: "protocol-summary.md",
|
|
67
|
+
name: "Protocol Summary",
|
|
68
|
+
description: "Condensed 7-stage reasoning loop overview (~50 lines)",
|
|
69
|
+
},
|
|
70
|
+
"reasoning-kernel": {
|
|
71
|
+
path: ".agent-loop/reasoning-kernel.md",
|
|
72
|
+
name: "Reasoning Kernel",
|
|
73
|
+
description: "Core 7-stage loop, transition map, context clearage, micro/macro classification",
|
|
74
|
+
},
|
|
75
|
+
"feedback": {
|
|
76
|
+
path: ".agent-loop/feedback.md",
|
|
77
|
+
name: "Feedback Loop",
|
|
78
|
+
description: "Failure diagnosis, patch protocol, micro-loop, branch pruning, learning persistence",
|
|
79
|
+
},
|
|
80
|
+
"validate-env": {
|
|
81
|
+
path: ".agent-loop/validate-env.md",
|
|
82
|
+
name: "Validate Environment (Stage 1)",
|
|
83
|
+
description: "NFR gates: type safety, tests, layer integrity, complexity, debug hygiene",
|
|
84
|
+
},
|
|
85
|
+
"validate-n": {
|
|
86
|
+
path: ".agent-loop/validate-n.md",
|
|
87
|
+
name: "Validate Neighbors (Stage 2)",
|
|
88
|
+
description: "Shape compatibility, boundary integrity, bridge contracts",
|
|
89
|
+
},
|
|
90
|
+
"patterns": {
|
|
91
|
+
path: ".agent-loop/patterns.md",
|
|
92
|
+
name: "Pattern Registry",
|
|
93
|
+
description: "Pattern routing index, architecture baseline, learned patterns, pruning records",
|
|
94
|
+
},
|
|
95
|
+
"glossary": {
|
|
96
|
+
path: ".agent-loop/glossary.md",
|
|
97
|
+
name: "Domain Glossary",
|
|
98
|
+
description: "Canonical terminology, naming rules, deprecated aliases",
|
|
99
|
+
},
|
|
100
|
+
"code-patterns": {
|
|
101
|
+
path: ".agent-loop/patterns/code-patterns.md",
|
|
102
|
+
name: "Code Patterns (P1-P11)",
|
|
103
|
+
description: "Port/adapter, domain modules, tasks, routes, DI, config, types, error handling",
|
|
104
|
+
},
|
|
105
|
+
"testing-guide": {
|
|
106
|
+
path: ".agent-loop/patterns/testing-guide.md",
|
|
107
|
+
name: "Testing Guide (R2)",
|
|
108
|
+
description: "Test pyramid, fixtures, factories, mocks, parametrized tests, naming",
|
|
109
|
+
},
|
|
110
|
+
"refactoring-workflow": {
|
|
111
|
+
path: ".agent-loop/patterns/refactoring-workflow.md",
|
|
112
|
+
name: "Refactoring Workflow (R1)",
|
|
113
|
+
description: "4-phase checklist for safe file moves and module restructuring",
|
|
114
|
+
},
|
|
115
|
+
"api-standards": {
|
|
116
|
+
path: ".agent-loop/patterns/api-standards.md",
|
|
117
|
+
name: "API Standards (R3)",
|
|
118
|
+
description: "Boundary contracts, typed models, error envelopes, versioning strategy",
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const DOC_ID_BY_PATH = {};
|
|
123
|
+
for (const [id, doc] of Object.entries(DOCS)) {
|
|
124
|
+
DOC_ID_BY_PATH[doc.path] = id;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function rewriteResourceLinks(content, sourcePath) {
|
|
128
|
+
return rewriteMarkdownLinks(content, (linkPath) => {
|
|
129
|
+
if (isExternalLink(linkPath)) return linkPath;
|
|
130
|
+
|
|
131
|
+
const { pathPart, hash } = splitHash(linkPath);
|
|
132
|
+
if (!pathPart) return linkPath;
|
|
133
|
+
|
|
134
|
+
let canonical = posix.normalize(posix.join(pathDir(sourcePath) || ".", pathPart)).replace(/^\.\//, "");
|
|
135
|
+
if (canonical === ".agent-loop/patterns") canonical = ".agent-loop/patterns.md";
|
|
136
|
+
if (canonical === "../AGENTS.md" || canonical === "AGENTS.md") canonical = "AGENTS.md";
|
|
137
|
+
if (canonical === "../README.md") return linkPath;
|
|
138
|
+
|
|
139
|
+
const docId = DOC_ID_BY_PATH[canonical];
|
|
140
|
+
if (!docId) return linkPath;
|
|
141
|
+
|
|
142
|
+
return `syncloop://docs/${docId}${hash}`;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function formatStacks(stacks) {
|
|
147
|
+
return stacks
|
|
148
|
+
.map((stack) => [
|
|
149
|
+
`\n### ${stack.name}${stack.path ? ` (${stack.path})` : ""}`,
|
|
150
|
+
`- Languages: ${stack.languages.join(", ")}`,
|
|
151
|
+
`- Frameworks: ${stack.frameworks.join(", ")}`,
|
|
152
|
+
stack.testRunner ? `- Test runner: ${stack.testRunner}` : null,
|
|
153
|
+
stack.typeChecker ? `- Type checker: ${stack.typeChecker}` : null,
|
|
154
|
+
stack.linter ? `- Linter: ${stack.linter}` : null,
|
|
155
|
+
stack.packageManager ? `- Package manager: ${stack.packageManager}` : null,
|
|
156
|
+
].filter(Boolean).join("\n"))
|
|
157
|
+
.join("\n");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Server
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
const server = new McpServer({
|
|
165
|
+
name: "sync_loop",
|
|
166
|
+
version: PACKAGE_JSON.version,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// Resources
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
for (const [id, doc] of Object.entries(DOCS)) {
|
|
174
|
+
server.registerResource(
|
|
175
|
+
doc.name,
|
|
176
|
+
`syncloop://docs/${id}`,
|
|
177
|
+
async (uri) => {
|
|
178
|
+
const raw = readTemplate(doc.path);
|
|
179
|
+
const rewritten = rewriteResourceLinks(raw, doc.path);
|
|
180
|
+
return {
|
|
181
|
+
contents: [{
|
|
182
|
+
uri: uri.href,
|
|
183
|
+
mimeType: "text/markdown",
|
|
184
|
+
text: rewritten,
|
|
185
|
+
}],
|
|
186
|
+
};
|
|
187
|
+
},
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// Tools
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
|
|
195
|
+
const StackSchema = z.object({
|
|
196
|
+
name: z.string().describe("Stack/layer name (for example: backend, frontend, worker)"),
|
|
197
|
+
languages: z.array(z.string()).describe("Programming languages used by this stack"),
|
|
198
|
+
frameworks: z.array(z.string()).describe("Frameworks/libraries for this stack"),
|
|
199
|
+
testRunner: z.string().optional().describe("Test runner command"),
|
|
200
|
+
typeChecker: z.string().optional().describe("Type checker command"),
|
|
201
|
+
linter: z.string().optional().describe("Lint/format command"),
|
|
202
|
+
packageManager: z.string().optional().describe("Package manager name"),
|
|
203
|
+
path: z.string().optional().describe("Root directory of this stack relative to project root"),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
server.registerTool(
|
|
207
|
+
"init",
|
|
208
|
+
"Scaffold SyncLoop protocol files into a project. If target is not explicitly provided, default to all. If stacks are not provided, auto-detect them by scanning the repository.",
|
|
209
|
+
{
|
|
210
|
+
projectPath: z.string().optional().describe("Project root path. Defaults to current working directory."),
|
|
211
|
+
target: z.enum(["copilot", "cursor", "claude", "all"]).optional().default("all"),
|
|
212
|
+
stacks: z.array(StackSchema).optional().describe("Optional stack definitions. Auto-detected when omitted."),
|
|
213
|
+
dryRun: z.boolean().optional().default(false).describe("Preview file writes without modifying files."),
|
|
214
|
+
overwrite: z.boolean().optional().default(true).describe("Overwrite existing generated files."),
|
|
215
|
+
},
|
|
216
|
+
async ({ projectPath, target = "all", stacks, dryRun = false, overwrite = true }) => {
|
|
217
|
+
try {
|
|
218
|
+
const resolvedProjectPath = resolve(projectPath ?? process.cwd());
|
|
219
|
+
const effectiveStacks = stacks?.length ? stacks : detectStacks(resolvedProjectPath);
|
|
220
|
+
const initResult = init(
|
|
221
|
+
resolvedProjectPath,
|
|
222
|
+
target,
|
|
223
|
+
effectiveStacks,
|
|
224
|
+
{ dryRun, overwrite },
|
|
225
|
+
);
|
|
226
|
+
const bootstrapPrompt = readTemplate("bootstrap-prompt.md");
|
|
227
|
+
const stackSummary = formatStacks(initResult.stacks);
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
content: [
|
|
231
|
+
{ type: "text", text: `SyncLoop initialized for ${target}:\n\n${initResult.results.join("\n")}` },
|
|
232
|
+
{ type: "text", text: `\n---\n\n## Options\n- dryRun: ${dryRun}\n- overwrite: ${overwrite}` },
|
|
233
|
+
{ type: "text", text: `\n---\n\n## Detected stacks\n${stackSummary}` },
|
|
234
|
+
{
|
|
235
|
+
type: "text",
|
|
236
|
+
text: `\n---\n\n**IMPORTANT: Now scan the codebase and wire the generated SyncLoop files to this project.**\n\n${bootstrapPrompt}`,
|
|
237
|
+
},
|
|
238
|
+
{ type: "text", text: `\n---\n\n## Machine-readable result\n\`\`\`json\n${JSON.stringify(initResult, null, 2)}\n\`\`\`` },
|
|
239
|
+
],
|
|
240
|
+
};
|
|
241
|
+
} catch (err) {
|
|
242
|
+
return {
|
|
243
|
+
content: [{ type: "text", text: `Error initializing SyncLoop: ${err.message}` }],
|
|
244
|
+
isError: true,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
// Prompts
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
|
|
254
|
+
server.registerPrompt(
|
|
255
|
+
"bootstrap",
|
|
256
|
+
"Bootstrap prompt - wire SyncLoop protocol to an existing project by scanning its codebase",
|
|
257
|
+
async () => ({
|
|
258
|
+
description: "Scan the project codebase and wire SyncLoop protocol references to real project structure",
|
|
259
|
+
messages: [{
|
|
260
|
+
role: "user",
|
|
261
|
+
content: {
|
|
262
|
+
type: "text",
|
|
263
|
+
text: readTemplate("bootstrap-prompt.md"),
|
|
264
|
+
},
|
|
265
|
+
}],
|
|
266
|
+
}),
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
server.registerPrompt(
|
|
270
|
+
"protocol",
|
|
271
|
+
"SyncLoop reasoning protocol summary - inject as system context for any AI coding task",
|
|
272
|
+
async () => ({
|
|
273
|
+
description: "The SyncLoop 7-stage reasoning protocol for self-correcting agent behavior",
|
|
274
|
+
messages: [{
|
|
275
|
+
role: "user",
|
|
276
|
+
content: {
|
|
277
|
+
type: "text",
|
|
278
|
+
text: readTemplate("protocol-summary.md"),
|
|
279
|
+
},
|
|
280
|
+
}],
|
|
281
|
+
}),
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
// Connect transport
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
|
|
288
|
+
const transport = new StdioServerTransport();
|
|
289
|
+
await server.connect(transport);
|
|
@@ -11,9 +11,9 @@ Add the SyncLoop MCP server to your AI coding client. The agent gets the full pr
|
|
|
11
11
|
```json
|
|
12
12
|
{
|
|
13
13
|
"mcpServers": {
|
|
14
|
-
|
|
14
|
+
"sync_loop": {
|
|
15
15
|
"command": "npx",
|
|
16
|
-
|
|
16
|
+
"args": ["-y", "-p", "@oleksandr.rudnychenko/sync_loop", "sync_loop"]
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -38,7 +38,7 @@ If you want the protocol files in your project (for offline use, customization,
|
|
|
38
38
|
|
|
39
39
|
### Via MCP (ask the agent)
|
|
40
40
|
|
|
41
|
-
Ask: "Use the
|
|
41
|
+
Ask: "Use the sync_loop init tool to scaffold files for [copilot/cursor/claude/all] into this project"
|
|
42
42
|
|
|
43
43
|
Before calling `init`, the agent should ask:
|
|
44
44
|
"Which SyncLoop target platform should I scaffold: `copilot`, `cursor`, `claude`, or `all`?"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|