@j-o-r/hello-dave 0.0.5 → 0.0.7

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 (127) hide show
  1. package/CHANGELOG.md +13 -26
  2. package/README.md +161 -522
  3. package/README.md.bak +144 -449
  4. package/{examples → agents}/ask_agent.js +5 -5
  5. package/{examples → agents}/codeserver.sh +14 -14
  6. package/{examples → agents}/daisy_agent.js +5 -5
  7. package/{examples → agents}/docs_agent.js +5 -5
  8. package/{examples → agents}/gpt_agent.js +5 -5
  9. package/{examples → agents}/grok_agent.js +5 -5
  10. package/agents/memory_agent.js +263 -0
  11. package/{examples → agents}/npm_agent.js +5 -5
  12. package/{examples → agents}/prompt_agent.js +5 -5
  13. package/agents/spawn_agent.js +137 -0
  14. package/{examples → agents}/test_agent.js +6 -8
  15. package/{examples → agents}/todo_agent.js +5 -5
  16. package/bin/codeDave +58 -0
  17. package/bin/dave.js +114 -96
  18. package/lib/AgentClient.js +111 -67
  19. package/lib/AgentManager.js +111 -80
  20. package/lib/AgentServer.js +144 -104
  21. package/lib/Cli.js +126 -93
  22. package/lib/Prompt.js +38 -5
  23. package/lib/Session.js +102 -79
  24. package/lib/ToolSet.js +79 -60
  25. package/lib/fafs.js +54 -19
  26. package/lib/genericToolset.js +109 -213
  27. package/lib/wsCli.js +50 -19
  28. package/lib/wsIO.js +11 -17
  29. package/package.json +2 -2
  30. package/types/AgentClient.d.ts +69 -35
  31. package/types/AgentManager.d.ts +50 -56
  32. package/types/AgentServer.d.ts +63 -16
  33. package/types/Cli.d.ts +56 -10
  34. package/types/Prompt.d.ts +36 -4
  35. package/types/Session.d.ts +23 -9
  36. package/types/ToolSet.d.ts +49 -32
  37. package/types/fafs.d.ts +68 -25
  38. package/types/wsCli.d.ts +14 -0
  39. package/types/wsIO.d.ts +9 -5
  40. package/utils/search_sessions.sh +100 -53
  41. package/README.md.backup +0 -269
  42. package/README.md.bak.1774780058 +0 -338
  43. package/README.md.bak2 +0 -531
  44. package/bin/spawn_agent.js +0 -293
  45. package/docs.bak.1774780058/agent-manager.md +0 -167
  46. package/docs.bak.1774780058/agent-manager.md.bak +0 -137
  47. package/docs.bak.1774780058/agent-manager.md.bak2 +0 -157
  48. package/docs.bak.1774780058/codeserver-pattern.md +0 -191
  49. package/docs.bak.1774780058/path-resolution-best-practices.md +0 -104
  50. package/docs.bak.1774780058/project-overview.md +0 -67
  51. package/docs.bak.1774780058/project-overview.md.bak +0 -67
  52. package/docs.bak.1774780058/prompt-class.md +0 -141
  53. package/docs.bak.1774780058/prompt-class.md.bak +0 -142
  54. package/docs.bak.1774780058/tools-syntax-validation.md +0 -121
  55. package/docs.bak.1774780058/tools-syntax-validation.md.bak2 +0 -125
  56. package/docs.bak.1774780058/tools-syntax-validation.md.bak3 +0 -125
  57. package/docs.bak.1774780058/tools-syntax-validation.md.bak4 +0 -106
  58. package/docs.bak.1774780058/tools-syntax-validation.md.bak_path +0 -106
  59. package/docs.bak.1774780058/toolset.md +0 -164
  60. package/docs.bak.1774780058/toolset.md.bak +0 -94
  61. package/docs.bak.1774780058/toolset.md.bak3 +0 -161
  62. package/docs.bak.1774780058/toolset.md.bak4 +0 -161
  63. package/docs.bak.1774780058/toolset.md.bak5 +0 -161
  64. package/docs.bak.1774780058/toolset.md.bak6 +0 -163
  65. package/docs.bak.1774780058/toolset.md.bak_path +0 -163
  66. package/docs.bak.1774780058/toolset.md.bak_syntax +0 -161
  67. package/docs.bak.1774780058/xai-responses.md +0 -111
  68. package/docs.bak.1774780058/xai-responses.md.bak +0 -107
  69. package/docs.bak.1774780058/xai-responses.md.bak2 +0 -107
  70. package/docs.bak.1774780058/xai_collections.md +0 -106
  71. package/examples/memory_agent.js +0 -152
  72. package/examples.bak.1774780058/ask_agent.js +0 -114
  73. package/examples.bak.1774780058/code_agent.js +0 -149
  74. package/examples.bak.1774780058/coderev_agent.js +0 -72
  75. package/examples.bak.1774780058/codeserver.sh +0 -47
  76. package/examples.bak.1774780058/daisy_agent.js +0 -177
  77. package/examples.bak.1774780058/docs_agent.js +0 -119
  78. package/examples.bak.1774780058/gpt_agent.js +0 -109
  79. package/examples.bak.1774780058/grok_agent.js +0 -98
  80. package/examples.bak.1774780058/memory_agent.js +0 -112
  81. package/examples.bak.1774780058/npm_agent.js +0 -175
  82. package/examples.bak.1774780058/prompt_agent.js +0 -112
  83. package/examples.bak.1774780058/readme_agent.js +0 -144
  84. package/examples.bak.1774780058/spawn_agent.js +0 -263
  85. package/examples.bak.1774780058/test_agent.js +0 -162
  86. package/examples.bak.1774780058/todo_agent.js +0 -138
  87. package/lib/genericToolset.js.bak_syntax +0 -402
  88. package/scenarios.bak.1774780058/data/eval_node_message.json +0 -9
  89. package/scenarios.bak.1774780058/data/hist_oa.json +0 -66
  90. package/scenarios.bak.1774780058/data/o3_response1.json +0 -96
  91. package/scenarios.bak.1774780058/data/oa_reasoning_parse.json +0 -112
  92. package/scenarios.bak.1774780058/data/tool_oa.json +0 -96
  93. package/scenarios.bak.1774780058/data/tool_xai.json +0 -59
  94. package/scenarios.bak.1774780058/data/tool_xai2.json +0 -40
  95. package/scenarios.bak.1774780058/data/xai-response-1.json +0 -59
  96. package/scenarios.bak.1774780058/data/xai-response-2.json +0 -10
  97. package/scenarios.bak.1774780058/data/xai_reasoning_tools_resp.json +0 -59
  98. package/scenarios.bak.1774780058/data/xai_search_response.json +0 -58
  99. package/scenarios.bak.1774780058/environment.js +0 -10
  100. package/scenarios.bak.1774780058/example.js +0 -17
  101. package/scenarios.bak.1774780058/genericToolset.test.js +0 -182
  102. package/scenarios.bak.1774780058/grok.js +0 -113
  103. package/scenarios.bak.1774780058/memory-tools.js +0 -51
  104. package/scenarios.bak.1774780058/openai-o3.js +0 -137
  105. package/scenarios.bak.1774780058/openai-prompt.js +0 -155
  106. package/scenarios.bak.1774780058/openai-session.js +0 -148
  107. package/scenarios.bak.1774780058/openai.js +0 -102
  108. package/scenarios.bak.1774780058/prompt.js +0 -118
  109. package/scenarios.bak.1774780058/promptFishbowl.js +0 -76
  110. package/scenarios.bak.1774780058/search.brave.com.js +0 -25
  111. package/scenarios.bak.1774780058/sh.js +0 -15
  112. package/scenarios.bak.1774780058/test-wsio.js +0 -26
  113. package/scenarios.bak.1774780058/testToolset.js +0 -42
  114. package/scenarios.bak.1774780058/toolset.js +0 -16
  115. package/scenarios.bak.1774780058/toolset.test.js +0 -141
  116. package/scenarios.bak.1774780058/write_file_syntax.test.js +0 -145
  117. package/scenarios.bak.1774780058/write_file_validation/README.md +0 -30
  118. package/scenarios.bak.1774780058/write_file_validation/bad.js +0 -3
  119. package/scenarios.bak.1774780058/write_file_validation/good.js +0 -4
  120. package/scenarios.bak.1774780058/write_file_validation/test.sh +0 -43
  121. package/scenarios.bak.1774780058/wsClient.js +0 -69
  122. package/scenarios.bak.1774780058/xai_responses.integration.test.js +0 -57
  123. package/scenarios.bak.1774780058/xai_responses.test.js +0 -154
  124. package/scenarios.bak.1774780058/xaicoll.js +0 -50
  125. package/scenarios.bak.1774780058/xaifiles.js +0 -48
  126. /package/{examples → agents}/code_agent.js +0 -0
  127. /package/{examples → agents}/readme_agent.js +0 -0
