Skip to content

Commit 92208b3

Browse files
authored
Merge pull request #21 from rusiaaman/2.7.1
2.7.1
2 parents c871e7d + 61ce520 commit 92208b3

23 files changed

+699
-447
lines changed

gpt_action_json_schema.json

+43-63
Original file line numberDiff line numberDiff line change
@@ -290,16 +290,16 @@
290290
}
291291
}
292292
},
293-
"/v1/knowledge_transfer": {
293+
"/v1/context_save": {
294294
"post": {
295295
"x-openai-isConsequential": false,
296-
"summary": "Knowledge Transfer",
297-
"operationId": "knowledge_transfer_v1_knowledge_transfer_post",
296+
"summary": "Context Save",
297+
"operationId": "context_save_v1_context_save_post",
298298
"requestBody": {
299299
"content": {
300300
"application/json": {
301301
"schema": {
302-
"$ref": "#/components/schemas/KTWithUUID"
302+
"$ref": "#/components/schemas/ContextSaveWithUUID"
303303
}
304304
}
305305
},
@@ -312,7 +312,7 @@
312312
"application/json": {
313313
"schema": {
314314
"type": "string",
315-
"title": "Response Knowledge Transfer V1 Knowledge Transfer Post"
315+
"title": "Response Context Save V1 Context Save Post"
316316
}
317317
}
318318
}
@@ -427,6 +427,44 @@
427427
],
428428
"title": "CommandWithUUID"
429429
},
430+
"ContextSaveWithUUID": {
431+
"properties": {
432+
"id": {
433+
"type": "string",
434+
"title": "Id"
435+
},
436+
"project_root_path": {
437+
"type": "string",
438+
"title": "Project Root Path"
439+
},
440+
"description": {
441+
"type": "string",
442+
"title": "Description"
443+
},
444+
"relevant_file_globs": {
445+
"items": {
446+
"type": "string"
447+
},
448+
"type": "array",
449+
"title": "Relevant File Globs"
450+
},
451+
"user_id": {
452+
"type": "string",
453+
"format": "uuid",
454+
"title": "User Id"
455+
}
456+
},
457+
"additionalProperties": false,
458+
"type": "object",
459+
"required": [
460+
"id",
461+
"project_root_path",
462+
"description",
463+
"relevant_file_globs",
464+
"user_id"
465+
],
466+
"title": "ContextSaveWithUUID"
467+
},
430468
"FileEditWithUUID": {
431469
"properties": {
432470
"file_path": {
@@ -498,64 +536,6 @@
498536
],
499537
"title": "InitializeWithUUID"
500538
},
501-
"KTWithUUID": {
502-
"properties": {
503-
"id": {
504-
"type": "string",
505-
"title": "Id"
506-
},
507-
"project_root_path": {
508-
"type": "string",
509-
"title": "Project Root Path"
510-
},
511-
"objective": {
512-
"type": "string",
513-
"title": "Objective"
514-
},
515-
"all_user_instructions": {
516-
"type": "string",
517-
"title": "All User Instructions"
518-
},
519-
"current_status_of_the_task": {
520-
"type": "string",
521-
"title": "Current Status Of The Task"
522-
},
523-
"all_issues_snippets": {
524-
"type": "string",
525-
"title": "All Issues Snippets"
526-
},
527-
"relevant_file_paths": {
528-
"items": {
529-
"type": "string"
530-
},
531-
"type": "array",
532-
"title": "Relevant File Paths"
533-
},
534-
"build_and_development_instructions": {
535-
"type": "string",
536-
"title": "Build And Development Instructions"
537-
},
538-
"user_id": {
539-
"type": "string",
540-
"format": "uuid",
541-
"title": "User Id"
542-
}
543-
},
544-
"additionalProperties": false,
545-
"type": "object",
546-
"required": [
547-
"id",
548-
"project_root_path",
549-
"objective",
550-
"all_user_instructions",
551-
"current_status_of_the_task",
552-
"all_issues_snippets",
553-
"relevant_file_paths",
554-
"build_and_development_instructions",
555-
"user_id"
556-
],
557-
"title": "KTWithUUID"
558-
},
559539
"ReadFileWithUUID": {
560540
"properties": {
561541
"file_paths": {

gpt_instructions.txt

+5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ Instructions for `BashInteraction`
4444
Instructions for `ResetShell`
4545
- Resets the shell. Use only if all interrupts and prompt reset attempts have failed repeatedly.
4646

47+
Instructions for `ContextSave`
48+
- Saves provided description and file contents of all the relevant file paths or globs in a single text file.
49+
- Provide random unqiue id or whatever user provided.
50+
- Leave project path as empty string if no project path
51+
4752
Instructions for `FileEdit`:
4853
- Use absolute file path only.
4954
- Use SEARCH/REPLACE blocks to edit the file.

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
authors = [{ name = "Aman Rusia", email = "[email protected]" }]
33
name = "wcgw"
4-
version = "2.7.0"
4+
version = "2.7.1"
55
description = "Shell and coding agent on claude and chatgpt"
66
readme = "README.md"
77
requires-python = ">=3.11, <3.13"

src/wcgw/client/anthropic_client.py

+12-17
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
from ..types_ import (
2626
BashCommand,
2727
BashInteraction,
28+
ContextSave,
2829
FileEdit,
2930
GetScreenInfo,
3031
Keyboard,
31-
KnowledgeTransfer,
3232
Mouse,
3333
ReadFiles,
3434
ReadImage,
@@ -130,7 +130,12 @@ def loop(
130130
memory = None
131131
if resume:
132132
try:
133-
memory = load_memory(resume)
133+
_, memory = load_memory(
134+
resume,
135+
8000,
136+
lambda x: default_enc.encode(x).ids,
137+
lambda x: default_enc.decode(x),
138+
)
134139
except OSError:
135140
if resume == "latest":
136141
resume_path = sorted(Path(".wcgw").iterdir(), key=os.path.getmtime)[-1]
@@ -215,22 +220,12 @@ def loop(
215220
""",
216221
),
217222
ToolParam(
218-
input_schema=KnowledgeTransfer.model_json_schema(),
219-
name="KnowledgeTransfer",
223+
input_schema=ContextSave.model_json_schema(),
224+
name="ContextSave",
220225
description="""
221-
Write detailed description in order to do a KT, if the user asks for it.
222-
Save all information necessary for a person to understand the task and the problems.
223-
224-
- `all_user_instructions` should contain all instructions user shared in the conversation.
225-
- `current_status_of_the_task` should contain only what is already achieved, not what's remaining.
226-
- `all_issues_snippets` should only contain snippets of error, traceback, file snippets, commands, etc., no comments or solutions (important!).
227-
- Be very verbose in `all_issues_snippets` providing as much error context as possible.
228-
- Provide an id if the user hasn't provided one.
229-
- This tool will return a text file path where the information is saved.
230-
- After the tool completes succesfully, tell the user the task id and the generate file path. (important!)
231-
- Leave arguments as empty string if they aren't relevant.
232-
- This tool marks end of your conversation, do not run any further tools after calling this.
233-
- Provide absolute file paths only in `relevant_file_paths` containing all relevant files.
226+
Saves provided description and file contents of all the relevant file paths or globs in a single text file.
227+
- Provide random unqiue id or whatever user provided.
228+
- Leave project path as empty string if no project path
234229
""",
235230
),
236231
]

src/wcgw/client/mcp_server/server.py

+44-20
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from ...types_ import (
1515
BashCommand,
1616
BashInteraction,
17+
ContextSave,
1718
FileEdit,
1819
GetScreenInfo,
1920
Initialize,
@@ -46,14 +47,47 @@ async def handle_read_resource(uri: AnyUrl) -> str:
4647

4748
@server.list_prompts() # type: ignore
4849
async def handle_list_prompts() -> list[types.Prompt]:
49-
return []
50+
return [
51+
types.Prompt(
52+
name="KnowledgeTransfer",
53+
description="Prompt for invoking ContextSave tool in order to do a comprehensive knowledge transfer of a coding task. Prompts to save detailed error log and instructions.",
54+
)
55+
]
5056

5157

5258
@server.get_prompt() # type: ignore
5359
async def handle_get_prompt(
5460
name: str, arguments: dict[str, str] | None
5561
) -> types.GetPromptResult:
56-
return types.GetPromptResult(messages=[])
62+
messages = []
63+
if name == "KnowledgeTransfer":
64+
messages = [
65+
types.PromptMessage(
66+
role="user",
67+
content=types.TextContent(
68+
type="text",
69+
text="""Use `ContextSave` tool to do a knowledge transfer of the task in hand.
70+
Write detailed description in order to do a KT.
71+
Save all information necessary for a person to understand the task and the problems.
72+
73+
Format the `description` field using Markdown with the following sections.
74+
- "# Objective" section containing project and task objective.
75+
- "# All user instructions" section should be provided containing all instructions user shared in the conversation.
76+
- "# Current status of the task" should be provided containing only what is already achieved, not what's remaining.
77+
- "# All issues with snippets" section containing snippets of error, traceback, file snippets, commands, etc. But no comments or solutions.
78+
- Be very verbose in the all issues with snippets section providing as much error context as possible.
79+
- "# Build and development instructions" section containing instructions to build or run project or run tests, or envrionment related information. Only include what's known. Leave empty if unknown.
80+
- After the tool completes succesfully, tell me the task id and the file path the tool generated (important!)
81+
- This tool marks end of your conversation, do not run any further tools after calling this.
82+
83+
Provide all relevant file paths in order to understand and solve the the task. Err towards providing more file paths than fewer.
84+
85+
(Note to self: this conversation can then be resumed later asking "Resume `<generated id>`" which should call Initialize tool)
86+
""",
87+
),
88+
)
89+
]
90+
return types.GetPromptResult(messages=messages)
5791

5892

5993
@server.list_tools() # type: ignore
@@ -153,24 +187,14 @@ async def handle_list_tools() -> list[types.Tool]:
153187
"""
154188
+ diffinstructions,
155189
),
156-
# ToolParam(
157-
# inputSchema=KnowledgeTransfer.model_json_schema(),
158-
# name="KnowledgeTransfer",
159-
# description="""
160-
# Write detailed description in order to do a KT, if the user asks for it.
161-
# Save all information necessary for a person to understand the task and the problems.
162-
# - `all_user_instructions` should contain all instructions user shared in the conversation.
163-
# - `current_status_of_the_task` should contain only what is already achieved, not what's remaining.
164-
# - `all_issues_snippets` should only contain snippets of error, traceback, file snippets, commands, etc., no comments or solutions (important!).
165-
# - Be very verbose in `all_issues_snippets` providing as much error context as possible.
166-
# - Provide an id if the user hasn't provided one.
167-
# - This tool will return a text file path where the information is saved.
168-
# - After the tool completes succesfully, tell the user the task id and the generate file path. (important!)
169-
# - Leave arguments as empty string if they aren't relevant.
170-
# - This tool marks end of your conversation, do not run any further tools after calling this.
171-
# - Provide absolute file paths only in `relevant_file_paths` containing all relevant files.
172-
# """,
173-
# ),
190+
ToolParam(
191+
inputSchema=ContextSave.model_json_schema(),
192+
name="ContextSave",
193+
description="""
194+
Saves provided description and file contents of all the relevant file paths or globs in a single text file.
195+
- Provide random unqiue id or whatever user provided.
196+
- Leave project path as empty string if no project path""",
197+
),
174198
]
175199
if COMPUTER_USE_ON_DOCKER_ENABLED:
176200
tools += [

src/wcgw/client/memory.py

+43-17
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,36 @@
11
import os
2+
import re
3+
import shlex
4+
from typing import Callable, Optional
25

3-
from ..types_ import KnowledgeTransfer
6+
from ..types_ import ContextSave
47

58

69
def get_app_dir_xdg() -> str:
710
xdg_data_dir = os.environ.get("XDG_DATA_HOME", os.path.expanduser("~/.local/share"))
811
return os.path.join(xdg_data_dir, "wcgw")
912

1013

11-
def format_memory(task_memory: KnowledgeTransfer, relevant_files: str) -> str:
12-
memory_data = f"""# Goal: {task_memory.objective}\n\n
13-
# Instructions:\n{task_memory.all_user_instructions}\n\n
14-
# Current Status:\n{task_memory.current_status_of_the_task}\n\n
15-
# Pending Issues:\n{task_memory.all_issues_snippets}\n\n
16-
# Build Instructions:\n{task_memory.build_and_development_instructions}\n"""
14+
def format_memory(task_memory: ContextSave, relevant_files: str) -> str:
15+
memory_data = ""
16+
if task_memory.project_root_path:
17+
memory_data += (
18+
f"# PROJECT ROOT = {shlex.quote(task_memory.project_root_path)}\n"
19+
)
20+
memory_data += task_memory.description
1721

18-
memory_data += "\n# Relevant Files:\n" + relevant_files
22+
memory_data += (
23+
"\n\n"
24+
+ "# Relevant file paths\n"
25+
+ ", ".join(map(shlex.quote, task_memory.relevant_file_globs))
26+
)
27+
28+
memory_data += "\n\n# Relevant Files:\n" + relevant_files
1929

2030
return memory_data
2131

2232

23-
def save_memory(task_memory: KnowledgeTransfer, relevant_files: str) -> str:
33+
def save_memory(task_memory: ContextSave, relevant_files: str) -> str:
2434
app_dir = get_app_dir_xdg()
2535
memory_dir = os.path.join(app_dir, "memory")
2636
os.makedirs(memory_dir, exist_ok=True)
@@ -30,23 +40,39 @@ def save_memory(task_memory: KnowledgeTransfer, relevant_files: str) -> str:
3040
raise Exception("Task id can not be empty")
3141
memory_data = format_memory(task_memory, relevant_files)
3242

33-
memory_file = os.path.join(memory_dir, f"{task_id}.json")
3443
memory_file_full = os.path.join(memory_dir, f"{task_id}.txt")
3544

3645
with open(memory_file_full, "w") as f:
3746
f.write(memory_data)
3847

39-
with open(memory_file, "w") as f:
40-
f.write(task_memory.model_dump_json())
41-
4248
return memory_file_full
4349

4450

45-
def load_memory(task_id: str) -> KnowledgeTransfer:
51+
def load_memory[T](
52+
task_id: str,
53+
max_tokens: Optional[int],
54+
encoder: Callable[[str], list[T]],
55+
decoder: Callable[[list[T]], str],
56+
) -> tuple[str, str]:
4657
app_dir = get_app_dir_xdg()
4758
memory_dir = os.path.join(app_dir, "memory")
48-
memory_file = os.path.join(memory_dir, f"{task_id}.json")
59+
memory_file = os.path.join(memory_dir, f"{task_id}.txt")
4960

5061
with open(memory_file, "r") as f:
51-
task_save = KnowledgeTransfer.model_validate_json(f.read())
52-
return task_save
62+
data = f.read()
63+
64+
if max_tokens:
65+
toks = encoder(data)
66+
if len(toks) > max_tokens:
67+
toks = toks[: max(0, max_tokens - 10)]
68+
data = decoder(toks)
69+
data += "\n(... truncated)"
70+
71+
project_root_match = re.search(r"# PROJECT ROOT = \s*(.*?)\s*$", data, re.MULTILINE)
72+
project_root_path = ""
73+
if project_root_match:
74+
matched_path = project_root_match.group(1)
75+
parsed_ = shlex.split(matched_path)
76+
if parsed_ and len(parsed_) == 1:
77+
project_root_path = parsed_[0]
78+
return project_root_path, data

0 commit comments

Comments
 (0)