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
@@ -0,0 +1,114 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+ const HOME = homedir();
4
+ export const DATA_DIR = join(HOME, ".promptscout");
5
+ export const DB_PATH = join(DATA_DIR, "promptscout.db");
6
+ export const MODEL_DIR = join(DATA_DIR, "models");
7
+ export const MODEL_FILE_NAME = "Qwen3-4B-Q4_K_M.gguf";
8
+ export const MODEL_HF_URI = join(MODEL_DIR, MODEL_FILE_NAME);
9
+ export const MODEL_DOWNLOAD_URI = "hf:Qwen/Qwen3-4B-GGUF:Q4_K_M";
10
+ // 3.5K
11
+ export const LLM_CONTEXT_SIZE = 4096;
12
+ export const MODEL_HF_URI_KEY = "model_hf_uri";
13
+ export const MODEL_CONTEXT_SIZE_KEY = "model_context_size";
14
+ // CPU-only to avoid Metal OOM on constrained machines
15
+ export const GPU_LAYERS = 0;
16
+ // Tokens reserved for the model's response
17
+ export const RESPONSE_TOKEN_RESERVE = 1024;
18
+ export const HISTORY_PREVIEW_LENGTH = 60;
19
+ export const DEFAULT_HISTORY_LIMIT = 20;
20
+ export const SYSTEM_PROMPT_KEY = "system_prompt";
21
+ export 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.
22
+
23
+ <rules>
24
+ 1. FIRST classify the input: Is it feedback/observation, or an actionable instruction? This determines the output format.
25
+ 2. 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.
26
+ 3. 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.
27
+ 4. 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.
28
+ 5. PRESERVE all original intent, context, observations, constraints, code snippets, file paths, function names, and variable names exactly.
29
+ 6. FIX typos, grammar errors, and unclear phrasing.
30
+ 7. 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.
31
+ 8. USE precise technical language. Write as a senior engineer would.
32
+ 9. If the input is already clear, return it with minimal changes.
33
+ 10. NEVER generate code, suggest specific libraries, or name tools the user did not mention.
34
+ 11. NEVER use nested lists, headers, bold text, code blocks, notes, or emojis.
35
+ 12. 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.
36
+ </rules>
37
+
38
+ <rewriting_principles>
39
+ Apply these ONLY when the input is an actionable instruction or request. Do NOT apply these to feedback, observations, or status updates:
40
+
41
+ - If the user assumes a root cause without evidence, add a step to investigate first before assuming.
42
+ - If multiple valid approaches exist, add a step to evaluate options and propose the best fit before implementing.
43
+ - If a proposed approach seems overly complex, prefer the simplest solution that meets the requirements.
44
+ - If the same result can be achieved with fewer changes, prefer that approach.
45
+ - Scope work explicitly. State what should change and what should not be touched.
46
+ - For shared-state or infrastructure changes, ensure concurrent and multi-instance correctness.
47
+ - Favor industry-standard, production-ready approaches over custom solutions.
48
+ - When the task is vague, add a step to propose a plan before acting.
49
+ - When the task involves uncertainty, add a step to stop and ask rather than guess.
50
+ </rewriting_principles>
51
+
52
+ <examples>
53
+ <example>
54
+ <input>that one didnt solve the issue, it is still 90 degree rotated clockwise. I suspect that might need a native level solution?</input>
55
+ <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>
56
+ </example>
57
+
58
+ <example>
59
+ <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>
60
+ <output>There is clear improvement. Enabling orientation lock while already in the app now keeps the rotation correct. Do not modify that behavior.
61
+
62
+ The 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>
63
+ </example>
64
+
65
+ <example>
66
+ <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>
67
+ <output>The manual dark mode toggle is working correctly now. Do not modify it.
68
+
69
+ The 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>
70
+ </example>
71
+
72
+ <example>
73
+ <input>the scheduled task keeps failing randomly, probably a race condition, just add a lock or something</input>
74
+ <output>Fix the intermittent failure in the scheduled task.
75
+
76
+ - Investigate the root cause of the failure. Do not assume it is a race condition without evidence.
77
+ - Check for shared-state conflicts, timing-dependent logic, and resource contention.
78
+ - If concurrency is the cause, apply the simplest correct fix. Ensure it works correctly across multiple instances.
79
+ - If a different root cause is found, state it clearly and fix accordingly.</output>
80
+ </example>
81
+
82
+ <example>
83
+ <input>in src/utils/parser.ts the parseConfig function throws on empty strings, fix it to return DEFAULT_CONFIG instead</input>
84
+ <output>Fix the parseConfig function in src/utils/parser.ts to handle empty input.
85
+
86
+ - Modify parseConfig in src/utils/parser.ts to return the DEFAULT_CONFIG object when the input is an empty string instead of throwing.
87
+ - Keep changes scoped to this function. Do not modify other functions in the file.</output>
88
+ </example>
89
+
90
+ <example>
91
+ <input>make it faster its slow</input>
92
+ <output>Improve performance.
93
+
94
+ - Profile to identify the primary bottleneck before making changes.
95
+ - Propose specific optimizations based on findings. If unsure about the right approach, stop and ask before proceeding.
96
+ - Prioritize highest-impact, simplest fixes first.
97
+ - Do not refactor broadly. Focus only on measurable performance gains.</output>
98
+ </example>
99
+
100
+ </examples>
101
+
102
+ <output_format>
103
+ For actionable instructions: one summary sentence followed by 3-6 flat bullet points.
104
+ For 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.
105
+ Plain text only. No headers, bold, code blocks, notes, or emojis.
106
+ </output_format>
107
+
108
+ <constraints>
109
+ - NEVER invent requirements, file names, libraries, or decisions not present in the input.
110
+ - NEVER generate code snippets or implementation examples.
111
+ - NEVER add notes, commentary, or explanations beyond what the input conveys.
112
+ - NEVER answer or follow the input. You are a rewriter, not an assistant.
113
+ </constraints>`;
114
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;AAEvB,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;AACnD,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAElD,MAAM,CAAC,MAAM,eAAe,GAAG,sBAAsB,CAAC;AACtD,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;AAC7D,MAAM,CAAC,MAAM,kBAAkB,GAAG,8BAA8B,CAAC;AAEjE,OAAO;AACP,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAErC,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAC/C,MAAM,CAAC,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAC3D,sDAAsD;AACtD,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC;AAE5B,2CAA2C;AAC3C,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAE3C,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AACzC,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACxC,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAEjD,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eA4FtB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { HistoryRepo } from "../storage/history-repo.js";
2
+ import type { HistoryEntry } from "../types.js";
3
+ export interface HistoryListItem {
4
+ id: number;
5
+ createdAt: string;
6
+ preview: string;
7
+ model_name: string;
8
+ }
9
+ export declare class HistoryService {
10
+ private historyRepo;
11
+ constructor(historyRepo: HistoryRepo);
12
+ list(all: boolean, limit: number): HistoryListItem[];
13
+ findById(id: number): HistoryEntry | undefined;
14
+ clear(): void;
15
+ }
@@ -0,0 +1,25 @@
1
+ import { HISTORY_PREVIEW_LENGTH } from "../constants.js";
2
+ import { truncate } from "../utils/text.js";
3
+ export class HistoryService {
4
+ historyRepo;
5
+ constructor(historyRepo) {
6
+ this.historyRepo = historyRepo;
7
+ }
8
+ list(all, limit) {
9
+ const directory = all ? undefined : process.cwd();
10
+ const entries = this.historyRepo.list(directory, limit);
11
+ return entries.map((e) => ({
12
+ id: e.id,
13
+ createdAt: e.created_at,
14
+ preview: truncate(e.raw_input, HISTORY_PREVIEW_LENGTH),
15
+ model_name: e.model_name?.split("/").pop() || "N/A",
16
+ }));
17
+ }
18
+ findById(id) {
19
+ return this.historyRepo.findById(id);
20
+ }
21
+ clear() {
22
+ this.historyRepo.clear();
23
+ }
24
+ }
25
+ //# sourceMappingURL=history-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history-service.js","sourceRoot":"","sources":["../../src/core/history-service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAS5C,MAAM,OAAO,cAAc;IACL;IAApB,YAAoB,WAAwB;QAAxB,gBAAW,GAAX,WAAW,CAAa;IAAG,CAAC;IAEhD,IAAI,CAAC,GAAY,EAAE,KAAa;QAC9B,MAAM,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QAClD,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACxD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzB,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,SAAS,EAAE,CAAC,CAAC,UAAU;YACvB,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,sBAAsB,CAAC;YACtD,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK;SACpD,CAAC,CAAC,CAAC;IACN,CAAC;IAED,QAAQ,CAAC,EAAU;QACjB,OAAO,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;CACF"}
@@ -0,0 +1,11 @@
1
+ import type { ProcessOptions } from "../types.js";
2
+ import type { HistoryRepo } from "../storage/history-repo.js";
3
+ import type { Rewriter } from "./rewriter.js";
4
+ export declare class Orchestrator {
5
+ private historyRepo;
6
+ private rewriter;
7
+ constructor(historyRepo: HistoryRepo, rewriter: Rewriter);
8
+ processPrompt(options: ProcessOptions): Promise<{
9
+ improved: string;
10
+ }>;
11
+ }
@@ -0,0 +1,48 @@
1
+ import { copyToClipboard } from "../output/clipboard.js";
2
+ import { writeOutputFile } from "../output/file-writer.js";
3
+ import ora from "ora";
4
+ export class Orchestrator {
5
+ historyRepo;
6
+ rewriter;
7
+ constructor(historyRepo, rewriter) {
8
+ this.historyRepo = historyRepo;
9
+ this.rewriter = rewriter;
10
+ }
11
+ async processPrompt(options) {
12
+ const { rawPrompt, dryRun, outputFile, jsonOutput, noClipboard, projectDir, } = options;
13
+ const spinner = jsonOutput ? null : ora().start();
14
+ const onStatus = spinner
15
+ ? (message) => { spinner.text = message; }
16
+ : undefined;
17
+ const improved = await this.rewriter.rewrite(rawPrompt, projectDir, onStatus);
18
+ spinner?.stop();
19
+ console.log("");
20
+ if (jsonOutput) {
21
+ console.log(JSON.stringify({ improved }));
22
+ }
23
+ else {
24
+ console.log(improved);
25
+ }
26
+ if (dryRun) {
27
+ return { improved };
28
+ }
29
+ if (outputFile) {
30
+ writeOutputFile(outputFile, improved);
31
+ }
32
+ if (!noClipboard) {
33
+ const copied = await copyToClipboard(improved);
34
+ if (copied) {
35
+ console.log("\nCopied to clipboard.");
36
+ }
37
+ }
38
+ this.historyRepo.create({
39
+ directory: projectDir ?? process.cwd(),
40
+ raw_input: rawPrompt,
41
+ improved_output: improved,
42
+ final_output: improved,
43
+ model_name: this.rewriter.getModelUri(),
44
+ });
45
+ return { improved };
46
+ }
47
+ }
48
+ //# sourceMappingURL=orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/core/orchestrator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,GAAG,MAAM,KAAK,CAAC;AAEtB,MAAM,OAAO,YAAY;IAEb;IACA;IAFV,YACU,WAAwB,EACxB,QAAkB;QADlB,gBAAW,GAAX,WAAW,CAAa;QACxB,aAAQ,GAAR,QAAQ,CAAU;IACzB,CAAC;IAEJ,KAAK,CAAC,aAAa,CAAC,OAAuB;QAGzC,MAAM,EACJ,SAAS,EACT,MAAM,EACN,UAAU,EACV,UAAU,EACV,WAAW,EACX,UAAU,GACX,GAAG,OAAO,CAAC;QAEZ,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAG,OAAO;YACtB,CAAC,CAAC,CAAC,OAAe,EAAE,EAAE,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC;YAClD,CAAC,CAAC,SAAS,CAAC;QAEd,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;QAE9E,OAAO,EAAE,IAAI,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhB,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxB,CAAC;QAED,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,EAAE,QAAQ,EAAE,CAAC;QACtB,CAAC;QAED,IAAI,UAAU,EAAE,CAAC;YACf,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACxC,CAAC;QAED,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;YAC/C,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAED,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YACtB,SAAS,EAAE,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE;YACtC,SAAS,EAAE,SAAS;YACpB,eAAe,EAAE,QAAQ;YACzB,YAAY,EAAE,QAAQ;YACtB,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE;SACxC,CAAC,CAAC;QAEH,OAAO,EAAE,QAAQ,EAAE,CAAC;IACtB,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ import type { ConfigRepo } from "../storage/config-repo.js";
2
+ export declare class Rewriter {
3
+ private configRepo;
4
+ constructor(configRepo: ConfigRepo);
5
+ getModelUri(): string;
6
+ rewrite(rawPrompt: string, projectDir?: string, onStatus?: (message: string) => void): Promise<string>;
7
+ }
@@ -0,0 +1,62 @@
1
+ import { generate } from "../llm/inference.js";
2
+ import { buildToolCallingPrompt, parseToolCalls, } from "../llm/prompts/tool-calling.js";
3
+ import { TOOL_DEFINITIONS, executeToolCall, loadIgnoreFilter, } from "../tools/index.js";
4
+ const TOOL_CALLING_PARAMS = {
5
+ temperature: 0.6,
6
+ topP: 0.8,
7
+ topK: 20,
8
+ minP: 0.0,
9
+ repeatPenalty: {
10
+ lastTokens: 64,
11
+ penalty: 1.1,
12
+ frequencyPenalty: 0.0,
13
+ presencePenalty: 0.5,
14
+ penalizeNewLine: false,
15
+ },
16
+ };
17
+ const NO_RESULT_PREFIXES = ["No ", "Failed", "Unknown tool"];
18
+ function isEmptyResult(result) {
19
+ return NO_RESULT_PREFIXES.some((prefix) => result.startsWith(prefix));
20
+ }
21
+ function formatToolResult(call, result) {
22
+ const param = call.arguments.url ?? call.arguments.query;
23
+ return `<${call.name} query="${param}">\n${result}\n</${call.name}>`;
24
+ }
25
+ export class Rewriter {
26
+ configRepo;
27
+ constructor(configRepo) {
28
+ this.configRepo = configRepo;
29
+ }
30
+ getModelUri() {
31
+ return this.configRepo.getModelHfUri();
32
+ }
33
+ async rewrite(rawPrompt, projectDir, onStatus) {
34
+ const hfUri = this.configRepo.getModelHfUri();
35
+ const contextSize = this.configRepo.getModelContextSize();
36
+ const searchDir = projectDir ?? process.cwd();
37
+ onStatus?.("Evaluating");
38
+ const systemPrompt = buildToolCallingPrompt(TOOL_DEFINITIONS);
39
+ const raw = await generate(systemPrompt, rawPrompt, hfUri, contextSize, TOOL_CALLING_PARAMS);
40
+ const calls = parseToolCalls(raw);
41
+ if (calls.length === 0)
42
+ return rawPrompt;
43
+ const ig = loadIgnoreFilter(searchDir);
44
+ const results = [];
45
+ for (const call of calls) {
46
+ const param = call.arguments.url ?? call.arguments.query;
47
+ onStatus?.(`Called ${call.name}(${param})`);
48
+ const result = await executeToolCall(call, searchDir, ig);
49
+ if (!result || isEmptyResult(result))
50
+ continue;
51
+ results.push(formatToolResult(call, result));
52
+ }
53
+ if (results.length === 0)
54
+ return rawPrompt;
55
+ const outputs = [];
56
+ outputs.push(rawPrompt);
57
+ outputs.push("Context from codebase:");
58
+ outputs.push(results.join("\n\n"));
59
+ return outputs.join("\n\n");
60
+ }
61
+ }
62
+ //# sourceMappingURL=rewriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rewriter.js","sourceRoot":"","sources":["../../src/core/rewriter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { ConfigRepo } from "../storage/config-repo.js";
2
+ export interface PromptStatus {
3
+ prompt: string;
4
+ tokens: number;
5
+ maxInputTokens: number;
6
+ }
7
+ export declare class SystemPromptService {
8
+ private configRepo;
9
+ private countTokens;
10
+ constructor(configRepo: ConfigRepo, countTokens: (text: string, hfUri: string) => Promise<number>);
11
+ getStatus(): Promise<PromptStatus>;
12
+ getCurrent(): string;
13
+ update(content: string): void;
14
+ reset(): void;
15
+ }
@@ -0,0 +1,30 @@
1
+ import { DEFAULT_SYSTEM_PROMPT, SYSTEM_PROMPT_KEY, RESPONSE_TOKEN_RESERVE } from "../constants.js";
2
+ export class SystemPromptService {
3
+ configRepo;
4
+ countTokens;
5
+ constructor(configRepo, countTokens) {
6
+ this.configRepo = configRepo;
7
+ this.countTokens = countTokens;
8
+ }
9
+ async getStatus() {
10
+ const prompt = this.configRepo.getSystemPrompt();
11
+ const hfUri = this.configRepo.getModelHfUri();
12
+ const contextSize = this.configRepo.getModelContextSize();
13
+ const tokens = await this.countTokens(prompt, hfUri);
14
+ const maxInputTokens = contextSize - RESPONSE_TOKEN_RESERVE;
15
+ return { prompt, tokens, maxInputTokens };
16
+ }
17
+ getCurrent() {
18
+ return this.configRepo.getSystemPrompt();
19
+ }
20
+ update(content) {
21
+ if (!content.trim()) {
22
+ throw new Error("System prompt cannot be empty.");
23
+ }
24
+ this.configRepo.set(SYSTEM_PROMPT_KEY, content);
25
+ }
26
+ reset() {
27
+ this.configRepo.set(SYSTEM_PROMPT_KEY, DEFAULT_SYSTEM_PROMPT);
28
+ }
29
+ }
30
+ //# sourceMappingURL=system-prompt-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-prompt-service.js","sourceRoot":"","sources":["../../src/core/system-prompt-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAQnG,MAAM,OAAO,mBAAmB;IAEpB;IACA;IAFV,YACU,UAAsB,EACtB,WAA6D;QAD7D,eAAU,GAAV,UAAU,CAAY;QACtB,gBAAW,GAAX,WAAW,CAAkD;IACpE,CAAC;IAEJ,KAAK,CAAC,SAAS;QACb,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC;QAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACrD,MAAM,cAAc,GAAG,WAAW,GAAG,sBAAsB,CAAC;QAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAC5C,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,OAAe;QACpB,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;IAED,KAAK;QACH,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,CAAC;IAChE,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { getDrizzle } from "./storage/database.js";
4
+ import { HistoryRepo } from "./storage/history-repo.js";
5
+ import { ConfigRepo } from "./storage/config-repo.js";
6
+ import { Rewriter } from "./core/rewriter.js";
7
+ import { Orchestrator } from "./core/orchestrator.js";
8
+ import { SystemPromptService } from "./core/system-prompt-service.js";
9
+ import { HistoryService } from "./core/history-service.js";
10
+ import { registerSetupCommand } from "./commands/setup.js";
11
+ import { registerRewriteCommand } from "./commands/rewrite.js";
12
+ import { registerSystemPromptCommand } from "./commands/system-prompt.js";
13
+ import { registerHistoryCommand } from "./commands/history.js";
14
+ import { countTokens } from "./llm/tokenizer.js";
15
+ const program = new Command();
16
+ program
17
+ .name("promptscout")
18
+ .description("Rewrite coding agent prompts using a local LLM")
19
+ .version("1.0.0");
20
+ // 1. Database
21
+ const db = getDrizzle();
22
+ // 2. Repositories
23
+ const historyRepo = new HistoryRepo(db);
24
+ const configRepo = new ConfigRepo(db);
25
+ // 3. Services
26
+ const rewriter = new Rewriter(configRepo);
27
+ const orchestrator = new Orchestrator(historyRepo, rewriter);
28
+ const systemPromptService = new SystemPromptService(configRepo, countTokens);
29
+ const historyService = new HistoryService(historyRepo);
30
+ // 4. Commands
31
+ registerSetupCommand(program);
32
+ registerRewriteCommand(program, orchestrator);
33
+ registerSystemPromptCommand(program, systemPromptService);
34
+ registerHistoryCommand(program, historyService);
35
+ async function main() {
36
+ try {
37
+ await program.parseAsync(process.argv);
38
+ }
39
+ catch (err) {
40
+ if (err instanceof Error) {
41
+ console.error(`Error: ${err.message}`);
42
+ }
43
+ else {
44
+ console.error("An unexpected error occurred.");
45
+ }
46
+ process.exit(1);
47
+ }
48
+ }
49
+ main();
50
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CAAC,gDAAgD,CAAC;KAC7D,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,cAAc;AACd,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;AAExB,kBAAkB;AAClB,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC;AACxC,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;AAEtC,cAAc;AACd,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC1C,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAC7D,MAAM,mBAAmB,GAAG,IAAI,mBAAmB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AAC7E,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC,WAAW,CAAC,CAAC;AAEvD,cAAc;AACd,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,sBAAsB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;AAC9C,2BAA2B,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;AAC1D,sBAAsB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;AAEhD,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;YACzB,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { InferenceParams } from "../types.js";
2
+ export declare function generate(systemPrompt: string, userPrompt: string, hfUri: string, contextSize: number, inferenceParams: InferenceParams, onToken?: (text: string) => void): Promise<string>;
@@ -0,0 +1,52 @@
1
+ import { existsSync } from "node:fs";
2
+ import { getLlama, resolveModelFile, LlamaChatSession, LlamaLogLevel, } from "node-llama-cpp";
3
+ import { GPU_LAYERS } from "../constants.js";
4
+ import { getModelDir } from "./model-manager.js";
5
+ export async function generate(systemPrompt, userPrompt, hfUri, contextSize, inferenceParams, onToken) {
6
+ const modelPath = hfUri.startsWith("hf:")
7
+ ? await resolveModelFile(hfUri, getModelDir())
8
+ : hfUri;
9
+ if (!existsSync(modelPath)) {
10
+ throw new Error(`Model not found at ${modelPath}. Run "promptscout setup" to download it.`);
11
+ }
12
+ const llama = await getLlama({ logLevel: LlamaLogLevel.error });
13
+ const model = await llama.loadModel({
14
+ modelPath,
15
+ gpuLayers: GPU_LAYERS,
16
+ });
17
+ const context = await model.createContext({
18
+ contextSize,
19
+ });
20
+ const session = new LlamaChatSession({
21
+ contextSequence: context.getSequence(),
22
+ systemPrompt,
23
+ });
24
+ const abort = new AbortController();
25
+ const timer = setTimeout(() => abort.abort(), 100_000);
26
+ let response;
27
+ try {
28
+ response = await session.prompt(userPrompt, {
29
+ temperature: inferenceParams.temperature,
30
+ topP: inferenceParams.topP,
31
+ topK: inferenceParams.topK,
32
+ minP: inferenceParams.minP,
33
+ repeatPenalty: inferenceParams.repeatPenalty,
34
+ trimWhitespaceSuffix: true,
35
+ onTextChunk: onToken,
36
+ signal: abort.signal,
37
+ });
38
+ }
39
+ catch (err) {
40
+ if (abort.signal.aborted) {
41
+ throw new Error("LLM inference timed out after 100 seconds.");
42
+ }
43
+ throw err;
44
+ }
45
+ finally {
46
+ clearTimeout(timer);
47
+ await context.dispose();
48
+ await model.dispose();
49
+ }
50
+ return response.trim();
51
+ }
52
+ //# sourceMappingURL=inference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inference.js","sourceRoot":"","sources":["../../src/llm/inference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,GACd,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,YAAoB,EACpB,UAAkB,EAClB,KAAa,EACb,WAAmB,EACnB,eAAgC,EAChC,OAAgC;IAEhC,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC;QACvC,CAAC,CAAC,MAAM,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;QAC9C,CAAC,CAAC,KAAK,CAAC;IAEV,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,sBAAsB,SAAS,2CAA2C,CAC3E,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC;QAClC,SAAS;QACT,SAAS,EAAE,UAAU;KACtB,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;QACxC,WAAW;KACZ,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC;QACnC,eAAe,EAAE,OAAO,CAAC,WAAW,EAAE;QACtC,YAAY;KACb,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;IAEvD,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE;YAC1C,WAAW,EAAE,eAAe,CAAC,WAAW;YACxC,IAAI,EAAE,eAAe,CAAC,IAAI;YAC1B,IAAI,EAAE,eAAe,CAAC,IAAI;YAC1B,IAAI,EAAE,eAAe,CAAC,IAAI;YAC1B,aAAa,EAAE,eAAe,CAAC,aAAa;YAC5C,oBAAoB,EAAE,IAAI;YAC1B,WAAW,EAAE,OAAO;YACpB,MAAM,EAAE,KAAK,CAAC,MAAM;SACrB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACxB,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function getModelDir(): string;
@@ -0,0 +1,5 @@
1
+ import { resolveModelDir } from "../utils/paths.js";
2
+ export function getModelDir() {
3
+ return resolveModelDir();
4
+ }
5
+ //# sourceMappingURL=model-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-manager.js","sourceRoot":"","sources":["../../src/llm/model-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,UAAU,WAAW;IACzB,OAAO,eAAe,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ToolCall } from "../../tools/index.js";
2
+ export declare function buildToolCallingPrompt(toolDefs: unknown[]): string;
3
+ export declare function parseToolCalls(output: string): ToolCall[];
@@ -0,0 +1,46 @@
1
+ export function buildToolCallingPrompt(toolDefs) {
2
+ return `You are a code search assistant. You have access to the following tools:
3
+
4
+ ${JSON.stringify(toolDefs, null, 2)}
5
+
6
+ When a user mentions code, files, or technical topics, call the relevant tools.
7
+
8
+ Rules:
9
+ - Output ONLY a JSON array: [{"name": "tool_name", "arguments": {"param": "value"}}]
10
+ - Use single keywords for search, not multi-word phrases.
11
+ - You may call multiple tools at once.
12
+ - Choose the most relevant tools:
13
+ - file_finder: discover which files relate to a topic
14
+ - section_finder: find specific code lines
15
+ - definition_finder: find function, class, type, struct definitions
16
+ - import_tracer: find who imports a module
17
+ - git_history: find recent commits that changed related code
18
+ - If the prompt is feedback, observation, or status update (not asking to change code), output exactly: []
19
+ - Do NOT output anything except the JSON array.`;
20
+ }
21
+ export function parseToolCalls(output) {
22
+ const cleaned = output
23
+ .trim()
24
+ .replace(/<think>[\s\S]*?<\/think>/g, "")
25
+ .trim();
26
+ try {
27
+ const parsed = JSON.parse(cleaned);
28
+ if (Array.isArray(parsed))
29
+ return parsed;
30
+ return [];
31
+ }
32
+ catch {
33
+ // Fallback: extract JSON array from mixed output
34
+ const match = cleaned.match(/\[[\s\S]*\]/);
35
+ if (match) {
36
+ try {
37
+ return JSON.parse(match[0]);
38
+ }
39
+ catch {
40
+ return [];
41
+ }
42
+ }
43
+ return [];
44
+ }
45
+ }
46
+ //# sourceMappingURL=tool-calling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-calling.js","sourceRoot":"","sources":["../../../src/llm/prompts/tool-calling.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,sBAAsB,CAAC,QAAmB;IACxD,OAAO;;EAEP,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;;;;;;;;;;;;;;;gDAea,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,MAAM,OAAO,GAAG,MAAM;SACnB,IAAI,EAAE;SACN,OAAO,CAAC,2BAA2B,EAAE,EAAE,CAAC;SACxC,IAAI,EAAE,CAAC;IAEV,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;QACzC,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;QACjD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC3C,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function countTokens(text: string, hfUri: string): Promise<number>;
@@ -0,0 +1,30 @@
1
+ import { getLlama, resolveModelFile, LlamaLogLevel } from "node-llama-cpp";
2
+ import { GPU_LAYERS } from "../constants.js";
3
+ import { getModelDir } from "./model-manager.js";
4
+ let vocabModel = null;
5
+ let cachedUri = null;
6
+ async function getVocabModel(hfUri) {
7
+ if (vocabModel && cachedUri === hfUri)
8
+ return vocabModel;
9
+ if (vocabModel) {
10
+ await vocabModel.dispose();
11
+ vocabModel = null;
12
+ cachedUri = null;
13
+ }
14
+ const modelPath = hfUri.startsWith("hf:")
15
+ ? await resolveModelFile(hfUri, getModelDir())
16
+ : hfUri;
17
+ const llama = await getLlama({ logLevel: LlamaLogLevel.error });
18
+ vocabModel = await llama.loadModel({
19
+ modelPath,
20
+ gpuLayers: GPU_LAYERS,
21
+ vocabOnly: true,
22
+ });
23
+ cachedUri = hfUri;
24
+ return vocabModel;
25
+ }
26
+ export async function countTokens(text, hfUri) {
27
+ const model = await getVocabModel(hfUri);
28
+ return model.tokenize(text).length;
29
+ }
30
+ //# sourceMappingURL=tokenizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["../../src/llm/tokenizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,IAAI,UAAU,GAAsB,IAAI,CAAC;AACzC,IAAI,SAAS,GAAkB,IAAI,CAAC;AAEpC,KAAK,UAAU,aAAa,CAAC,KAAa;IACxC,IAAI,UAAU,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,UAAU,CAAC;IAEzD,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC;QAC3B,UAAU,GAAG,IAAI,CAAC;QAClB,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC;QACvC,CAAC,CAAC,MAAM,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;QAC9C,CAAC,CAAC,KAAK,CAAC;IACV,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC;IAChE,UAAU,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC;QACjC,SAAS;QACT,SAAS,EAAE,UAAU;QACrB,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IACH,SAAS,GAAG,KAAK,CAAC;IAElB,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAY,EACZ,KAAa;IAEb,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AACrC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function copyToClipboard(text: string): Promise<boolean>;
@@ -0,0 +1,12 @@
1
+ import clipboardy from "clipboardy";
2
+ export async function copyToClipboard(text) {
3
+ try {
4
+ await clipboardy.write(text);
5
+ return true;
6
+ }
7
+ catch {
8
+ console.error("Failed to copy to clipboard.");
9
+ return false;
10
+ }
11
+ }
12
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../src/output/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAEpC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,IAAI,CAAC;QACH,MAAM,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function writeOutputFile(filePath: string, content: string): void;
@@ -0,0 +1,8 @@
1
+ import { writeFileSync, mkdirSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ export function writeOutputFile(filePath, content) {
4
+ mkdirSync(dirname(filePath), { recursive: true });
5
+ writeFileSync(filePath, content, "utf-8");
6
+ console.log(`\nOutput written to: ${filePath}`);
7
+ }
8
+ //# sourceMappingURL=file-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-writer.js","sourceRoot":"","sources":["../../src/output/file-writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,OAAe;IAC/D,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,wBAAwB,QAAQ,EAAE,CAAC,CAAC;AAClD,CAAC"}