gsd-pi 2.67.0-dev.509bd95 → 2.67.0-dev.6fc2289
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 +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +152 -70
- package/dist/resources/extensions/gsd/auto/session.js +10 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
- package/dist/resources/extensions/gsd/auto-start.js +16 -30
- package/dist/resources/extensions/gsd/auto-worktree.js +62 -15
- package/dist/resources/extensions/gsd/auto.js +121 -59
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +7 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -1
- package/dist/resources/extensions/gsd/commands-mcp-status.js +43 -7
- package/dist/resources/extensions/gsd/doctor-git-checks.js +4 -4
- package/dist/resources/extensions/gsd/doctor-proactive.js +3 -3
- package/dist/resources/extensions/gsd/doctor.js +8 -4
- package/dist/resources/extensions/gsd/guided-flow.js +40 -31
- package/dist/resources/extensions/gsd/init-wizard.js +37 -0
- package/dist/resources/extensions/gsd/interrupted-session.js +146 -0
- package/dist/resources/extensions/gsd/mcp-project-config.js +83 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +90 -19
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/{page-0c485498795110d6.js → page-f1e30ab6bb269149.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{webpack-b49b09f97429b5d0.js → webpack-6e4d7e9a4f57bed4.js} +1 -1
- package/package.json +4 -2
- package/packages/mcp-server/dist/cli.d.ts +9 -0
- package/packages/mcp-server/dist/cli.d.ts.map +1 -0
- package/packages/mcp-server/dist/cli.js +58 -0
- package/packages/mcp-server/dist/cli.js.map +1 -0
- package/packages/mcp-server/dist/index.d.ts +20 -0
- package/packages/mcp-server/dist/index.d.ts.map +1 -0
- package/packages/mcp-server/dist/index.js +14 -0
- package/packages/mcp-server/dist/index.js.map +1 -0
- package/packages/mcp-server/dist/readers/captures.d.ts +25 -0
- package/packages/mcp-server/dist/readers/captures.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/captures.js +67 -0
- package/packages/mcp-server/dist/readers/captures.js.map +1 -0
- package/packages/mcp-server/dist/readers/doctor-lite.d.ts +20 -0
- package/packages/mcp-server/dist/readers/doctor-lite.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/doctor-lite.js +173 -0
- package/packages/mcp-server/dist/readers/doctor-lite.js.map +1 -0
- package/packages/mcp-server/dist/readers/index.d.ts +14 -0
- package/packages/mcp-server/dist/readers/index.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/index.js +10 -0
- package/packages/mcp-server/dist/readers/index.js.map +1 -0
- package/packages/mcp-server/dist/readers/knowledge.d.ts +18 -0
- package/packages/mcp-server/dist/readers/knowledge.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/knowledge.js +82 -0
- package/packages/mcp-server/dist/readers/knowledge.js.map +1 -0
- package/packages/mcp-server/dist/readers/metrics.d.ts +32 -0
- package/packages/mcp-server/dist/readers/metrics.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/metrics.js +74 -0
- package/packages/mcp-server/dist/readers/metrics.js.map +1 -0
- package/packages/mcp-server/dist/readers/paths.d.ts +42 -0
- package/packages/mcp-server/dist/readers/paths.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/paths.js +199 -0
- package/packages/mcp-server/dist/readers/paths.js.map +1 -0
- package/packages/mcp-server/dist/readers/roadmap.d.ts +26 -0
- package/packages/mcp-server/dist/readers/roadmap.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/roadmap.js +194 -0
- package/packages/mcp-server/dist/readers/roadmap.js.map +1 -0
- package/packages/mcp-server/dist/readers/state.d.ts +43 -0
- package/packages/mcp-server/dist/readers/state.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/state.js +184 -0
- package/packages/mcp-server/dist/readers/state.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +28 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -0
- package/packages/mcp-server/dist/server.js +319 -0
- package/packages/mcp-server/dist/server.js.map +1 -0
- package/packages/mcp-server/dist/session-manager.d.ts +54 -0
- package/packages/mcp-server/dist/session-manager.d.ts.map +1 -0
- package/packages/mcp-server/dist/session-manager.js +284 -0
- package/packages/mcp-server/dist/session-manager.js.map +1 -0
- package/packages/mcp-server/dist/types.d.ts +61 -0
- package/packages/mcp-server/dist/types.d.ts.map +1 -0
- package/packages/mcp-server/dist/types.js +11 -0
- package/packages/mcp-server/dist/types.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts +9 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.js +532 -0
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -0
- package/packages/mcp-server/src/workflow-tools.ts +13 -2
- package/packages/mcp-server/tsconfig.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +14 -6
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +53 -0
- package/packages/pi-agent-core/src/agent-loop.ts +20 -6
- package/packages/pi-coding-agent/dist/core/contextual-tips.d.ts +43 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.js +208 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.js +227 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/index.js +1 -0
- package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +28 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +19 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +14 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +15 -12
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/contextual-tips.test.ts +259 -0
- package/packages/pi-coding-agent/src/core/contextual-tips.ts +232 -0
- package/packages/pi-coding-agent/src/core/index.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +54 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -12
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +21 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +19 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +19 -15
- package/packages/rpc-client/dist/index.d.ts +10 -0
- package/packages/rpc-client/dist/index.d.ts.map +1 -0
- package/packages/rpc-client/dist/index.js +9 -0
- package/packages/rpc-client/dist/index.js.map +1 -0
- package/packages/rpc-client/dist/jsonl.d.ts +17 -0
- package/packages/rpc-client/dist/jsonl.d.ts.map +1 -0
- package/packages/rpc-client/dist/jsonl.js +54 -0
- package/packages/rpc-client/dist/jsonl.js.map +1 -0
- package/packages/rpc-client/dist/rpc-client.d.ts +259 -0
- package/packages/rpc-client/dist/rpc-client.d.ts.map +1 -0
- package/packages/rpc-client/dist/rpc-client.js +541 -0
- package/packages/rpc-client/dist/rpc-client.js.map +1 -0
- package/packages/rpc-client/dist/rpc-client.test.d.ts +2 -0
- package/packages/rpc-client/dist/rpc-client.test.d.ts.map +1 -0
- package/packages/rpc-client/dist/rpc-client.test.js +477 -0
- package/packages/rpc-client/dist/rpc-client.test.js.map +1 -0
- package/packages/rpc-client/dist/rpc-types.d.ts +566 -0
- package/packages/rpc-client/dist/rpc-types.d.ts.map +1 -0
- package/packages/rpc-client/dist/rpc-types.js +12 -0
- package/packages/rpc-client/dist/rpc-types.js.map +1 -0
- package/scripts/ensure-workspace-builds.cjs +2 -0
- package/scripts/link-workspace-packages.cjs +21 -14
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +190 -93
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +89 -116
- package/src/resources/extensions/gsd/auto/session.ts +10 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
- package/src/resources/extensions/gsd/auto-start.ts +23 -55
- package/src/resources/extensions/gsd/auto-worktree.ts +59 -15
- package/src/resources/extensions/gsd/auto.ts +133 -64
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +8 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -1
- package/src/resources/extensions/gsd/commands-mcp-status.ts +53 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +4 -4
- package/src/resources/extensions/gsd/doctor-proactive.ts +3 -3
- package/src/resources/extensions/gsd/doctor.ts +9 -5
- package/src/resources/extensions/gsd/guided-flow.ts +42 -36
- package/src/resources/extensions/gsd/init-wizard.ts +40 -0
- package/src/resources/extensions/gsd/interrupted-session.ts +224 -0
- package/src/resources/extensions/gsd/mcp-project-config.ts +128 -0
- package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +668 -2
- package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +14 -4
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +380 -2
- package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/integration/doctor-fixlevel.test.ts +52 -1
- package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +2 -9
- package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +0 -33
- package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +169 -0
- package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +146 -0
- package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +136 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +212 -13
- package/src/resources/extensions/gsd/workflow-mcp.ts +106 -19
- package/dist/web/standalone/.next/static/chunks/6502.b804e48b7919f55e.js +0 -9
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.d.ts +0 -13
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.js +0 -27
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.js.map +0 -1
- package/packages/pi-coding-agent/src/modes/interactive/provider-auth-setup.ts +0 -40
- /package/dist/web/standalone/.next/static/{mHJZ3Z8yGRzZ32BmQs-I7 → yh2vT27L1E6PChb_C1N_F}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{mHJZ3Z8yGRzZ32BmQs-I7 → yh2vT27L1E6PChb_C1N_F}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contextual tips system — shows non-intrusive, session-scoped hints
|
|
3
|
+
* when user behavior suggests they'd benefit from knowing a feature.
|
|
4
|
+
*
|
|
5
|
+
* Each tip fires at most `maxShows` times per session. Tips are
|
|
6
|
+
* evaluated in order; the first match wins per input event.
|
|
7
|
+
*/
|
|
8
|
+
// Shell commands that obviously run locally and don't need the LLM.
|
|
9
|
+
// Intentionally conservative — these are unambiguous filesystem/info commands.
|
|
10
|
+
const LOCAL_SHELL_COMMANDS = new Set([
|
|
11
|
+
"ls",
|
|
12
|
+
"ll",
|
|
13
|
+
"la",
|
|
14
|
+
"pwd",
|
|
15
|
+
"cd",
|
|
16
|
+
"dir",
|
|
17
|
+
"cat",
|
|
18
|
+
"head",
|
|
19
|
+
"tail",
|
|
20
|
+
"wc",
|
|
21
|
+
"file",
|
|
22
|
+
"which",
|
|
23
|
+
"whoami",
|
|
24
|
+
"echo",
|
|
25
|
+
"date",
|
|
26
|
+
"tree",
|
|
27
|
+
"find",
|
|
28
|
+
"grep",
|
|
29
|
+
"rg",
|
|
30
|
+
"clear",
|
|
31
|
+
"env",
|
|
32
|
+
"df",
|
|
33
|
+
"du",
|
|
34
|
+
"uname",
|
|
35
|
+
"hostname",
|
|
36
|
+
"mkdir",
|
|
37
|
+
"rm",
|
|
38
|
+
"cp",
|
|
39
|
+
"mv",
|
|
40
|
+
"touch",
|
|
41
|
+
"chmod",
|
|
42
|
+
"less",
|
|
43
|
+
"more",
|
|
44
|
+
"sort",
|
|
45
|
+
"uniq",
|
|
46
|
+
"sed",
|
|
47
|
+
"awk",
|
|
48
|
+
"curl",
|
|
49
|
+
"wget",
|
|
50
|
+
"tar",
|
|
51
|
+
"zip",
|
|
52
|
+
"unzip",
|
|
53
|
+
"git",
|
|
54
|
+
"docker",
|
|
55
|
+
"npm",
|
|
56
|
+
"npx",
|
|
57
|
+
"yarn",
|
|
58
|
+
"pnpm",
|
|
59
|
+
"node",
|
|
60
|
+
"python",
|
|
61
|
+
"python3",
|
|
62
|
+
"pip",
|
|
63
|
+
"pip3",
|
|
64
|
+
"make",
|
|
65
|
+
"cargo",
|
|
66
|
+
"go",
|
|
67
|
+
"ruby",
|
|
68
|
+
"brew",
|
|
69
|
+
]);
|
|
70
|
+
/**
|
|
71
|
+
* Extract the first token from input, ignoring leading whitespace.
|
|
72
|
+
* Returns lowercase for case-insensitive matching.
|
|
73
|
+
*/
|
|
74
|
+
function firstToken(input) {
|
|
75
|
+
const trimmed = input.trimStart();
|
|
76
|
+
const spaceIdx = trimmed.search(/\s/);
|
|
77
|
+
const token = spaceIdx === -1 ? trimmed : trimmed.slice(0, spaceIdx);
|
|
78
|
+
return token.toLowerCase();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check if input looks like a bare shell command (no !, //, or slash prefix).
|
|
82
|
+
*/
|
|
83
|
+
function looksLikeShellCommand(input) {
|
|
84
|
+
const trimmed = input.trimStart();
|
|
85
|
+
// Already prefixed — user knows what they're doing
|
|
86
|
+
if (trimmed.startsWith("!") || trimmed.startsWith("/"))
|
|
87
|
+
return false;
|
|
88
|
+
// Multi-line or very long inputs are probably prompts
|
|
89
|
+
if (trimmed.includes("\n") || trimmed.length > 120)
|
|
90
|
+
return false;
|
|
91
|
+
return LOCAL_SHELL_COMMANDS.has(firstToken(trimmed));
|
|
92
|
+
}
|
|
93
|
+
const TIPS = [
|
|
94
|
+
// 1. Shell command reminder
|
|
95
|
+
{
|
|
96
|
+
id: "shell-command-prefix",
|
|
97
|
+
maxShows: 2,
|
|
98
|
+
evaluate(ctx) {
|
|
99
|
+
if (!looksLikeShellCommand(ctx.input))
|
|
100
|
+
return null;
|
|
101
|
+
const cmd = firstToken(ctx.input);
|
|
102
|
+
return `Tip: "${cmd}" looks like a shell command. Prefix with ! to run locally, or !! to run without using tokens.`;
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
// 2. Large paste warning
|
|
106
|
+
{
|
|
107
|
+
id: "large-paste",
|
|
108
|
+
maxShows: 2,
|
|
109
|
+
evaluate(ctx) {
|
|
110
|
+
if (ctx.input.length < 2000)
|
|
111
|
+
return null;
|
|
112
|
+
// Slash commands and bash prefixes are intentional
|
|
113
|
+
if (ctx.input.trimStart().startsWith("/") || ctx.input.trimStart().startsWith("!"))
|
|
114
|
+
return null;
|
|
115
|
+
return "Tip: Large inputs consume many tokens. Consider saving to a file and asking the agent to read it.";
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
// 3. Thinking level awareness
|
|
119
|
+
{
|
|
120
|
+
id: "thinking-level-high",
|
|
121
|
+
maxShows: 1,
|
|
122
|
+
evaluate(ctx) {
|
|
123
|
+
const level = ctx.thinkingLevel?.toLowerCase();
|
|
124
|
+
if (level !== "high" && level !== "xhigh")
|
|
125
|
+
return null;
|
|
126
|
+
// Only fire for short, simple-looking inputs (likely simple questions)
|
|
127
|
+
const trimmed = ctx.input.trim();
|
|
128
|
+
if (trimmed.length > 80 || trimmed.includes("\n"))
|
|
129
|
+
return null;
|
|
130
|
+
// Don't fire on slash or bash commands
|
|
131
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("!"))
|
|
132
|
+
return null;
|
|
133
|
+
return `Tip: Thinking is set to ${level}. Use Ctrl+T to lower it for simple questions — saves tokens.`;
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
// 4. Double-bang reminder
|
|
137
|
+
{
|
|
138
|
+
id: "double-bang-reminder",
|
|
139
|
+
maxShows: 2,
|
|
140
|
+
evaluate(ctx) {
|
|
141
|
+
// Fire after user has run 3+ included (!) bash commands
|
|
142
|
+
if (ctx.bashIncludedCount < 3)
|
|
143
|
+
return null;
|
|
144
|
+
// Only trigger on a ! command (not !!)
|
|
145
|
+
const trimmed = ctx.input.trimStart();
|
|
146
|
+
if (!trimmed.startsWith("!") || trimmed.startsWith("!!"))
|
|
147
|
+
return null;
|
|
148
|
+
return "Tip: Use !! instead of ! to keep command output out of agent context and save tokens.";
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
// 5. Compaction nudge
|
|
152
|
+
{
|
|
153
|
+
id: "compaction-nudge",
|
|
154
|
+
maxShows: 1,
|
|
155
|
+
evaluate(ctx) {
|
|
156
|
+
if (ctx.contextPercent === undefined || ctx.contextPercent < 70)
|
|
157
|
+
return null;
|
|
158
|
+
// Don't nag on slash/bash
|
|
159
|
+
const trimmed = ctx.input.trimStart();
|
|
160
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("!"))
|
|
161
|
+
return null;
|
|
162
|
+
return "Tip: Context is getting full. Use /compact to summarize the conversation and free up space.";
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
// ─── Session-scoped tracker ──────────────────────────────────────────────────
|
|
167
|
+
export class ContextualTips {
|
|
168
|
+
constructor() {
|
|
169
|
+
/** Map of tip ID → number of times shown this session */
|
|
170
|
+
this.showCounts = new Map();
|
|
171
|
+
/** Track ! bash commands for double-bang reminder */
|
|
172
|
+
this._bashIncludedCount = 0;
|
|
173
|
+
}
|
|
174
|
+
/** Increment the bash-included counter. Call when user runs ! (not !!) command. */
|
|
175
|
+
recordBashIncluded() {
|
|
176
|
+
this._bashIncludedCount++;
|
|
177
|
+
}
|
|
178
|
+
get bashIncludedCount() {
|
|
179
|
+
return this._bashIncludedCount;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Evaluate all tips against the current input context.
|
|
183
|
+
* Returns the first matching tip message, or null if none apply.
|
|
184
|
+
*/
|
|
185
|
+
evaluate(ctx) {
|
|
186
|
+
const fullCtx = {
|
|
187
|
+
...ctx,
|
|
188
|
+
bashIncludedCount: this._bashIncludedCount,
|
|
189
|
+
};
|
|
190
|
+
for (const tip of TIPS) {
|
|
191
|
+
const shown = this.showCounts.get(tip.id) ?? 0;
|
|
192
|
+
if (shown >= tip.maxShows)
|
|
193
|
+
continue;
|
|
194
|
+
const message = tip.evaluate(fullCtx);
|
|
195
|
+
if (message) {
|
|
196
|
+
this.showCounts.set(tip.id, shown + 1);
|
|
197
|
+
return message;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
/** Reset all counters (e.g. on new session). */
|
|
203
|
+
reset() {
|
|
204
|
+
this.showCounts.clear();
|
|
205
|
+
this._bashIncludedCount = 0;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=contextual-tips.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextual-tips.js","sourceRoot":"","sources":["../../src/core/contextual-tips.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Contextual tips system — shows non-intrusive, session-scoped hints\n * when user behavior suggests they'd benefit from knowing a feature.\n *\n * Each tip fires at most `maxShows` times per session. Tips are\n * evaluated in order; the first match wins per input event.\n */\n\n// ─── Tip definitions ─────────────────────────────────────────────────────────\n\nexport interface TipContext {\n\t/** The raw input text the user submitted */\n\tinput: string;\n\t/** Whether the agent is currently streaming */\n\tisStreaming: boolean;\n\t/** Current thinking level (e.g. \"off\", \"low\", \"high\", \"xhigh\") */\n\tthinkingLevel?: string;\n\t/** Number of `!` (included) bash commands run this session */\n\tbashIncludedCount: number;\n\t/** Approximate context usage percentage (0–100), if known */\n\tcontextPercent?: number;\n}\n\nexport interface Tip {\n\tid: string;\n\t/** Maximum times this tip is shown per session */\n\tmaxShows: number;\n\t/** Returns the tip message if the tip should fire, or null to skip */\n\tevaluate: (ctx: TipContext) => string | null;\n}\n\n// Shell commands that obviously run locally and don't need the LLM.\n// Intentionally conservative — these are unambiguous filesystem/info commands.\nconst LOCAL_SHELL_COMMANDS = new Set([\n\t\"ls\",\n\t\"ll\",\n\t\"la\",\n\t\"pwd\",\n\t\"cd\",\n\t\"dir\",\n\t\"cat\",\n\t\"head\",\n\t\"tail\",\n\t\"wc\",\n\t\"file\",\n\t\"which\",\n\t\"whoami\",\n\t\"echo\",\n\t\"date\",\n\t\"tree\",\n\t\"find\",\n\t\"grep\",\n\t\"rg\",\n\t\"clear\",\n\t\"env\",\n\t\"df\",\n\t\"du\",\n\t\"uname\",\n\t\"hostname\",\n\t\"mkdir\",\n\t\"rm\",\n\t\"cp\",\n\t\"mv\",\n\t\"touch\",\n\t\"chmod\",\n\t\"less\",\n\t\"more\",\n\t\"sort\",\n\t\"uniq\",\n\t\"sed\",\n\t\"awk\",\n\t\"curl\",\n\t\"wget\",\n\t\"tar\",\n\t\"zip\",\n\t\"unzip\",\n\t\"git\",\n\t\"docker\",\n\t\"npm\",\n\t\"npx\",\n\t\"yarn\",\n\t\"pnpm\",\n\t\"node\",\n\t\"python\",\n\t\"python3\",\n\t\"pip\",\n\t\"pip3\",\n\t\"make\",\n\t\"cargo\",\n\t\"go\",\n\t\"ruby\",\n\t\"brew\",\n]);\n\n/**\n * Extract the first token from input, ignoring leading whitespace.\n * Returns lowercase for case-insensitive matching.\n */\nfunction firstToken(input: string): string {\n\tconst trimmed = input.trimStart();\n\tconst spaceIdx = trimmed.search(/\\s/);\n\tconst token = spaceIdx === -1 ? trimmed : trimmed.slice(0, spaceIdx);\n\treturn token.toLowerCase();\n}\n\n/**\n * Check if input looks like a bare shell command (no !, //, or slash prefix).\n */\nfunction looksLikeShellCommand(input: string): boolean {\n\tconst trimmed = input.trimStart();\n\t// Already prefixed — user knows what they're doing\n\tif (trimmed.startsWith(\"!\") || trimmed.startsWith(\"/\")) return false;\n\t// Multi-line or very long inputs are probably prompts\n\tif (trimmed.includes(\"\\n\") || trimmed.length > 120) return false;\n\treturn LOCAL_SHELL_COMMANDS.has(firstToken(trimmed));\n}\n\nconst TIPS: Tip[] = [\n\t// 1. Shell command reminder\n\t{\n\t\tid: \"shell-command-prefix\",\n\t\tmaxShows: 2,\n\t\tevaluate(ctx) {\n\t\t\tif (!looksLikeShellCommand(ctx.input)) return null;\n\t\t\tconst cmd = firstToken(ctx.input);\n\t\t\treturn `Tip: \"${cmd}\" looks like a shell command. Prefix with ! to run locally, or !! to run without using tokens.`;\n\t\t},\n\t},\n\n\t// 2. Large paste warning\n\t{\n\t\tid: \"large-paste\",\n\t\tmaxShows: 2,\n\t\tevaluate(ctx) {\n\t\t\tif (ctx.input.length < 2000) return null;\n\t\t\t// Slash commands and bash prefixes are intentional\n\t\t\tif (ctx.input.trimStart().startsWith(\"/\") || ctx.input.trimStart().startsWith(\"!\")) return null;\n\t\t\treturn \"Tip: Large inputs consume many tokens. Consider saving to a file and asking the agent to read it.\";\n\t\t},\n\t},\n\n\t// 3. Thinking level awareness\n\t{\n\t\tid: \"thinking-level-high\",\n\t\tmaxShows: 1,\n\t\tevaluate(ctx) {\n\t\t\tconst level = ctx.thinkingLevel?.toLowerCase();\n\t\t\tif (level !== \"high\" && level !== \"xhigh\") return null;\n\t\t\t// Only fire for short, simple-looking inputs (likely simple questions)\n\t\t\tconst trimmed = ctx.input.trim();\n\t\t\tif (trimmed.length > 80 || trimmed.includes(\"\\n\")) return null;\n\t\t\t// Don't fire on slash or bash commands\n\t\t\tif (trimmed.startsWith(\"/\") || trimmed.startsWith(\"!\")) return null;\n\t\t\treturn `Tip: Thinking is set to ${level}. Use Ctrl+T to lower it for simple questions — saves tokens.`;\n\t\t},\n\t},\n\n\t// 4. Double-bang reminder\n\t{\n\t\tid: \"double-bang-reminder\",\n\t\tmaxShows: 2,\n\t\tevaluate(ctx) {\n\t\t\t// Fire after user has run 3+ included (!) bash commands\n\t\t\tif (ctx.bashIncludedCount < 3) return null;\n\t\t\t// Only trigger on a ! command (not !!)\n\t\t\tconst trimmed = ctx.input.trimStart();\n\t\t\tif (!trimmed.startsWith(\"!\") || trimmed.startsWith(\"!!\")) return null;\n\t\t\treturn \"Tip: Use !! instead of ! to keep command output out of agent context and save tokens.\";\n\t\t},\n\t},\n\n\t// 5. Compaction nudge\n\t{\n\t\tid: \"compaction-nudge\",\n\t\tmaxShows: 1,\n\t\tevaluate(ctx) {\n\t\t\tif (ctx.contextPercent === undefined || ctx.contextPercent < 70) return null;\n\t\t\t// Don't nag on slash/bash\n\t\t\tconst trimmed = ctx.input.trimStart();\n\t\t\tif (trimmed.startsWith(\"/\") || trimmed.startsWith(\"!\")) return null;\n\t\t\treturn \"Tip: Context is getting full. Use /compact to summarize the conversation and free up space.\";\n\t\t},\n\t},\n];\n\n// ─── Session-scoped tracker ──────────────────────────────────────────────────\n\nexport class ContextualTips {\n\t/** Map of tip ID → number of times shown this session */\n\tprivate showCounts = new Map<string, number>();\n\t/** Track ! bash commands for double-bang reminder */\n\tprivate _bashIncludedCount = 0;\n\n\t/** Increment the bash-included counter. Call when user runs ! (not !!) command. */\n\trecordBashIncluded(): void {\n\t\tthis._bashIncludedCount++;\n\t}\n\n\tget bashIncludedCount(): number {\n\t\treturn this._bashIncludedCount;\n\t}\n\n\t/**\n\t * Evaluate all tips against the current input context.\n\t * Returns the first matching tip message, or null if none apply.\n\t */\n\tevaluate(ctx: Omit<TipContext, \"bashIncludedCount\">): string | null {\n\t\tconst fullCtx: TipContext = {\n\t\t\t...ctx,\n\t\t\tbashIncludedCount: this._bashIncludedCount,\n\t\t};\n\n\t\tfor (const tip of TIPS) {\n\t\t\tconst shown = this.showCounts.get(tip.id) ?? 0;\n\t\t\tif (shown >= tip.maxShows) continue;\n\n\t\t\tconst message = tip.evaluate(fullCtx);\n\t\t\tif (message) {\n\t\t\t\tthis.showCounts.set(tip.id, shown + 1);\n\t\t\t\treturn message;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/** Reset all counters (e.g. on new session). */\n\treset(): void {\n\t\tthis.showCounts.clear();\n\t\tthis._bashIncludedCount = 0;\n\t}\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextual-tips.test.d.ts","sourceRoot":"","sources":["../../src/core/contextual-tips.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { ContextualTips } from "./contextual-tips.js";
|
|
4
|
+
const baseCtx = {
|
|
5
|
+
input: "hello world",
|
|
6
|
+
isStreaming: false,
|
|
7
|
+
thinkingLevel: "off",
|
|
8
|
+
contextPercent: undefined,
|
|
9
|
+
};
|
|
10
|
+
describe("ContextualTips", () => {
|
|
11
|
+
describe("shell-command-prefix tip", () => {
|
|
12
|
+
it("fires for bare shell commands", () => {
|
|
13
|
+
const tips = new ContextualTips();
|
|
14
|
+
const result = tips.evaluate({ ...baseCtx, input: "ls -la" });
|
|
15
|
+
assert.ok(result);
|
|
16
|
+
assert.ok(result.includes("looks like a shell command"));
|
|
17
|
+
assert.ok(result.includes("!"));
|
|
18
|
+
});
|
|
19
|
+
it("fires for various known commands", () => {
|
|
20
|
+
for (const cmd of ["pwd", "cd src", "cat file.txt", "grep foo bar", "git status", "npm install", "docker ps"]) {
|
|
21
|
+
const tips = new ContextualTips();
|
|
22
|
+
const result = tips.evaluate({ ...baseCtx, input: cmd });
|
|
23
|
+
assert.ok(result, `Expected tip for "${cmd}"`);
|
|
24
|
+
assert.ok(result.includes("looks like a shell command"));
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
it("does not fire for commands already prefixed with !", () => {
|
|
28
|
+
const tips = new ContextualTips();
|
|
29
|
+
const result = tips.evaluate({ ...baseCtx, input: "!ls -la" });
|
|
30
|
+
assert.equal(result, null);
|
|
31
|
+
});
|
|
32
|
+
it("does not fire for commands prefixed with !!", () => {
|
|
33
|
+
const tips = new ContextualTips();
|
|
34
|
+
const result = tips.evaluate({ ...baseCtx, input: "!!ls -la" });
|
|
35
|
+
assert.equal(result, null);
|
|
36
|
+
});
|
|
37
|
+
it("does not fire for slash commands", () => {
|
|
38
|
+
const tips = new ContextualTips();
|
|
39
|
+
const result = tips.evaluate({ ...baseCtx, input: "/clear" });
|
|
40
|
+
assert.equal(result, null);
|
|
41
|
+
});
|
|
42
|
+
it("does not fire for unknown commands", () => {
|
|
43
|
+
const tips = new ContextualTips();
|
|
44
|
+
const result = tips.evaluate({ ...baseCtx, input: "please help me fix this bug" });
|
|
45
|
+
assert.equal(result, null);
|
|
46
|
+
});
|
|
47
|
+
it("does not fire for very long inputs", () => {
|
|
48
|
+
const tips = new ContextualTips();
|
|
49
|
+
const longInput = "ls " + "a".repeat(200);
|
|
50
|
+
const result = tips.evaluate({ ...baseCtx, input: longInput });
|
|
51
|
+
assert.equal(result, null);
|
|
52
|
+
});
|
|
53
|
+
it("respects maxShows (2)", () => {
|
|
54
|
+
const tips = new ContextualTips();
|
|
55
|
+
tips.evaluate({ ...baseCtx, input: "ls" });
|
|
56
|
+
tips.evaluate({ ...baseCtx, input: "pwd" });
|
|
57
|
+
const third = tips.evaluate({ ...baseCtx, input: "cat foo" });
|
|
58
|
+
assert.equal(third, null);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe("large-paste tip", () => {
|
|
62
|
+
it("fires for large inputs", () => {
|
|
63
|
+
const tips = new ContextualTips();
|
|
64
|
+
const largeInput = "a".repeat(2500);
|
|
65
|
+
const result = tips.evaluate({ ...baseCtx, input: largeInput });
|
|
66
|
+
assert.ok(result);
|
|
67
|
+
assert.ok(result.includes("Large inputs"));
|
|
68
|
+
});
|
|
69
|
+
it("does not fire for normal-length inputs", () => {
|
|
70
|
+
const tips = new ContextualTips();
|
|
71
|
+
const result = tips.evaluate({ ...baseCtx, input: "fix the login bug" });
|
|
72
|
+
assert.equal(result, null);
|
|
73
|
+
});
|
|
74
|
+
it("does not fire for large bash commands", () => {
|
|
75
|
+
const tips = new ContextualTips();
|
|
76
|
+
const result = tips.evaluate({ ...baseCtx, input: "!" + "a".repeat(2500) });
|
|
77
|
+
assert.equal(result, null);
|
|
78
|
+
});
|
|
79
|
+
it("respects maxShows (2)", () => {
|
|
80
|
+
const tips = new ContextualTips();
|
|
81
|
+
const large = "x".repeat(3000);
|
|
82
|
+
tips.evaluate({ ...baseCtx, input: large });
|
|
83
|
+
tips.evaluate({ ...baseCtx, input: large });
|
|
84
|
+
const third = tips.evaluate({ ...baseCtx, input: large });
|
|
85
|
+
assert.equal(third, null);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe("thinking-level-high tip", () => {
|
|
89
|
+
it("fires for short inputs with high thinking", () => {
|
|
90
|
+
const tips = new ContextualTips();
|
|
91
|
+
const result = tips.evaluate({ ...baseCtx, input: "what is 2+2?", thinkingLevel: "high" });
|
|
92
|
+
assert.ok(result);
|
|
93
|
+
assert.ok(result.includes("Thinking is set to high"));
|
|
94
|
+
});
|
|
95
|
+
it("fires for xhigh thinking", () => {
|
|
96
|
+
const tips = new ContextualTips();
|
|
97
|
+
const result = tips.evaluate({ ...baseCtx, input: "what time is it?", thinkingLevel: "xhigh" });
|
|
98
|
+
assert.ok(result);
|
|
99
|
+
assert.ok(result.includes("Thinking is set to xhigh"));
|
|
100
|
+
});
|
|
101
|
+
it("does not fire for low/medium thinking", () => {
|
|
102
|
+
const tips = new ContextualTips();
|
|
103
|
+
const result = tips.evaluate({ ...baseCtx, input: "what is 2+2?", thinkingLevel: "medium" });
|
|
104
|
+
assert.equal(result, null);
|
|
105
|
+
});
|
|
106
|
+
it("does not fire for long inputs", () => {
|
|
107
|
+
const tips = new ContextualTips();
|
|
108
|
+
const longInput = "Please help me refactor this entire authentication module to use JWT tokens instead of session cookies. " +
|
|
109
|
+
"I need to update the middleware, the login handler, and the user model.";
|
|
110
|
+
const result = tips.evaluate({ ...baseCtx, input: longInput, thinkingLevel: "high" });
|
|
111
|
+
assert.equal(result, null);
|
|
112
|
+
});
|
|
113
|
+
it("does not fire for slash commands", () => {
|
|
114
|
+
const tips = new ContextualTips();
|
|
115
|
+
const result = tips.evaluate({ ...baseCtx, input: "/model", thinkingLevel: "high" });
|
|
116
|
+
assert.equal(result, null);
|
|
117
|
+
});
|
|
118
|
+
it("respects maxShows (1)", () => {
|
|
119
|
+
const tips = new ContextualTips();
|
|
120
|
+
tips.evaluate({ ...baseCtx, input: "hi", thinkingLevel: "high" });
|
|
121
|
+
const second = tips.evaluate({ ...baseCtx, input: "hello", thinkingLevel: "high" });
|
|
122
|
+
assert.equal(second, null);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe("double-bang-reminder tip", () => {
|
|
126
|
+
it("fires after 3+ included bash commands", () => {
|
|
127
|
+
const tips = new ContextualTips();
|
|
128
|
+
tips.recordBashIncluded();
|
|
129
|
+
tips.recordBashIncluded();
|
|
130
|
+
tips.recordBashIncluded();
|
|
131
|
+
const result = tips.evaluate({ ...baseCtx, input: "!ls" });
|
|
132
|
+
assert.ok(result);
|
|
133
|
+
assert.ok(result.includes("!!"));
|
|
134
|
+
});
|
|
135
|
+
it("does not fire with fewer than 3 included commands", () => {
|
|
136
|
+
const tips = new ContextualTips();
|
|
137
|
+
tips.recordBashIncluded();
|
|
138
|
+
tips.recordBashIncluded();
|
|
139
|
+
const result = tips.evaluate({ ...baseCtx, input: "!ls" });
|
|
140
|
+
assert.equal(result, null);
|
|
141
|
+
});
|
|
142
|
+
it("does not fire for !! commands", () => {
|
|
143
|
+
const tips = new ContextualTips();
|
|
144
|
+
tips.recordBashIncluded();
|
|
145
|
+
tips.recordBashIncluded();
|
|
146
|
+
tips.recordBashIncluded();
|
|
147
|
+
const result = tips.evaluate({ ...baseCtx, input: "!!ls" });
|
|
148
|
+
assert.equal(result, null);
|
|
149
|
+
});
|
|
150
|
+
it("respects maxShows (2)", () => {
|
|
151
|
+
const tips = new ContextualTips();
|
|
152
|
+
for (let i = 0; i < 5; i++)
|
|
153
|
+
tips.recordBashIncluded();
|
|
154
|
+
tips.evaluate({ ...baseCtx, input: "!ls" });
|
|
155
|
+
tips.evaluate({ ...baseCtx, input: "!pwd" });
|
|
156
|
+
const third = tips.evaluate({ ...baseCtx, input: "!cat foo" });
|
|
157
|
+
assert.equal(third, null);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe("compaction-nudge tip", () => {
|
|
161
|
+
it("fires when context is >= 70%", () => {
|
|
162
|
+
const tips = new ContextualTips();
|
|
163
|
+
const result = tips.evaluate({ ...baseCtx, input: "fix the bug", contextPercent: 75 });
|
|
164
|
+
assert.ok(result);
|
|
165
|
+
assert.ok(result.includes("/compact"));
|
|
166
|
+
});
|
|
167
|
+
it("does not fire when context is < 70%", () => {
|
|
168
|
+
const tips = new ContextualTips();
|
|
169
|
+
const result = tips.evaluate({ ...baseCtx, input: "fix the bug", contextPercent: 50 });
|
|
170
|
+
assert.equal(result, null);
|
|
171
|
+
});
|
|
172
|
+
it("does not fire when contextPercent is undefined", () => {
|
|
173
|
+
const tips = new ContextualTips();
|
|
174
|
+
const result = tips.evaluate({ ...baseCtx, input: "fix the bug", contextPercent: undefined });
|
|
175
|
+
assert.equal(result, null);
|
|
176
|
+
});
|
|
177
|
+
it("does not fire for slash commands", () => {
|
|
178
|
+
const tips = new ContextualTips();
|
|
179
|
+
const result = tips.evaluate({ ...baseCtx, input: "/model", contextPercent: 90 });
|
|
180
|
+
assert.equal(result, null);
|
|
181
|
+
});
|
|
182
|
+
it("respects maxShows (1)", () => {
|
|
183
|
+
const tips = new ContextualTips();
|
|
184
|
+
tips.evaluate({ ...baseCtx, input: "hello", contextPercent: 80 });
|
|
185
|
+
const second = tips.evaluate({ ...baseCtx, input: "world", contextPercent: 85 });
|
|
186
|
+
assert.equal(second, null);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
describe("reset", () => {
|
|
190
|
+
it("resets all show counters", () => {
|
|
191
|
+
const tips = new ContextualTips();
|
|
192
|
+
// Exhaust shell-command-prefix tip
|
|
193
|
+
tips.evaluate({ ...baseCtx, input: "ls" });
|
|
194
|
+
tips.evaluate({ ...baseCtx, input: "pwd" });
|
|
195
|
+
assert.equal(tips.evaluate({ ...baseCtx, input: "cat foo" }), null);
|
|
196
|
+
tips.reset();
|
|
197
|
+
// Should fire again after reset
|
|
198
|
+
const result = tips.evaluate({ ...baseCtx, input: "ls" });
|
|
199
|
+
assert.ok(result);
|
|
200
|
+
assert.ok(result.includes("looks like a shell command"));
|
|
201
|
+
});
|
|
202
|
+
it("resets bash included count", () => {
|
|
203
|
+
const tips = new ContextualTips();
|
|
204
|
+
for (let i = 0; i < 5; i++)
|
|
205
|
+
tips.recordBashIncluded();
|
|
206
|
+
assert.equal(tips.bashIncludedCount, 5);
|
|
207
|
+
tips.reset();
|
|
208
|
+
assert.equal(tips.bashIncludedCount, 0);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
describe("priority — first match wins", () => {
|
|
212
|
+
it("shell-command-prefix takes priority over compaction nudge", () => {
|
|
213
|
+
const tips = new ContextualTips();
|
|
214
|
+
const result = tips.evaluate({ ...baseCtx, input: "ls", contextPercent: 80 });
|
|
215
|
+
assert.ok(result);
|
|
216
|
+
assert.ok(result.includes("looks like a shell command"));
|
|
217
|
+
});
|
|
218
|
+
it("large-paste takes priority over compaction nudge", () => {
|
|
219
|
+
const tips = new ContextualTips();
|
|
220
|
+
const largeInput = "x".repeat(3000);
|
|
221
|
+
const result = tips.evaluate({ ...baseCtx, input: largeInput, contextPercent: 80 });
|
|
222
|
+
assert.ok(result);
|
|
223
|
+
assert.ok(result.includes("Large inputs"));
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
//# sourceMappingURL=contextual-tips.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextual-tips.test.js","sourceRoot":"","sources":["../../src/core/contextual-tips.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,OAAO,GAAG;IACf,KAAK,EAAE,aAAa;IACpB,WAAW,EAAE,KAAK;IAClB,aAAa,EAAE,KAAe;IAC9B,cAAc,EAAE,SAA+B;CAC/C,CAAC;AAEF,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC/B,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACzC,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;YACxC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9D,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAClB,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC,CAAC;YACzD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC3C,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,CAAC,EAAE,CAAC;gBAC/G,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;gBAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;gBACzD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,GAAG,GAAG,CAAC,CAAC;gBAC/C,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC,CAAC;YAC1D,CAAC;QACF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;YAC7D,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;YAC/D,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACtD,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;YAChE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC3C,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9D,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC7C,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAC,CAAC;YACnF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC7C,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,SAAS,GAAG,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;YAC/D,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAChC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;YAC9D,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;YACjC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACpC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;YAChE,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAClB,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YACjD,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;YACzE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAChD,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC5E,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAChC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5C,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC1D,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACpD,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;YAC3F,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAClB,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;YACnC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;YAChG,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAClB,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAChD,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7F,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;YACxC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,SAAS,GAAG,0GAA0G;gBAC3H,yEAAyE,CAAC;YAC3E,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;YACtF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC3C,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;YACrF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAChC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;YAClE,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;YACpF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACzC,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAChD,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC3D,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAClB,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC5D,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC3D,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;YACxC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5D,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAChC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;gBAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACtD,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5C,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;YAC/D,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;YACvC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;YACvF,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAClB,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;YAC9C,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;YACvF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;YACzD,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC,CAAC;YAC9F,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC3C,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;YAClF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAChC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;YAClE,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;YACjF,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;QACtB,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;YACnC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,mCAAmC;YACnC,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;YAEpE,IAAI,CAAC,KAAK,EAAE,CAAC;YAEb,gCAAgC;YAChC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAClB,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;YACrC,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;gBAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACtD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC;YAExC,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;QAC5C,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;YACpE,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;YAC9E,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAClB,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC3D,MAAM,IAAI,GAAG,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACpC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;YACpF,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAClB,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { ContextualTips } from \"./contextual-tips.js\";\n\nconst baseCtx = {\n\tinput: \"hello world\",\n\tisStreaming: false,\n\tthinkingLevel: \"off\" as string,\n\tcontextPercent: undefined as number | undefined,\n};\n\ndescribe(\"ContextualTips\", () => {\n\tdescribe(\"shell-command-prefix tip\", () => {\n\t\tit(\"fires for bare shell commands\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"ls -la\" });\n\t\t\tassert.ok(result);\n\t\t\tassert.ok(result.includes(\"looks like a shell command\"));\n\t\t\tassert.ok(result.includes(\"!\"));\n\t\t});\n\n\t\tit(\"fires for various known commands\", () => {\n\t\t\tfor (const cmd of [\"pwd\", \"cd src\", \"cat file.txt\", \"grep foo bar\", \"git status\", \"npm install\", \"docker ps\"]) {\n\t\t\t\tconst tips = new ContextualTips();\n\t\t\t\tconst result = tips.evaluate({ ...baseCtx, input: cmd });\n\t\t\t\tassert.ok(result, `Expected tip for \"${cmd}\"`);\n\t\t\t\tassert.ok(result.includes(\"looks like a shell command\"));\n\t\t\t}\n\t\t});\n\n\t\tit(\"does not fire for commands already prefixed with !\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"!ls -la\" });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"does not fire for commands prefixed with !!\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"!!ls -la\" });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"does not fire for slash commands\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"/clear\" });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"does not fire for unknown commands\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"please help me fix this bug\" });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"does not fire for very long inputs\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst longInput = \"ls \" + \"a\".repeat(200);\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: longInput });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"respects maxShows (2)\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\ttips.evaluate({ ...baseCtx, input: \"ls\" });\n\t\t\ttips.evaluate({ ...baseCtx, input: \"pwd\" });\n\t\t\tconst third = tips.evaluate({ ...baseCtx, input: \"cat foo\" });\n\t\t\tassert.equal(third, null);\n\t\t});\n\t});\n\n\tdescribe(\"large-paste tip\", () => {\n\t\tit(\"fires for large inputs\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst largeInput = \"a\".repeat(2500);\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: largeInput });\n\t\t\tassert.ok(result);\n\t\t\tassert.ok(result.includes(\"Large inputs\"));\n\t\t});\n\n\t\tit(\"does not fire for normal-length inputs\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"fix the login bug\" });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"does not fire for large bash commands\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"!\" + \"a\".repeat(2500) });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"respects maxShows (2)\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst large = \"x\".repeat(3000);\n\t\t\ttips.evaluate({ ...baseCtx, input: large });\n\t\t\ttips.evaluate({ ...baseCtx, input: large });\n\t\t\tconst third = tips.evaluate({ ...baseCtx, input: large });\n\t\t\tassert.equal(third, null);\n\t\t});\n\t});\n\n\tdescribe(\"thinking-level-high tip\", () => {\n\t\tit(\"fires for short inputs with high thinking\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"what is 2+2?\", thinkingLevel: \"high\" });\n\t\t\tassert.ok(result);\n\t\t\tassert.ok(result.includes(\"Thinking is set to high\"));\n\t\t});\n\n\t\tit(\"fires for xhigh thinking\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"what time is it?\", thinkingLevel: \"xhigh\" });\n\t\t\tassert.ok(result);\n\t\t\tassert.ok(result.includes(\"Thinking is set to xhigh\"));\n\t\t});\n\n\t\tit(\"does not fire for low/medium thinking\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"what is 2+2?\", thinkingLevel: \"medium\" });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"does not fire for long inputs\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst longInput = \"Please help me refactor this entire authentication module to use JWT tokens instead of session cookies. \" +\n\t\t\t\t\"I need to update the middleware, the login handler, and the user model.\";\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: longInput, thinkingLevel: \"high\" });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"does not fire for slash commands\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"/model\", thinkingLevel: \"high\" });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"respects maxShows (1)\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\ttips.evaluate({ ...baseCtx, input: \"hi\", thinkingLevel: \"high\" });\n\t\t\tconst second = tips.evaluate({ ...baseCtx, input: \"hello\", thinkingLevel: \"high\" });\n\t\t\tassert.equal(second, null);\n\t\t});\n\t});\n\n\tdescribe(\"double-bang-reminder tip\", () => {\n\t\tit(\"fires after 3+ included bash commands\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\ttips.recordBashIncluded();\n\t\t\ttips.recordBashIncluded();\n\t\t\ttips.recordBashIncluded();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"!ls\" });\n\t\t\tassert.ok(result);\n\t\t\tassert.ok(result.includes(\"!!\"));\n\t\t});\n\n\t\tit(\"does not fire with fewer than 3 included commands\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\ttips.recordBashIncluded();\n\t\t\ttips.recordBashIncluded();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"!ls\" });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"does not fire for !! commands\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\ttips.recordBashIncluded();\n\t\t\ttips.recordBashIncluded();\n\t\t\ttips.recordBashIncluded();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"!!ls\" });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"respects maxShows (2)\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tfor (let i = 0; i < 5; i++) tips.recordBashIncluded();\n\t\t\ttips.evaluate({ ...baseCtx, input: \"!ls\" });\n\t\t\ttips.evaluate({ ...baseCtx, input: \"!pwd\" });\n\t\t\tconst third = tips.evaluate({ ...baseCtx, input: \"!cat foo\" });\n\t\t\tassert.equal(third, null);\n\t\t});\n\t});\n\n\tdescribe(\"compaction-nudge tip\", () => {\n\t\tit(\"fires when context is >= 70%\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"fix the bug\", contextPercent: 75 });\n\t\t\tassert.ok(result);\n\t\t\tassert.ok(result.includes(\"/compact\"));\n\t\t});\n\n\t\tit(\"does not fire when context is < 70%\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"fix the bug\", contextPercent: 50 });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"does not fire when contextPercent is undefined\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"fix the bug\", contextPercent: undefined });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"does not fire for slash commands\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"/model\", contextPercent: 90 });\n\t\t\tassert.equal(result, null);\n\t\t});\n\n\t\tit(\"respects maxShows (1)\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\ttips.evaluate({ ...baseCtx, input: \"hello\", contextPercent: 80 });\n\t\t\tconst second = tips.evaluate({ ...baseCtx, input: \"world\", contextPercent: 85 });\n\t\t\tassert.equal(second, null);\n\t\t});\n\t});\n\n\tdescribe(\"reset\", () => {\n\t\tit(\"resets all show counters\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\t// Exhaust shell-command-prefix tip\n\t\t\ttips.evaluate({ ...baseCtx, input: \"ls\" });\n\t\t\ttips.evaluate({ ...baseCtx, input: \"pwd\" });\n\t\t\tassert.equal(tips.evaluate({ ...baseCtx, input: \"cat foo\" }), null);\n\n\t\t\ttips.reset();\n\n\t\t\t// Should fire again after reset\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"ls\" });\n\t\t\tassert.ok(result);\n\t\t\tassert.ok(result.includes(\"looks like a shell command\"));\n\t\t});\n\n\t\tit(\"resets bash included count\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tfor (let i = 0; i < 5; i++) tips.recordBashIncluded();\n\t\t\tassert.equal(tips.bashIncludedCount, 5);\n\n\t\t\ttips.reset();\n\t\t\tassert.equal(tips.bashIncludedCount, 0);\n\t\t});\n\t});\n\n\tdescribe(\"priority — first match wins\", () => {\n\t\tit(\"shell-command-prefix takes priority over compaction nudge\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: \"ls\", contextPercent: 80 });\n\t\t\tassert.ok(result);\n\t\t\tassert.ok(result.includes(\"looks like a shell command\"));\n\t\t});\n\n\t\tit(\"large-paste takes priority over compaction nudge\", () => {\n\t\t\tconst tips = new ContextualTips();\n\t\t\tconst largeInput = \"x\".repeat(3000);\n\t\t\tconst result = tips.evaluate({ ...baseCtx, input: largeInput, contextPercent: 80 });\n\t\t\tassert.ok(result);\n\t\t\tassert.ok(result.includes(\"Large inputs\"));\n\t\t});\n\t});\n});\n"]}
|
|
@@ -7,4 +7,5 @@ export { FallbackResolver, type FallbackResult } from "./fallback-resolver.js";
|
|
|
7
7
|
export type { CompactionResult } from "./compaction/index.js";
|
|
8
8
|
export { createEventBus, type EventBus, type EventBusController } from "./event-bus.js";
|
|
9
9
|
export { type AgentEndEvent, type AgentStartEvent, type AgentToolResult, type AgentToolUpdateCallback, type BeforeAgentStartEvent, type ContextEvent, discoverAndLoadExtensions, type ExecOptions, type ExecResult, type Extension, type ExtensionAPI, type ExtensionManifest, type ExtensionCommandContext, type ExtensionContext, type ExtensionError, type ExtensionEvent, type ExtensionFactory, type ExtensionFlag, type ExtensionHandler, ExtensionRunner, type ExtensionShortcut, type ExtensionUIContext, type LoadExtensionsResult, type MessageRenderer, type RegisteredCommand, type SessionBeforeCompactEvent, type SessionBeforeForkEvent, type SessionBeforeSwitchEvent, type SessionBeforeTreeEvent, type SessionCompactEvent, type SessionForkEvent, type SessionShutdownEvent, type SessionStartEvent, type SessionSwitchEvent, type SessionTreeEvent, type ToolCallEvent, readManifest, readManifestFromEntryPath, type SortResult, type SortWarning, sortExtensionPaths, type ToolDefinition, type ToolRenderResultOptions, type ToolResultEvent, type TurnEndEvent, type TurnStartEvent, wrapToolsWithExtensions, } from "./extensions/index.js";
|
|
10
|
+
export { ContextualTips, type TipContext } from "./contextual-tips.js";
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,UAAU,EAAE,WAAW,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AACvH,OAAO,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC/E,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGxF,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,yBAAyB,EACzB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,YAAY,EACZ,yBAAyB,EACzB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,uBAAuB,GACvB,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,UAAU,EAAE,WAAW,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AACvH,OAAO,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC/E,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGxF,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,yBAAyB,EACzB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,YAAY,EACZ,yBAAyB,EACzB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,uBAAuB,GACvB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -7,4 +7,5 @@ export { FallbackResolver } from "./fallback-resolver.js";
|
|
|
7
7
|
export { createEventBus } from "./event-bus.js";
|
|
8
8
|
// Extensions system
|
|
9
9
|
export { discoverAndLoadExtensions, ExtensionRunner, readManifest, readManifestFromEntryPath, sortExtensionPaths, wrapToolsWithExtensions, } from "./extensions/index.js";
|
|
10
|
+
export { ContextualTips } from "./contextual-tips.js";
|
|
10
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,YAAY,GAOZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAA6C,WAAW,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AACvH,OAAO,EAAE,gBAAgB,EAAuB,MAAM,wBAAwB,CAAC;AAE/E,OAAO,EAAE,cAAc,EAA0C,MAAM,gBAAgB,CAAC;AAExF,oBAAoB;AACpB,OAAO,EAON,yBAAyB,EAazB,eAAe,EAiBf,YAAY,EACZ,yBAAyB,EAGzB,kBAAkB,EAMlB,uBAAuB,GACvB,MAAM,uBAAuB,CAAC","sourcesContent":["/**\n * Core modules shared between all run modes.\n */\n\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./agent-session.js\";\nexport { type BashExecutorOptions, type BashResult, executeBash, executeBashWithOperations } from \"./bash-executor.js\";\nexport { FallbackResolver, type FallbackResult } from \"./fallback-resolver.js\";\nexport type { CompactionResult } from \"./compaction/index.js\";\nexport { createEventBus, type EventBus, type EventBusController } from \"./event-bus.js\";\n\n// Extensions system\nexport {\n\ttype AgentEndEvent,\n\ttype AgentStartEvent,\n\ttype AgentToolResult,\n\ttype AgentToolUpdateCallback,\n\ttype BeforeAgentStartEvent,\n\ttype ContextEvent,\n\tdiscoverAndLoadExtensions,\n\ttype ExecOptions,\n\ttype ExecResult,\n\ttype Extension,\n\ttype ExtensionAPI,\n\ttype ExtensionManifest,\n\ttype ExtensionCommandContext,\n\ttype ExtensionContext,\n\ttype ExtensionError,\n\ttype ExtensionEvent,\n\ttype ExtensionFactory,\n\ttype ExtensionFlag,\n\ttype ExtensionHandler,\n\tExtensionRunner,\n\ttype ExtensionShortcut,\n\ttype ExtensionUIContext,\n\ttype LoadExtensionsResult,\n\ttype MessageRenderer,\n\ttype RegisteredCommand,\n\ttype SessionBeforeCompactEvent,\n\ttype SessionBeforeForkEvent,\n\ttype SessionBeforeSwitchEvent,\n\ttype SessionBeforeTreeEvent,\n\ttype SessionCompactEvent,\n\ttype SessionForkEvent,\n\ttype SessionShutdownEvent,\n\ttype SessionStartEvent,\n\ttype SessionSwitchEvent,\n\ttype SessionTreeEvent,\n\ttype ToolCallEvent,\n\treadManifest,\n\treadManifestFromEntryPath,\n\ttype SortResult,\n\ttype SortWarning,\n\tsortExtensionPaths,\n\ttype ToolDefinition,\n\ttype ToolRenderResultOptions,\n\ttype ToolResultEvent,\n\ttype TurnEndEvent,\n\ttype TurnStartEvent,\n\twrapToolsWithExtensions,\n} from \"./extensions/index.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,YAAY,GAOZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAA6C,WAAW,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AACvH,OAAO,EAAE,gBAAgB,EAAuB,MAAM,wBAAwB,CAAC;AAE/E,OAAO,EAAE,cAAc,EAA0C,MAAM,gBAAgB,CAAC;AAExF,oBAAoB;AACpB,OAAO,EAON,yBAAyB,EAazB,eAAe,EAiBf,YAAY,EACZ,yBAAyB,EAGzB,kBAAkB,EAMlB,uBAAuB,GACvB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,cAAc,EAAmB,MAAM,sBAAsB,CAAC","sourcesContent":["/**\n * Core modules shared between all run modes.\n */\n\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./agent-session.js\";\nexport { type BashExecutorOptions, type BashResult, executeBash, executeBashWithOperations } from \"./bash-executor.js\";\nexport { FallbackResolver, type FallbackResult } from \"./fallback-resolver.js\";\nexport type { CompactionResult } from \"./compaction/index.js\";\nexport { createEventBus, type EventBus, type EventBusController } from \"./event-bus.js\";\n\n// Extensions system\nexport {\n\ttype AgentEndEvent,\n\ttype AgentStartEvent,\n\ttype AgentToolResult,\n\ttype AgentToolUpdateCallback,\n\ttype BeforeAgentStartEvent,\n\ttype ContextEvent,\n\tdiscoverAndLoadExtensions,\n\ttype ExecOptions,\n\ttype ExecResult,\n\ttype Extension,\n\ttype ExtensionAPI,\n\ttype ExtensionManifest,\n\ttype ExtensionCommandContext,\n\ttype ExtensionContext,\n\ttype ExtensionError,\n\ttype ExtensionEvent,\n\ttype ExtensionFactory,\n\ttype ExtensionFlag,\n\ttype ExtensionHandler,\n\tExtensionRunner,\n\ttype ExtensionShortcut,\n\ttype ExtensionUIContext,\n\ttype LoadExtensionsResult,\n\ttype MessageRenderer,\n\ttype RegisteredCommand,\n\ttype SessionBeforeCompactEvent,\n\ttype SessionBeforeForkEvent,\n\ttype SessionBeforeSwitchEvent,\n\ttype SessionBeforeTreeEvent,\n\ttype SessionCompactEvent,\n\ttype SessionForkEvent,\n\ttype SessionShutdownEvent,\n\ttype SessionStartEvent,\n\ttype SessionSwitchEvent,\n\ttype SessionTreeEvent,\n\ttype ToolCallEvent,\n\treadManifest,\n\treadManifestFromEntryPath,\n\ttype SortResult,\n\ttype SortWarning,\n\tsortExtensionPaths,\n\ttype ToolDefinition,\n\ttype ToolRenderResultOptions,\n\ttype ToolResultEvent,\n\ttype TurnEndEvent,\n\ttype TurnStartEvent,\n\twrapToolsWithExtensions,\n} from \"./extensions/index.js\";\n\nexport { ContextualTips, type TipContext } from \"./contextual-tips.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-execution.test.d.ts","sourceRoot":"","sources":["../../../../../src/modes/interactive/components/__tests__/tool-execution.test.ts"],"names":[],"mappings":""}
|
package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import stripAnsi from "strip-ansi";
|
|
4
|
+
import { ToolExecutionComponent } from "../tool-execution.js";
|
|
5
|
+
import { initTheme } from "../../theme/theme.js";
|
|
6
|
+
initTheme("dark", false);
|
|
7
|
+
function renderTool(toolName, args, result) {
|
|
8
|
+
const component = new ToolExecutionComponent(toolName, args, {}, undefined, { requestRender() { } });
|
|
9
|
+
component.setExpanded(true);
|
|
10
|
+
if (result)
|
|
11
|
+
component.updateResult(result);
|
|
12
|
+
return stripAnsi(component.render(120).join("\n"));
|
|
13
|
+
}
|
|
14
|
+
describe("ToolExecutionComponent", () => {
|
|
15
|
+
test("renders capitalized Claude Code Bash tool names with bash output instead of generic args JSON", () => {
|
|
16
|
+
const rendered = renderTool("Bash", { command: "pwd" }, { content: [{ type: "text", text: "/tmp/gsd-pr-fix" }], isError: false });
|
|
17
|
+
assert.match(rendered, /\$ pwd/);
|
|
18
|
+
assert.match(rendered, /\/tmp\/gsd-pr-fix/);
|
|
19
|
+
assert.doesNotMatch(rendered, /^\{\s*\}$/m);
|
|
20
|
+
});
|
|
21
|
+
test("renders capitalized Claude Code Read tool names with read output", () => {
|
|
22
|
+
const rendered = renderTool("Read", { path: "/tmp/demo.txt" }, { content: [{ type: "text", text: "hello\nworld" }], isError: false });
|
|
23
|
+
assert.match(rendered, /read .*demo\.txt/);
|
|
24
|
+
assert.match(rendered, /hello/);
|
|
25
|
+
assert.match(rendered, /world/);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=tool-execution.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-execution.test.js","sourceRoot":"","sources":["../../../../../src/modes/interactive/components/__tests__/tool-execution.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAEzB,SAAS,UAAU,CAClB,QAAgB,EAChB,IAA6B,EAC7B,MAIC;IAED,MAAM,SAAS,GAAG,IAAI,sBAAsB,CAC3C,QAAQ,EACR,IAAI,EACJ,EAAE,EACF,SAAS,EACT,EAAE,aAAa,KAAI,CAAC,EAAS,CAC7B,CAAC;IACF,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,MAAM;QAAE,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3C,OAAO,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACvC,IAAI,CAAC,+FAA+F,EAAE,GAAG,EAAE;QAC1G,MAAM,QAAQ,GAAG,UAAU,CAC1B,MAAM,EACN,EAAE,OAAO,EAAE,KAAK,EAAE,EAClB,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CACxE,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QAC5C,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC7E,MAAM,QAAQ,GAAG,UAAU,CAC1B,MAAM,EACN,EAAE,IAAI,EAAE,eAAe,EAAE,EACzB,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CACrE,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import { describe, test } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport stripAnsi from \"strip-ansi\";\nimport { ToolExecutionComponent } from \"../tool-execution.js\";\nimport { initTheme } from \"../../theme/theme.js\";\n\ninitTheme(\"dark\", false);\n\nfunction renderTool(\n\ttoolName: string,\n\targs: Record<string, unknown>,\n\tresult?: {\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t\tisError: boolean;\n\t\tdetails?: Record<string, unknown>;\n\t},\n): string {\n\tconst component = new ToolExecutionComponent(\n\t\ttoolName,\n\t\targs,\n\t\t{},\n\t\tundefined,\n\t\t{ requestRender() {} } as any,\n\t);\n\tcomponent.setExpanded(true);\n\tif (result) component.updateResult(result);\n\treturn stripAnsi(component.render(120).join(\"\\n\"));\n}\n\ndescribe(\"ToolExecutionComponent\", () => {\n\ttest(\"renders capitalized Claude Code Bash tool names with bash output instead of generic args JSON\", () => {\n\t\tconst rendered = renderTool(\n\t\t\t\"Bash\",\n\t\t\t{ command: \"pwd\" },\n\t\t\t{ content: [{ type: \"text\", text: \"/tmp/gsd-pr-fix\" }], isError: false },\n\t\t);\n\n\t\tassert.match(rendered, /\\$ pwd/);\n\t\tassert.match(rendered, /\\/tmp\\/gsd-pr-fix/);\n\t\tassert.doesNotMatch(rendered, /^\\{\\s*\\}$/m);\n\t});\n\n\ttest(\"renders capitalized Claude Code Read tool names with read output\", () => {\n\t\tconst rendered = renderTool(\n\t\t\t\"Read\",\n\t\t\t{ path: \"/tmp/demo.txt\" },\n\t\t\t{ content: [{ type: \"text\", text: \"hello\\nworld\" }], isError: false },\n\t\t);\n\n\t\tassert.match(rendered, /read .*demo\\.txt/);\n\t\tassert.match(rendered, /hello/);\n\t\tassert.match(rendered, /world/);\n\t});\n});\n"]}
|
|
@@ -26,6 +26,7 @@ export declare class ToolExecutionComponent extends Container {
|
|
|
26
26
|
private resolvedImageDimensions;
|
|
27
27
|
private writeHighlightCache?;
|
|
28
28
|
private hideComponent;
|
|
29
|
+
private get normalizedToolName();
|
|
29
30
|
constructor(toolName: string, args: any, options: ToolExecutionOptions | undefined, toolDefinition: ToolDefinition | undefined, ui: TUI, cwd?: string);
|
|
30
31
|
/**
|
|
31
32
|
* Check if we should use built-in rendering for this tool.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/tool-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,SAAS,EAOT,KAAK,GAAG,EAER,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAwCxE,MAAM,WAAW,oBAAoB;IACpC,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAUD;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,WAAW,CAAO;IAC1B,OAAO,CAAC,eAAe,CAAe;IACtC,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAC,CAIb;IAEF,OAAO,CAAC,eAAe,CAAC,CAAiC;IACzD,OAAO,CAAC,eAAe,CAAC,CAAS;IAEjC,OAAO,CAAC,eAAe,CAA8D;IAGrF,OAAO,CAAC,uBAAuB,CAA2C;IAE1E,OAAO,CAAC,mBAAmB,CAAC,CAAsB;IAElD,OAAO,CAAC,aAAa,CAAS;
|
|
1
|
+
{"version":3,"file":"tool-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/tool-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,SAAS,EAOT,KAAK,GAAG,EAER,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAwCxE,MAAM,WAAW,oBAAoB;IACpC,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAUD;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,WAAW,CAAO;IAC1B,OAAO,CAAC,eAAe,CAAe;IACtC,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAC,CAIb;IAEF,OAAO,CAAC,eAAe,CAAC,CAAiC;IACzD,OAAO,CAAC,eAAe,CAAC,CAAS;IAEjC,OAAO,CAAC,eAAe,CAA8D;IAGrF,OAAO,CAAC,uBAAuB,CAA2C;IAE1E,OAAO,CAAC,mBAAmB,CAAC,CAAsB;IAElD,OAAO,CAAC,aAAa,CAAS;IAE9B,OAAO,KAAK,kBAAkB,GAE7B;gBAGA,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,GAAG,EACT,OAAO,EAAE,oBAAoB,YAAK,EAClC,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,EAAE,EAAE,GAAG,EACP,GAAG,GAAE,MAAsB;IA2B5B;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;IAOhC,OAAO,IAAI,IAAI;IASf,UAAU,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI;IAQ3B,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,2BAA2B;IAYnC,OAAO,CAAC,8BAA8B;IAkBtC,OAAO,CAAC,oCAAoC;IAyD5C;;;OAGG;IACH,eAAe,IAAI,IAAI;IAWvB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IA6B5B,YAAY,CACX,MAAM,EAAE;QACP,OAAO,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAClF,OAAO,CAAC,EAAE,GAAG,CAAC;QACd,OAAO,EAAE,OAAO,CAAC;KACjB,EACD,SAAS,UAAQ,GACf,IAAI;IAeP;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IA2BlC,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAKpC,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAKzB,UAAU,IAAI,IAAI;IAKlB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAOxC,OAAO,CAAC,aAAa;IAiJrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA+EzB,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,mBAAmB;CA0U3B"}
|