package/lib/Session.js CHANGED
@@ -1,7 +1,26 @@
1
1
  /**
2
- * Records sessions
3
- * Get sessions
4
- * This is an optional extension for the prompt
2
+ * Session manager for recording and retrieving chat sessions tied to a Prompt instance.
3
+ * Handles caching of messages, records, and logs in NDJSON files under `.cache/hello-dave/<name>/`.
4
+ * Automatically creates session names from first non-sticky user message.
5
+ * Listens to Prompt events (message, record, reset, truncated, others) for persistence.
6
+ *
7
+ * Properties:
8
+ * - `name` (string, readonly): Sanitized prompt name used as session root folder.
9
+ *
10
+ * Usage:
11
+ * ```
12
+ * const session = new Session('my-prompt', prompt, './custom-cache');
13
+ * console.log(session.info()); // Session details
14
+ * const sessions = session.sessionList(); // ['user query', 'another']
15
+ * const messages = session.set('user-query'); // Load/advance session
16
+ * session.empty(); // Clear all data
17
+ * ```
18
+ *
19
+ * @property {string} name - Sanitized prompt name (read-only).
20
+ * @fires Prompt#message - Appends message to current session cache.
21
+ * @fires Prompt#record - Appends record to daily records cache.
22
+ * @fires Prompt#reset - Resets session counter/name/queue to prompt.messages.
23
+ * @fires Prompt#truncated - Resets session like #reset.
5
24
  */
