raggiecode 0.2.1__py3-none-any.whl

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 (93) hide show
  1. Agent/__init__.py +0 -0
  2. Agent/agent.py +891 -0
  3. Agent/chat_history_db.py +1500 -0
  4. Agent/command.py +49 -0
  5. Agent/config.py +46 -0
  6. Agent/effort_levels.py +33 -0
  7. Agent/git_manager.py +727 -0
  8. Agent/tools.py +35 -0
  9. Commands/__init__.py +18 -0
  10. Commands/effort.py +42 -0
  11. Commands/global_todo.py +23 -0
  12. Commands/help.py +22 -0
  13. Commands/reasoning.py +24 -0
  14. Commands/redo.py +11 -0
  15. Commands/reindex.py +27 -0
  16. Commands/shell.py +28 -0
  17. Commands/stream.py +24 -0
  18. Commands/undo.py +13 -0
  19. Commands/unlimited_effort.py +8 -0
  20. Commands/window_size.py +29 -0
  21. RAG/__init__.py +0 -0
  22. RAG/document.py +119 -0
  23. RAG/find.py +408 -0
  24. RAG/graph.py +231 -0
  25. Tools/GetFileCodeStructure.py +43 -0
  26. Tools/GetSymbolSourceCode.py +27 -0
  27. Tools/__init__.py +39 -0
  28. Tools/ask_user.py +102 -0
  29. Tools/dispatch_subagent.py +215 -0
  30. Tools/document.py +35 -0
  31. Tools/edit_symbol.py +250 -0
  32. Tools/fuzzy_search.py +119 -0
  33. Tools/list_dir.py +51 -0
  34. Tools/read.py +49 -0
  35. Tools/read_image.py +75 -0
  36. Tools/remove.py +75 -0
  37. Tools/replace.py +305 -0
  38. Tools/search.py +41 -0
  39. Tools/shell.py +149 -0
  40. Tools/shell_kill.py +87 -0
  41. Tools/temp_background_service.py +113 -0
  42. Tools/todo_list.py +481 -0
  43. Tools/utils.py +116 -0
  44. Tools/view_changes.py +179 -0
  45. Tools/walk_call_tree.py +30 -0
  46. Tools/web_fetch.py +175 -0
  47. Tools/web_search.py +69 -0
  48. Tools/write.py +48 -0
  49. cli.py +111 -0
  50. config/__init__.py +0 -0
  51. config/coder_system_prompt.md +119 -0
  52. config/roles.json +43 -0
  53. config/tools.json +709 -0
  54. indexing/__init__.py +0 -0
  55. indexing/cli.py +128 -0
  56. indexing/code_index_sdk.py +832 -0
  57. indexing/code_indexer.py +1763 -0
  58. indexing/db_schema.py +396 -0
  59. indexing/export_to_json.py +346 -0
  60. indexing/extractors.py +189 -0
  61. indexing/file_utils.py +97 -0
  62. indexing/frontend/__init__.py +0 -0
  63. indexing/frontend/css_extractor.py +195 -0
  64. indexing/frontend/css_parser.py +387 -0
  65. indexing/frontend/css_selector_utils.py +226 -0
  66. indexing/frontend/edit_safety.py +573 -0
  67. indexing/frontend/graph.py +838 -0
  68. indexing/frontend/html_extractor.py +496 -0
  69. indexing/frontend/html_parser.py +314 -0
  70. indexing/frontend/jsx_extractor.py +1204 -0
  71. indexing/frontend/location_lookup.py +247 -0
  72. indexing/frontend/resolver.py +485 -0
  73. indexing/frontend/runtime_resolver.py +862 -0
  74. indexing/frontend/semantic_output.py +705 -0
  75. indexing/frontend/source_location.py +69 -0
  76. indexing/frontend_config.py +72 -0
  77. indexing/frontend_models.py +347 -0
  78. indexing/language_config.py +360 -0
  79. indexing/models.py +284 -0
  80. indexing/node_utils.py +1112 -0
  81. indexing/parse_worker.py +1082 -0
  82. indexing/queries.py +1542 -0
  83. indexing/sdk_examples.py +426 -0
  84. interactive.py +248 -0
  85. raggie.py +673 -0
  86. raggiecode-0.2.1.dist-info/METADATA +944 -0
  87. raggiecode-0.2.1.dist-info/RECORD +93 -0
  88. raggiecode-0.2.1.dist-info/WHEEL +5 -0
  89. raggiecode-0.2.1.dist-info/entry_points.txt +2 -0
  90. raggiecode-0.2.1.dist-info/top_level.txt +10 -0
  91. skills/__init__.py +3 -0
  92. skills/manager.py +114 -0
  93. skills/tool.py +121 -0
