agentic-loop 3.12.3 → 3.14.1
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/.claude/skills/loopgram/SKILL.md +19 -0
- package/README.md +1 -0
- package/bin/ralph.sh +17 -11
- package/dist/loopgram/claude.d.ts +18 -0
- package/dist/loopgram/claude.d.ts.map +1 -0
- package/dist/loopgram/claude.js +89 -0
- package/dist/loopgram/claude.js.map +1 -0
- package/dist/loopgram/context-search.d.ts +26 -0
- package/dist/loopgram/context-search.d.ts.map +1 -0
- package/dist/loopgram/context-search.js +175 -0
- package/dist/loopgram/context-search.js.map +1 -0
- package/dist/loopgram/conversation.d.ts +39 -0
- package/dist/loopgram/conversation.d.ts.map +1 -0
- package/dist/loopgram/conversation.js +158 -0
- package/dist/loopgram/conversation.js.map +1 -0
- package/dist/loopgram/index.d.ts +3 -0
- package/dist/loopgram/index.d.ts.map +1 -0
- package/dist/loopgram/index.js +246 -0
- package/dist/loopgram/index.js.map +1 -0
- package/dist/loopgram/loop-monitor.d.ts +16 -0
- package/dist/loopgram/loop-monitor.d.ts.map +1 -0
- package/dist/loopgram/loop-monitor.js +149 -0
- package/dist/loopgram/loop-monitor.js.map +1 -0
- package/dist/loopgram/loop-runner.d.ts +28 -0
- package/dist/loopgram/loop-runner.d.ts.map +1 -0
- package/dist/loopgram/loop-runner.js +157 -0
- package/dist/loopgram/loop-runner.js.map +1 -0
- package/dist/loopgram/prd-generator.d.ts +37 -0
- package/dist/loopgram/prd-generator.d.ts.map +1 -0
- package/dist/loopgram/prd-generator.js +134 -0
- package/dist/loopgram/prd-generator.js.map +1 -0
- package/dist/loopgram/saver.d.ts +9 -0
- package/dist/loopgram/saver.d.ts.map +1 -0
- package/dist/loopgram/saver.js +35 -0
- package/dist/loopgram/saver.js.map +1 -0
- package/dist/loopgram/types.d.ts +37 -0
- package/dist/loopgram/types.d.ts.map +1 -0
- package/dist/loopgram/types.js +5 -0
- package/dist/loopgram/types.js.map +1 -0
- package/package.json +6 -2
- package/ralph/hooks/common.sh +89 -0
- package/ralph/hooks/warn-debug.sh +14 -32
- package/ralph/hooks/warn-empty-catch.sh +13 -29
- package/ralph/hooks/warn-secrets.sh +19 -37
- package/ralph/hooks/warn-urls.sh +17 -33
- package/ralph/init.sh +30 -0
- package/ralph/loop.sh +5 -2
- package/ralph/setup/ui.sh +0 -42
- package/ralph/setup.sh +69 -45
- package/ralph/utils.sh +167 -31
- package/templates/config/fastmcp.json +5 -5
- package/templates/hooks/warn-uv-python.sh +38 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Review and manage brainstorm ideas from Telegram
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Loopgram Review
|
|
6
|
+
|
|
7
|
+
Review ideas captured from Telegram via Loopgram.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
- `/loopgram list` - Show all saved ideas
|
|
12
|
+
- `/loopgram review <name>` - Review and refine a specific idea
|
|
13
|
+
- `/loopgram prd <name>` - Convert idea to PRD for Ralph execution
|
|
14
|
+
|
|
15
|
+
## Workflow
|
|
16
|
+
|
|
17
|
+
1. List recent ideas: `ls docs/ideas/`
|
|
18
|
+
2. Read an idea: Read the file
|
|
19
|
+
3. If ready, run `/prd docs/ideas/{name}.md` to convert to executable PRD
|
package/README.md
CHANGED
|
@@ -86,6 +86,7 @@ npx agentic-loop run # Execute PRDs autonomously
|
|
|
86
86
|
- [How Ralph Works](docs/RALPH.md) - Architecture, config, full reference
|
|
87
87
|
- [Cheatsheet](docs/CHEATSHEET.md) - All commands at a glance
|
|
88
88
|
- [Hooks Reference](docs/HOOKS.md) - Pre-commit and Claude Code hooks
|
|
89
|
+
- [Loopgram](docs/LOOPGRAM.md) - Telegram bot for mobile idea capture and loop monitoring
|
|
89
90
|
- [Troubleshooting](docs/TROUBLESHOOTING.md) - Common issues and fixes
|
|
90
91
|
- [Contributing](docs/CONTRIBUTING.md) - How to contribute
|
|
91
92
|
|
package/bin/ralph.sh
CHANGED
|
@@ -61,13 +61,22 @@ if [[ ! -f ".ralph/config.json" ]]; then
|
|
|
61
61
|
_project_type="node"
|
|
62
62
|
fi
|
|
63
63
|
|
|
64
|
+
# Check user template override first
|
|
65
|
+
_user_template="$HOME/.config/ralph/templates/config/${_project_type}.json"
|
|
64
66
|
_config_template="$RALPH_TEMPLATES/config/${_project_type}.json"
|
|
65
|
-
|
|
67
|
+
|
|
68
|
+
if [[ -f "$_user_template" ]]; then
|
|
69
|
+
cp "$_user_template" ".ralph/config.json"
|
|
70
|
+
echo "Using user template: $_user_template" >&2
|
|
71
|
+
elif [[ -f "$_config_template" ]]; then
|
|
66
72
|
cp "$_config_template" ".ralph/config.json"
|
|
67
73
|
else
|
|
68
74
|
# Fall back to minimal config if template doesn't exist
|
|
75
|
+
_user_minimal="$HOME/.config/ralph/templates/config/minimal.json"
|
|
69
76
|
_config_template="$RALPH_TEMPLATES/config/minimal.json"
|
|
70
|
-
if [[ -f "$
|
|
77
|
+
if [[ -f "$_user_minimal" ]]; then
|
|
78
|
+
cp "$_user_minimal" ".ralph/config.json"
|
|
79
|
+
elif [[ -f "$_config_template" ]]; then
|
|
71
80
|
cp "$_config_template" ".ralph/config.json"
|
|
72
81
|
else
|
|
73
82
|
echo '{"projectType": "unknown"}' > ".ralph/config.json"
|
|
@@ -101,6 +110,11 @@ if [[ "${_ralph_needs_autoconfig:-}" == "true" ]]; then
|
|
|
101
110
|
unset _ralph_needs_autoconfig
|
|
102
111
|
fi
|
|
103
112
|
|
|
113
|
+
# Merge user config defaults into project config
|
|
114
|
+
if [[ -f "$RALPH_DIR/config.json" && -f "$HOME/.config/ralph/config.json" ]]; then
|
|
115
|
+
_merge_user_config
|
|
116
|
+
fi
|
|
117
|
+
|
|
104
118
|
# Main entry point
|
|
105
119
|
main() {
|
|
106
120
|
local cmd="${1:-help}"
|
|
@@ -185,15 +199,7 @@ main() {
|
|
|
185
199
|
fi
|
|
186
200
|
;;
|
|
187
201
|
config)
|
|
188
|
-
|
|
189
|
-
print_error "Ralph not initialized. Run 'ralph init' first."
|
|
190
|
-
exit 1
|
|
191
|
-
fi
|
|
192
|
-
echo "Auto-detecting project configuration..."
|
|
193
|
-
auto_configure_project
|
|
194
|
-
echo ""
|
|
195
|
-
echo "Current config:"
|
|
196
|
-
jq '.' "$RALPH_DIR/config.json"
|
|
202
|
+
ralph_config "$@"
|
|
197
203
|
;;
|
|
198
204
|
help|-h|--help)
|
|
199
205
|
ralph_help
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Message } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build system prompt with optional project context
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildSystemPrompt(projectContext?: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Send a message to Claude and get a response
|
|
8
|
+
*/
|
|
9
|
+
export declare function chat(history: Message[], model: string, projectContext?: string): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a summary of the conversation for saving
|
|
12
|
+
*/
|
|
13
|
+
export declare function summarize(history: Message[], model: string): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Extract a title from a summary document
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractTitle(summary: string): string;
|
|
18
|
+
//# sourceMappingURL=claude.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/loopgram/claude.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAuB1C;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAWjE;AAWD;;GAEG;AACH,wBAAsB,IAAI,CACxB,OAAO,EAAE,OAAO,EAAE,EAClB,KAAK,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,OAAO,EAAE,EAClB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAepD"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
const anthropic = new Anthropic();
|
|
3
|
+
const BASE_SYSTEM_PROMPT = `You are a thoughtful brainstorming partner helping develop software ideas.
|
|
4
|
+
|
|
5
|
+
Your role:
|
|
6
|
+
- Ask clarifying questions to deeply understand the idea
|
|
7
|
+
- Explore edge cases and trade-offs
|
|
8
|
+
- Challenge assumptions constructively
|
|
9
|
+
- Help refine vague concepts into concrete plans
|
|
10
|
+
- Keep responses concise but insightful (2-4 sentences usually)
|
|
11
|
+
|
|
12
|
+
When the user says "save this" or similar:
|
|
13
|
+
- Summarize the key decisions made
|
|
14
|
+
- Format as a structured idea document
|
|
15
|
+
- Confirm what you're saving
|
|
16
|
+
|
|
17
|
+
Don't:
|
|
18
|
+
- Write code unless asked
|
|
19
|
+
- Be overly agreeable - push back on weak ideas
|
|
20
|
+
- Give long lectures - keep it conversational`;
|
|
21
|
+
/**
|
|
22
|
+
* Build system prompt with optional project context
|
|
23
|
+
*/
|
|
24
|
+
export function buildSystemPrompt(projectContext) {
|
|
25
|
+
if (!projectContext) {
|
|
26
|
+
return BASE_SYSTEM_PROMPT;
|
|
27
|
+
}
|
|
28
|
+
return `${BASE_SYSTEM_PROMPT}
|
|
29
|
+
|
|
30
|
+
## Project Context
|
|
31
|
+
You are brainstorming for a specific project. Here's context about it:
|
|
32
|
+
|
|
33
|
+
${projectContext}`;
|
|
34
|
+
}
|
|
35
|
+
const SUMMARY_PROMPT = `Summarize this brainstorming conversation into a structured idea document with:
|
|
36
|
+
- Title (short, descriptive)
|
|
37
|
+
- Summary (2-3 sentences)
|
|
38
|
+
- Key Decisions (bullet points)
|
|
39
|
+
- Open Questions (if any)
|
|
40
|
+
- Next Steps
|
|
41
|
+
|
|
42
|
+
Format as clean markdown.`;
|
|
43
|
+
/**
|
|
44
|
+
* Send a message to Claude and get a response
|
|
45
|
+
*/
|
|
46
|
+
export async function chat(history, model, projectContext) {
|
|
47
|
+
const response = await anthropic.messages.create({
|
|
48
|
+
model,
|
|
49
|
+
max_tokens: 500,
|
|
50
|
+
system: buildSystemPrompt(projectContext),
|
|
51
|
+
messages: history,
|
|
52
|
+
});
|
|
53
|
+
const content = response.content[0];
|
|
54
|
+
return content.type === 'text' ? content.text : '';
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Generate a summary of the conversation for saving
|
|
58
|
+
*/
|
|
59
|
+
export async function summarize(history, model) {
|
|
60
|
+
const conversationText = history
|
|
61
|
+
.map((m) => `${m.role.toUpperCase()}: ${m.content}`)
|
|
62
|
+
.join('\n\n');
|
|
63
|
+
const response = await anthropic.messages.create({
|
|
64
|
+
model,
|
|
65
|
+
max_tokens: 1000,
|
|
66
|
+
system: SUMMARY_PROMPT,
|
|
67
|
+
messages: [{ role: 'user', content: conversationText }],
|
|
68
|
+
});
|
|
69
|
+
const content = response.content[0];
|
|
70
|
+
return content.type === 'text' ? content.text : '';
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Extract a title from a summary document
|
|
74
|
+
*/
|
|
75
|
+
export function extractTitle(summary) {
|
|
76
|
+
// Try to find a markdown heading
|
|
77
|
+
const headingMatch = summary.match(/^#\s*(.+)/m);
|
|
78
|
+
if (headingMatch) {
|
|
79
|
+
return headingMatch[1].replace(/^Title:?\s*/i, '').trim();
|
|
80
|
+
}
|
|
81
|
+
// Try to find "Title:" line
|
|
82
|
+
const titleMatch = summary.match(/^Title:?\s*(.+)/im);
|
|
83
|
+
if (titleMatch) {
|
|
84
|
+
return titleMatch[1].trim();
|
|
85
|
+
}
|
|
86
|
+
// Fallback to timestamp
|
|
87
|
+
return `brainstorm-${Date.now()}`;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/loopgram/claude.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAG1C,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;AAElC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;8CAiBmB,CAAC;AAE/C;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,cAAuB;IACvD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,OAAO,GAAG,kBAAkB;;;;;EAK5B,cAAc,EAAE,CAAA;AAClB,CAAC;AAED,MAAM,cAAc,GAAG;;;;;;;0BAOG,CAAC;AAE3B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CACxB,OAAkB,EAClB,KAAa,EACb,cAAuB;IAEvB,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC/C,KAAK;QACL,UAAU,EAAE,GAAG;QACf,MAAM,EAAE,iBAAiB,CAAC,cAAc,CAAC;QACzC,QAAQ,EAAE,OAAO;KAClB,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACpC,OAAO,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,OAAkB,EAClB,KAAa;IAEb,MAAM,gBAAgB,GAAG,OAAO;SAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;SACnD,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC/C,KAAK;QACL,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,cAAc;QACtB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;KACxD,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACpC,OAAO,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,iCAAiC;IACjC,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACjD,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5D,CAAC;IAED,4BAA4B;IAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACtD,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;IAED,wBAAwB;IACxB,OAAO,cAAc,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
interface SearchResult {
|
|
2
|
+
file: string;
|
|
3
|
+
matches: string[];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Search a project for files related to a topic using ripgrep
|
|
7
|
+
*/
|
|
8
|
+
export declare function searchProject(projectPath: string, topic: string): SearchResult[];
|
|
9
|
+
/**
|
|
10
|
+
* Read key project files for additional context
|
|
11
|
+
*/
|
|
12
|
+
export declare function readProjectFiles(projectPath: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Generate a summary of search results using Claude
|
|
15
|
+
*/
|
|
16
|
+
export declare function generateContextSummary(projectName: string, topic: string, searchResults: SearchResult[], projectContext: string, model: string): Promise<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Search project and generate context for brainstorming
|
|
19
|
+
*/
|
|
20
|
+
export declare function getTopicContext(projectPath: string, projectName: string, topic: string, model: string): Promise<{
|
|
21
|
+
summary: string;
|
|
22
|
+
filesFound: string[];
|
|
23
|
+
searchTerms: string[];
|
|
24
|
+
}>;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=context-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-search.d.ts","sourceRoot":"","sources":["../../src/loopgram/context-search.ts"],"names":[],"mappings":"AAOA,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AA+BD;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CA+ChF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA2B5D;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,YAAY,EAAE,EAC7B,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAsCjB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAyC3E"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { join, relative } from 'path';
|
|
4
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
5
|
+
const anthropic = new Anthropic();
|
|
6
|
+
/**
|
|
7
|
+
* Use AI to generate smart search terms for a topic
|
|
8
|
+
*/
|
|
9
|
+
async function generateSearchTerms(topic, model) {
|
|
10
|
+
const response = await anthropic.messages.create({
|
|
11
|
+
model,
|
|
12
|
+
max_tokens: 200,
|
|
13
|
+
system: `You generate search terms for code search. Given a topic, output 5-8 relevant keywords/patterns to search for in a codebase. Output ONLY the terms, one per line, no explanations. Include variations (e.g., "auth", "authenticate", "authentication").`,
|
|
14
|
+
messages: [
|
|
15
|
+
{ role: 'user', content: `Topic: ${topic}` },
|
|
16
|
+
],
|
|
17
|
+
});
|
|
18
|
+
const text = response.content[0];
|
|
19
|
+
if (text.type !== 'text')
|
|
20
|
+
return [topic];
|
|
21
|
+
const terms = text.text
|
|
22
|
+
.split('\n')
|
|
23
|
+
.map(t => t.trim().toLowerCase())
|
|
24
|
+
.filter(t => t.length > 0 && t.length < 30);
|
|
25
|
+
// Always include the original topic
|
|
26
|
+
if (!terms.includes(topic.toLowerCase())) {
|
|
27
|
+
terms.unshift(topic.toLowerCase());
|
|
28
|
+
}
|
|
29
|
+
return terms.slice(0, 8);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Search a project for files related to a topic using ripgrep
|
|
33
|
+
*/
|
|
34
|
+
export function searchProject(projectPath, topic) {
|
|
35
|
+
const results = [];
|
|
36
|
+
try {
|
|
37
|
+
// Use ripgrep to find matching files (case insensitive)
|
|
38
|
+
// Escape special regex characters in topic
|
|
39
|
+
const escapedTopic = topic.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
40
|
+
// Use grep instead of rg for compatibility
|
|
41
|
+
const cmd = `grep -r -i -l "${escapedTopic}" "${projectPath}" --include="*.py" --include="*.ts" --include="*.js" --include="*.md" --include="*.json" --include="*.sh" 2>/dev/null | grep -v node_modules | grep -v __pycache__ | grep -v ".git" | head -20 || true`;
|
|
42
|
+
console.log(`[search] Running: ${cmd}`);
|
|
43
|
+
const rgOutput = execSync(cmd, {
|
|
44
|
+
encoding: 'utf-8',
|
|
45
|
+
maxBuffer: 1024 * 1024,
|
|
46
|
+
shell: '/bin/zsh'
|
|
47
|
+
}).trim();
|
|
48
|
+
console.log(`[search] Found ${rgOutput ? rgOutput.split('\n').length : 0} files for "${topic}"`);
|
|
49
|
+
if (!rgOutput) {
|
|
50
|
+
return results;
|
|
51
|
+
}
|
|
52
|
+
const files = rgOutput.split('\n').filter(Boolean);
|
|
53
|
+
for (const file of files.slice(0, 10)) { // Limit to 10 files
|
|
54
|
+
try {
|
|
55
|
+
// Get matching lines with context
|
|
56
|
+
const matchOutput = execSync(`rg -i -C 2 "${topic}" "${file}" 2>/dev/null | head -30`, { encoding: 'utf-8', maxBuffer: 1024 * 1024 }).trim();
|
|
57
|
+
results.push({
|
|
58
|
+
file: relative(projectPath, file),
|
|
59
|
+
matches: matchOutput ? [matchOutput] : [],
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// File might not have readable matches
|
|
64
|
+
results.push({ file: relative(projectPath, file), matches: [] });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// ripgrep not found or no matches
|
|
70
|
+
console.error('Search error:', error);
|
|
71
|
+
}
|
|
72
|
+
return results;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Read key project files for additional context
|
|
76
|
+
*/
|
|
77
|
+
export function readProjectFiles(projectPath) {
|
|
78
|
+
const contextFiles = [
|
|
79
|
+
'CLAUDE.md',
|
|
80
|
+
'README.md',
|
|
81
|
+
'package.json',
|
|
82
|
+
'pyproject.toml',
|
|
83
|
+
];
|
|
84
|
+
let context = '';
|
|
85
|
+
for (const file of contextFiles) {
|
|
86
|
+
const filePath = join(projectPath, file);
|
|
87
|
+
if (existsSync(filePath)) {
|
|
88
|
+
try {
|
|
89
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
90
|
+
// Truncate large files
|
|
91
|
+
const truncated = content.length > 2000
|
|
92
|
+
? content.substring(0, 2000) + '\n...[truncated]'
|
|
93
|
+
: content;
|
|
94
|
+
context += `\n### ${file}\n${truncated}\n`;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Skip unreadable files
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return context;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Generate a summary of search results using Claude
|
|
105
|
+
*/
|
|
106
|
+
export async function generateContextSummary(projectName, topic, searchResults, projectContext, model) {
|
|
107
|
+
if (searchResults.length === 0) {
|
|
108
|
+
return `No code found related to "${topic}" in ${projectName}. This might be a new area to build.`;
|
|
109
|
+
}
|
|
110
|
+
// Build the content to summarize
|
|
111
|
+
let content = `Project: ${projectName}\nTopic: ${topic}\n\n`;
|
|
112
|
+
content += `## Project Context\n${projectContext}\n\n`;
|
|
113
|
+
content += `## Search Results\n`;
|
|
114
|
+
for (const result of searchResults) {
|
|
115
|
+
content += `\n### ${result.file}\n`;
|
|
116
|
+
if (result.matches.length > 0) {
|
|
117
|
+
content += '```\n' + result.matches.join('\n---\n') + '\n```\n';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const response = await anthropic.messages.create({
|
|
121
|
+
model,
|
|
122
|
+
max_tokens: 800,
|
|
123
|
+
system: `You are summarizing code search results to provide context for brainstorming.
|
|
124
|
+
Be concise but thorough. Focus on:
|
|
125
|
+
- What exists related to this topic
|
|
126
|
+
- Key patterns and approaches used
|
|
127
|
+
- Important files/functions
|
|
128
|
+
- Potential extension points
|
|
129
|
+
|
|
130
|
+
Keep the summary under 500 words.`,
|
|
131
|
+
messages: [
|
|
132
|
+
{
|
|
133
|
+
role: 'user',
|
|
134
|
+
content: `Summarize what exists in this codebase related to "${topic}":\n\n${content}`,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
const text = response.content[0];
|
|
139
|
+
return text.type === 'text' ? text.text : 'Unable to generate summary.';
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Search project and generate context for brainstorming
|
|
143
|
+
*/
|
|
144
|
+
export async function getTopicContext(projectPath, projectName, topic, model) {
|
|
145
|
+
// Use AI to generate smart search terms
|
|
146
|
+
console.log(`[context] Generating search terms for: ${topic}`);
|
|
147
|
+
const searchTerms = await generateSearchTerms(topic, model);
|
|
148
|
+
console.log(`[context] Search terms: ${searchTerms.join(', ')}`);
|
|
149
|
+
// Search for each term and combine results
|
|
150
|
+
const allResults = new Map();
|
|
151
|
+
for (const term of searchTerms) {
|
|
152
|
+
const results = searchProject(projectPath, term);
|
|
153
|
+
for (const result of results) {
|
|
154
|
+
// Dedupe by file path, merge matches
|
|
155
|
+
if (allResults.has(result.file)) {
|
|
156
|
+
const existing = allResults.get(result.file);
|
|
157
|
+
existing.matches.push(...result.matches);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
allResults.set(result.file, result);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const searchResults = Array.from(allResults.values()).slice(0, 15);
|
|
165
|
+
// Get basic project context
|
|
166
|
+
const projectContext = readProjectFiles(projectPath);
|
|
167
|
+
// Generate summary
|
|
168
|
+
const summary = await generateContextSummary(projectName, topic, searchResults, projectContext, model);
|
|
169
|
+
return {
|
|
170
|
+
summary,
|
|
171
|
+
filesFound: searchResults.map((r) => r.file),
|
|
172
|
+
searchTerms,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=context-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-search.js","sourceRoot":"","sources":["../../src/loopgram/context-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACtC,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;AAOlC;;GAEG;AACH,KAAK,UAAU,mBAAmB,CAAC,KAAa,EAAE,KAAa;IAC7D,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC/C,KAAK;QACL,UAAU,EAAE,GAAG;QACf,MAAM,EAAE,yPAAyP;QACjQ,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,KAAK,EAAE,EAAE;SAC7C;KACF,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAEzC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI;SACpB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;SAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAE9C,oCAAoC;IACpC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;QACzC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,WAAmB,EAAE,KAAa;IAC9D,MAAM,OAAO,GAAmB,EAAE,CAAC;IAEnC,IAAI,CAAC;QACH,wDAAwD;QACxD,2CAA2C;QAC3C,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QAClE,2CAA2C;QAC3C,MAAM,GAAG,GAAG,kBAAkB,YAAY,MAAM,WAAW,wMAAwM,CAAC;QACpQ,OAAO,CAAC,GAAG,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;QAExC,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,EAAE;YAC7B,QAAQ,EAAE,OAAO;YACjB,SAAS,EAAE,IAAI,GAAG,IAAI;YACtB,KAAK,EAAE,UAAU;SAClB,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,kBAAkB,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,KAAK,GAAG,CAAC,CAAC;QAEjG,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEnD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,oBAAoB;YAC3D,IAAI,CAAC;gBACH,kCAAkC;gBAClC,MAAM,WAAW,GAAG,QAAQ,CAC1B,eAAe,KAAK,MAAM,IAAI,0BAA0B,EACxD,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,CAC9C,CAAC,IAAI,EAAE,CAAC;gBAET,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;oBACjC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE;iBAC1C,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,uCAAuC;gBACvC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,kCAAkC;QAClC,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,MAAM,YAAY,GAAG;QACnB,WAAW;QACX,WAAW;QACX,cAAc;QACd,gBAAgB;KACjB,CAAC;IAEF,IAAI,OAAO,GAAG,EAAE,CAAC;IAEjB,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACzC,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAChD,uBAAuB;gBACvB,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI;oBACrC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,kBAAkB;oBACjD,CAAC,CAAC,OAAO,CAAC;gBACZ,OAAO,IAAI,SAAS,IAAI,KAAK,SAAS,IAAI,CAAC;YAC7C,CAAC;YAAC,MAAM,CAAC;gBACP,wBAAwB;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,WAAmB,EACnB,KAAa,EACb,aAA6B,EAC7B,cAAsB,EACtB,KAAa;IAEb,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,6BAA6B,KAAK,QAAQ,WAAW,sCAAsC,CAAC;IACrG,CAAC;IAED,iCAAiC;IACjC,IAAI,OAAO,GAAG,YAAY,WAAW,YAAY,KAAK,MAAM,CAAC;IAC7D,OAAO,IAAI,uBAAuB,cAAc,MAAM,CAAC;IACvD,OAAO,IAAI,qBAAqB,CAAC;IAEjC,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;QACnC,OAAO,IAAI,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC;QACpC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;QAClE,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC/C,KAAK;QACL,UAAU,EAAE,GAAG;QACf,MAAM,EAAE;;;;;;;kCAOsB;QAC9B,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,sDAAsD,KAAK,SAAS,OAAO,EAAE;aACvF;SACF;KACF,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,6BAA6B,CAAC;AAC1E,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,WAAmB,EACnB,WAAmB,EACnB,KAAa,EACb,KAAa;IAEb,wCAAwC;IACxC,OAAO,CAAC,GAAG,CAAC,0CAA0C,KAAK,EAAE,CAAC,CAAC;IAC/D,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5D,OAAO,CAAC,GAAG,CAAC,2BAA2B,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEjE,2CAA2C;IAC3C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAwB,CAAC;IAEnD,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACjD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,qCAAqC;YACrC,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAE,CAAC;gBAC9C,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEnE,4BAA4B;IAC5B,MAAM,cAAc,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAErD,mBAAmB;IACnB,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAC1C,WAAW,EACX,KAAK,EACL,aAAa,EACb,cAAc,EACd,KAAK,CACN,CAAC;IAEF,OAAO;QACL,OAAO;QACP,UAAU,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5C,WAAW;KACZ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Context } from 'telegraf';
|
|
2
|
+
import type { Message, BrainstormConfig, ProjectConfig } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Set context for a conversation (from /context command)
|
|
5
|
+
*/
|
|
6
|
+
export declare function setConversationContext(chatId: number, context: string): void;
|
|
7
|
+
/**
|
|
8
|
+
* Get context for a conversation
|
|
9
|
+
*/
|
|
10
|
+
export declare function getConversationContext(chatId: number): string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Get conversation history for a chat, creating if needed
|
|
13
|
+
*/
|
|
14
|
+
export declare function getHistory(chatId: number): Message[];
|
|
15
|
+
/**
|
|
16
|
+
* Clear conversation history for a chat
|
|
17
|
+
*/
|
|
18
|
+
export declare function clearConversation(chatId: number): void;
|
|
19
|
+
/**
|
|
20
|
+
* Handle incoming text messages
|
|
21
|
+
*/
|
|
22
|
+
export declare function handleMessage(ctx: Context, config: BrainstormConfig, project: ProjectConfig | null): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Handle /save command - summarize and save the conversation
|
|
25
|
+
*/
|
|
26
|
+
export declare function handleSaveCommand(ctx: Context, projectPath: string | null, config: BrainstormConfig): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Handle /clear command - clear conversation history
|
|
29
|
+
*/
|
|
30
|
+
export declare function handleClearCommand(ctx: Context): void;
|
|
31
|
+
/**
|
|
32
|
+
* Handle /projects command - list configured projects
|
|
33
|
+
*/
|
|
34
|
+
export declare function handleProjectsCommand(ctx: Context, config: BrainstormConfig): void;
|
|
35
|
+
/**
|
|
36
|
+
* Handle /status command - show current conversation status
|
|
37
|
+
*/
|
|
38
|
+
export declare function handleStatusCommand(ctx: Context, projectPath: string | null, config: BrainstormConfig): void;
|
|
39
|
+
//# sourceMappingURL=conversation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/loopgram/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAa3E;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAE5E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEzE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CAKpD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGtD;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,aAAa,GAAG,IAAI,GAC5B,OAAO,CAAC,IAAI,CAAC,CA4Cf;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,IAAI,CAAC,CA6Cf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAMrD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,gBAAgB,GACvB,IAAI,CAUN;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,EACZ,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,MAAM,EAAE,gBAAgB,GACvB,IAAI,CAoBN"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { chat, summarize, extractTitle } from './claude.js';
|
|
2
|
+
import { saveIdea, slugify } from './saver.js';
|
|
3
|
+
// Store conversation history per chat (group or DM)
|
|
4
|
+
const conversations = new Map();
|
|
5
|
+
// Store loaded context per chat (from /context command)
|
|
6
|
+
const conversationContexts = new Map();
|
|
7
|
+
// Max history length (20 exchanges = 40 messages)
|
|
8
|
+
const MAX_HISTORY_LENGTH = 40;
|
|
9
|
+
/**
|
|
10
|
+
* Set context for a conversation (from /context command)
|
|
11
|
+
*/
|
|
12
|
+
export function setConversationContext(chatId, context) {
|
|
13
|
+
conversationContexts.set(chatId, context);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get context for a conversation
|
|
17
|
+
*/
|
|
18
|
+
export function getConversationContext(chatId) {
|
|
19
|
+
return conversationContexts.get(chatId);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get conversation history for a chat, creating if needed
|
|
23
|
+
*/
|
|
24
|
+
export function getHistory(chatId) {
|
|
25
|
+
if (!conversations.has(chatId)) {
|
|
26
|
+
conversations.set(chatId, []);
|
|
27
|
+
}
|
|
28
|
+
return conversations.get(chatId);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Clear conversation history for a chat
|
|
32
|
+
*/
|
|
33
|
+
export function clearConversation(chatId) {
|
|
34
|
+
conversations.delete(chatId);
|
|
35
|
+
conversationContexts.delete(chatId);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Handle incoming text messages
|
|
39
|
+
*/
|
|
40
|
+
export async function handleMessage(ctx, config, project) {
|
|
41
|
+
const chatId = ctx.chat?.id;
|
|
42
|
+
const message = ctx.message;
|
|
43
|
+
if (!chatId || !message || !('text' in message)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const userMessage = message.text;
|
|
47
|
+
const history = getHistory(chatId);
|
|
48
|
+
// Add user message to history
|
|
49
|
+
history.push({ role: 'user', content: userMessage });
|
|
50
|
+
// Build project context if available
|
|
51
|
+
let projectContext = project
|
|
52
|
+
? `**Project:** ${project.name}\n**Description:** ${project.description || 'No description'}\n**Path:** ${project.path}`
|
|
53
|
+
: undefined;
|
|
54
|
+
// Add loaded context from /context command if available
|
|
55
|
+
const loadedContext = getConversationContext(chatId);
|
|
56
|
+
if (loadedContext && projectContext) {
|
|
57
|
+
projectContext += `\n\n## Codebase Context\n${loadedContext}`;
|
|
58
|
+
}
|
|
59
|
+
else if (loadedContext) {
|
|
60
|
+
projectContext = `## Codebase Context\n${loadedContext}`;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
// Get response from Claude with project context
|
|
64
|
+
const response = await chat(history, config.anthropic.model, projectContext);
|
|
65
|
+
// Add assistant response to history
|
|
66
|
+
history.push({ role: 'assistant', content: response });
|
|
67
|
+
// Trim history if too long
|
|
68
|
+
if (history.length > MAX_HISTORY_LENGTH) {
|
|
69
|
+
history.splice(0, 2);
|
|
70
|
+
}
|
|
71
|
+
await ctx.reply(response);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
console.error('Error calling Claude:', error);
|
|
75
|
+
await ctx.reply('Sorry, I had trouble processing that. Please try again.');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Handle /save command - summarize and save the conversation
|
|
80
|
+
*/
|
|
81
|
+
export async function handleSaveCommand(ctx, projectPath, config) {
|
|
82
|
+
const chatId = ctx.chat?.id;
|
|
83
|
+
if (!chatId) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!projectPath) {
|
|
87
|
+
await ctx.reply('This group is not configured for a project. ' +
|
|
88
|
+
'Add this group ID to ~/.config/ralph/brainstorm.json');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const history = getHistory(chatId);
|
|
92
|
+
if (history.length < 2) {
|
|
93
|
+
await ctx.reply('Nothing to save yet. Start a conversation first!');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
await ctx.reply('Summarizing conversation...');
|
|
98
|
+
// Generate summary
|
|
99
|
+
const summary = await summarize(history, config.anthropic.model);
|
|
100
|
+
// Extract title and create filename
|
|
101
|
+
const title = extractTitle(summary);
|
|
102
|
+
const filename = slugify(title);
|
|
103
|
+
// Save to project
|
|
104
|
+
const filepath = saveIdea(projectPath, filename, summary);
|
|
105
|
+
// Report success
|
|
106
|
+
const shortSummary = summary.length > 500 ? summary.slice(0, 500) + '...' : summary;
|
|
107
|
+
await ctx.reply(`✅ Saved to ${filepath}\n\n${shortSummary}`);
|
|
108
|
+
// Clear conversation after saving
|
|
109
|
+
clearConversation(chatId);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error('Error saving idea:', error);
|
|
113
|
+
await ctx.reply('Sorry, I had trouble saving that. Please try again.');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Handle /clear command - clear conversation history
|
|
118
|
+
*/
|
|
119
|
+
export function handleClearCommand(ctx) {
|
|
120
|
+
const chatId = ctx.chat?.id;
|
|
121
|
+
if (chatId) {
|
|
122
|
+
clearConversation(chatId);
|
|
123
|
+
ctx.reply('Conversation cleared. Start fresh!');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Handle /projects command - list configured projects
|
|
128
|
+
*/
|
|
129
|
+
export function handleProjectsCommand(ctx, config) {
|
|
130
|
+
const projects = Object.values(config.projects);
|
|
131
|
+
if (projects.length === 0) {
|
|
132
|
+
ctx.reply('No projects configured yet. Add them to ~/.config/ralph/brainstorm.json');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const list = projects.map((p) => `• ${p.name}: ${p.path}`).join('\n');
|
|
136
|
+
ctx.reply(`Configured projects:\n${list}`);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Handle /status command - show current conversation status
|
|
140
|
+
*/
|
|
141
|
+
export function handleStatusCommand(ctx, projectPath, config) {
|
|
142
|
+
const chatId = ctx.chat?.id;
|
|
143
|
+
if (!chatId) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const history = getHistory(chatId);
|
|
147
|
+
const project = projectPath
|
|
148
|
+
? Object.values(config.projects).find((p) => p.path === projectPath)
|
|
149
|
+
: null;
|
|
150
|
+
const lines = [
|
|
151
|
+
`📊 **Status**`,
|
|
152
|
+
`Messages: ${history.length}`,
|
|
153
|
+
`Project: ${project?.name || 'Not configured'}`,
|
|
154
|
+
`Model: ${config.anthropic.model}`,
|
|
155
|
+
];
|
|
156
|
+
ctx.reply(lines.join('\n'));
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=conversation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.js","sourceRoot":"","sources":["../../src/loopgram/conversation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/loopgram/index.ts"],"names":[],"mappings":""}
|