6
25
 
7
26
  import path from 'path';
@@ -14,12 +33,12 @@ import Prompt from './Prompt.js';
14
33
  */
15
34
 
16
35
  /**
17
- * Get the next version number for a given key in the array.
18
- *
19
- * @param {string[]} arr - The array of versioned strings.
20
- * @param {string} key - The key to check for the highest version.
21
- * @returns {number} - The next version number.
22
- */
36
+ * Get the next version number for a given key in the array.
37
+ *
38
+ * @param {string[]} arr - The array of versioned strings.
39
+ * @param {string} key - The key to check for the highest version.
40
+ * @returns {number} The next version number.
41
+ */
23
42
  const getNextVersion = (arr, key) => {
24
43
  const regex = new RegExp(`^${key}_(\\d+)$`);
25
44
  const versions = arr
@@ -33,51 +52,44 @@ const getNextVersion = (arr, key) => {
33
52
  };
34
53
 
35
54
  /**
36
- */
55
+ * Session class for managing persistent chat sessions with Prompt.
56
+ */
37
57
  class Session {
38
- /**
39
- * sticky message que
40
- * @type {Message[]}
41
- */
42
- #que = [];
43
- /** @type {string} session name */
58
+ /** @type {Message[]} Sticky message queue. */
59
+ #queue = [];
60
+ /** @type {string} Current session name. */
44
61
  #sessionName = '';
45
- /** @type {number} count the number of session iterations */
62
+ /** @type {number} Current session iteration counter. */
46
63
  #sessionCounter = 0;
47
- /** @type {CacheSync} */
64
+ /** @type {CacheSync} Session messages cache. */
48
65
  #sessionCache;
49
- /** @type {CacheSync} */
66
+ /** @type {CacheSync} Records cache (billing/optimizing). */
50
67
  #recordCache;
51
- /** @type {CacheSync} */
68
+ /** @type {CacheSync} Logs cache (events). */
52
69
  #logCache;
70
+ /** @type {string} Sanitized prompt name (public readonly). */
71
+ name;
53
72
 
54
73
  /**
55
- * Create and set the cache folders
74
+ * Initializes cache folders and event listeners.
56
75
  * @param {string} promptName - The name of the prompt.
57
- * @param {Prompt} prompt - The prompt
58
- * @param {string} [baseFolder] - The base storage folder, default this is process.cwd()
76
+ * @param {Prompt} prompt - The Prompt instance.
77
+ * @param {string} [baseFolder] - Base storage folder; defaults to `.cache/hello-dave`.
78
+ * @private
59
79
  */
60
- #inititializeCache = (promptName, prompt, baseFolder) => {
61
- // If not base folder, we take FOLDERS.ROOT
62
- // else we use the baseFolder
80
+ #initializeCache = (promptName, prompt, baseFolder) => {
63
81
  if (!baseFolder) {
64
- // Relative from cwd()
65
82
  baseFolder = path.resolve('.cache', 'hello-dave');
66
83
  }
67
- // Create the root folder, just in case
68
84
  const root = new CacheSync(baseFolder, true, 'any');
69
- // this.#baseStorage = baseFolder;
70
85
  this.name = root.sanitizeKey(promptName);
71
86
  const sessionStorage = path.resolve(baseFolder, this.name);
72
- // Create the session folder
73
87
  new CacheSync(sessionStorage, true, 'any');
74
- // Create the records cache
75
88
  this.#logCache = new CacheSync(path.resolve(sessionStorage, 'logs'), true, 'ndjson');
76
89
  this.#recordCache = new CacheSync(path.resolve(sessionStorage, 'records'), true, 'ndjson');
77
90
  if (this.name.length === 0) {
78
91
  throw new Error(`Invalid session name: ${promptName}`);
79
92
  }
80
- // create and init the session storage
81
93
  this.#sessionCache = new CacheSync(path.resolve(sessionStorage, 'sessions'), true, 'ndjson');
82
94
  prompt.on(prompt.EVENTS.message, (msg) => {
83
95
  this.#msg(msg);
@@ -86,89 +98,93 @@ class Session {
86
98
  this.#rec(rec);
87
99
  });
88
100
  prompt.on(prompt.EVENTS.reset, () => {
89
- // Prompt has only sticky messages
90
- // fill the que
91
101
  this.#sessionCounter = 0;
92
102
  this.#sessionName = '';
93
- this.#que = prompt.messages;
103
+ this.#queue = prompt.messages;
94
104
  });
95
105
  prompt.on(prompt.EVENTS.truncated, () => {
96
- // Prompt has only sticky messages
97
- // fill the que
98
106
  this.#sessionCounter = 0;
99
107
  this.#sessionName = '';
100
- this.#que = prompt.messages;
108
+ this.#queue = prompt.messages;
101
109
  });
102
110
  const events = Object.keys(prompt.EVENTS);
103
111
  events.forEach((evt) => {
104
112
  prompt.on(evt, (msg) => {
105
113
  this.#log({ evt, msg });
106
114
  });
107
- })
108
- }
115
+ });
116
+ };
117
+
109
118
  /**
110
- * Constructs a new Prompt instance.
111
- * If contextWindow = 0 (defaut) the prompt will have no context building up (ONE_SHOT)
119
+ * Constructs a new Session instance.
120
+ * If contextWindow = 0 (default) the prompt will have no context building up (ONE_SHOT).
121
+ *
112
122
  * @param {string} name - The name of the prompt.
113
- * @param {Prompt} prompt - The prompt
114
- * @param {string} [storage] - The base storage folder, default this is process.cwd()
123
+ * @param {Prompt} prompt - The Prompt instance.
124
+ * @param {string} [storage] - The base storage folder; defaults to process.cwd()/.cache/hello-dave.
115
125
  */
