@askthew/mcp-plugin 0.4.0 → 0.4.3
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 +24 -13
- 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 +293 -37
- 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 +47 -13
- package/dist/index.js +1103 -106
- package/dist/index.test.js +609 -6
- package/dist/install.d.ts +40 -0
- package/dist/install.js +155 -18
- package/dist/install.test.js +62 -2
- 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 +5 -1
- package/dist/lib/free-tier-policy.js +16 -1
- package/dist/lib/local-identity.d.ts +44 -0
- package/dist/lib/local-identity.js +81 -0
- package/dist/lib/local-store.d.ts +33 -2
- package/dist/lib/local-store.js +191 -19
- package/dist/lib/paths.d.ts +2 -0
- package/dist/lib/paths.js +6 -0
- package/dist/lib/telemetry.js +28 -2
- package/dist/lib/timeline-insights.d.ts +23 -0
- package/dist/lib/timeline-insights.js +115 -0
- package/dist/lib/upgrade-nudge.d.ts +1 -1
- package/dist/lib/upgrade-nudge.js +8 -1
- package/dist/local-identity.test.d.ts +1 -0
- package/dist/local-identity.test.js +29 -0
- package/dist/local-store.test.js +34 -0
- package/dist/scope.d.ts +1 -1
- package/dist/scope.js +56 -2
- package/dist/scope.test.js +17 -0
- package/dist/timeline-insights.test.d.ts +1 -0
- package/dist/timeline-insights.test.js +85 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
4
7
|
import { createAskTheWMcpServer } from "./index.js";
|
|
5
|
-
import {
|
|
8
|
+
import { clearPendingAuth, pendingAuth, pendingAuthForEmail } from "./lib/auth-pending.js";
|
|
9
|
+
import { verifyMagicLinkCode as verifyMagicLinkCodeDefault, } from "./lib/auth-magic-link.js";
|
|
6
10
|
import { credentialsPath, ensureAskTheWDataDir } from "./lib/paths.js";
|
|
7
11
|
import { loadCliCredentials } from "./lib/free-tier-policy.js";
|
|
12
|
+
import { describeFreeIdentity, tryRegisterFreeInstall } from "./lib/free-install-registration.js";
|
|
13
|
+
import { ensureLocalIdentity, loadLocalIdentity, publicIdentity } from "./lib/local-identity.js";
|
|
8
14
|
import { LocalStore } from "./lib/local-store.js";
|
|
9
15
|
import { buildTelemetryPayload } from "./lib/telemetry.js";
|
|
10
16
|
import { syncDryRun, uploadLocalStore } from "./lib/upgrade-sync.js";
|
|
11
|
-
import {
|
|
17
|
+
import { installPreCommitHook, localScopeKey, preCommitDecisionGap, stagedFiles, writeWeeklyDigest, } from "./lib/cli-actions.js";
|
|
18
|
+
import { createHostConfigSnippet, formatInstallCommand, installBehaviorInstructions, installHostConfig, sendInstallHeartbeat, uninstallBehaviorInstructions, uninstallHostConfig, } from "./install.js";
|
|
12
19
|
function usage() {
|
|
13
20
|
return [
|
|
14
21
|
"Ask The W Coding Agent Connector",
|
|
@@ -16,11 +23,17 @@ function usage() {
|
|
|
16
23
|
"Usage:",
|
|
17
24
|
" askthew-mcp",
|
|
18
25
|
" askthew-mcp install --host <claude_code|codex|cursor> --token <install-token> --api-url <url> --server-name <name> [--client-id <id>] [--client-label <label>] [--dry-run] [--no-agent-instructions]",
|
|
19
|
-
" askthew-mcp install --host <claude_code|codex|cursor> --free [--api-url <url>] [--server-name <name>]",
|
|
20
|
-
" askthew-mcp
|
|
26
|
+
" askthew-mcp install --host <claude_code|codex|cursor> --free [--email <email>] [--api-url <url>] [--server-name <name>]",
|
|
27
|
+
" askthew-mcp uninstall --host <claude_code|codex|cursor> [--server-name <name>] [--dry-run] [--keep-local-data] [--keep-auth] [--keep-agent-instructions]",
|
|
28
|
+
" askthew-mcp identify --email <email> [--no-telemetry]",
|
|
29
|
+
" askthew-mcp identity status",
|
|
30
|
+
" askthew-mcp auth login --email <email> [--no-telemetry]",
|
|
31
|
+
" askthew-mcp auth verify --code <code> [--email <email>]",
|
|
21
32
|
" askthew-mcp auth logout | status",
|
|
22
33
|
" askthew-mcp telemetry status | opt-out | opt-in | preview",
|
|
23
34
|
" askthew-mcp local stats | reset --hard",
|
|
35
|
+
" askthew-mcp install-hook --pre-commit",
|
|
36
|
+
" askthew-mcp digest --weekly",
|
|
24
37
|
" askthew-mcp sync upload [--dry-run]",
|
|
25
38
|
" askthew-mcp print-config --host <claude_code|codex|cursor> --token <install-token> --api-url <url> --server-name <name> [--client-id <id>] [--client-label <label>]",
|
|
26
39
|
].join("\n");
|
|
@@ -35,6 +48,7 @@ function parseInstallArgs(argv) {
|
|
|
35
48
|
let dryRun = false;
|
|
36
49
|
let installAgentInstructions = true;
|
|
37
50
|
let free = false;
|
|
51
|
+
let email = process.env.ASKTHEW_EMAIL?.trim() || "";
|
|
38
52
|
for (let index = 0; index < argv.length; index += 1) {
|
|
39
53
|
const argument = argv[index];
|
|
40
54
|
if (argument === "--dry-run") {
|
|
@@ -86,6 +100,11 @@ function parseInstallArgs(argv) {
|
|
|
86
100
|
index += 1;
|
|
87
101
|
continue;
|
|
88
102
|
}
|
|
103
|
+
if (argument === "--email") {
|
|
104
|
+
email = next;
|
|
105
|
+
index += 1;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
89
108
|
throw new Error(`Unknown argument: ${argument}`);
|
|
90
109
|
}
|
|
91
110
|
if (!hostType) {
|
|
@@ -110,11 +129,45 @@ function parseInstallArgs(argv) {
|
|
|
110
129
|
dryRun,
|
|
111
130
|
installAgentInstructions,
|
|
112
131
|
free,
|
|
132
|
+
email: email || undefined,
|
|
113
133
|
};
|
|
114
134
|
}
|
|
115
135
|
function normalizeInstallToken(token) {
|
|
116
136
|
return String(token ?? "").trim().replace(/^['"]/, "").replace(/['"]$/, "");
|
|
117
137
|
}
|
|
138
|
+
function detectLoginEmail() {
|
|
139
|
+
for (const value of [
|
|
140
|
+
process.env.ASKTHEW_EMAIL,
|
|
141
|
+
process.env.GIT_AUTHOR_EMAIL,
|
|
142
|
+
process.env.GIT_COMMITTER_EMAIL,
|
|
143
|
+
process.env.EMAIL,
|
|
144
|
+
]) {
|
|
145
|
+
const email = String(value ?? "").trim();
|
|
146
|
+
if (/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email))
|
|
147
|
+
return email;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
const email = execFileSync("git", ["config", "user.email"], {
|
|
151
|
+
encoding: "utf8",
|
|
152
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
153
|
+
}).trim();
|
|
154
|
+
if (/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email))
|
|
155
|
+
return email;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return "";
|
|
159
|
+
}
|
|
160
|
+
return "";
|
|
161
|
+
}
|
|
162
|
+
function loginCommandHint() {
|
|
163
|
+
const email = detectLoginEmail();
|
|
164
|
+
return email
|
|
165
|
+
? `askthew-mcp identify --email ${email}`
|
|
166
|
+
: "askthew-mcp identify --email <your-email>";
|
|
167
|
+
}
|
|
168
|
+
function installIdentityEmail(optionsEmail) {
|
|
169
|
+
return optionsEmail?.trim() || detectLoginEmail() || undefined;
|
|
170
|
+
}
|
|
118
171
|
async function main() {
|
|
119
172
|
const [command, ...argv] = process.argv.slice(2);
|
|
120
173
|
if (command === "--help" || command === "-h" || command === "help") {
|
|
@@ -129,6 +182,13 @@ async function main() {
|
|
|
129
182
|
}
|
|
130
183
|
if (command === "install") {
|
|
131
184
|
const options = parseInstallArgs(argv);
|
|
185
|
+
let freeIdentity = null;
|
|
186
|
+
if (options.free && !options.dryRun) {
|
|
187
|
+
freeIdentity = ensureLocalIdentity({
|
|
188
|
+
emailClaim: installIdentityEmail(options.email),
|
|
189
|
+
apiUrl: options.apiUrl,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
132
192
|
const result = installHostConfig(options);
|
|
133
193
|
const instructions = options.installAgentInstructions
|
|
134
194
|
? installBehaviorInstructions({
|
|
@@ -142,12 +202,25 @@ async function main() {
|
|
|
142
202
|
console.log(result.wroteFile ? "Ask The W plugin install complete." : "Ask The W plugin dry run complete.");
|
|
143
203
|
console.log(`Settings path: ${result.settingsPath}`);
|
|
144
204
|
if (instructions) {
|
|
145
|
-
console.log(`Agent instructions: ${instructions.path}`);
|
|
205
|
+
console.log(`Agent instructions: ${instructions.paths?.join(", ") ?? instructions.path}`);
|
|
146
206
|
}
|
|
147
207
|
console.log(`Install command: ${formatInstallCommand(options)}`);
|
|
148
208
|
if (result.wroteFile) {
|
|
209
|
+
if (freeIdentity) {
|
|
210
|
+
const registration = await tryRegisterFreeInstall({
|
|
211
|
+
identity: freeIdentity,
|
|
212
|
+
deviceLabel: options.clientLabel ?? `${options.hostType} free install`,
|
|
213
|
+
repo: {
|
|
214
|
+
repoName: process.env.ASKTHEW_REPO_NAME,
|
|
215
|
+
repoRoot: process.env.ASKTHEW_REPO_ROOT,
|
|
216
|
+
hostType: options.hostType,
|
|
217
|
+
},
|
|
218
|
+
options: { apiUrl: options.apiUrl },
|
|
219
|
+
});
|
|
220
|
+
console.log(registration.ok ? "Free install identity registered with Ask The W." : "Free install identity saved locally; cloud registration will retry later.");
|
|
221
|
+
}
|
|
149
222
|
console.log(options.free
|
|
150
|
-
? "Free local mode installed.
|
|
223
|
+
? "Free local mode installed. Restart or reload your coding app; captures and decisions will write to ~/.askthew/store.sqlite."
|
|
151
224
|
: heartbeatSent
|
|
152
225
|
? "Ask The W install heartbeat sent. Refresh the app to confirm the plugin shows Installed."
|
|
153
226
|
: "Ask The W install heartbeat could not be sent yet. Restart or reload your coding app, then refresh Ask The W.");
|
|
@@ -159,6 +232,18 @@ async function main() {
|
|
|
159
232
|
}
|
|
160
233
|
return;
|
|
161
234
|
}
|
|
235
|
+
if (command === "identify") {
|
|
236
|
+
await runIdentifyCommand(argv);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (command === "identity") {
|
|
240
|
+
await runIdentityCommand(argv);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (command === "uninstall") {
|
|
244
|
+
await runUninstallCommand(argv);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
162
247
|
if (command === "auth") {
|
|
163
248
|
await runAuthCommand(argv);
|
|
164
249
|
return;
|
|
@@ -171,6 +256,18 @@ async function main() {
|
|
|
171
256
|
await runLocalCommand(argv);
|
|
172
257
|
return;
|
|
173
258
|
}
|
|
259
|
+
if (command === "install-hook") {
|
|
260
|
+
await runInstallHookCommand(argv);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (command === "hook-check") {
|
|
264
|
+
await runHookCheckCommand(argv);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (command === "digest") {
|
|
268
|
+
await runDigestCommand(argv);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
174
271
|
if (command === "sync") {
|
|
175
272
|
await runSyncCommand(argv);
|
|
176
273
|
return;
|
|
@@ -189,59 +286,189 @@ async function main() {
|
|
|
189
286
|
const transport = new StdioServerTransport();
|
|
190
287
|
await server.connect(transport);
|
|
191
288
|
}
|
|
289
|
+
async function runUninstallCommand(argv) {
|
|
290
|
+
let hostType;
|
|
291
|
+
let serverName = process.env.ASKTHEW_SERVER_NAME?.trim() || "askthew";
|
|
292
|
+
let dryRun = false;
|
|
293
|
+
let keepLocalData = false;
|
|
294
|
+
let keepAuth = false;
|
|
295
|
+
let keepAgentInstructions = false;
|
|
296
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
297
|
+
const argument = argv[index];
|
|
298
|
+
if (argument === "--dry-run") {
|
|
299
|
+
dryRun = true;
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (argument === "--keep-local-data") {
|
|
303
|
+
keepLocalData = true;
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (argument === "--keep-auth") {
|
|
307
|
+
keepAuth = true;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (argument === "--keep-agent-instructions") {
|
|
311
|
+
keepAgentInstructions = true;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
const next = argv[index + 1];
|
|
315
|
+
if (!next)
|
|
316
|
+
throw new Error(`Missing value for ${argument}.`);
|
|
317
|
+
if (argument === "--host") {
|
|
318
|
+
if (next !== "claude_code" && next !== "codex" && next !== "cursor") {
|
|
319
|
+
throw new Error(`Unsupported host "${next}". Expected claude_code, codex, or cursor.`);
|
|
320
|
+
}
|
|
321
|
+
hostType = next;
|
|
322
|
+
index += 1;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (argument === "--server-name") {
|
|
326
|
+
serverName = next;
|
|
327
|
+
index += 1;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
throw new Error(`Unknown argument: ${argument}`);
|
|
331
|
+
}
|
|
332
|
+
if (!hostType) {
|
|
333
|
+
throw new Error("Usage: askthew-mcp uninstall --host <claude_code|codex|cursor> [--server-name <name>]");
|
|
334
|
+
}
|
|
335
|
+
const config = uninstallHostConfig({ hostType, serverName, dryRun });
|
|
336
|
+
const instructions = keepAgentInstructions
|
|
337
|
+
? null
|
|
338
|
+
: uninstallBehaviorInstructions({ hostType, dryRun });
|
|
339
|
+
if (!keepLocalData && !dryRun) {
|
|
340
|
+
fs.rmSync(ensureAskTheWDataDir(), { recursive: true, force: true });
|
|
341
|
+
}
|
|
342
|
+
if (!keepAuth && !dryRun) {
|
|
343
|
+
const file = credentialsPath();
|
|
344
|
+
if (fs.existsSync(file))
|
|
345
|
+
fs.rmSync(file, { force: true });
|
|
346
|
+
}
|
|
347
|
+
console.log(dryRun ? "Ask The W plugin uninstall dry run complete." : "Ask The W plugin uninstall complete.");
|
|
348
|
+
console.log(`Settings path: ${config.settingsPath}`);
|
|
349
|
+
if (instructions) {
|
|
350
|
+
console.log(`Agent instructions removed: ${instructions.paths.join(", ") || "none"}`);
|
|
351
|
+
}
|
|
352
|
+
console.log(keepLocalData ? "Local data kept." : "Local data removed.");
|
|
353
|
+
console.log(keepAuth ? "Auth tokens kept." : "Auth tokens removed.");
|
|
354
|
+
if (dryRun) {
|
|
355
|
+
console.log("");
|
|
356
|
+
console.log(config.json);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
192
359
|
function argValue(argv, name) {
|
|
193
360
|
const index = argv.indexOf(name);
|
|
194
361
|
return index >= 0 ? argv[index + 1] : undefined;
|
|
195
362
|
}
|
|
196
|
-
async function runAuthCommand(argv) {
|
|
363
|
+
export async function runAuthCommand(argv, deps = {}) {
|
|
364
|
+
const log = deps.log ?? console.log;
|
|
365
|
+
const verifyCode = deps.verifyMagicLinkCode ?? verifyMagicLinkCodeDefault;
|
|
366
|
+
const registerInstall = deps.registerFreeInstall ?? tryRegisterFreeInstall;
|
|
197
367
|
const [subcommand] = argv;
|
|
198
368
|
if (subcommand === "status") {
|
|
369
|
+
const identity = loadLocalIdentity();
|
|
199
370
|
const credentials = loadCliCredentials();
|
|
200
|
-
|
|
201
|
-
? `
|
|
202
|
-
:
|
|
371
|
+
log(identity
|
|
372
|
+
? `Identified local free install ${identity.installId}${identity.emailClaim ? ` with email claim ${identity.emailClaim}` : ""}. Email claim is unverified until upgrade.`
|
|
373
|
+
: credentials
|
|
374
|
+
? `Logged in as ${credentials.email ?? credentials.userId}. Telemetry: ${credentials.telemetryOptOut ? "off" : "on"}`
|
|
375
|
+
: pendingAuth()
|
|
376
|
+
? `Not logged in. Pending code for ${pendingAuth()?.email}. Run \`askthew-mcp auth verify --code <6-digit-code>\`.`
|
|
377
|
+
: `No local identity yet. Run \`askthew-mcp identify --email <your-email>\`, or install with \`--free --email <your-email>\`.`);
|
|
203
378
|
return;
|
|
204
379
|
}
|
|
205
380
|
if (subcommand === "logout") {
|
|
206
381
|
const file = credentialsPath();
|
|
207
382
|
if (fs.existsSync(file))
|
|
208
383
|
fs.rmSync(file);
|
|
209
|
-
|
|
384
|
+
log("Logged out of Ask The W local free tier.");
|
|
210
385
|
return;
|
|
211
386
|
}
|
|
212
|
-
if (subcommand !== "login") {
|
|
213
|
-
throw new Error("Usage: askthew-mcp auth login --email <email> [--code <code>
|
|
387
|
+
if (subcommand !== "login" && subcommand !== "verify") {
|
|
388
|
+
throw new Error("Usage: askthew-mcp auth login --email <email> [--no-telemetry] | askthew-mcp auth verify --code <code> [--email <email>]");
|
|
214
389
|
}
|
|
215
390
|
ensureAskTheWDataDir();
|
|
216
391
|
const email = argValue(argv, "--email")?.trim();
|
|
392
|
+
const code = argValue(argv, "--code")?.trim();
|
|
393
|
+
if (subcommand === "verify" || code) {
|
|
394
|
+
if (!code)
|
|
395
|
+
throw new Error("Missing --code.");
|
|
396
|
+
const pending = email ? pendingAuthForEmail(email) : pendingAuth();
|
|
397
|
+
if (!pending) {
|
|
398
|
+
throw new Error(email
|
|
399
|
+
? `No pending Ask The W login request for ${email}. Run \`askthew-mcp auth login --email ${email}\` first.`
|
|
400
|
+
: "No pending Ask The W login request. Run `askthew-mcp auth login --email <email>` first.");
|
|
401
|
+
}
|
|
402
|
+
if (subcommand === "login") {
|
|
403
|
+
log("Using the pending Ask The W login request. Next time, run `askthew-mcp auth verify --code <6-digit-code>`.");
|
|
404
|
+
}
|
|
405
|
+
const credentials = await verifyCode({
|
|
406
|
+
requestId: pending.requestId,
|
|
407
|
+
code,
|
|
408
|
+
telemetryOptOut: pending.telemetryOptOut,
|
|
409
|
+
});
|
|
410
|
+
clearPendingAuth();
|
|
411
|
+
log(`Logged in. Account status: ${credentials.accountStatus}. Credentials stored with mode 0600.`);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
217
414
|
if (!email)
|
|
218
415
|
throw new Error("Missing --email.");
|
|
219
416
|
const noTelemetry = argv.includes("--no-telemetry");
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
417
|
+
const identity = ensureLocalIdentity({ emailClaim: email, telemetryOptOut: noTelemetry });
|
|
418
|
+
const registration = await registerInstall({
|
|
419
|
+
identity,
|
|
420
|
+
deviceLabel: "askthew-mcp",
|
|
421
|
+
});
|
|
422
|
+
log(`Local free install identified as ${identity.installId}.`);
|
|
423
|
+
log(`Email claim: ${identity.emailClaim ?? "none"} (unverified until upgrade).`);
|
|
424
|
+
log(`Claim code: ${identity.claimCode}`);
|
|
425
|
+
log(registration.ok ? "Registered install with Ask The W." : "Saved locally; cloud registration will retry later.");
|
|
426
|
+
log("No email code is required for free local capture.");
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
async function runIdentifyCommand(argv) {
|
|
430
|
+
const email = argValue(argv, "--email")?.trim() || detectLoginEmail();
|
|
431
|
+
const noTelemetry = argv.includes("--no-telemetry");
|
|
432
|
+
if (!email)
|
|
433
|
+
throw new Error("Missing --email.");
|
|
434
|
+
const identity = ensureLocalIdentity({ emailClaim: email, telemetryOptOut: noTelemetry });
|
|
435
|
+
const registration = await tryRegisterFreeInstall({
|
|
436
|
+
identity,
|
|
437
|
+
deviceLabel: "askthew-mcp",
|
|
232
438
|
});
|
|
233
|
-
console.log(`
|
|
439
|
+
console.log(`Local free install identified as ${identity.installId}.`);
|
|
440
|
+
console.log(`Email claim: ${identity.emailClaim ?? "none"} (unverified until upgrade).`);
|
|
441
|
+
console.log(`Claim code: ${identity.claimCode}`);
|
|
442
|
+
console.log(registration.ok ? "Registered install with Ask The W." : "Saved locally; cloud registration will retry later.");
|
|
443
|
+
}
|
|
444
|
+
async function runIdentityCommand(argv) {
|
|
445
|
+
const [subcommand] = argv;
|
|
446
|
+
if (subcommand !== "status") {
|
|
447
|
+
throw new Error("Usage: askthew-mcp identity status");
|
|
448
|
+
}
|
|
449
|
+
const identity = loadLocalIdentity();
|
|
450
|
+
if (!identity) {
|
|
451
|
+
console.log("No local free install identity yet.");
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
console.log(describeFreeIdentity(publicIdentity(identity)));
|
|
455
|
+
console.log("Email claims are attribution only; upgrade/import requires local possession proof.");
|
|
234
456
|
}
|
|
235
457
|
async function runTelemetryCommand(argv) {
|
|
236
458
|
const [subcommand] = argv;
|
|
237
459
|
const credentials = loadCliCredentials();
|
|
238
460
|
if (!credentials)
|
|
239
|
-
throw new Error("
|
|
461
|
+
throw new Error("No local identity. Run `askthew-mcp identify --email <your-email>` first.");
|
|
240
462
|
if (subcommand === "status") {
|
|
241
463
|
console.log(`Telemetry: ${credentials.telemetryOptOut ? "off" : "on"}`);
|
|
242
464
|
return;
|
|
243
465
|
}
|
|
244
466
|
if (subcommand === "opt-out" || subcommand === "opt-in") {
|
|
467
|
+
if (credentials.identityKind === "local_install" && credentials.localIdentity) {
|
|
468
|
+
ensureLocalIdentity({ telemetryOptOut: subcommand === "opt-out" });
|
|
469
|
+
console.log(`Telemetry: ${subcommand === "opt-out" ? "off" : "on"}`);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
245
472
|
const next = { ...credentials, telemetryOptOut: subcommand === "opt-out" };
|
|
246
473
|
fs.writeFileSync(credentialsPath(), `${JSON.stringify(next, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
247
474
|
fs.chmodSync(credentialsPath(), 0o600);
|
|
@@ -270,12 +497,38 @@ async function runLocalCommand(argv) {
|
|
|
270
497
|
}
|
|
271
498
|
throw new Error("Usage: askthew-mcp local stats | reset --hard");
|
|
272
499
|
}
|
|
500
|
+
async function runInstallHookCommand(argv) {
|
|
501
|
+
if (!argv.includes("--pre-commit")) {
|
|
502
|
+
throw new Error("Usage: askthew-mcp install-hook --pre-commit");
|
|
503
|
+
}
|
|
504
|
+
const hookPath = installPreCommitHook();
|
|
505
|
+
console.log(`Ask The W pre-commit hook installed: ${hookPath}`);
|
|
506
|
+
}
|
|
507
|
+
async function runHookCheckCommand(argv) {
|
|
508
|
+
if (!argv.includes("--pre-commit")) {
|
|
509
|
+
throw new Error("Usage: askthew-mcp hook-check --pre-commit");
|
|
510
|
+
}
|
|
511
|
+
const store = LocalStore.open();
|
|
512
|
+
const gap = preCommitDecisionGap({ store, stagedFiles: stagedFiles(), scopeKey: localScopeKey() });
|
|
513
|
+
if (gap.missing) {
|
|
514
|
+
console.log('Ask The W: this change has no decision attached, draft one?');
|
|
515
|
+
console.log("Run: npx @askthew/mcp-plugin digest --weekly or ask your agent to call promote_signal_to_decision.");
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
async function runDigestCommand(argv) {
|
|
519
|
+
if (!argv.includes("--weekly")) {
|
|
520
|
+
throw new Error("Usage: askthew-mcp digest --weekly");
|
|
521
|
+
}
|
|
522
|
+
const store = LocalStore.open();
|
|
523
|
+
const filePath = writeWeeklyDigest({ store });
|
|
524
|
+
console.log(`Weekly decision digest written: ${filePath}`);
|
|
525
|
+
}
|
|
273
526
|
async function runSyncCommand(argv) {
|
|
274
527
|
if (argv[0] !== "upload")
|
|
275
528
|
throw new Error("Usage: askthew-mcp sync upload [--dry-run]");
|
|
276
529
|
const credentials = loadCliCredentials();
|
|
277
530
|
if (!credentials)
|
|
278
|
-
throw new Error("
|
|
531
|
+
throw new Error("No local identity. Run `askthew-mcp identify --email <your-email>` first.");
|
|
279
532
|
const store = LocalStore.open();
|
|
280
533
|
if (argv.includes("--dry-run")) {
|
|
281
534
|
console.log(JSON.stringify(syncDryRun(store), null, 2));
|
|
@@ -283,12 +536,15 @@ async function runSyncCommand(argv) {
|
|
|
283
536
|
}
|
|
284
537
|
console.log(JSON.stringify(await uploadLocalStore({ store, credentials }), null, 2));
|
|
285
538
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
539
|
+
const isDirectCliExecution = Boolean(process.argv[1]) && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
540
|
+
if (isDirectCliExecution) {
|
|
541
|
+
main().catch((error) => {
|
|
542
|
+
if (error instanceof Error) {
|
|
543
|
+
console.error(error.message);
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
console.error("Ask The W plugin failed to start.", error);
|
|
547
|
+
}
|
|
548
|
+
process.exit(1);
|
|
549
|
+
});
|
|
550
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|