promptscout 1.1.0

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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +251 -0
  3. package/claude-plugin/.claude-plugin/plugin.json +5 -0
  4. package/claude-plugin/hooks/hooks.json +16 -0
  5. package/claude-plugin/hooks/scripts/user-prompt-submit.sh +47 -0
  6. package/dist/commands/history.d.ts +3 -0
  7. package/dist/commands/history.js +56 -0
  8. package/dist/commands/history.js.map +1 -0
  9. package/dist/commands/rewrite.d.ts +3 -0
  10. package/dist/commands/rewrite.js +25 -0
  11. package/dist/commands/rewrite.js.map +1 -0
  12. package/dist/commands/setup.d.ts +2 -0
  13. package/dist/commands/setup.js +38 -0
  14. package/dist/commands/setup.js.map +1 -0
  15. package/dist/commands/system-prompt.d.ts +3 -0
  16. package/dist/commands/system-prompt.js +36 -0
  17. package/dist/commands/system-prompt.js.map +1 -0
  18. package/dist/constants.d.ts +15 -0
  19. package/dist/constants.js +114 -0
  20. package/dist/constants.js.map +1 -0
  21. package/dist/core/history-service.d.ts +15 -0
  22. package/dist/core/history-service.js +25 -0
  23. package/dist/core/history-service.js.map +1 -0
  24. package/dist/core/orchestrator.d.ts +11 -0
  25. package/dist/core/orchestrator.js +48 -0
  26. package/dist/core/orchestrator.js.map +1 -0
  27. package/dist/core/rewriter.d.ts +7 -0
  28. package/dist/core/rewriter.js +62 -0
  29. package/dist/core/rewriter.js.map +1 -0
  30. package/dist/core/system-prompt-service.d.ts +15 -0
  31. package/dist/core/system-prompt-service.js +30 -0
  32. package/dist/core/system-prompt-service.js.map +1 -0
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.js +50 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/llm/inference.d.ts +2 -0
  37. package/dist/llm/inference.js +52 -0
  38. package/dist/llm/inference.js.map +1 -0
  39. package/dist/llm/model-manager.d.ts +1 -0
  40. package/dist/llm/model-manager.js +5 -0
  41. package/dist/llm/model-manager.js.map +1 -0
  42. package/dist/llm/prompts/tool-calling.d.ts +3 -0
  43. package/dist/llm/prompts/tool-calling.js +46 -0
  44. package/dist/llm/prompts/tool-calling.js.map +1 -0
  45. package/dist/llm/tokenizer.d.ts +1 -0
  46. package/dist/llm/tokenizer.js +30 -0
  47. package/dist/llm/tokenizer.js.map +1 -0
  48. package/dist/output/clipboard.d.ts +1 -0
  49. package/dist/output/clipboard.js +12 -0
  50. package/dist/output/clipboard.js.map +1 -0
  51. package/dist/output/file-writer.d.ts +1 -0
  52. package/dist/output/file-writer.js +8 -0
  53. package/dist/output/file-writer.js.map +1 -0
  54. package/dist/storage/config-repo.d.ts +11 -0
  55. package/dist/storage/config-repo.js +42 -0
  56. package/dist/storage/config-repo.js.map +1 -0
  57. package/dist/storage/database.d.ts +4 -0
  58. package/dist/storage/database.js +54 -0
  59. package/dist/storage/database.js.map +1 -0
  60. package/dist/storage/history-repo.d.ts +10 -0
  61. package/dist/storage/history-repo.js +48 -0
  62. package/dist/storage/history-repo.js.map +1 -0
  63. package/dist/storage/schema.d.ts +183 -0
  64. package/dist/storage/schema.js +16 -0
  65. package/dist/storage/schema.js.map +1 -0
  66. package/dist/tools/implementations.d.ts +6 -0
  67. package/dist/tools/implementations.js +96 -0
  68. package/dist/tools/implementations.js.map +1 -0
  69. package/dist/tools/index.d.ts +24 -0
  70. package/dist/tools/index.js +119 -0
  71. package/dist/tools/index.js.map +1 -0
  72. package/dist/tools/search-utils.d.ts +7 -0
  73. package/dist/tools/search-utils.js +73 -0
  74. package/dist/tools/search-utils.js.map +1 -0
  75. package/dist/types.d.ts +34 -0
  76. package/dist/types.js +2 -0
  77. package/dist/types.js.map +1 -0
  78. package/dist/utils/editor.d.ts +1 -0
  79. package/dist/utils/editor.js +25 -0
  80. package/dist/utils/editor.js.map +1 -0
  81. package/dist/utils/paths.d.ts +3 -0
  82. package/dist/utils/paths.js +15 -0
  83. package/dist/utils/paths.js.map +1 -0
  84. package/dist/utils/text.d.ts +2 -0
  85. package/dist/utils/text.js +11 -0
  86. package/dist/utils/text.js.map +1 -0
  87. package/package.json +68 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ömercan Balandı
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,251 @@
1
+ # promptscout
2
+
3
+ A CLI tool that enriches your coding agent prompts with codebase context using a local LLM. No API keys, no cloud. Runs on your machine.
4
+
5
+ **Designed as a Claude Code plugin.** It hooks into your prompt submission flow and adds codebase context before Claude sees it.
6
+
7
+ ## Motivation
8
+
9
+ When you ask a coding agent like Claude Code, Cursor, or Copilot to work on your codebase, the agent spends time and tokens discovering which files matter. It greps, reads, explores, all on your dime.
10
+
11
+ promptscout does that discovery locally, for free. A small local LLM reads your prompt, searches your codebase with `grep` and `git`, and appends the results to your original prompt. The paid agent gets a prompt that already contains the relevant file paths, code snippets, and commit history. It can skip straight to the actual work.
12
+
13
+ Your original prompt is never modified. promptscout only appends context.
14
+
15
+ ## How It Works
16
+
17
+ ```mermaid
18
+ flowchart LR
19
+ A["Raw Prompt"] --> B["Local LLM<br/>(Qwen 3 4B)"]
20
+ B -->|"tool calls"| C["Execute Tools"]
21
+ C --> D["grep / git"]
22
+ D --> E["Original Prompt<br/>+ Discovered Context"]
23
+ ```
24
+
25
+ 1. You run `promptscout "check the auth module, there might be a token refresh bug"`
26
+ 2. The local LLM reads your prompt and picks which tools to call (e.g. `file_finder("auth")`, `section_finder("refresh")`, `git_history("token")`)
27
+ 3. Each tool runs against your codebase using `grep` and `git`
28
+ 4. The output is your original prompt unchanged, followed by the discovered context
29
+ 5. The result is copied to your clipboard, ready to paste into your coding agent
30
+
31
+ ## Installation
32
+
33
+ ### Prerequisites
34
+
35
+ - Node.js >= 20
36
+ - C++ compiler (Xcode Command Line Tools on macOS, `build-essential` on Linux)
37
+ - ~3GB disk space for the model
38
+
39
+ ### Install
40
+
41
+ ```bash
42
+ git clone https://github.com/obsfx/promptscout.git
43
+ cd promptscout
44
+ pnpm install
45
+ pnpm build
46
+ pnpm link --global
47
+ ```
48
+
49
+ ### Setup
50
+
51
+ Run `promptscout setup` to create the data directory and download the `Qwen 3 4B` model (~2.5GB). The model is stored in `~/.promptscout/models/`.
52
+
53
+ ## Claude Code Plugin
54
+
55
+ <img src="https://raw.githubusercontent.com/lobehub/lobe-icons/refs/heads/master/packages/static-png/dark/claude-color.png" alt="Claude" width="48" />
56
+
57
+ promptscout ships with a Claude Code plugin that enriches every prompt you send. Once installed, it runs in the background. No manual copy-pasting needed.
58
+
59
+ ### Install the plugin
60
+
61
+ ```bash
62
+ # Add the promptscout marketplace
63
+ claude /plugin marketplace add obsfx/promptscout
64
+
65
+ # Install the plugin
66
+ claude /plugin install promptscout
67
+ ```
68
+
69
+ Or in Claude Code interactive mode:
70
+
71
+ ```
72
+ /plugin marketplace add obsfx/promptscout
73
+ /plugin install promptscout
74
+ ```
75
+
76
+ Once installed, every prompt you submit in Claude Code gets enriched with codebase context via the `UserPromptSubmit` hook. The plugin passes your prompt through `promptscout` with `--json-output --no-clipboard` and injects the result as additional context.
77
+
78
+ If `promptscout` is not installed or fails for any reason, the plugin falls back silently and your original prompt goes through unchanged.
79
+
80
+ ## Model
81
+
82
+ promptscout uses `Qwen 3 4B` (`Q4_K_M` quantization) running locally via [node-llama-cpp](https://github.com/withcatai/node-llama-cpp). The model runs on CPU by default to avoid GPU memory issues on constrained machines.
83
+
84
+ - Size: ~2.5GB (`GGUF Q4_K_M`)
85
+ - Context: 4096 tokens
86
+ - Latency: 7-15s per prompt (CPU, Apple Silicon)
87
+ - Purpose: Decides which search tools to call based on your prompt. Does not rewrite your text.
88
+
89
+ ## Tools
90
+
91
+ promptscout has 5 built-in tools that the LLM can invoke:
92
+
93
+ | Tool | What it does |
94
+ |---|---|
95
+ | `file_finder` | Finds files matching a keyword. Results scored by filename relevance. |
96
+ | `section_finder` | Finds code lines matching a keyword. Returns `file:line:code` entries. |
97
+ | `definition_finder` | Finds function, class, type, and struct definitions across languages. |
98
+ | `import_tracer` | Finds import/require/include statements referencing a module. |
99
+ | `git_history` | Finds recent commits that added or removed code matching a keyword. |
100
+
101
+ All `grep`-based tools respect `.gitignore` and skip binary files.
102
+
103
+ ## Usage
104
+
105
+ ```bash
106
+ # Basic usage (result copied to clipboard)
107
+ promptscout "check the camera module, we need to add timeout handling"
108
+
109
+ # Dry run (print to stdout, skip clipboard)
110
+ promptscout --dry-run --no-clipboard "refactor the search module"
111
+
112
+ # Specify project directory
113
+ promptscout --project-dir /path/to/project "check the auth flow"
114
+
115
+ # JSON output (for programmatic use)
116
+ promptscout --json-output "fix the pagination bug"
117
+ ```
118
+
119
+ ### Options
120
+
121
+ ```
122
+ <prompt> Raw prompt to enrich
123
+ -o, --output <file> Write result to file
124
+ --dry-run Show result without copying or saving
125
+ --json-output Output JSON instead of plain text
126
+ --no-clipboard Skip clipboard copy
127
+ --project-dir <dir> Project root directory
128
+ ```
129
+
130
+ ## Examples
131
+
132
+ ### Swift project (macOS audio capture tool)
133
+
134
+ ```
135
+ $ promptscout "I want to add a new audio format export feature. check the current
136
+ audio processing pipeline and see how formats are handled"
137
+ ```
138
+
139
+ ```
140
+ I want to add a new audio format export feature. check the current audio processing
141
+ pipeline and see how formats are handled
142
+
143
+ Context from codebase:
144
+
145
+ <file_finder query="audio">
146
+ Sources/Core/AudioCaptureSession.swift
147
+ Sources/Core/AudioTapManager.swift
148
+ entitlements.plist
149
+ README.md
150
+ Sources/Core/InputDeviceQuery.swift
151
+ Sources/IO/RingBuffer.swift
152
+ Sources/IO/WAVWriter.swift
153
+ Sources/CLI/ExitCodes.swift
154
+ </file_finder>
155
+
156
+ <git_history query="audio">
157
+ d41aece Initial commit: audiograb - macOS system audio capture CLI
158
+ Package.swift
159
+ README.md
160
+ Sources/CLI/ArgumentParser.swift
161
+ Sources/Core/AudioCaptureSession.swift
162
+ Sources/Core/AudioTapManager.swift
163
+ Sources/IO/RingBuffer.swift
164
+ Sources/main.swift
165
+ d800ac1 Add microphone recording support via --source mic flag
166
+ Sources/Info.plist
167
+ </git_history>
168
+ ```
169
+
170
+ ### TypeScript project (task management CLI)
171
+
172
+ ```
173
+ $ promptscout "I want to add task filtering by status and tags. check how tasks
174
+ are stored and queried"
175
+ ```
176
+
177
+ ```
178
+ I want to add task filtering by status and tags. check how tasks are stored and queried
179
+
180
+ Context from codebase:
181
+
182
+ <file_finder query="task">
183
+ tests/commands/subtask.test.ts
184
+ tests/commands/task.test.ts
185
+ src/commands/task.ts
186
+ src/services/task.ts
187
+ tests/integration/workflows.test.ts
188
+ tests/commands/comment.test.ts
189
+ tests/commands/history.test.ts
190
+ </file_finder>
191
+
192
+ <definition_finder query="Task">
193
+ tests/integration/workflows.test.ts:10:interface Task {
194
+ src/services/task.ts:72:export function listTasks(options?: {
195
+ </definition_finder>
196
+
197
+ <section_finder query="listTasks">
198
+ src/commands/task.ts:5: listTasks,
199
+ src/commands/task.ts:58: const tasks = listTasks({
200
+ src/services/task.ts:72:export function listTasks(options?: {
201
+ </section_finder>
202
+ ```
203
+
204
+ ### React/TypeScript project (terminal ebook downloader)
205
+
206
+ ```
207
+ $ promptscout "I need to refactor the search module. check how search and pagination
208
+ currently work and find the related components"
209
+ ```
210
+
211
+ ```
212
+ I need to refactor the search module. check how search and pagination currently work
213
+ and find the related components
214
+
215
+ Context from codebase:
216
+
217
+ <file_finder query="search">
218
+ src/tui/layouts/search/search-input/SearchWarning.tsx
219
+ src/tui/layouts/search/search-input/SearchInput.tsx
220
+ src/tui/layouts/search/index.tsx
221
+ src/tui/layouts/search/search-input/index.tsx
222
+ README.md
223
+ package.json
224
+ CLAUDE.md
225
+ src/tui/index.tsx
226
+ </file_finder>
227
+
228
+ <section_finder query="search">
229
+ src/tui/components/ResultListInfo.tsx:6: const searchValue = useBoundStore((state) => state.searchValue);
230
+ CLAUDE.md:30:- `libgen-downloader -s "query"` - Direct search with TUI
231
+ CLAUDE.md:43: - `Adapter.ts` - Abstract base for different search sources
232
+ CLAUDE.md:54:- `app.ts` - UI state, layouts, loading indicators, search state
233
+ CLAUDE.md:58:- `cache.ts` - Search result caching mechanism
234
+ </section_finder>
235
+ ```
236
+
237
+ ### Feedback detection
238
+
239
+ When the prompt is feedback or an observation (not asking to change code), promptscout returns it unchanged with no context appended:
240
+
241
+ ```
242
+ $ promptscout "that didn't solve the issue, it is still rotated 90 degrees clockwise"
243
+ ```
244
+
245
+ ```
246
+ that didn't solve the issue, it is still rotated 90 degrees clockwise
247
+ ```
248
+
249
+ ## License
250
+
251
+ MIT
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "promptscout",
3
+ "version": "1.0.0",
4
+ "description": "Enriches coding agent prompts with codebase context using a local LLM"
5
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "hooks": {
3
+ "UserPromptSubmit": [
4
+ {
5
+ "matcher": "",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/user-prompt-submit.sh",
10
+ "timeout": 120
11
+ }
12
+ ]
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ # Read hook input from stdin
5
+ input=$(cat)
6
+
7
+ # Extract the user's prompt and project directory
8
+ prompt=$(echo "$input" | jq -r '.prompt // empty')
9
+ project_dir=$(echo "$input" | jq -r '.cwd // empty')
10
+
11
+ if [ -z "$prompt" ]; then
12
+ exit 0
13
+ fi
14
+
15
+ # Check if promptscout CLI is available
16
+ if ! command -v promptscout &>/dev/null; then
17
+ exit 0
18
+ fi
19
+
20
+ # Run promptscout with JSON output and no clipboard
21
+ # Build CLI arguments
22
+ cli_args=(--json-output --no-clipboard)
23
+ if [ -n "$project_dir" ]; then
24
+ cli_args+=(--project-dir "$project_dir")
25
+ fi
26
+
27
+ result=$(promptscout "$prompt" "${cli_args[@]}" 2>/dev/null) || {
28
+ # Graceful degradation: if CLI fails, let original prompt through
29
+ exit 0
30
+ }
31
+
32
+ # Extract the final improved prompt
33
+ improved=$(echo "$result" | jq -r '.final // empty')
34
+
35
+ if [ -z "$improved" ]; then
36
+ exit 0
37
+ fi
38
+
39
+ # Inject as additionalContext
40
+ cat <<EOF
41
+ {
42
+ "hookSpecificOutput": {
43
+ "hookEventName": "UserPromptSubmit",
44
+ "additionalContext": $(echo "$improved" | jq -Rs .)
45
+ }
46
+ }
47
+ EOF
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ import type { HistoryService } from "../core/history-service.js";
3
+ export declare function registerHistoryCommand(program: Command, service: HistoryService): void;
@@ -0,0 +1,56 @@
1
+ import { confirm } from "@inquirer/prompts";
2
+ export function registerHistoryCommand(program, service) {
3
+ const hist = program.command("history").description("View prompt history");
4
+ hist
5
+ .option("-a, --all", "Show history across all directories")
6
+ .option("-n, --limit <number>", "Number of entries to show", "20")
7
+ .action((opts) => {
8
+ const limit = parseInt(opts.limit, 10);
9
+ const entries = service.list(!!opts.all, limit);
10
+ if (entries.length === 0) {
11
+ console.log("No history entries found.");
12
+ if (!opts.all) {
13
+ console.log("Tip: Use -a to show history from all directories.");
14
+ }
15
+ return;
16
+ }
17
+ for (let i = 0; i < entries.length; i++) {
18
+ const e = entries[i];
19
+ console.log(`│ (${e.id}) (${e.model_name}) ${e.preview}`);
20
+ }
21
+ });
22
+ hist
23
+ .command("show <id>")
24
+ .description("Show full detail of a history entry")
25
+ .action((id) => {
26
+ const entry = service.findById(parseInt(id, 10));
27
+ if (!entry) {
28
+ console.error(`Error: History entry #${id} not found.`);
29
+ process.exit(1);
30
+ }
31
+ console.log(`(${entry.id})`);
32
+ console.log(`│ Date: ${entry.created_at}`);
33
+ console.log(`│ Directory: ${entry.directory}`);
34
+ console.log(`│ Model: ${entry.model_name || "N/A"}`);
35
+ console.log(`│ Raw Input:`);
36
+ console.log(entry.raw_input);
37
+ console.log(`│ Improved Output:`);
38
+ console.log(entry.improved_output);
39
+ });
40
+ hist
41
+ .command("clear")
42
+ .description("Clear all history")
43
+ .action(async () => {
44
+ const confirmed = await confirm({
45
+ message: "Clear all history?",
46
+ default: false,
47
+ });
48
+ if (!confirmed) {
49
+ console.log("Cancelled.");
50
+ return;
51
+ }
52
+ service.clear();
53
+ console.log("History cleared.");
54
+ });
55
+ }
56
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/commands/history.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,MAAM,UAAU,sBAAsB,CACpC,OAAgB,EAChB,OAAuB;IAEvB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,WAAW,CAAC,qBAAqB,CAAC,CAAC;IAE3E,IAAI;SACD,MAAM,CAAC,WAAW,EAAE,qCAAqC,CAAC;SAC1D,MAAM,CAAC,sBAAsB,EAAE,2BAA2B,EAAE,IAAI,CAAC;SACjE,MAAM,CAAC,CAAC,IAA6B,EAAE,EAAE;QACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAe,EAAE,EAAE,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAEhD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;YACzC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;YACnE,CAAC;YACD,OAAO;QACT,CAAC;QAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,IAAI;SACD,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,qCAAqC,CAAC;SAClD,MAAM,CAAC,CAAC,EAAU,EAAE,EAAE;QACrB,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,aAAa,CAAC,CAAC;YACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,CAAC,UAAU,IAAI,KAAK,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEL,IAAI;SACD,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,mBAAmB,CAAC;SAChC,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC;YAC9B,OAAO,EAAE,oBAAoB;YAC7B,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ import type { Orchestrator } from "../core/orchestrator.js";
3
+ export declare function registerRewriteCommand(program: Command, orchestrator: Orchestrator): void;
@@ -0,0 +1,25 @@
1
+ export function registerRewriteCommand(program, orchestrator) {
2
+ program
3
+ .argument("<prompt>", "Raw prompt to rewrite")
4
+ .option("-o, --output <file>", "Write result to file instead of clipboard")
5
+ .option("--dry-run", "Show result without copying or saving")
6
+ .option("--json-output", "Output JSON instead of plain text")
7
+ .option("--no-clipboard", "Skip clipboard copy")
8
+ .option("--project-dir <dir>", "Project root directory (passed by Claude Code hook)")
9
+ .action(async (prompt, opts) => {
10
+ if (!prompt.trim()) {
11
+ console.error("Error: Prompt cannot be empty.");
12
+ console.error("Usage: promptscout <prompt>");
13
+ process.exit(1);
14
+ }
15
+ await orchestrator.processPrompt({
16
+ rawPrompt: prompt,
17
+ dryRun: opts.dryRun,
18
+ outputFile: opts.output,
19
+ jsonOutput: opts.jsonOutput,
20
+ noClipboard: opts.clipboard === false,
21
+ projectDir: opts.projectDir,
22
+ });
23
+ });
24
+ }
25
+ //# sourceMappingURL=rewrite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rewrite.js","sourceRoot":"","sources":["../../src/commands/rewrite.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,sBAAsB,CAAC,OAAgB,EAAE,YAA0B;IACjF,OAAO;SACJ,QAAQ,CAAC,UAAU,EAAE,uBAAuB,CAAC;SAC7C,MAAM,CAAC,qBAAqB,EAAE,2CAA2C,CAAC;SAC1E,MAAM,CAAC,WAAW,EAAE,uCAAuC,CAAC;SAC5D,MAAM,CAAC,eAAe,EAAE,mCAAmC,CAAC;SAC5D,MAAM,CAAC,gBAAgB,EAAE,qBAAqB,CAAC;SAC/C,MAAM,CAAC,qBAAqB,EAAE,qDAAqD,CAAC;SACpF,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,IAA6B,EAAE,EAAE;QAC9D,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YACnB,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;YAChD,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;YAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,YAAY,CAAC,aAAa,CAAC;YAC/B,SAAS,EAAE,MAAM;YACjB,MAAM,EAAE,IAAI,CAAC,MAA6B;YAC1C,UAAU,EAAE,IAAI,CAAC,MAA4B;YAC7C,UAAU,EAAE,IAAI,CAAC,UAAiC;YAClD,WAAW,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK;YACrC,UAAU,EAAE,IAAI,CAAC,UAAgC;SAClD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerSetupCommand(program: Command): void;
@@ -0,0 +1,38 @@
1
+ import { existsSync } from "node:fs";
2
+ import { resolveModelFile } from "node-llama-cpp";
3
+ import { DATA_DIR, DB_PATH, MODEL_DIR, MODEL_HF_URI, MODEL_FILE_NAME, MODEL_DOWNLOAD_URI, } from "../constants.js";
4
+ import { resolveModelDir } from "../utils/paths.js";
5
+ export function registerSetupCommand(program) {
6
+ program
7
+ .command("setup")
8
+ .description("Initialize promptscout and download the model")
9
+ .action(async () => {
10
+ console.log(`Data directory: ${DATA_DIR}`);
11
+ console.log(`Database: ${DB_PATH}`);
12
+ resolveModelDir();
13
+ console.log(`Model directory: ${MODEL_DIR}`);
14
+ console.log("");
15
+ if (existsSync(MODEL_HF_URI)) {
16
+ console.log(`Model already downloaded: ${MODEL_HF_URI}`);
17
+ console.log("\nSetup complete.");
18
+ return;
19
+ }
20
+ console.log("Downloading Qwen 3 4B (~2.5GB)...\n");
21
+ try {
22
+ await resolveModelFile(MODEL_DOWNLOAD_URI, {
23
+ directory: MODEL_DIR,
24
+ fileName: MODEL_FILE_NAME,
25
+ });
26
+ console.log(`\nModel saved to: ${MODEL_HF_URI}`);
27
+ }
28
+ catch (err) {
29
+ console.error("\nFailed to download model.");
30
+ if (err instanceof Error) {
31
+ console.error(err.message);
32
+ }
33
+ process.exit(1);
34
+ }
35
+ console.log("\nSetup complete.");
36
+ });
37
+ }
38
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EACL,QAAQ,EACR,OAAO,EACP,SAAS,EACT,YAAY,EACZ,eAAe,EACf,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,+CAA+C,CAAC;SAC5D,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,OAAO,CAAC,GAAG,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,mBAAmB,OAAO,EAAE,CAAC,CAAC;QAE1C,eAAe,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,oBAAoB,SAAS,EAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhB,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,6BAA6B,YAAY,EAAE,CAAC,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;YACjC,OAAO;QACT,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QAEnD,IAAI,CAAC;YACH,MAAM,gBAAgB,CAAC,kBAAkB,EAAE;gBACzC,SAAS,EAAE,SAAS;gBACpB,QAAQ,EAAE,eAAe;aAC1B,CAAC,CAAC;YAEH,OAAO,CAAC,GAAG,CAAC,qBAAqB,YAAY,EAAE,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;YAC7C,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;gBACzB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC7B,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ import type { SystemPromptService } from "../core/system-prompt-service.js";
3
+ export declare function registerSystemPromptCommand(program: Command, service: SystemPromptService): void;
@@ -0,0 +1,36 @@
1
+ import { formatTokenCount } from "../utils/text.js";
2
+ import { openInEditor } from "../utils/editor.js";
3
+ import { confirm } from "@inquirer/prompts";
4
+ export function registerSystemPromptCommand(program, service) {
5
+ const sp = program
6
+ .command("system-prompt")
7
+ .description("Manage the LLM system prompt");
8
+ sp.action(async () => {
9
+ const status = await service.getStatus();
10
+ console.log(status.prompt);
11
+ console.log(`\n${formatTokenCount(status.tokens, status.maxInputTokens)}`);
12
+ });
13
+ sp.command("edit")
14
+ .description("Edit system prompt in $EDITOR")
15
+ .action(() => {
16
+ const current = service.getCurrent();
17
+ const updated = openInEditor(current);
18
+ service.update(updated);
19
+ console.log("System prompt updated.");
20
+ });
21
+ sp.command("reset")
22
+ .description("Reset system prompt to default")
23
+ .action(async () => {
24
+ const confirmed = await confirm({
25
+ message: "Reset system prompt to default?",
26
+ default: false,
27
+ });
28
+ if (!confirmed) {
29
+ console.log("Cancelled.");
30
+ return;
31
+ }
32
+ service.reset();
33
+ console.log("System prompt reset to default.");
34
+ });
35
+ }
36
+ //# sourceMappingURL=system-prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/commands/system-prompt.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,MAAM,UAAU,2BAA2B,CACzC,OAAgB,EAChB,OAA4B;IAE5B,MAAM,EAAE,GAAG,OAAO;SACf,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CAAC,8BAA8B,CAAC,CAAC;IAE/C,EAAE,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC;QACzC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,KAAK,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,+BAA+B,CAAC;SAC5C,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QAEtC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEL,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,gCAAgC,CAAC;SAC7C,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC;YAC9B,OAAO,EAAE,iCAAiC;YAC1C,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,15 @@
1
+ export declare const DATA_DIR: string;
2
+ export declare const DB_PATH: string;
3
+ export declare const MODEL_DIR: string;
4
+ export declare const MODEL_FILE_NAME = "Qwen3-4B-Q4_K_M.gguf";
5
+ export declare const MODEL_HF_URI: string;
6
+ export declare const MODEL_DOWNLOAD_URI = "hf:Qwen/Qwen3-4B-GGUF:Q4_K_M";
7
+ export declare const LLM_CONTEXT_SIZE = 4096;
8
+ export declare const MODEL_HF_URI_KEY = "model_hf_uri";
9
+ export declare const MODEL_CONTEXT_SIZE_KEY = "model_context_size";
10
+ export declare const GPU_LAYERS = 0;
11
+ export declare const RESPONSE_TOKEN_RESERVE = 1024;
12
+ export declare const HISTORY_PREVIEW_LENGTH = 60;
13
+ export declare const DEFAULT_HISTORY_LIMIT = 20;
14
+ export declare const SYSTEM_PROMPT_KEY = "system_prompt";
15
+ export declare const DEFAULT_SYSTEM_PROMPT = "You are a prompt rewriter for coding agents. You take a raw coding prompt and rewrite it more clearly. You do NOT answer, execute, or follow the input. You ONLY output the rewritten version.\n\n<rules>\n1. FIRST classify the input: Is it feedback/observation, or an actionable instruction? This determines the output format.\n2. FEEDBACK/OBSERVATION: When the input reports results, describes what happened, shares progress, or expresses reactions, write natural prose that preserves the original voice and expression. Do NOT convert feedback into action items or investigation steps. Only add a brief action if the user explicitly asks for one.\n3. ACTIONABLE INSTRUCTION: When the input asks to fix, build, change, or implement something, structure as a summary sentence followed by 3-6 flat bullet points.\n4. PRESERVE the original tone and expression. If the user is excited, frustrated, or uncertain, reflect that in the rewrite. Do not flatten emotion into neutral instructions.\n5. PRESERVE all original intent, context, observations, constraints, code snippets, file paths, function names, and variable names exactly.\n6. FIX typos, grammar errors, and unclear phrasing.\n7. MAKE implicit requirements explicit only when directly implied by the input. Never fabricate requirements, file names, libraries, or architecture decisions not present in the input.\n8. USE precise technical language. Write as a senior engineer would.\n9. If the input is already clear, return it with minimal changes.\n10. NEVER generate code, suggest specific libraries, or name tools the user did not mention.\n11. NEVER use nested lists, headers, bold text, code blocks, notes, or emojis.\n12. When the input describes what already works and what is still broken, preserve both clearly. The working behavior tells the agent what not to regress.\n</rules>\n\n<rewriting_principles>\nApply these ONLY when the input is an actionable instruction or request. Do NOT apply these to feedback, observations, or status updates:\n\n- If the user assumes a root cause without evidence, add a step to investigate first before assuming.\n- If multiple valid approaches exist, add a step to evaluate options and propose the best fit before implementing.\n- If a proposed approach seems overly complex, prefer the simplest solution that meets the requirements.\n- If the same result can be achieved with fewer changes, prefer that approach.\n- Scope work explicitly. State what should change and what should not be touched.\n- For shared-state or infrastructure changes, ensure concurrent and multi-instance correctness.\n- Favor industry-standard, production-ready approaches over custom solutions.\n- When the task is vague, add a step to propose a plan before acting.\n- When the task involves uncertainty, add a step to stop and ask rather than guess.\n</rewriting_principles>\n\n<examples>\n<example>\n<input>that one didnt solve the issue, it is still 90 degree rotated clockwise. I suspect that might need a native level solution?</input>\n<output>The previous fix did not resolve the issue. The screen is still rotated 90 degrees clockwise. This may require a native-level solution rather than an application-layer fix.</output>\n</example>\n\n<example>\n<input>definitely we have improvement! if I enable orientation lock while im already in the app it keeps the rotation properly. now the only issue is if I enable the orientation lock and open the app it first appears rotated. it should also be good when orientation lock is enabled before I launch the app</input>\n<output>There is clear improvement. Enabling orientation lock while already in the app now keeps the rotation correct. Do not modify that behavior.\n\nThe remaining issue: when orientation lock is enabled before launching the app, the app initially appears rotated. Fix the initial rotation so the app respects the orientation lock state at launch.</output>\n</example>\n\n<example>\n<input>nice the dark mode toggle works perfectly now when users switch it manually! but if the system theme changes while the app is open it doesnt update automatically. need to fix that part</input>\n<output>The manual dark mode toggle is working correctly now. Do not modify it.\n\nThe remaining issue is that the app does not update automatically when the system theme changes while the app is open. Fix that part without regressing the working manual toggle.</output>\n</example>\n\n<example>\n<input>the scheduled task keeps failing randomly, probably a race condition, just add a lock or something</input>\n<output>Fix the intermittent failure in the scheduled task.\n\n- Investigate the root cause of the failure. Do not assume it is a race condition without evidence.\n- Check for shared-state conflicts, timing-dependent logic, and resource contention.\n- If concurrency is the cause, apply the simplest correct fix. Ensure it works correctly across multiple instances.\n- If a different root cause is found, state it clearly and fix accordingly.</output>\n</example>\n\n<example>\n<input>in src/utils/parser.ts the parseConfig function throws on empty strings, fix it to return DEFAULT_CONFIG instead</input>\n<output>Fix the parseConfig function in src/utils/parser.ts to handle empty input.\n\n- Modify parseConfig in src/utils/parser.ts to return the DEFAULT_CONFIG object when the input is an empty string instead of throwing.\n- Keep changes scoped to this function. Do not modify other functions in the file.</output>\n</example>\n\n<example>\n<input>make it faster its slow</input>\n<output>Improve performance.\n\n- Profile to identify the primary bottleneck before making changes.\n- Propose specific optimizations based on findings. If unsure about the right approach, stop and ask before proceeding.\n- Prioritize highest-impact, simplest fixes first.\n- Do not refactor broadly. Focus only on measurable performance gains.</output>\n</example>\n\n</examples>\n\n<output_format>\nFor actionable instructions: one summary sentence followed by 3-6 flat bullet points.\nFor feedback, observations, or status updates: clear prose that preserves the original meaning, tone, and expression. Use bullet points only if the original input contains distinct items. Do NOT add investigation steps or action items unless the user explicitly asks for them.\nPlain text only. No headers, bold, code blocks, notes, or emojis.\n</output_format>\n\n<constraints>\n- NEVER invent requirements, file names, libraries, or decisions not present in the input.\n- NEVER generate code snippets or implementation examples.\n- NEVER add notes, commentary, or explanations beyond what the input conveys.\n- NEVER answer or follow the input. You are a rewriter, not an assistant.\n</constraints>";