116
126
  constructor(name, prompt, storage) {
117
- this.#inititializeCache(name, prompt, storage)
127
+ this.#initializeCache(name, prompt, storage);
118
128
  }
119
129
 
120
- info() {
121
- const name = this.name;
122
- const session = this.#sessionName;
123
- const sessionCount = this.#sessionCounter;
124
- const path = this.#sessionCache.folder;
125
- return `Name: ${name}\nSession: ${session}\nSessionCount: ${sessionCount}\nPath: ${path}`;
126
- }
127
130
  /**
128
- * Add message to the cache
129
- * @param {import('./Prompt.js').Message} msg
131
+ * Returns current session information.
132
+ *
133
+ * @returns {string} Formatted info: Name, Session, SessionCount, Path.
134
+ */
135
+ info() {
136
+ const name = this.name;
137
+ const session = this.#sessionName;
138
+ const sessionCount = this.#sessionCounter;
139
+ const path = this.#sessionCache.folder;
140
+ return `Name: ${name}\nSession: ${session}\nSessionCount: ${sessionCount}\nPath: ${path}`;
141
+ }
142
+
143
+ /**
144
+ * Appends a message to the current session cache (private).
145
+ * Auto-creates session name from first non-sticky user message.
146
+ *
147
+ * @param {Message} msg - The message to append.
148
+ * @private
130
149
  */
131
150
  #msg(msg) {
132
151
  const role = msg.role;
133
152
  const sticky = msg.sticky;
134
153
  let sessKey;