@@ -0,0 +1,93 @@
1
+ cli.py,sha256=SJehNTIu9RNqIF0STVueFSrPqhkBDBF8HxQFqWyOnQg,5410
2
+ interactive.py,sha256=9KflbeVtVNBzGHXW_gg9j8n9HvgPwyvUfQaIFo2JPGU,8138
3
+ raggie.py,sha256=Al34Mo5GF0FwxUq_8B08twhSAEQOqdkihLaS4b3pijI,22085
4
+ Agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ Agent/agent.py,sha256=R-gq8LRnOgP9lhj-ZZjSMGOAofdlk3TaTkCm56P20CA,36383
6
+ Agent/chat_history_db.py,sha256=72oGUrEePJoT-0dqKRo8p38lPZVrbW7NazndQGU-nJ4,46920
7
+ Agent/command.py,sha256=sVYQqk54dkP_Ty_XO3ZPKZJEqNhKDULJES1NWcWftAM,1735
8
+ Agent/config.py,sha256=DzMPVgeVDl7uHhEGiUaV6fIYTx7pfEvfX-youjoqyzY,1416
9
+ Agent/effort_levels.py,sha256=Ri4YMEYd4cJlmwQC5xyPZZe9lDxtXV-9KCKejvcSU_E,800
10
+ Agent/git_manager.py,sha256=paE1u6DScyj9H8_9tuJNeYanKzWwJGQVRQ8ak6aC4zM,28195
11
+ Agent/tools.py,sha256=GnOn5JDGHo8j38vMSMvXR2vME99_hTNSWtuQuddf4RU,1183
12
+ Commands/__init__.py,sha256=Sc_Nt4wo_dEC7_yTVQYS-qpIztzoYWHsgysSy6PFJtA,834
13
+ Commands/effort.py,sha256=b3U5HgEBi5PsBJOxxKnvelLpT9UUd-OgCWX9QWhGNS8,1371
14
+ Commands/global_todo.py,sha256=1TghrpYQEApgXu2CH0XUh5cf9ZLBBdQYLVwoT8kPs68,912
15
+ Commands/help.py,sha256=-LH9Rmz0zHq8TpZ8Ap36xYZRRiRZeUf9LtgdjX4-IEg,1148
16
+ Commands/reasoning.py,sha256=4UHGntpbWZNE8TrKH0XUQUjEhL18XHi9w3WvXPXxOl8,747
17
+ Commands/redo.py,sha256=vobzA9xU4SsKvHkw7hFNyDPZPRKUdYHws5gF9LYB87k,332
18
+ Commands/reindex.py,sha256=UncnVpR-nJUDR2VlIaEU9sODEUCX59wotiJt5Z2sapw,804
19
+ Commands/shell.py,sha256=_anLkvDGAV7t7cczuL0LsKkg5vz4TZe_cSyr6LOMBdI,716
20
+ Commands/stream.py,sha256=e-CubXDcMBHZzmtz7IvQMkz2NJfe4CZo7Tc8bXLkEGY,738
21
+ Commands/undo.py,sha256=29QKE9WUZT2UgSUaOCfxz3CgSS-8qKs-1MSWqc1zQug,350
22
+ Commands/unlimited_effort.py,sha256=uM8pi7d_DjY7UE_oqZqpoRuho6VH0alDuBr5T7Pdaoc,302
23
+ Commands/window_size.py,sha256=oriT9ACRxu9IFqrxbQRRyP62nHw49hhoR2k-sPz2V58,739
24
+ RAG/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ RAG/document.py,sha256=mO0b64qOIWMtZB_OVImw5_9QLEoQNKYMgpBqUgQqlAA,4903
26
+ RAG/find.py,sha256=rLjWDqe0I3dyJlXfhC1U6oZ3NdACc2UC6F0m1-G6Ugo,15319
27
+ RAG/graph.py,sha256=kOYeaSIvpi7Wg4YAGzMul4DcbvvjLwTK6zX423fsWgQ,9522
28
+ Tools/GetFileCodeStructure.py,sha256=WbVVpa3Q5YkBJ12V2aRlh43iIIpIOvH7OhOQ2bK34-U,1542
29
+ Tools/GetSymbolSourceCode.py,sha256=mbODXc7SKg3wLnE64nhKSl5Xj7WfIQex5MiUT9x4dRM,870
30
+ Tools/__init__.py,sha256=uEot7KyesKc5-EFuG30tZ5kOaiMvTu_nNkVs6SOxv00,2598
31
+ Tools/ask_user.py,sha256=_d9spFoWJ56xlJWHQTNDCryIIgS5Wa-ppoJys-k1hZw,3591
32
+ Tools/dispatch_subagent.py,sha256=4wR003hrC24JJ79p_91ne09MB5a8rylMALY8OM3gr2M,8427
33
+ Tools/document.py,sha256=dum7_sbAttWo4mMfS0_oH8oPgAglArvJn-jovUtSz4o,1285
34
+ Tools/edit_symbol.py,sha256=0l5hrWhu5H3vNrLsmyGRX3CFjzqKkkgJZJq4dr8JHGw,8538
35
+ Tools/fuzzy_search.py,sha256=GK_XnxC0UhmhD-9txZbjD6G3KPb_qyqh5sZYAJNPWs0,3609
36
+ Tools/list_dir.py,sha256=xO4nKdDT-DYs_jZHWMukrRZC1K9T1dcHISjP7k_FrdA,1648
37
+ Tools/read.py,sha256=KhNS2C8isBerIvHrpWXWAGaHI-51jobboNqI4VWfenE,1457
38
+ Tools/read_image.py,sha256=aEKnX--hYv0BGkNgJDTCLdh0FBNcwYQaEcIMy7AHvHo,2538
39
+ Tools/remove.py,sha256=r0FAI4xxQRLjHRLR0rTi1m3sjl86y3y2-aGC5MewnB4,2886
40
+ Tools/replace.py,sha256=F5S0NnOf4lzLeYQ-LgmN_275Ywg7MruXUymN2OIZIg4,11114
41
+ Tools/search.py,sha256=c-7AL6gM-AXPJhUJP3y6XG36V1aunNGycQaw1WhGWew,1254
42
+ Tools/shell.py,sha256=hotlLHye6rlK4RAaxQwkzy997QmLAiJ8xV86pvAoFfQ,5056
43
+ Tools/shell_kill.py,sha256=fmBDscJqvccx4AwzwHo_-y8-eRc0vf8hzUEYKYQYWTI,2535
44
+ Tools/temp_background_service.py,sha256=C1tWMMLT9rBNae8vZTWIr6JVfYsUQT72csdTksoNSBM,3797
45
+ Tools/todo_list.py,sha256=zeuqu4vPwuUyLjZNJPd3PW1lo5bPkahgg3mAYeQwbW4,19101
46
+ Tools/utils.py,sha256=qR7VQ-DDoeb51wFwm9ZiF8uGFiYYsXVLVVzmV8heGOM,3367
47
+ Tools/view_changes.py,sha256=pMn1NgKGUPNnHgFiPXAUfZTl5P6t3KDNpYKABbjHtyQ,7123
48
+ Tools/walk_call_tree.py,sha256=7GopT44xWQdMti35F-WmHKKCkI4MyRzKurVWfGJ9km0,1031
49
+ Tools/web_fetch.py,sha256=eBM5wQvEbP3AUDQwC2G4nMKpJ2hCNTiIlXe1RV6RJm0,5407
50
+ Tools/web_search.py,sha256=tBxao6YCZpPDKSm3z1-XNJUIL6PH6kzKhM56FOl7qU4,1948
51
+ Tools/write.py,sha256=jyAUt-kGbBacNzLNT2WBsxrPLUPFa1z8FnGYbCwdeCI,1745
52
+ config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
+ config/coder_system_prompt.md,sha256=cK0PKoZOHUXihDtqXrqsN6glWyqv7PmW0WRoQQWRGbo,7102
54
+ config/roles.json,sha256=OhEUl309DqQTtumRw94GM3CT59XssA3hNT6BFlzu_uE,1132
55
+ config/tools.json,sha256=YOLnfCkKvFYhOufAmCArQZPeP-muI9hlIfYq4JsDHAk,39638
56
+ indexing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
+ indexing/cli.py,sha256=k8BR-anjCjvK-swb6N8Tsqhsd8MOUOcD9fjW0o2pL4w,4007
58
+ indexing/code_index_sdk.py,sha256=4g5y4WbWp-a85qTljw0Ud3HridmK85RPIl8xudAGrLo,34921
59
+ indexing/code_indexer.py,sha256=FtBQeYZA6M0gyuuxp7kX5Wv6nvz_sbhSoC9W9gYVRm8,80149
60
+ indexing/db_schema.py,sha256=pJs_6BqD08RidX67k0f9JQVc2IU8Dp0JK6YqInpAzpE,18918
61
+ indexing/export_to_json.py,sha256=srml0G802HiuCIg29aLqxCKF3tQ_N8XqLN927lNuQBM,12253
62
+ indexing/extractors.py,sha256=RFMgtS3AZWNI0fllQ5q19SHUeWyembt5BGpqfODtufY,5606
63
+ indexing/file_utils.py,sha256=Kan4jJGQ4i1XRuzwaC_kP_cMxg_ZRl_dksOE_qnQ_UI,3562
64
+ indexing/frontend_config.py,sha256=kQy4uYW6NmoDKzIgmk3GB7vXlJtfUn08KQThwuHP0ao,2606
65
+ indexing/frontend_models.py,sha256=eN-JjZt-3KKeE68oTnfUdEW-oMbX6isG4rlSE-wtfJc,10549
66
+ indexing/language_config.py,sha256=4lJl5sEw9IbIolA7E-BSQS5odoIvjHZb_roH1lL4GWk,11297
67
+ indexing/models.py,sha256=qsB6tSRFae2SrUBRgarE9QEPz1YMUWB-daiKgLK9Q_w,8381
68
+ indexing/node_utils.py,sha256=7eJhdL745w6_IvClASIB-ZrZghRPXZjaoHHZ5w6U59s,50731
69
+ indexing/parse_worker.py,sha256=HRipYB0Vskp5hcAFBg6fsB_7mX__tPAZ7Hu3LlNOHlY,46830
70
+ indexing/queries.py,sha256=_mFw-qBjmxYIO5AGvpVFq5Gy2eGrn5C2j_s9LtbtJKw,60879
71
+ indexing/sdk_examples.py,sha256=VP2G_z9GY1hP7YNEqeMJkvzzz1Aau8kx_zPhMwo7niE,16283
72
+ indexing/frontend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
+ indexing/frontend/css_extractor.py,sha256=P8cvi-fc6fx_U76TutMcNK_Flh7g45_9nYA3GtcUpQc,7633
74
+ indexing/frontend/css_parser.py,sha256=i6oSQWX4yGN2BpTVBeiJEHlFEYc2Cdfu4O_UDmxgLqo,13579
75
+ indexing/frontend/css_selector_utils.py,sha256=0bSlzT4Gg7HZ3WlYWky7UrjPM5Hu4TLhSWJlwB8xoFo,7439
76
+ indexing/frontend/edit_safety.py,sha256=0ScSjrS9_h5VuAjttNjGawMp0xkoMawPi7nKEqh3FcE,21239
77
+ indexing/frontend/graph.py,sha256=Bts0h6WFSGRsjKTV9Iy4DcsSuM1P8sNoLV-9SLYWwCI,33156
78
+ indexing/frontend/html_extractor.py,sha256=8MqUHuyYBilMd6dGPLL7vCDlaynq4_TwTYccEQ-_LKE,20579
79
+ indexing/frontend/html_parser.py,sha256=8AAuj5doTPZ-6QFxEBiYPl53M-uZcLuXeDQOevCVDeo,11084
80
+ indexing/frontend/jsx_extractor.py,sha256=bhHsaAwyJTJ7nKayAVpRPkytBGlZNKsc36P-_ew_7G8,49804
81
+ indexing/frontend/location_lookup.py,sha256=qWwl9FPtwKsooZCj7wP_Igrr1y2ofrnLDydvNQpGDAA,7312
82
+ indexing/frontend/resolver.py,sha256=FuXhdmhQEzVW5iWVIyRVnn4KZ1WjnphvRu_5q26GFjE,18974
83
+ indexing/frontend/runtime_resolver.py,sha256=5K8jqVYtvkvI8ah7YbFgUoJEtf_khBPWa7polwXTs_o,34418
84
+ indexing/frontend/semantic_output.py,sha256=yDtIow5AXYJ5qT3RFHu6dTfvE9n1os-W4MyyU5T7hJU,27782
85
+ indexing/frontend/source_location.py,sha256=guLp3l7G2zWPexkMN9KmetrDgC7mREQ3m5cil8MghdI,2289
86
+ skills/__init__.py,sha256=HJpODd-3moOdeHPqwpCJpqaF-JjoEmAQH3iuD9FhzOA,62
87
+ skills/manager.py,sha256=K8JZojN6w-rGSLQA_2bQ4kCShI0pl6NK-qBRF8YH9X4,4023
88
+ skills/tool.py,sha256=UQq50ASdp6DKmfv0M6PO5YFJ3mGd647E9QLbdAvuAlw,3708
89
+ raggiecode-0.2.1.dist-info/METADATA,sha256=6tnme1W4fITgFMx9d614VAR77PP5VPIyjddeDf6PduA,43901
90
+ raggiecode-0.2.1.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
91
+ raggiecode-0.2.1.dist-info/entry_points.txt,sha256=aJhU834hBoHFBoD4l8QlZgtYLz8YErjn3_2OUFJ29fY,39
92
+ raggiecode-0.2.1.dist-info/top_level.txt,sha256=fotJtewIK0UIUyouskZsCa59JIC-J5dduxa6ijRlR10,71
93
+ raggiecode-0.2.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ raggie = raggie:main
@@ -0,0 +1,10 @@
1
+ Agent
2
+ Commands
3
+ RAG
4
+ Tools
5
+ cli
6
+ config
7
+ indexing
8
+ interactive
9
+ raggie
10
+ skills
skills/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .manager import SkillManager
2
+
3
+ __all__ = ['SkillManager']
skills/manager.py ADDED
@@ -0,0 +1,114 @@
1
+ import sqlite3
2
+ from pathlib import Path
3
+ from typing import Optional
4
+ from Agent.chat_history_db import DB_PATH
5
+
6
+
7
+ class SkillManager:
8
+ """Manages role-specific skills stored in the database."""
9
+
10
+ def __init__(self):
11
+ """Initialize the skill manager."""
12
+ # No directory creation needed since DB_PATH is the file itself
13
+
14
+ def get_skill(self, role: str, name: str) -> Optional[str]:
15
+ """Load a specific skill for a role by name from the database."""
16
+ conn = sqlite3.connect(DB_PATH)
17
+ cursor = conn.cursor()
18
+
19
+ cursor.execute("""
20
+ SELECT content FROM skills
21
+ WHERE role = ? AND name = ?
22
+ """, (role, name))
23
+
24
+ result = cursor.fetchone()
25
+ conn.close()
26
+
27
+ return result[0] if result else None
28
+
29
+ def list_skills_by_role(self, role: str) -> list:
30
+ """Return all skills for a specific role with brief summaries."""
31
+ conn = sqlite3.connect(DB_PATH)
32
+ cursor = conn.cursor()
33
+
34
+ cursor.execute("""
35
+ SELECT name, content FROM skills
36
+ WHERE role = ?
37
+ ORDER BY name ASC
38
+ """, (role,))
39
+
40
+ skills = []
41
+ for row in cursor.fetchall():
42
+ name = row[0]
43
+ content = row[1] or ""
44
+ summary = content.strip().split("\n")[0][:200]
45
+ skills.append({"role": role, "name": name, "summary": summary})
46
+
47
+ conn.close()
48
+ return skills
49
+
50
+ def list_skills(self) -> list:
51
+ """Return all skills with brief summaries (role + name + first ~200 chars of content)."""
52
+ conn = sqlite3.connect(DB_PATH)
53
+ cursor = conn.cursor()
54
+
55
+ cursor.execute("""
56
+ SELECT role, name, content FROM skills
57
+ ORDER BY role ASC, name ASC
58
+ """)
59
+
60
+ skills = []
61
+ for row in cursor.fetchall():
62
+ role = row[0]
63
+ name = row[1]
64
+ content = row[2] or ""
65
+ summary = content.strip().split("\n")[0][:200]
66
+ skills.append({"role": role, "name": name, "summary": summary})
67
+
68
+ conn.close()
69
+ return skills
70
+
71
+ def set_skill(self, role: str, name: str, content: str) -> None:
72
+ """Save a skill for a specific role with a given name to the database."""
73
+ conn = sqlite3.connect(DB_PATH)
74
+ cursor = conn.cursor()
75
+
76
+ cursor.execute("""
77
+ INSERT INTO skills (role, name, content, updated_at)
78
+ VALUES (?, ?, ?, CURRENT_TIMESTAMP)
79
+ ON CONFLICT(role, name) DO UPDATE SET content = excluded.content, updated_at = CURRENT_TIMESTAMP
80
+ """, (role, name, content))
81
+
82
+ conn.commit()
83
+ conn.close()
84
+
85
+ def import_from_markdown(self, role: str, name: str, file_path: str) -> None:
86
+ """Load skill from a markdown file into the database."""
87
+ path = Path(file_path)
88
+ if not path.exists():
89
+ raise FileNotFoundError(f"Skill file not found: {file_path}")
90
+
91
+ content = path.read_text(encoding='utf-8')
92
+ self.set_skill(role, name, content)
93
+
94
+ def export_to_markdown(self, role: str, name: str, file_path: str) -> None:
95
+ """Export skill from the database to a markdown file."""
96
+ content = self.get_skill(role, name)
97
+ if content is None:
98
+ raise ValueError(f"No skill found for role '{role}' with name '{name}'")
99
+
100
+ path = Path(file_path)
101
+ path.parent.mkdir(parents=True, exist_ok=True)
102
+ path.write_text(content, encoding='utf-8')
103
+
104
+ def delete_skill(self, role: str, name: str) -> bool:
105
+ """Delete a skill by role and name. Returns True if a row was deleted."""
106
+ conn = sqlite3.connect(DB_PATH)
107
+ cursor = conn.cursor()
108
+ cursor.execute("""
109
+ DELETE FROM skills WHERE role = ? AND name = ?
110
+ """, (role, name))
111
+ deleted = cursor.rowcount > 0
112
+ conn.commit()
113
+ conn.close()
114
+ return deleted
skills/tool.py ADDED
@@ -0,0 +1,121 @@
1
+ from .manager import SkillManager
2
+
3
+
4
+ def handle_get_skill(args, tool_call_id, agent_role=None):
5
+ """Handle the GetSkill tool call — fetches the full content of a specific skill by name for the caller's role."""
6
+ name = args.get("name")
7
+
8
+ if not agent_role:
9
+ return {
10
+ "role": "tool",
11
+ "tool_call_id": tool_call_id,
12
+ "content": "Error: could not determine agent role"
13
+ }
14
+
15
+ if not name:
16
+ return {
17
+ "role": "tool",
18
+ "tool_call_id": tool_call_id,
19
+ "content": "Error: 'name' parameter is required"
20
+ }
21
+
22
+ manager = SkillManager()
23
+ content = manager.get_skill(agent_role, name)
24
+
25
+ if content is None:
26
+ return {
27
+ "role": "tool",
28
+ "tool_call_id": tool_call_id,
29
+ "content": f"No skill found for role '{agent_role}' with name '{name}'. Use SetSkill to create one."
30
+ }
31
+
32
+ return {
33
+ "role": "tool",
34
+ "tool_call_id": tool_call_id,
35
+ "content": f"Skill '{name}' for role '{agent_role}':\n\n{content}"
36
+ }
37
+
38
+
39
+ def handle(args, tool_call_id, agent_role=None):
40
+ """Handle the SetSkill tool call with user consent.
41
+
42
+ The agent can only create/update skills for its own role.
43
+ """
44
+ name = args.get("name")
45
+ content = args.get("content")
46
+
47
+ if not agent_role:
48
+ return {
49
+ "role": "tool",
50
+ "tool_call_id": tool_call_id,
51
+ "content": "Error: could not determine agent role"
52
+ }
53
+
54
+ if not name:
55
+ return {
56
+ "role": "tool",
57
+ "tool_call_id": tool_call_id,
58
+ "content": "Error: 'name' parameter is required"
59
+ }
60
+
61
+ if not content:
62
+ return {
63
+ "role": "tool",
64
+ "tool_call_id": tool_call_id,
65
+ "content": "Error: 'content' parameter is required"
66
+ }
67
+
68
+ # Check if skill already exists to provide better feedback
69
+ manager = SkillManager()
70
+ existing_skill = manager.get_skill(agent_role, name)
71
+ action = "update" if existing_skill else "create"
72
+
73
+ # Ask for user consent
74
+ print(f"\n{'='*60}")
75
+ print(f"Agent wants to {action} skill '{name}' for role: {agent_role}")
76
+ print(f"{'='*60}")
77
+ print(f"Proposed skill content:")
78
+ print("-" * 60)
79
+ print(content)
80
+ print("-" * 60)
81
+
82
+ consent = ""
83
+ try:
84
+ consent = input(f"\nDo you accept this {action} of '{name}' skill for role '{agent_role}'? (y/n): ").strip().lower()
85
+ except KeyboardInterrupt:
86
+ print()
87
+ exit(0)
88
+ except EOFError:
89
+ print()
90
+ exit(0)
91
+
92
+ if consent == 'y' or consent == 'yes':
93
+ try:
94
+ manager.set_skill(agent_role, name, content)
95
+ return {
96
+ "role": "tool",
97
+ "tool_call_id": tool_call_id,
98
+ "content": f"Successfully {action}d skill '{name}' for role '{agent_role}'"
99
+ }
100
+ except Exception as e:
101
+ return {
102
+ "role": "tool",
103
+ "tool_call_id": tool_call_id,
104
+ "content": f"Error {action}ing skill: {str(e)}"
105
+ }
106
+ else:
107
+ try:
108
+ reason = input(f"Reason for refusal (optional, press Enter to skip): ").strip()
109
+ except (KeyboardInterrupt, EOFError):
110
+ reason = ""
111
+ if reason:
112
+ return {
113
+ "role": "tool",
114
+ "tool_call_id": tool_call_id,
115
+ "content": f"Skill {action} rejected by user. Reason: {reason}"
116
+ }
117
+ return {
118
+ "role": "tool",
119
+ "tool_call_id": tool_call_id,
120
+ "content": f"Skill {action} rejected by user"
121
+ }