@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/README.md
CHANGED
|
@@ -1,31 +1,55 @@
|
|
|
1
|
-
# Ask The W
|
|
1
|
+
# Ask The W Plugin
|
|
2
2
|
|
|
3
|
-
Connect a local coding agent to Ask The W.
|
|
3
|
+
Connect a local coding agent to Ask The W. The fastest path is free and local-first:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx -y --prefer-online @askthew/mcp-plugin@latest install --host claude_code --free --email you@founder.com
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
This captures decisions and session signals to `~/.askthew/store.sqlite` and lets your agent run `review_decisions`, `review_session`, `recap`, `coach`, and `promote_signal_to_decision` without onboarding into the web app.
|
|
10
|
+
|
|
11
|
+
Founder-friendly promise: install from npm, then ask your coding agent to review the last session. You should see value in under 60 seconds.
|
|
4
12
|
|
|
5
13
|
This package runs a small MCP server that lets Codex, Claude Code, Cursor, and other MCP-capable tools send compact work-session signals to an Ask The W workspace.
|
|
6
14
|
|
|
7
15
|
## What It Does
|
|
8
16
|
|
|
9
|
-
- Installs an Ask The W
|
|
17
|
+
- Installs an Ask The W plugin server entry into a supported local client.
|
|
10
18
|
- Preserves existing MCP servers and settings.
|
|
11
19
|
- Adds marked project instructions so future coding-agent sessions know when to send Ask The W updates.
|
|
12
|
-
-
|
|
13
|
-
-
|
|
20
|
+
- Free mode stores full-fidelity signals and decisions locally in SQLite.
|
|
21
|
+
- Paid workspace mode sends a startup heartbeat so Ask The W can show the plugin as installed.
|
|
22
|
+
- Exposes `capture_session_signal` plus v1 API tools for decisions, outcomes, signals, outcome detail graphs, and north star reads or updates.
|
|
14
23
|
- Redacts obvious secrets from summaries, evidence excerpts, commands, and metadata before sending.
|
|
15
24
|
- Adds lightweight workspace metadata such as host type, repo name, app path, and server name.
|
|
16
25
|
|
|
17
26
|
## What It Does Not Do
|
|
18
27
|
|
|
19
28
|
- It does not send full transcripts by default.
|
|
20
|
-
- It does not
|
|
29
|
+
- It does not send local free-tier content to Ask The W unless you upgrade and run sync upload.
|
|
21
30
|
- It does not link outcomes, score confidence, dedupe signals, or update the graph locally.
|
|
22
31
|
- It does not include the Ask The W app, private server code, Supabase code, or internal analytics code.
|
|
23
32
|
|
|
24
33
|
Ask The W performs inference, linking, approval state, dedupe, and outcome updates in the app.
|
|
25
34
|
|
|
26
|
-
## Install
|
|
35
|
+
## Free Local Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx -y --prefer-online @askthew/mcp-plugin@latest install \
|
|
39
|
+
--host claude_code \
|
|
40
|
+
--free \
|
|
41
|
+
--email you@founder.com
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Free install is local-first: it generates `~/.askthew/identity.json`, writes MCP config and agent instructions immediately, and never requires an email code before local capture works. The email is an unverified claim used for upgrade onboarding only; data is keyed by the generated install ID, not email alone.
|
|
45
|
+
|
|
46
|
+
Telemetry is aggregate-only and opt-out. Ask The W does not receive code, file contents, file paths, file names, command text, summaries, or decision content in free mode telemetry. Aggregate summaries are signed by the local install identity and stored under the generated install ID. Opt out with `--no-telemetry`, `ASKTHEW_TELEMETRY=off`, or `askthew-mcp telemetry opt-out`.
|
|
27
47
|
|
|
28
|
-
|
|
48
|
+
## Workspace Install
|
|
49
|
+
|
|
50
|
+
Create a workspace token in Ask The W at `/decisions/settings/connectors`, then run the installer from your coding agent or terminal. Treat the token like a password; anyone with it can write compact source signals into that workspace.
|
|
51
|
+
|
|
52
|
+
Use setup tokens promptly. They expire after 24 hours if the connector never sends activity. Once connected, active tokens renew while in use and expire after 90 days without activity. Rotate the token in Ask The W if it is exposed or if the connector reports an expired or revoked token.
|
|
29
53
|
|
|
30
54
|
Codex:
|
|
31
55
|
|
|
@@ -57,15 +81,17 @@ npx -y --prefer-online @askthew/mcp-plugin@latest install \
|
|
|
57
81
|
--server-name "askthew"
|
|
58
82
|
```
|
|
59
83
|
|
|
60
|
-
After install, restart or reload your coding agent if needed.
|
|
84
|
+
After install, restart or reload your coding agent if needed. At the start of every new coding-agent session in this repo, before plan mode or exploration, call `capture_session_signal` with `kind: "setup_complete"` and choose "Always allow" if the host prompts for tool permission. If you realize later that the startup call was missed, send it immediately with `metadata.recovered_missed_startup=true`.
|
|
85
|
+
|
|
86
|
+
Claude Desktop and Cowork custom connectors use Ask The W's hosted Remote MCP URL instead of this local `npx` installer. In Ask The W, create a Claude Remote URL from the plugin source, then paste it into Claude's custom connector form as the Remote MCP server URL. The URL must be public HTTPS; `localhost`, `127.0.0.1`, LAN IPs, VPN-only hosts, and firewall-blocked servers will fail because Claude connects from Anthropic's cloud. Treat the URL like a password and rotate it if it leaks.
|
|
61
87
|
|
|
62
|
-
The installer also adds safe, marked project instructions:
|
|
88
|
+
The installer also adds safe, marked project instructions to both markdown convention files plus any host-specific rule file:
|
|
63
89
|
|
|
64
90
|
- Codex: `AGENTS.md`
|
|
65
91
|
- Claude Code: `CLAUDE.md`
|
|
66
92
|
- Cursor: `.cursor/rules/askthew.mdc`
|
|
67
93
|
|
|
68
|
-
These instructions tell the coding agent to send compact Ask The W updates after meaningful direction changes, implementation work, verification, long-session checkpoints, and final summaries. Existing instruction files are preserved.
|
|
94
|
+
These instructions tell the coding agent to send compact Ask The W updates at the start of every new repo session, after meaningful direction changes, implementation work, verification, long-session checkpoints, and final summaries. Existing instruction files are preserved.
|
|
69
95
|
|
|
70
96
|
To skip this behavior, pass:
|
|
71
97
|
|
|
@@ -116,7 +142,7 @@ Optional environment variables:
|
|
|
116
142
|
|
|
117
143
|
## Tool Contract
|
|
118
144
|
|
|
119
|
-
The
|
|
145
|
+
The session-signal tool remains the main automatic capture path.
|
|
120
146
|
|
|
121
147
|
```json
|
|
122
148
|
{
|
|
@@ -129,19 +155,42 @@ The public tool surface is intentionally small.
|
|
|
129
155
|
"evidence": [{ "role": "user | assistant | system", "excerpt": "string" }],
|
|
130
156
|
"filesTouched": ["string"],
|
|
131
157
|
"commandsRun": ["string"],
|
|
132
|
-
"metadata": {}
|
|
158
|
+
"metadata": {},
|
|
159
|
+
"echo": "summary | full"
|
|
133
160
|
}
|
|
134
161
|
}
|
|
135
162
|
```
|
|
136
163
|
|
|
137
|
-
Use compact summaries and short evidence excerpts. Do not send full transcripts.
|
|
164
|
+
Use compact summaries and short evidence excerpts. Do not send full transcripts. By default, write tools return compact responses; use `echo: "full"` only when you need the larger payload for debugging.
|
|
165
|
+
|
|
166
|
+
The plugin also exposes v1 API tools that map to the app's authenticated routes:
|
|
167
|
+
|
|
168
|
+
| Tool | Purpose |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `list_decisions`, `get_decision`, `create_decision`, `update_decision`, `delete_decision` | Work with decision feed entries. |
|
|
171
|
+
| `review_session`, `recap`, `coach`, `promote_signal_to_decision`, `find_signal_by_summary` | Review local sessions, get session coaching, find recent evidence, and turn signals into decisions in free mode. |
|
|
172
|
+
| `list_outcomes`, `get_outcome`, `list_outcome_signals`, `create_outcome`, `update_outcome`, `delete_outcome` | Work with outcomes and their linked signals. |
|
|
173
|
+
| `get_north_star`, `update_north_star` | Read or update the workspace north star. API updates are allowed only for private workspaces. |
|
|
174
|
+
| `list_signals`, `get_signal` | Read workspace signals. |
|
|
175
|
+
|
|
176
|
+
Free PLG helpers:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npx @askthew/mcp-plugin install-hook --pre-commit
|
|
180
|
+
npx @askthew/mcp-plugin digest --weekly
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The hook prompts when staged files recently had implementation signals but no linked decision. The weekly digest writes `~/Documents/askthew-digest-YYYY-WW.md`.
|
|
184
|
+
|
|
185
|
+
API mutations are text-only and are recorded back into the workspace signal feed. Decision and outcome deletes require a `confirmText` value that exactly matches the stored decision headline or outcome name after whitespace normalization. North star delete is not available through the API.
|
|
138
186
|
|
|
139
187
|
## Troubleshooting
|
|
140
188
|
|
|
141
189
|
- Empty `list_mcp_resources` or `list_mcp_resource_templates` results are normal. This connector is tool-driven.
|
|
142
190
|
- If Ask The W shows "Waiting for install", restart or reload your coding agent.
|
|
143
|
-
- If Ask The W shows "Installed", restart or reload your coding agent if it was already open.
|
|
144
|
-
-
|
|
191
|
+
- If Ask The W shows "Installed", restart or reload your coding agent if it was already open. At the start of the next repo session, the coding agent should call `capture_session_signal` with `kind: "setup_complete"` before plan mode; choose "Always allow" if it asks for tool permission.
|
|
192
|
+
- For Claude Desktop or Cowork, set `capture_session_signal` to "Always allow" in connector Tool permissions when that panel is available, or choose "Allow always" on the first tool prompt.
|
|
193
|
+
- If a token fails, check whether the error says missing, malformed, expired, or revoked. Copy the full token if it is malformed; rotate it in Ask The W and rerun the installer if it expired or was revoked.
|
|
145
194
|
|
|
146
195
|
## Development
|
|
147
196
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { clearPendingAuth, pendingAuth, pendingAuthForEmail, savePendingAuth } from "./lib/auth-pending.js";
|
|
7
|
+
import { configPath } from "./lib/paths.js";
|
|
8
|
+
function withTempEnv(fn) {
|
|
9
|
+
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "askthew-auth-pending-"));
|
|
10
|
+
try {
|
|
11
|
+
return fn({ ASKTHEW_DATA_DIR: dataDir });
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
test("pending auth stores and resolves the request id for the matching email", () => {
|
|
18
|
+
withTempEnv((env) => {
|
|
19
|
+
savePendingAuth({
|
|
20
|
+
email: "Founder@Example.com",
|
|
21
|
+
requestId: "request_1",
|
|
22
|
+
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
23
|
+
telemetryOptOut: true,
|
|
24
|
+
}, env);
|
|
25
|
+
const pending = pendingAuthForEmail("founder@example.com", env);
|
|
26
|
+
assert.equal(pending?.requestId, "request_1");
|
|
27
|
+
assert.equal(pending?.telemetryOptOut, true);
|
|
28
|
+
assert.equal(pendingAuth(env)?.email, "Founder@Example.com");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
test("pending auth ignores other emails and clears expired requests", () => {
|
|
32
|
+
withTempEnv((env) => {
|
|
33
|
+
savePendingAuth({
|
|
34
|
+
email: "founder@example.com",
|
|
35
|
+
requestId: "request_1",
|
|
36
|
+
expiresAt: new Date(Date.now() - 1_000).toISOString(),
|
|
37
|
+
}, env);
|
|
38
|
+
assert.equal(pendingAuthForEmail("other@example.com", env), null);
|
|
39
|
+
assert.equal(pendingAuthForEmail("founder@example.com", env), null);
|
|
40
|
+
const config = JSON.parse(fs.readFileSync(configPath(env), "utf8"));
|
|
41
|
+
assert.equal("pendingAuth" in config, false);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
test("pending auth clear keeps the config file private and removes only pending auth", () => {
|
|
45
|
+
withTempEnv((env) => {
|
|
46
|
+
savePendingAuth({
|
|
47
|
+
email: "founder@example.com",
|
|
48
|
+
requestId: "request_1",
|
|
49
|
+
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
50
|
+
}, env);
|
|
51
|
+
clearPendingAuth(env);
|
|
52
|
+
const config = JSON.parse(fs.readFileSync(configPath(env), "utf8"));
|
|
53
|
+
assert.equal("pendingAuth" in config, false);
|
|
54
|
+
assert.equal((fs.statSync(configPath(env)).mode & 0o777), 0o600);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { LocalStore } from "./lib/local-store.js";
|
|
8
|
+
import { buildWeeklyDigest, installPreCommitHook, preCommitDecisionGap, writeWeeklyDigest, } from "./lib/cli-actions.js";
|
|
9
|
+
test("pre-commit gap detects staged implementation updates without linked decisions", () => {
|
|
10
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "askthew-hook-store-"));
|
|
11
|
+
const store = LocalStore.open({ path: path.join(dir, "store.sqlite") });
|
|
12
|
+
const signal = store.insertSignal({
|
|
13
|
+
sessionId: "s1",
|
|
14
|
+
sequence: 1,
|
|
15
|
+
kind: "implementation_update",
|
|
16
|
+
summary: "Changed onboarding page.",
|
|
17
|
+
filesTouched: ["apps/app/page.tsx"],
|
|
18
|
+
commandsRun: [],
|
|
19
|
+
});
|
|
20
|
+
const gap = preCommitDecisionGap({
|
|
21
|
+
store,
|
|
22
|
+
stagedFiles: ["apps/app/page.tsx"],
|
|
23
|
+
now: new Date(signal.capturedAt),
|
|
24
|
+
});
|
|
25
|
+
assert.equal(gap.missing, true);
|
|
26
|
+
assert.deepEqual(gap.matchedSignals, [signal.id]);
|
|
27
|
+
store.createDecision({
|
|
28
|
+
rawContent: "Keep onboarding page copy direct.",
|
|
29
|
+
sessionId: "s1",
|
|
30
|
+
sourceSignalIds: [signal.id],
|
|
31
|
+
});
|
|
32
|
+
const resolved = preCommitDecisionGap({
|
|
33
|
+
store,
|
|
34
|
+
stagedFiles: ["apps/app/page.tsx"],
|
|
35
|
+
now: new Date(signal.capturedAt),
|
|
36
|
+
});
|
|
37
|
+
assert.equal(resolved.missing, false);
|
|
38
|
+
store.close();
|
|
39
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
40
|
+
});
|
|
41
|
+
test("pre-commit hook installer writes the documented inline prompt hook", () => {
|
|
42
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "askthew-hook-install-"));
|
|
43
|
+
execFileSync("git", ["init"], { cwd: dir, stdio: "ignore" });
|
|
44
|
+
const hookPath = installPreCommitHook({ cwd: dir });
|
|
45
|
+
const hook = fs.readFileSync(hookPath, "utf8");
|
|
46
|
+
assert.match(hook, /Ask The W pre-commit decision prompt/);
|
|
47
|
+
assert.match(hook, /@askthew\/mcp-plugin@latest hook-check --pre-commit/);
|
|
48
|
+
assert.equal((fs.statSync(hookPath).mode & 0o111) > 0, true);
|
|
49
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
50
|
+
});
|
|
51
|
+
test("weekly digest writes markdown with documented footer", () => {
|
|
52
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "askthew-digest-store-"));
|
|
53
|
+
const outDir = fs.mkdtempSync(path.join(os.tmpdir(), "askthew-digest-out-"));
|
|
54
|
+
const store = LocalStore.open({ path: path.join(dir, "store.sqlite") });
|
|
55
|
+
store.createDecision({
|
|
56
|
+
rawContent: "Ship the free-tier polish.",
|
|
57
|
+
sessionId: "s1",
|
|
58
|
+
status: "committed",
|
|
59
|
+
});
|
|
60
|
+
const now = new Date("2026-05-06T12:00:00.000Z");
|
|
61
|
+
const digest = buildWeeklyDigest({ store, now });
|
|
62
|
+
assert.match(digest, /# Ask The W Weekly Decision Digest 2026-19/);
|
|
63
|
+
assert.match(digest, /Ship the free-tier polish/);
|
|
64
|
+
assert.match(digest, /_Captured by Ask The W\._/);
|
|
65
|
+
const filePath = writeWeeklyDigest({ store, now, outputDir: outDir });
|
|
66
|
+
assert.equal(path.basename(filePath), "askthew-digest-2026-19.md");
|
|
67
|
+
assert.match(fs.readFileSync(filePath, "utf8"), /_Captured by Ask The W\._/);
|
|
68
|
+
store.close();
|
|
69
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
70
|
+
fs.rmSync(outDir, { recursive: true, force: true });
|
|
71
|
+
});
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { requestMagicLinkCode as requestMagicLinkCodeDefault, verifyMagicLinkCode as verifyMagicLinkCodeDefault } from "./lib/auth-magic-link.js";
|
|
3
|
+
import { tryRegisterFreeInstall } from "./lib/free-install-registration.js";
|
|
4
|
+
type AuthCommandDeps = {
|
|
5
|
+
log?: (message: string) => void;
|
|
6
|
+
requestMagicLinkCode?: typeof requestMagicLinkCodeDefault;
|
|
7
|
+
verifyMagicLinkCode?: typeof verifyMagicLinkCodeDefault;
|
|
8
|
+
registerFreeInstall?: typeof tryRegisterFreeInstall;
|
|
9
|
+
};
|
|
10
|
+
export declare function runAuthCommand(argv: string[], deps?: AuthCommandDeps): Promise<void>;
|
|
2
11
|
export {};
|