135
154
  if (this.#sessionName === '' && role === 'user' && sticky === false) {
136
- // Create a sessioname
137
155
  if (msg.content.length > 0 && msg.content[0].type === 'text') {
138
156
  /**
139
- * @param {string} str
140
- */
157
+ * @param {string} str
158
+ */
141
159
  const truncateString = (str) => str.length > 50 ? str.slice(0, 80) : str;
142
- // @ts-ignore
143
160
  this.#sessionName = this.#sessionCache.sanitizeKey(truncateString(msg.content[0].text).trim());
144
161
  this.#sessionCounter = getNextVersion(this.#sessionCache.list(), this.#sessionName);
145
162
  sessKey = this.#sessionCache.sanitizeKey(`${this.#sessionName}_${this.#sessionCounter}`);
146
163
  }
147
- } else if(this.#sessionName !== '') {
164
+ } else if (this.#sessionName !== '') {
148
165
  sessKey = this.#sessionCache.sanitizeKey(`${this.#sessionName}_${this.#sessionCounter}`);
149
166
  }
150
167
  if (sessKey) {
151
- // Alway write the que if there is a name
152
- // May the must be written sticky nots or a truncated previous sessions
153
- if (this.#que.length > 0) {
154
- // Write outstanding messages
168
+ if (this.#queue.length > 0) {
155
169
  let i = 0;
156
- const len = this.#que.length;
170
+ const len = this.#queue.length;
157
171
  for (; i < len; i++) {
158
- this.#sessionCache.append(sessKey, this.#que[i]);
172
+ this.#sessionCache.append(sessKey, this.#queue[i]);
159
173
  }
160
- this.#que = [];
174
+ this.#queue = [];
161
175
  }
162
176
  this.#sessionCache.append(sessKey, msg);
163
177
  } else {
164
- this.#que.push(msg);
178
+ this.#queue.push(msg);
165
179
  }
166
180
  }
167
181
 
168
182
  /**
169
- * store a record
170
- * @param {Record} record - Records for billing and optimizing
183
+ * Stores a record (billing/optimizing).
184
+ *
185
+ * @param {Record} record - The record to store.
171
186
  * @throws {Error} If the record is invalid.
187
+ * @private
172
188
  */
173
189
  #rec(record) {
174
190
  const key = new Date().toISOString().split('T')[0];
@@ -176,21 +192,27 @@ class Session {
176
192
  }
177
193
 
178
194
  /**
179
- * log all events
180
- * @param {*} o - just log
181
- * @throws {Error} If the record is invalid.
195
+ * Logs an event object.
196
+ *
197
+ * @param {*} o - The object to log.
198
+ * @throws {Error} If logging fails.
199
+ * @private
182
200
  */
183
201
  #log(o) {
184
202
  const key = new Date().toISOString().split('T')[0];
185
203
  this.#logCache.append(key, o);
186
204
  }
