@askthew/mcp-plugin 0.2.8 → 0.4.2
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 +65 -16
- package/dist/auth-pending.test.d.ts +1 -0
- package/dist/auth-pending.test.js +56 -0
- package/dist/cli-actions.test.d.ts +1 -0
- package/dist/cli-actions.test.js +71 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +412 -18
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +274 -0
- package/dist/free-tier-policy.test.d.ts +1 -0
- package/dist/free-tier-policy.test.js +57 -0
- package/dist/index.d.ts +59 -13
- package/dist/index.js +1736 -103
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +952 -0
- package/dist/install.d.ts +56 -1
- package/dist/install.js +171 -26
- package/dist/install.test.d.ts +1 -0
- package/dist/install.test.js +297 -0
- package/dist/lib/auth-magic-link.d.ts +22 -0
- package/dist/lib/auth-magic-link.js +43 -0
- package/dist/lib/auth-pending.d.ts +23 -0
- package/dist/lib/auth-pending.js +36 -0
- package/dist/lib/cli-actions.d.ts +28 -0
- package/dist/lib/cli-actions.js +104 -0
- package/dist/lib/free-install-registration.d.ts +27 -0
- package/dist/lib/free-install-registration.js +52 -0
- package/dist/lib/free-tier-policy.d.ts +23 -0
- package/dist/lib/free-tier-policy.js +68 -0
- package/dist/lib/local-identity.d.ts +44 -0
- package/dist/lib/local-identity.js +81 -0
- package/dist/lib/local-store.d.ts +130 -0
- package/dist/lib/local-store.js +595 -0
- package/dist/lib/loopback-auth.d.ts +8 -0
- package/dist/lib/loopback-auth.js +30 -0
- package/dist/lib/paths.d.ts +9 -0
- package/dist/lib/paths.js +50 -0
- package/dist/lib/telemetry.d.ts +25 -0
- package/dist/lib/telemetry.js +159 -0
- package/dist/lib/timeline-insights.d.ts +23 -0
- package/dist/lib/timeline-insights.js +115 -0
- package/dist/lib/tip-engine.d.ts +18 -0
- package/dist/lib/tip-engine.js +237 -0
- package/dist/lib/upgrade-nudge.d.ts +19 -0
- package/dist/lib/upgrade-nudge.js +37 -0
- package/dist/lib/upgrade-sync.d.ts +38 -0
- package/dist/lib/upgrade-sync.js +60 -0
- package/dist/local-identity.test.d.ts +1 -0
- package/dist/local-identity.test.js +29 -0
- package/dist/local-store.test.d.ts +1 -0
- package/dist/local-store.test.js +71 -0
- package/dist/scope.d.ts +1 -2
- package/dist/scope.js +56 -8
- package/dist/scope.test.d.ts +1 -0
- package/dist/scope.test.js +49 -0
- package/dist/timeline-insights.test.d.ts +1 -0
- package/dist/timeline-insights.test.js +85 -0
- package/dist/tip-engine.test.d.ts +1 -0
- package/dist/tip-engine.test.js +51 -0
- package/package.json +7 -10
package/dist/install.d.ts
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
export type SupportedHostType = "claude_code" | "codex" | "cursor";
|
|
2
2
|
interface HostConfigInput {
|
|
3
3
|
hostType: SupportedHostType;
|
|
4
|
-
token
|
|
4
|
+
token?: string;
|
|
5
5
|
apiUrl: string;
|
|
6
6
|
serverName: string;
|
|
7
7
|
clientId?: string;
|
|
8
8
|
clientLabel?: string;
|
|
9
|
+
free?: boolean;
|
|
10
|
+
email?: string;
|
|
11
|
+
cwd?: string;
|
|
9
12
|
}
|
|
10
13
|
interface InstallHostConfigInput extends HostConfigInput {
|
|
11
14
|
dryRun?: boolean;
|
|
12
15
|
homeDirectory?: string;
|
|
13
16
|
cwd?: string;
|
|
14
17
|
}
|
|
18
|
+
interface UninstallHostConfigInput {
|
|
19
|
+
hostType: SupportedHostType;
|
|
20
|
+
serverName?: string;
|
|
21
|
+
dryRun?: boolean;
|
|
22
|
+
homeDirectory?: string;
|
|
23
|
+
cwd?: string;
|
|
24
|
+
}
|
|
15
25
|
export declare function resolveSettingsPath(input: {
|
|
16
26
|
hostType: SupportedHostType;
|
|
17
27
|
homeDirectory?: string;
|
|
@@ -20,8 +30,23 @@ export declare function createServerEntry(input: HostConfigInput): {
|
|
|
20
30
|
command: string;
|
|
21
31
|
args: string[];
|
|
22
32
|
env: {
|
|
33
|
+
ASKTHEW_SERVICE_NAME?: string | undefined;
|
|
34
|
+
ASKTHEW_APP_PATH?: string | undefined;
|
|
35
|
+
ASKTHEW_REPO_ROOT?: string | undefined;
|
|
23
36
|
ASKTHEW_HOST_TYPE: SupportedHostType;
|
|
24
37
|
ASKTHEW_SERVER_NAME: string;
|
|
38
|
+
ASKTHEW_REPO_NAME: string;
|
|
39
|
+
ASKTHEW_CLIENT_LABEL?: string | undefined;
|
|
40
|
+
ASKTHEW_CLIENT_ID?: string | undefined;
|
|
41
|
+
ASKTHEW_FREE_MODE: string;
|
|
42
|
+
ASKTHEW_API_URL: string;
|
|
43
|
+
} | {
|
|
44
|
+
ASKTHEW_SERVICE_NAME?: string | undefined;
|
|
45
|
+
ASKTHEW_APP_PATH?: string | undefined;
|
|
46
|
+
ASKTHEW_REPO_ROOT?: string | undefined;
|
|
47
|
+
ASKTHEW_HOST_TYPE: SupportedHostType;
|
|
48
|
+
ASKTHEW_SERVER_NAME: string;
|
|
49
|
+
ASKTHEW_REPO_NAME: string;
|
|
25
50
|
ASKTHEW_CLIENT_LABEL?: string | undefined;
|
|
26
51
|
ASKTHEW_CLIENT_ID?: string | undefined;
|
|
27
52
|
ASKTHEW_INSTALL_TOKEN: string;
|
|
@@ -40,8 +65,23 @@ export declare function createHostConfigSnippet(input: HostConfigInput): {
|
|
|
40
65
|
command: string;
|
|
41
66
|
args: string[];
|
|
42
67
|
env: {
|
|
68
|
+
ASKTHEW_SERVICE_NAME?: string | undefined;
|
|
69
|
+
ASKTHEW_APP_PATH?: string | undefined;
|
|
70
|
+
ASKTHEW_REPO_ROOT?: string | undefined;
|
|
71
|
+
ASKTHEW_HOST_TYPE: SupportedHostType;
|
|
72
|
+
ASKTHEW_SERVER_NAME: string;
|
|
73
|
+
ASKTHEW_REPO_NAME: string;
|
|
74
|
+
ASKTHEW_CLIENT_LABEL?: string | undefined;
|
|
75
|
+
ASKTHEW_CLIENT_ID?: string | undefined;
|
|
76
|
+
ASKTHEW_FREE_MODE: string;
|
|
77
|
+
ASKTHEW_API_URL: string;
|
|
78
|
+
} | {
|
|
79
|
+
ASKTHEW_SERVICE_NAME?: string | undefined;
|
|
80
|
+
ASKTHEW_APP_PATH?: string | undefined;
|
|
81
|
+
ASKTHEW_REPO_ROOT?: string | undefined;
|
|
43
82
|
ASKTHEW_HOST_TYPE: SupportedHostType;
|
|
44
83
|
ASKTHEW_SERVER_NAME: string;
|
|
84
|
+
ASKTHEW_REPO_NAME: string;
|
|
45
85
|
ASKTHEW_CLIENT_LABEL?: string | undefined;
|
|
46
86
|
ASKTHEW_CLIENT_ID?: string | undefined;
|
|
47
87
|
ASKTHEW_INSTALL_TOKEN: string;
|
|
@@ -67,6 +107,12 @@ export declare function installHostConfig(input: InstallHostConfigInput): {
|
|
|
67
107
|
wroteFile: boolean;
|
|
68
108
|
nextStep: string;
|
|
69
109
|
};
|
|
110
|
+
export declare function uninstallHostConfig(input: UninstallHostConfigInput): {
|
|
111
|
+
settingsPath: string;
|
|
112
|
+
json: string;
|
|
113
|
+
removedServerName: string;
|
|
114
|
+
wroteFile: boolean;
|
|
115
|
+
};
|
|
70
116
|
export declare function sendInstallHeartbeat(input: HostConfigInput & {
|
|
71
117
|
cwd?: string;
|
|
72
118
|
fetchImpl?: typeof fetch;
|
|
@@ -77,7 +123,16 @@ export declare function installBehaviorInstructions(input: {
|
|
|
77
123
|
dryRun?: boolean;
|
|
78
124
|
}): {
|
|
79
125
|
path: string;
|
|
126
|
+
paths: string[];
|
|
80
127
|
wroteFile: boolean;
|
|
81
128
|
content: string;
|
|
82
129
|
};
|
|
130
|
+
export declare function uninstallBehaviorInstructions(input: {
|
|
131
|
+
hostType: SupportedHostType;
|
|
132
|
+
cwd?: string;
|
|
133
|
+
dryRun?: boolean;
|
|
134
|
+
}): {
|
|
135
|
+
paths: string[];
|
|
136
|
+
wroteFile: boolean;
|
|
137
|
+
};
|
|
83
138
|
export {};
|
package/dist/install.js
CHANGED
|
@@ -18,16 +18,21 @@ export function resolveSettingsPath(input) {
|
|
|
18
18
|
return path.join(homeDirectory, ".claude.json");
|
|
19
19
|
}
|
|
20
20
|
export function createServerEntry(input) {
|
|
21
|
+
const scope = resolvePluginScope(input.cwd ?? process.cwd());
|
|
21
22
|
return {
|
|
22
23
|
command: "npx",
|
|
23
24
|
args: ["-y", "--prefer-online", "@askthew/mcp-plugin@latest"],
|
|
24
25
|
env: {
|
|
25
|
-
ASKTHEW_INSTALL_TOKEN: input.token,
|
|
26
26
|
ASKTHEW_API_URL: input.apiUrl,
|
|
27
|
+
...(input.free ? { ASKTHEW_FREE_MODE: "1" } : { ASKTHEW_INSTALL_TOKEN: input.token ?? "" }),
|
|
27
28
|
...(input.clientId ? { ASKTHEW_CLIENT_ID: input.clientId } : {}),
|
|
28
29
|
...(input.clientLabel ? { ASKTHEW_CLIENT_LABEL: input.clientLabel } : {}),
|
|
29
30
|
ASKTHEW_HOST_TYPE: input.hostType,
|
|
30
31
|
ASKTHEW_SERVER_NAME: input.serverName,
|
|
32
|
+
ASKTHEW_REPO_NAME: scope.repoName,
|
|
33
|
+
...(scope.repoRoot ? { ASKTHEW_REPO_ROOT: scope.repoRoot } : {}),
|
|
34
|
+
...(scope.appPath ? { ASKTHEW_APP_PATH: scope.appPath } : {}),
|
|
35
|
+
...(scope.serviceName ? { ASKTHEW_SERVICE_NAME: scope.serviceName } : {}),
|
|
31
36
|
},
|
|
32
37
|
};
|
|
33
38
|
}
|
|
@@ -124,7 +129,7 @@ export function mergeHostSettings(input) {
|
|
|
124
129
|
};
|
|
125
130
|
}
|
|
126
131
|
export function formatInstallCommand(input) {
|
|
127
|
-
|
|
132
|
+
const parts = [
|
|
128
133
|
"npx",
|
|
129
134
|
"-y",
|
|
130
135
|
"--prefer-online",
|
|
@@ -132,17 +137,25 @@ export function formatInstallCommand(input) {
|
|
|
132
137
|
"install",
|
|
133
138
|
"--host",
|
|
134
139
|
input.hostType,
|
|
135
|
-
"--token",
|
|
136
|
-
JSON.stringify(input.token),
|
|
137
140
|
"--api-url",
|
|
138
141
|
JSON.stringify(input.apiUrl),
|
|
139
142
|
"--server-name",
|
|
140
143
|
JSON.stringify(input.serverName),
|
|
141
|
-
]
|
|
144
|
+
];
|
|
145
|
+
if (input.free) {
|
|
146
|
+
parts.push("--free");
|
|
147
|
+
if (input.email) {
|
|
148
|
+
parts.push("--email", JSON.stringify(input.email));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
parts.splice(7, 0, "--token", JSON.stringify(input.token ?? ""));
|
|
153
|
+
}
|
|
154
|
+
return parts.join(" ");
|
|
142
155
|
}
|
|
143
156
|
export function verificationNextStep(hostType) {
|
|
144
157
|
const hostLabel = hostType === "claude_code" ? "Claude Code" : hostType === "cursor" ? "Cursor" : "Codex";
|
|
145
|
-
return `Refresh Ask The W to confirm the plugin shows Installed. Restart or reload ${hostLabel} if it is already open.
|
|
158
|
+
return `Refresh Ask The W to confirm the plugin shows Installed. Restart or reload ${hostLabel} if it is already open. At the start of every new ${hostLabel} session in this repo, the installed project instructions tell the coding agent to send a setup_complete update before plan mode or exploration. Choose Always allow if ${hostLabel} asks for Ask The W tool permission. list_mcp_resources/list_mcp_resource_templates may be empty for this tool-driven connector and are not failure signals.`;
|
|
146
159
|
}
|
|
147
160
|
export function installHostConfig(input) {
|
|
148
161
|
const settingsPath = resolveSettingsPath({
|
|
@@ -174,6 +187,8 @@ export function installHostConfig(input) {
|
|
|
174
187
|
serverName: input.serverName,
|
|
175
188
|
clientId: input.clientId,
|
|
176
189
|
clientLabel: input.clientLabel,
|
|
190
|
+
free: input.free,
|
|
191
|
+
cwd: input.cwd,
|
|
177
192
|
};
|
|
178
193
|
const json = input.hostType === "codex"
|
|
179
194
|
? mergeCodexSettings({
|
|
@@ -197,6 +212,65 @@ export function installHostConfig(input) {
|
|
|
197
212
|
nextStep: verificationNextStep(input.hostType),
|
|
198
213
|
};
|
|
199
214
|
}
|
|
215
|
+
export function uninstallHostConfig(input) {
|
|
216
|
+
const settingsPath = resolveSettingsPath({
|
|
217
|
+
hostType: input.hostType,
|
|
218
|
+
homeDirectory: input.homeDirectory,
|
|
219
|
+
});
|
|
220
|
+
const serverName = input.serverName?.trim() || "askthew";
|
|
221
|
+
let json = "";
|
|
222
|
+
if (fs.existsSync(settingsPath)) {
|
|
223
|
+
const raw = fs.readFileSync(settingsPath, "utf8");
|
|
224
|
+
if (input.hostType === "codex") {
|
|
225
|
+
json = removeCodexTomlServer(raw, serverName);
|
|
226
|
+
if (serverName !== "askthew") {
|
|
227
|
+
json = removeCodexTomlServer(json, "askthew");
|
|
228
|
+
}
|
|
229
|
+
json = json ? `${json}\n` : "";
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
const parsed = raw.trim() ? JSON.parse(raw) : {};
|
|
233
|
+
if (input.hostType === "claude_code") {
|
|
234
|
+
const cwd = path.resolve(input.cwd ?? process.cwd());
|
|
235
|
+
const existingProjects = isRecord(parsed.projects) ? parsed.projects : {};
|
|
236
|
+
const existingProject = isRecord(existingProjects[cwd]) ? existingProjects[cwd] : {};
|
|
237
|
+
const existingMcpServers = isRecord(existingProject.mcpServers) ? existingProject.mcpServers : {};
|
|
238
|
+
const nextServers = { ...existingMcpServers };
|
|
239
|
+
delete nextServers[serverName];
|
|
240
|
+
if (serverName !== "askthew")
|
|
241
|
+
delete nextServers.askthew;
|
|
242
|
+
json = JSON.stringify({
|
|
243
|
+
...parsed,
|
|
244
|
+
projects: {
|
|
245
|
+
...existingProjects,
|
|
246
|
+
[cwd]: {
|
|
247
|
+
...existingProject,
|
|
248
|
+
mcpServers: nextServers,
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
}, null, 2);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
const existingMcpServers = isRecord(parsed.mcpServers) ? parsed.mcpServers : {};
|
|
255
|
+
const nextServers = { ...existingMcpServers };
|
|
256
|
+
delete nextServers[serverName];
|
|
257
|
+
if (serverName !== "askthew")
|
|
258
|
+
delete nextServers.askthew;
|
|
259
|
+
json = JSON.stringify({ ...parsed, mcpServers: nextServers }, null, 2);
|
|
260
|
+
}
|
|
261
|
+
json = `${json}\n`;
|
|
262
|
+
}
|
|
263
|
+
if (!input.dryRun) {
|
|
264
|
+
fs.writeFileSync(settingsPath, json, "utf8");
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
settingsPath,
|
|
269
|
+
json,
|
|
270
|
+
removedServerName: serverName,
|
|
271
|
+
wroteFile: !input.dryRun,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
200
274
|
export async function sendInstallHeartbeat(input) {
|
|
201
275
|
const fetcher = input.fetchImpl ?? fetch;
|
|
202
276
|
const scope = resolvePluginScope(input.cwd ?? process.cwd());
|
|
@@ -220,8 +294,41 @@ export async function sendInstallHeartbeat(input) {
|
|
|
220
294
|
});
|
|
221
295
|
return response.ok;
|
|
222
296
|
}
|
|
223
|
-
function
|
|
297
|
+
function detectStackGuidance(cwd) {
|
|
298
|
+
const packagePath = path.join(cwd, "package.json");
|
|
299
|
+
if (!fs.existsSync(packagePath)) {
|
|
300
|
+
return [];
|
|
301
|
+
}
|
|
302
|
+
let manifest = {};
|
|
303
|
+
try {
|
|
304
|
+
manifest = JSON.parse(fs.readFileSync(packagePath, "utf8"));
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
309
|
+
const deps = {
|
|
310
|
+
...(manifest.dependencies ?? {}),
|
|
311
|
+
...(manifest.devDependencies ?? {}),
|
|
312
|
+
};
|
|
313
|
+
const names = new Set(Object.keys(deps));
|
|
314
|
+
const guidance = [];
|
|
315
|
+
if (names.has("next")) {
|
|
316
|
+
guidance.push("- Next.js detected: after changing route handlers, server actions, middleware, or cache behavior, capture `verification_result` with the command/result.");
|
|
317
|
+
}
|
|
318
|
+
if (names.has("express") || names.has("@types/express")) {
|
|
319
|
+
guidance.push("- Express detected: after changing middleware, request validation, or response envelopes, capture `verification_result` with the command/result.");
|
|
320
|
+
}
|
|
321
|
+
if (names.has("vite") || names.has("@vitejs/plugin-react")) {
|
|
322
|
+
guidance.push("- Vite detected: after changing client entrypoints, env handling, or build config, capture `verification_result` with the command/result.");
|
|
323
|
+
}
|
|
324
|
+
if (names.has("openai") || names.has("@openai/agents") || names.has("@ai-sdk/openai")) {
|
|
325
|
+
guidance.push("- OpenAI SDK detected: after editing prompts, model calls, streaming/SSE, or schema-validated responses, capture `direction_change` for prompt choices and `verification_result` for checked outputs.");
|
|
326
|
+
}
|
|
327
|
+
return guidance.slice(0, 4);
|
|
328
|
+
}
|
|
329
|
+
function behaviorInstructions(hostType, cwd = process.cwd()) {
|
|
224
330
|
const hostLabel = hostType === "claude_code" ? "Claude Code" : hostType === "cursor" ? "Cursor" : "Codex";
|
|
331
|
+
const stackGuidance = detectStackGuidance(cwd);
|
|
225
332
|
return [
|
|
226
333
|
ASKTHEW_INSTRUCTIONS_START,
|
|
227
334
|
"",
|
|
@@ -229,29 +336,32 @@ function behaviorInstructions(hostType) {
|
|
|
229
336
|
"",
|
|
230
337
|
`This workspace uses the Ask The W Plugin. When working in this repo with ${hostLabel}, send compact Ask The W updates automatically with the MCP tool \`capture_session_signal\`.`,
|
|
231
338
|
"",
|
|
339
|
+
"Startup update:",
|
|
340
|
+
`- At the start of every new ${hostLabel} session in this repo, before plan mode, exploration, or any normal reply, call \`capture_session_signal\` with \`kind: "setup_complete"\` if the tool is available.`,
|
|
341
|
+
`- If the tool is unavailable, tell the user to restart or reload ${hostLabel}; do not use \`list_mcp_resources\` or \`list_mcp_resource_templates\` as pass/fail checks.`,
|
|
342
|
+
"- If you realize later in the conversation that the startup call was missed, send it immediately with `metadata.recovered_missed_startup=true`.",
|
|
343
|
+
"",
|
|
232
344
|
"Send an update:",
|
|
233
345
|
"- after the user accepts or rejects product, architecture, or implementation direction",
|
|
234
|
-
"- after meaningful implementation changes",
|
|
346
|
+
"- before using tools that write files, after meaningful implementation changes",
|
|
235
347
|
"- after tests, builds, lint, or other verification",
|
|
236
|
-
"- every 8-12 turns in long sessions",
|
|
237
348
|
"- at the final summary",
|
|
349
|
+
...(stackGuidance.length > 0 ? ["", "Stack-specific nudges:", ...stackGuidance] : []),
|
|
238
350
|
"",
|
|
239
351
|
"Keep updates compact: short summary, minimal evidence excerpts, files touched, commands run, and useful metadata. Do not send full transcripts. Redact obvious secrets before sending.",
|
|
240
352
|
"",
|
|
241
|
-
"If this is the first session after install or reload, immediately send a `setup_complete` update with `capture_session_signal` before continuing. If the host asks for Ask The W tool permission, the user should choose Always allow if available.",
|
|
242
|
-
"",
|
|
243
353
|
ASKTHEW_INSTRUCTIONS_END,
|
|
244
354
|
"",
|
|
245
355
|
].join("\n");
|
|
246
356
|
}
|
|
247
|
-
function cursorBehaviorInstructions() {
|
|
357
|
+
function cursorBehaviorInstructions(cwd = process.cwd()) {
|
|
248
358
|
return [
|
|
249
359
|
"---",
|
|
250
360
|
"description: Ask The W Plugin capture rules",
|
|
251
361
|
"alwaysApply: true",
|
|
252
362
|
"---",
|
|
253
363
|
"",
|
|
254
|
-
behaviorInstructions("cursor"),
|
|
364
|
+
behaviorInstructions("cursor", cwd),
|
|
255
365
|
].join("\n");
|
|
256
366
|
}
|
|
257
367
|
function upsertMarkedBlock(existing, block) {
|
|
@@ -265,21 +375,56 @@ function upsertMarkedBlock(existing, block) {
|
|
|
265
375
|
}
|
|
266
376
|
export function installBehaviorInstructions(input) {
|
|
267
377
|
const cwd = path.resolve(input.cwd ?? process.cwd());
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
378
|
+
const markdownBlock = behaviorInstructions(input.hostType, cwd);
|
|
379
|
+
const markdownTargets = [path.join(cwd, "CLAUDE.md"), path.join(cwd, "AGENTS.md")];
|
|
380
|
+
const writtenPaths = [];
|
|
381
|
+
let primaryPath = input.hostType === "claude_code" ? markdownTargets[0] : markdownTargets[1];
|
|
382
|
+
for (const instructionsPath of markdownTargets) {
|
|
383
|
+
const existing = fs.existsSync(instructionsPath) ? fs.readFileSync(instructionsPath, "utf8") : "";
|
|
384
|
+
const next = upsertMarkedBlock(existing, markdownBlock);
|
|
385
|
+
if (!input.dryRun) {
|
|
386
|
+
fs.mkdirSync(path.dirname(instructionsPath), { recursive: true });
|
|
387
|
+
fs.writeFileSync(instructionsPath, next, "utf8");
|
|
388
|
+
}
|
|
389
|
+
writtenPaths.push(instructionsPath);
|
|
390
|
+
}
|
|
391
|
+
if (input.hostType === "cursor") {
|
|
392
|
+
const cursorPath = path.join(cwd, ".cursor", "rules", "askthew.mdc");
|
|
393
|
+
if (!input.dryRun) {
|
|
394
|
+
fs.mkdirSync(path.dirname(cursorPath), { recursive: true });
|
|
395
|
+
fs.writeFileSync(cursorPath, cursorBehaviorInstructions(cwd), "utf8");
|
|
396
|
+
}
|
|
397
|
+
writtenPaths.push(cursorPath);
|
|
398
|
+
primaryPath = cursorPath;
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
path: primaryPath,
|
|
402
|
+
paths: writtenPaths,
|
|
403
|
+
wroteFile: !input.dryRun,
|
|
404
|
+
content: markdownBlock,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
export function uninstallBehaviorInstructions(input) {
|
|
408
|
+
const cwd = path.resolve(input.cwd ?? process.cwd());
|
|
409
|
+
const markdownTargets = [path.join(cwd, "CLAUDE.md"), path.join(cwd, "AGENTS.md")];
|
|
410
|
+
const touchedPaths = [];
|
|
411
|
+
for (const instructionsPath of markdownTargets) {
|
|
412
|
+
if (!fs.existsSync(instructionsPath))
|
|
413
|
+
continue;
|
|
414
|
+
const existing = fs.readFileSync(instructionsPath, "utf8");
|
|
415
|
+
const next = existing.replace(new RegExp(`\\n?${ASKTHEW_INSTRUCTIONS_START}[\\s\\S]*?${ASKTHEW_INSTRUCTIONS_END}\\n?`, "g"), "\n").trimEnd() + "\n";
|
|
416
|
+
if (!input.dryRun)
|
|
417
|
+
fs.writeFileSync(instructionsPath, next, "utf8");
|
|
418
|
+
touchedPaths.push(instructionsPath);
|
|
419
|
+
}
|
|
420
|
+
const cursorPath = path.join(cwd, ".cursor", "rules", "askthew.mdc");
|
|
421
|
+
if (input.hostType === "cursor" && fs.existsSync(cursorPath)) {
|
|
422
|
+
if (!input.dryRun)
|
|
423
|
+
fs.rmSync(cursorPath, { force: true });
|
|
424
|
+
touchedPaths.push(cursorPath);
|
|
279
425
|
}
|
|
280
426
|
return {
|
|
281
|
-
|
|
427
|
+
paths: touchedPaths,
|
|
282
428
|
wroteFile: !input.dryRun,
|
|
283
|
-
content: next,
|
|
284
429
|
};
|
|
285
430
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|