205
+
206
+ /**
207
+ * Lists unique session names (processed: remove _\d+, _ → space).
208
+ *
209
+ * @returns {string[]} Array of unique session names.
210
+ */
187
211
  sessionList() {
188
212
  const list = this.#sessionCache.list();
189
213
  const processedSet = new Set(
190
214
  list.map(str => {
191
- // Remove the last '_[number]' part
192
215
  const withoutNumber = str.replace(/_\d+$/, '');
193
- // Replace '_' with ' '
194
216
  return withoutNumber.replace(/_/g, ' ');
195
217
  })
196
218
  );
@@ -198,10 +220,11 @@ class Session {
198
220
  }
199
221
 
200
222
  /**
201
- * Get messages fro, a session
202
- *
223
+ * Sets/advances to a session and loads its messages.
224
+ * Updates counter to last version if exists.
225
+ *
203
226
  * @param {string} sess - The session name.
204
- * @return {Message[]}
227
+ * @returns {Message[]} Messages from the session (or []).
205
228
  */
206
229
  set(sess) {
207
230
  const counter = getNextVersion(this.#sessionCache.list(), sess);
@@ -210,7 +233,6 @@ class Session {
210
233
  } else {
211
234
  this.#sessionCounter = counter - 1;
212
235
  }
213
- // Load last know session
214
236
  const sessKey = this.#sessionCache.sanitizeKey(`${sess}_${this.#sessionCounter}`);
215
237
  const res = this.#sessionCache.read(sessKey) || [];
216
238
 
@@ -219,8 +241,9 @@ class Session {
219
241
  }
220
242
  return res;
221
243
  }
244
+
222
245
  /**
223
- * Remove all previous sessions and records
246
+ * Empties all caches: sessions, records, logs.
224
247
  */
225
248
  empty() {
226
249
  this.#sessionCache.empty();
package/lib/ToolSet.js CHANGED
@@ -1,30 +1,30 @@
1
1
  import Prompt from "./Prompt.js";
2
2
  /**
3
- * @module @j-o-r/toolset
4
- * Register function_calls / tools
5
- * This library can be used stand-alone or as a wrapper for AI functions_calls tools
6
- */
3
+ * @module @j-o-r/toolset
4
+ * Register function_calls / tools
5
+ * This library can be used stand-alone or as a wrapper for AI functions_calls tools
6
+ */
7
7
 
8
8
  /**
9
- * @typedef {object} TSSchema
10
- * @property {string} type - Type of the object
11
- * @property {object} properties - Description of properties
12
- * @property {string[]} [required] - Required properties
13
- */
9
+ * @typedef {object} TSSchema
10
+ * @property {string} type - Type of the object
11
+ * @property {object} properties - Description of properties
12
+ * @property {string[]} [required] - Required properties
13
+ */
14
14
  /**
15
- * @typedef {object} TSTool
16
- * @property {string} description - Command description
17
- * @property {TSSchema} parameters - JS JSON schema
18
- * @property {function(object): Promise} method
19
- */
15
+ * @typedef {object} TSTool
16
+ * @property {string} description - Command description
17
+ * @property {TSSchema} parameters - JS JSON schema
18
+ * @property {function(object): Promise<*>} method - The async method to execute
19
+ */
20
20
  /**
21
- * @typedef {object} TSToolListItem
22
- * @property {string} name - Command name
23
- * @property {string} description - Command description
24
- * @property {TSSchema} parameters - JS JSON schema
25
- */
21
+ * @typedef {object} TSToolListItem
22
+ * @property {string} name - Command name
23
+ * @property {string} description - Command description
24
+ * @property {TSSchema} parameters - JS JSON schema
25
+ */
26
26
 
27
- /** Type of choices */
27
+ /** @enum {string} Type of choices for tool usage */
28
28
  const CHOICES = {
29
29
  NONE: 'none',
30
30
  AUTO: 'auto',
@@ -32,21 +32,27 @@ const CHOICES = {
32
32
  };
33
33
 
34
34
  /**
35
- * The function name is limited
36
- * @param {string} s
37
- * @returns {boolean}
38
- */
35
+ * Validates if the given string is a valid tool name.
36
+ * Valid names match the regex /^[#!a-z_0-9]{2,}$/
37
+ * @param {string} s - The string to validate
38
+ * @returns {boolean} True if valid, false otherwise
39
+ */
39
40
  const isValidName = (s) => /^[#!a-z_0-9]{2,}$/.test(s);
40
41
 
42
+ /**
43
+ * @classdesc ToolSet manages a collection of tools (functions) that can be registered and executed, typically for AI agent function calling.
44
+ * Supports schema definition compatible with JSON Schema for parameters.
45
+ */
41
46
  class ToolSet {
42
- /** @type {Map<string, TSTool>} */
47
+ /** @type {Map<string, TSTool>} Private map storing registered tools */
43
48
  #tools = new Map();
44
- /** default auto */
49
+ /** @private Default tool choice setting */
45
50
  #toolChoice = 'auto';
46
51
 
47
52
  /**
48
- * @param {string} [choice] - Default 'auto' auto|none|required
49
- */
53
+ * Constructs a new ToolSet instance.
54
+ * @param {string} [choice='auto'] - Default tool choice: 'auto' | 'none' | 'required'
55
+ */
50
56
  constructor(choice = 'auto') {
51
57
  if (choice && Object.values(CHOICES).includes(choice)) {
52
58
  this.#toolChoice = choice;
@@ -56,20 +62,21 @@ class ToolSet {
56
62
  }
57
63
 
58
64
  /**
59
- * How many functions have we registered
60
- * @returns {number}
61
- */
65
+ * Getter for the number of registered tools.
66
+ * @returns {number} The count of tools in the set
67
+ */
62
68
  get length() {
63
69
  return this.#tools.size;
64
70
  }
65
71
 
66
72
  /**
67
- * Register a Tool/Command/Function
68
- * @param {string} name - [a-z_0-9]{2,} The lowercase string name of the callback e.g. 'get_node_version'
69
- * @param {string} description - What does it do
70
- * @param {TSSchema} parameters - SCHEMA object describing the function's input parameters
71
- * @param {function(object): Promise<*>} method - Async function to call
72
- */
73
+ * Registers a new tool/command/function in the set.
74
+ * Overwrites if already exists (no error thrown).
75
+ * @param {string} name - The lowercase string name of the tool e.g. 'get_node_version'. Must match /^[#!a-z_0-9]{2,}$/
76
+ * @param {string} description - Human-readable description of what the tool does
77
+ * @param {TSSchema} parameters - JSON Schema object describing the input parameters
78
+ * @param {function(object): Promise<*>} method - The async function to call with parsed parameters
79
+ */
73
80
  add(name, description, parameters, method) {
74
81
  if (typeof name !== 'string') {
75
82
  throw new Error('Tool name must be a string');
@@ -78,42 +85,45 @@ class ToolSet {
78
85
  throw new Error(`Invalid tool name '${name}': must match /^[#!a-z_0-9]{2,}$/`);
79
86
  }
80
87
  if (this.has(name)) {
81
- // throw new Error('Function already defined');
88
+ // Note: Allows overwrite without error
82
89
  }
83
90
  this.#tools.set(name, { description, parameters, method });
84
91
  }
85
92
 
86
93
  /**
87
- * Get a tool from the toolset
88
- * @param {string} name
89
- * @returns {TSTool}
90
- */
94
+ * Retrieves a registered tool by name.
95
+ * @param {string} name - The name of the tool
96
+ * @returns {TSTool} The tool object
97
+ * @throws {Error} If tool not found
98
+ */
91
99
  get(name) {
92
100
  if (!this.has(name)) {
93
101
  throw new Error('Function not found');
94
102
  }
95
103
  return this.#tools.get(name);
96
104
  }
105
+
97
106
  /**
98
- * Delete a function_call
99
- * @param {string} name
100
- */
107
+ * Deletes a tool from the set by name.
108
+ * @param {string} name - The name of the tool to delete
109
+ */
101
110
  delete(name) {
102
111
  this.#tools.delete(name);
103
112
  }
113
+
104
114
  /**
105
- * Is 'name' already registered
106
- * @param {string} name
107
- * @returns {boolean}
108
- */
115
+ * Checks if a tool with the given name is registered.
116
+ * @param {string} name - The tool name to check
117
+ * @returns {boolean} True if the tool exists
118
+ */
109
119
  has(name) {
110
120
  return this.#tools.has(name);
111
121
  }
112
122
 
113
123
  /**
114
- * Get a list of tools available
115
- * @returns {TSToolListItem[]}
116
- */
124
+ * Returns a sorted list of all registered tools (excluding methods).
125
+ * @returns {TSToolListItem[]} Array of tool summaries, sorted by name
126
+ */
117
127
  list() {
118
128
  return Array.from(this.#tools.entries()).map(([name, value]) => ({
119
129
  name,
@@ -122,15 +132,21 @@ class ToolSet {
122
132
  })).sort((a, b) => a.name.localeCompare(b.name));
123
133
  }
124
134
 
135
+ /**
136
+ * Getter for the current tool choice setting.
137
+ * @returns {string} The tool choice: 'auto', 'none', or 'required'
138
+ */
125
139
  get toolChoice() {
126
140
  return this.#toolChoice;
127
141
  }
142
+
128
143
  /**
129
- * Execute a method
130
- * @param {string} name
131
- * @param {object} params
132
- * @returns {Promise<*>}
133
- */
144
+ * Executes a specific tool by name with given parameters.
145
+ * @param {string} name - The tool name
146
+ * @param {object} params - The parameters object
147
+ * @returns {Promise<*>} The result from the tool method
148
+ * @throws {Error} If tool not found or execution fails
149
+ */
134
150
  async call(name, params) {
135
151
  if (!this.has(name)) {
136
152
  throw new Error('Function not found');
@@ -138,9 +154,12 @@ class ToolSet {
138
154
  const tool = this.#tools.get(name);
139
155
  return tool.method(params);
140
156
  }
157
+
141
158
  /**
142
- * Execute function requests from the last message.
143
- * @param {Prompt} prompt - Handle tool calls.
159
+ * Processes and executes function requests from the last message in a Prompt.
160
+ * Automatically handles multiple calls, emits events, and adds responses back to the prompt.
161
+ * @param {Prompt} prompt - The Prompt instance containing the last message with function_request(s)
162
+ * @returns {Promise<void>}
144
163
  */
145
164
  async execute(prompt) {
146
165
  const lastMessage = prompt.getLastMessage();
@@ -187,4 +206,4 @@ class ToolSet {
187
206
  }
188
207
  }
189
208
 
190
- export default ToolSet;
209
+ export default ToolSet;
package/lib/fafs.js CHANGED
@@ -10,27 +10,55 @@ new CacheAsync(RELATIVE_CACHE, true, 'bin');
10
10
  const APP_CACHE = path.resolve(RELATIVE_CACHE, APPNAME);
11
11
  new CacheAsync(APP_CACHE, true, 'bin');
12
12
 
13
+ /**
14
+ * Frequently Asked Functions (FAFS) - Utility module providing environment and system information utilities.
15
+ *
16
+ * Main features:
17
+ * - `env()`: Gathers user, system, location, and path info (cached 31 days).
18
+ * - `systemInfo()`: Detailed OS/architecture/working dir string.
19
+ * - `GLOBAL`: Shared config (max requests, cache path, secret).
20
+ *
21
+ * @module fafs
22
+ * @exports {function} env
23
+ * @exports {function} systemInfo
24
+ * @exports {Object} GLOBAL
25
+ * @exports {function} jsType
26
+ */
27
+
28
+ /**
29
+ * @typedef {Object} EnvironmentInfo
30
+ * @property {string} name - User's full name (from passwd GECOS field)
31
+ * @property {string} system - System information (OS release, architecture, kernel, cwd)
32
+ * @property {string} city - City from external IP geolocation
33
+ * @property {string} region - Region/State from IP
34
+ * @property {string} country - Country from IP
35
+ * @property {string} timezone - Timezone from IP
36
+ * @property {string} external_ip - External IP address
37
+ * @property {string} cwd - Current working directory (relative to home as ~)
38
+ */
39
+
40
+ /**
41
+ * Global configuration and constants for the application.
42
+ * @type {Object}
43
+ * @property {number} max_recursive_requests - Maximum number of recursive requests allowed (default: 20)
44
+ * @property {string} default_cache - Path to the default application cache directory
45
+ * @property {string} secret - Secret key for cache encryption/security
46
+ */
13
47
  const GLOBAL = {
14
- max_recursive_requests: 20, // max number recursive of request
48
+ max_recursive_requests: 20,
15
49
  default_cache: APP_CACHE,
16
50
  secret: 'tdb.e3a0cd73dd6a429283f921f9fc1bad41'
17
51
  }
18
52
 
19
53
  /**
20
- * @typedef {Object} EnvironmentInfo
21
- * @property {string} name - User's name
22
- * @property {string} system - Linux OS / PROC
23
- * @property {string} city
24
- * @property {string} region
25
- * @property {string} country
26
- * @property {string} timezone
27
- * @property {string} external_ip
28
- * @property {string} cwd - current working folder
29
- */
30
-
31
- /**
32
- * @returns {Promise<string>}
33
- */
54
+ * Retrieves detailed system information including OS release (/etc/issue), machine architecture (uname -m),
55
+ * operating system (uname -o), and current working directory.
56
+ *
57
+ * @returns {Promise<string>} Formatted multi-line string with system details.
58
+ * @example
59
+ * const info = await systemInfo();
60
+ * // "system: Ubuntu 25.10 \n\nx86_64\nGNU/Linux\ncwd: /home/user/project"
61
+ */
34
62
  async function systemInfo() {
35
63
  let system = 'system: ' + (await SH`cat /etc/issue`.run()).trim().replace('\\n', '').replace('\\l', '');
36
64
  system += (await SH`uname -m`.run()).trim().replace('\\n', '').replace('\\l', '');
@@ -48,9 +76,15 @@ async function systemInfo() {
48
76
  }
49
77
 
50
78
  /**
51
- * Gather information about this environment
52
- * @returns {Promise<EnvironmentInfo>}
53
- */
79
+ * Gathers comprehensive environment information: user name, fresh system details, IP-based geolocation,
80
+ * and normalized current working directory. Results are cached in ~/.cache/hello-dave/env for 31 days,
81
+ * refreshed on expiry. On cache hit, updates with fresh system info and cwd.
82
+ *
83
+ * @returns {Promise<EnvironmentInfo>} The environment information object.
84
+ * @example
85
+ * const envInfo = await env();
86
+ * console.log(envInfo.name, envInfo.city, envInfo.system);
87
+ */
54
88
  async function env() {
55
89
  const CACHE_PATH = (await SH`echo ~/.cache/${APPNAME}`.run()).trim();
56
90
  const KEY = 'env';
@@ -82,6 +116,7 @@ async function env() {
82
116
  timezone: userInfo.timezone,
83
117
  external_ip: userInfo.ip
84
118
  }
119
+ info.cwd = cwd;
85
120
  await storage.write(KEY, JSON.stringify(info));
86
121
  return info;
87
122
  }
@@ -91,4 +126,4 @@ export {
91
126
  GLOBAL,
92
127
  env,
93
128
  systemInfo
94
- }
129
+ }