dhti-cli 1.3.1 → 1.3.3
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/README.md +7 -2
- package/dist/commands/copilot.d.ts +17 -16
- package/dist/commands/copilot.js +176 -144
- package/oclif.manifest.json +14 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
> 🚀 Dhanvantari rose out of the water with his four hands, holding a pot full of elixirs.
|
|
15
15
|
|
|
16
16
|
# DHTI
|
|
17
|
-
*DHTI enables rapid prototyping, sharing, and testing of GenAI healthcare applications inside an EHR, helping experiments move smoothly into practice. DHTI also includes [skills](/.
|
|
17
|
+
*DHTI enables rapid prototyping, sharing, and testing of GenAI healthcare applications inside an EHR, helping experiments move smoothly into practice. DHTI also includes [skills](/.agents/skills/) that generate GenAI components from problem‑oriented [prompts](/.agents/skills/start-dhti/examples/e2e-sample.md).*
|
|
18
18
|
|
|
19
19
|
### Why?
|
|
20
20
|
|
|
@@ -32,7 +32,12 @@ DHTI includes ready‑to‑use [skills](/.agents/skills/) that can prompt agenti
|
|
|
32
32
|
|
|
33
33
|
Other skills from the open agent skills ecosystem may be useful too! For example, use `npx skills find clinical trial` to find clinical trial related skills. From the results, you can use `npx skills add <skill-name>` to use the skill in your agentic platform. (e.g.`npx skills add anthropics/healthcare@clinical-trial-protocol-skill`)
|
|
34
34
|
|
|
35
|
-
**🤖 [AI-Powered Workflow with GitHub Copilot SDK:](/notes/COPILOT.md) - WIP**
|
|
35
|
+
## **🤖 [AI-Powered Workflow with GitHub Copilot SDK:](/notes/COPILOT.md) - WIP**
|
|
36
|
+
### Quick example
|
|
37
|
+
```bash
|
|
38
|
+
npx dhti-cli copilot --model gpt-5.3-codex --skill elixir-generator --prompt "Generate an elixir glycemic_advisor that summarizes diabetic patients' latest lab results and medications"
|
|
39
|
+
npx dhti-cli copilot --model gpt-5.3-codex --skill start-dhti --prompt "Start the glycemic_advisor elixir and display in CDS-Hooks sandbox"
|
|
40
|
+
```
|
|
36
41
|
|
|
37
42
|
## Try it out
|
|
38
43
|
[[Cheatsheet](/notes/cheatsheet.md) | [Download PDF Cheatsheet](https://nuchange.ca/wp-content/uploads/2026/01/dhti_cheatsheet.pdf)]
|
|
@@ -12,13 +12,25 @@ export default class Copilot extends Command {
|
|
|
12
12
|
model: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
13
|
prompt: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
14
|
skill: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
16
|
};
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Clears the conversation history
|
|
20
|
+
*/
|
|
21
|
+
private clearConversationHistory;
|
|
16
22
|
/**
|
|
17
23
|
* Detects the appropriate skill based on the prompt content
|
|
18
24
|
* @param prompt - The user's prompt text
|
|
19
25
|
* @returns The detected skill name
|
|
20
26
|
*/
|
|
21
27
|
private detectSkill;
|
|
28
|
+
/**
|
|
29
|
+
* Fetches skill content from GitHub if not available locally
|
|
30
|
+
* @param skillName - The name of the skill to fetch
|
|
31
|
+
* @returns The skill content or null if not found
|
|
32
|
+
*/
|
|
33
|
+
private fetchSkillFromGitHub;
|
|
22
34
|
/**
|
|
23
35
|
* Gets the path to the conversation history file
|
|
24
36
|
* @returns The path to the history file
|
|
@@ -29,26 +41,15 @@ export default class Copilot extends Command {
|
|
|
29
41
|
* @returns Array of conversation turns or empty array if no history
|
|
30
42
|
*/
|
|
31
43
|
private loadConversationHistory;
|
|
32
|
-
/**
|
|
33
|
-
* Saves conversation history to file
|
|
34
|
-
* @param history - Array of conversation turns to save
|
|
35
|
-
*/
|
|
36
|
-
private saveConversationHistory;
|
|
37
|
-
/**
|
|
38
|
-
* Clears the conversation history
|
|
39
|
-
*/
|
|
40
|
-
private clearConversationHistory;
|
|
41
|
-
/**
|
|
42
|
-
* Fetches skill content from GitHub if not available locally
|
|
43
|
-
* @param skillName - The name of the skill to fetch
|
|
44
|
-
* @returns The skill content or null if not found
|
|
45
|
-
*/
|
|
46
|
-
private fetchSkillFromGitHub;
|
|
47
44
|
/**
|
|
48
45
|
* Loads skill instructions from local or remote source
|
|
49
46
|
* @param skillName - The name of the skill to load
|
|
50
47
|
* @returns The skill content or null if not found
|
|
51
48
|
*/
|
|
52
49
|
private loadSkill;
|
|
53
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Saves conversation history to file
|
|
52
|
+
* @param history - Array of conversation turns to save
|
|
53
|
+
*/
|
|
54
|
+
private saveConversationHistory;
|
|
54
55
|
}
|
package/dist/commands/copilot.js
CHANGED
|
@@ -13,13 +13,15 @@ export default class Copilot extends Command {
|
|
|
13
13
|
static description = 'Interact with DHTI using GitHub Copilot SDK with streaming responses';
|
|
14
14
|
static examples = [
|
|
15
15
|
'<%= config.bin %> <%= command.id %> --prompt "Start the DHTI stack with langserve"',
|
|
16
|
-
'<%= config.bin %> <%= command.id %> --file ./my-prompt.txt --model gpt-
|
|
16
|
+
'<%= config.bin %> <%= command.id %> --file ./my-prompt.txt --model gpt-5.2',
|
|
17
17
|
'<%= config.bin %> <%= command.id %> --prompt "Generate a new elixir for patient risk assessment" --skill elixir-generator',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> --prompt "Complex task" --timeout 300 # Increase timeout for long-running requests',
|
|
18
19
|
'<%= config.bin %> <%= command.id %> --clear-history --prompt "Start fresh conversation"',
|
|
19
20
|
'<%= config.bin %> <%= command.id %> --clear-history # Clear history without starting new conversation',
|
|
20
21
|
];
|
|
21
22
|
static flags = {
|
|
22
23
|
'clear-history': Flags.boolean({
|
|
24
|
+
char: 'c',
|
|
23
25
|
default: false,
|
|
24
26
|
description: 'Clear conversation history and start a new session',
|
|
25
27
|
}),
|
|
@@ -31,7 +33,12 @@ export default class Copilot extends Command {
|
|
|
31
33
|
model: Flags.string({
|
|
32
34
|
char: 'm',
|
|
33
35
|
default: 'gpt-4.1',
|
|
34
|
-
description: 'Model to use for copilot-sdk interactions'
|
|
36
|
+
description: 'Model to use for copilot-sdk interactions. Supported models include: ' +
|
|
37
|
+
'GPT-4.1 (default), GPT-5.1, GPT-5.2, GPT-5.3, o1-mini, o3-mini, o4-mini, ' +
|
|
38
|
+
'Claude Haiku 4.5, Claude Opus 4.1/4.5/4.6, Claude Sonnet 3.5/3.7/4.5, ' +
|
|
39
|
+
'Gemini 2.0 Flash, Gemini 2.5 Pro, Gemini 3 Flash/Pro, Grok Code Fast 1, Raptor mini. ' +
|
|
40
|
+
'Model availability depends on your GitHub Copilot subscription. ' +
|
|
41
|
+
'See https://docs.github.com/en/copilot/reference/ai-models/supported-models for details.',
|
|
35
42
|
}),
|
|
36
43
|
prompt: Flags.string({
|
|
37
44
|
char: 'p',
|
|
@@ -43,142 +50,13 @@ export default class Copilot extends Command {
|
|
|
43
50
|
default: 'auto',
|
|
44
51
|
description: 'Skill to use for copilot-sdk interactions (auto, start-dhti, elixir-generator, conch-generator)',
|
|
45
52
|
}),
|
|
53
|
+
timeout: Flags.integer({
|
|
54
|
+
char: 't',
|
|
55
|
+
default: 180,
|
|
56
|
+
description: 'Timeout in seconds for model response (default: 180). Increase for complex prompts or slower models.',
|
|
57
|
+
min: 30,
|
|
58
|
+
}),
|
|
46
59
|
};
|
|
47
|
-
/**
|
|
48
|
-
* Detects the appropriate skill based on the prompt content
|
|
49
|
-
* @param prompt - The user's prompt text
|
|
50
|
-
* @returns The detected skill name
|
|
51
|
-
*/
|
|
52
|
-
detectSkill(prompt) {
|
|
53
|
-
const lowerPrompt = prompt.toLowerCase();
|
|
54
|
-
// Check for elixir-related keywords
|
|
55
|
-
if (lowerPrompt.includes('elixir') ||
|
|
56
|
-
lowerPrompt.includes('backend') ||
|
|
57
|
-
lowerPrompt.includes('langserve') ||
|
|
58
|
-
lowerPrompt.includes('genai app')) {
|
|
59
|
-
return 'elixir-generator';
|
|
60
|
-
}
|
|
61
|
-
// Check for conch-related keywords
|
|
62
|
-
if (lowerPrompt.includes('conch') ||
|
|
63
|
-
lowerPrompt.includes('frontend') ||
|
|
64
|
-
lowerPrompt.includes('ui') ||
|
|
65
|
-
lowerPrompt.includes('openmrs')) {
|
|
66
|
-
return 'conch-generator';
|
|
67
|
-
}
|
|
68
|
-
// Use start-dhti if prompt includes 'start', 'show', or 'run'
|
|
69
|
-
if (lowerPrompt.includes('start') || lowerPrompt.includes('show') || lowerPrompt.includes('run')) {
|
|
70
|
-
return 'start-dhti';
|
|
71
|
-
}
|
|
72
|
-
// If none of the skills match, exit asking for a skill name and show available skills
|
|
73
|
-
const availableSkills = ['start-dhti', 'elixir-generator', 'conch-generator'];
|
|
74
|
-
this.error(`Could not detect the appropriate skill from the prompt.\n` +
|
|
75
|
-
`Please specify a skill name using --skill.\n` +
|
|
76
|
-
`Available skills: ${availableSkills.join(', ')}`);
|
|
77
|
-
return '';
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Gets the path to the conversation history file
|
|
81
|
-
* @returns The path to the history file
|
|
82
|
-
*/
|
|
83
|
-
getHistoryFilePath() {
|
|
84
|
-
const dhtiDir = path.join(os.homedir(), '.dhti');
|
|
85
|
-
if (!fs.existsSync(dhtiDir)) {
|
|
86
|
-
fs.mkdirSync(dhtiDir, { recursive: true });
|
|
87
|
-
}
|
|
88
|
-
return path.join(dhtiDir, 'copilot-history.json');
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Loads conversation history from file
|
|
92
|
-
* @returns Array of conversation turns or empty array if no history
|
|
93
|
-
*/
|
|
94
|
-
loadConversationHistory() {
|
|
95
|
-
try {
|
|
96
|
-
const historyPath = this.getHistoryFilePath();
|
|
97
|
-
if (fs.existsSync(historyPath)) {
|
|
98
|
-
const historyData = fs.readFileSync(historyPath, 'utf8');
|
|
99
|
-
return JSON.parse(historyData);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
catch (error) {
|
|
103
|
-
this.warn(chalk.yellow(`Failed to load conversation history: ${error}`));
|
|
104
|
-
}
|
|
105
|
-
return [];
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Saves conversation history to file
|
|
109
|
-
* @param history - Array of conversation turns to save
|
|
110
|
-
*/
|
|
111
|
-
saveConversationHistory(history) {
|
|
112
|
-
try {
|
|
113
|
-
const historyPath = this.getHistoryFilePath();
|
|
114
|
-
fs.writeFileSync(historyPath, JSON.stringify(history, null, 2), 'utf8');
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
this.warn(chalk.yellow(`Failed to save conversation history: ${error}`));
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Clears the conversation history
|
|
122
|
-
*/
|
|
123
|
-
clearConversationHistory() {
|
|
124
|
-
try {
|
|
125
|
-
const historyPath = this.getHistoryFilePath();
|
|
126
|
-
if (fs.existsSync(historyPath)) {
|
|
127
|
-
fs.unlinkSync(historyPath);
|
|
128
|
-
this.log(chalk.green('✓ Conversation history cleared'));
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
this.log(chalk.yellow('No conversation history to clear'));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
this.warn(chalk.yellow(`Failed to clear conversation history: ${error}`));
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Fetches skill content from GitHub if not available locally
|
|
140
|
-
* @param skillName - The name of the skill to fetch
|
|
141
|
-
* @returns The skill content or null if not found
|
|
142
|
-
*/
|
|
143
|
-
async fetchSkillFromGitHub(skillName) {
|
|
144
|
-
try {
|
|
145
|
-
const url = `https://raw.githubusercontent.com/dermatologist/dhti/develop/.agents/skills/${skillName}/SKILL.md`;
|
|
146
|
-
const response = await fetch(url);
|
|
147
|
-
if (!response.ok) {
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
return response.text();
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
this.warn(`Failed to fetch skill ${skillName} from GitHub: ${error}`);
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Loads skill instructions from local or remote source
|
|
159
|
-
* @param skillName - The name of the skill to load
|
|
160
|
-
* @returns The skill content or null if not found
|
|
161
|
-
*/
|
|
162
|
-
async loadSkill(skillName) {
|
|
163
|
-
// Resolve skills directory
|
|
164
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
165
|
-
const __dirname = path.dirname(__filename);
|
|
166
|
-
const skillsDir = path.resolve(__dirname, '../../.agents/skills');
|
|
167
|
-
const skillPath = path.join(skillsDir, skillName, 'SKILL.md');
|
|
168
|
-
// Try to load from local directory first
|
|
169
|
-
if (fs.existsSync(skillPath)) {
|
|
170
|
-
try {
|
|
171
|
-
return fs.readFileSync(skillPath, 'utf8');
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
this.warn(`Failed to read local skill file: ${error}`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
// If not found locally, try to fetch from GitHub
|
|
178
|
-
this.log(chalk.yellow(`Skill ${skillName} not found locally, fetching from GitHub...`));
|
|
179
|
-
return this.fetchSkillFromGitHub(skillName);
|
|
180
|
-
}
|
|
181
|
-
// eslint-disable-next-line perfectionist/sort-classes
|
|
182
60
|
async run() {
|
|
183
61
|
const { flags } = await this.parse(Copilot);
|
|
184
62
|
// Handle clear-history flag
|
|
@@ -273,14 +151,33 @@ export default class Copilot extends Command {
|
|
|
273
151
|
process.stdout.write(content);
|
|
274
152
|
assistantResponse += content;
|
|
275
153
|
});
|
|
276
|
-
// Handle session
|
|
277
|
-
session.on('session.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
154
|
+
// Handle session errors
|
|
155
|
+
session.on('session.error', (error) => {
|
|
156
|
+
this.warn(chalk.yellow(`Session error: ${error}`));
|
|
157
|
+
});
|
|
158
|
+
// Send the prompt with a configurable timeout
|
|
159
|
+
const timeoutMs = flags.timeout * 1000;
|
|
160
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
161
|
+
setTimeout(() => {
|
|
162
|
+
reject(new Error(`Request timeout after ${timeoutMs}ms - increase with --timeout flag if needed`));
|
|
163
|
+
}, timeoutMs);
|
|
281
164
|
});
|
|
282
|
-
|
|
283
|
-
|
|
165
|
+
this.log(chalk.dim(`⏱ Timeout set to ${flags.timeout} seconds (use --timeout to adjust)`));
|
|
166
|
+
try {
|
|
167
|
+
await Promise.race([session.sendAndWait({ prompt: promptContent }), timeoutPromise]);
|
|
168
|
+
}
|
|
169
|
+
catch (timeoutError) {
|
|
170
|
+
if (assistantResponse.trim()) {
|
|
171
|
+
this.log(chalk.yellow(`\n⚠ Response incomplete due to timeout, but partial response was captured.`));
|
|
172
|
+
this.log(chalk.yellow(`💡 Tip: Try increasing timeout with --timeout ${flags.timeout * 2} for longer responses`));
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
throw timeoutError;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (responseStarted) {
|
|
179
|
+
console.log('\n'); // Add newline after response
|
|
180
|
+
}
|
|
284
181
|
this.log(chalk.blue('\n--- End of Response ---\n'));
|
|
285
182
|
// Save conversation history
|
|
286
183
|
conversationHistory.push({ content: promptContent, role: 'user' });
|
|
@@ -289,6 +186,7 @@ export default class Copilot extends Command {
|
|
|
289
186
|
}
|
|
290
187
|
this.saveConversationHistory(conversationHistory);
|
|
291
188
|
this.log(chalk.dim(`💾 Conversation saved (${conversationHistory.length} messages). Use --clear-history to reset.`));
|
|
189
|
+
this.log(chalk.yellow('🧹 Cleaning up session, please wait...'));
|
|
292
190
|
}
|
|
293
191
|
catch (error) {
|
|
294
192
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -305,4 +203,138 @@ export default class Copilot extends Command {
|
|
|
305
203
|
}
|
|
306
204
|
}
|
|
307
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Clears the conversation history
|
|
208
|
+
*/
|
|
209
|
+
clearConversationHistory() {
|
|
210
|
+
try {
|
|
211
|
+
const historyPath = this.getHistoryFilePath();
|
|
212
|
+
if (fs.existsSync(historyPath)) {
|
|
213
|
+
fs.unlinkSync(historyPath);
|
|
214
|
+
this.log(chalk.green('✓ Conversation history cleared'));
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
this.log(chalk.yellow('No conversation history to clear'));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
this.warn(chalk.yellow(`Failed to clear conversation history: ${error}`));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Detects the appropriate skill based on the prompt content
|
|
226
|
+
* @param prompt - The user's prompt text
|
|
227
|
+
* @returns The detected skill name
|
|
228
|
+
*/
|
|
229
|
+
detectSkill(prompt) {
|
|
230
|
+
const lowerPrompt = prompt.toLowerCase();
|
|
231
|
+
// Check for elixir-related keywords
|
|
232
|
+
if (lowerPrompt.includes('elixir') ||
|
|
233
|
+
lowerPrompt.includes('backend') ||
|
|
234
|
+
lowerPrompt.includes('langserve') ||
|
|
235
|
+
lowerPrompt.includes('genai app')) {
|
|
236
|
+
return 'elixir-generator';
|
|
237
|
+
}
|
|
238
|
+
// Check for conch-related keywords
|
|
239
|
+
if (lowerPrompt.includes('conch') ||
|
|
240
|
+
lowerPrompt.includes('frontend') ||
|
|
241
|
+
lowerPrompt.includes('ui') ||
|
|
242
|
+
lowerPrompt.includes('openmrs')) {
|
|
243
|
+
return 'conch-generator';
|
|
244
|
+
}
|
|
245
|
+
// Use start-dhti if prompt includes 'start', 'show', or 'run'
|
|
246
|
+
if (lowerPrompt.includes('start') || lowerPrompt.includes('show') || lowerPrompt.includes('run')) {
|
|
247
|
+
return 'start-dhti';
|
|
248
|
+
}
|
|
249
|
+
// If none of the skills match, exit asking for a skill name and show available skills
|
|
250
|
+
const availableSkills = ['start-dhti', 'elixir-generator', 'conch-generator'];
|
|
251
|
+
this.error(`Could not detect the appropriate skill from the prompt.\n` +
|
|
252
|
+
`Please specify a skill name using --skill.\n` +
|
|
253
|
+
`Available skills: ${availableSkills.join(', ')}`);
|
|
254
|
+
return '';
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Fetches skill content from GitHub if not available locally
|
|
258
|
+
* @param skillName - The name of the skill to fetch
|
|
259
|
+
* @returns The skill content or null if not found
|
|
260
|
+
*/
|
|
261
|
+
async fetchSkillFromGitHub(skillName) {
|
|
262
|
+
try {
|
|
263
|
+
const url = `https://raw.githubusercontent.com/dermatologist/dhti/develop/.agents/skills/${skillName}/SKILL.md`;
|
|
264
|
+
const response = await fetch(url);
|
|
265
|
+
if (!response.ok) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
return response.text();
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
this.warn(`Failed to fetch skill ${skillName} from GitHub: ${error}`);
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Gets the path to the conversation history file
|
|
277
|
+
* @returns The path to the history file
|
|
278
|
+
*/
|
|
279
|
+
getHistoryFilePath() {
|
|
280
|
+
const dhtiDir = path.join(os.homedir(), '.dhti');
|
|
281
|
+
if (!fs.existsSync(dhtiDir)) {
|
|
282
|
+
fs.mkdirSync(dhtiDir, { recursive: true });
|
|
283
|
+
}
|
|
284
|
+
return path.join(dhtiDir, 'copilot-history.json');
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Loads conversation history from file
|
|
288
|
+
* @returns Array of conversation turns or empty array if no history
|
|
289
|
+
*/
|
|
290
|
+
loadConversationHistory() {
|
|
291
|
+
try {
|
|
292
|
+
const historyPath = this.getHistoryFilePath();
|
|
293
|
+
if (fs.existsSync(historyPath)) {
|
|
294
|
+
const historyData = fs.readFileSync(historyPath, 'utf8');
|
|
295
|
+
return JSON.parse(historyData);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
this.warn(chalk.yellow(`Failed to load conversation history: ${error}`));
|
|
300
|
+
}
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Loads skill instructions from local or remote source
|
|
305
|
+
* @param skillName - The name of the skill to load
|
|
306
|
+
* @returns The skill content or null if not found
|
|
307
|
+
*/
|
|
308
|
+
async loadSkill(skillName) {
|
|
309
|
+
// Resolve skills directory
|
|
310
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
311
|
+
const __dirname = path.dirname(__filename);
|
|
312
|
+
const skillsDir = path.resolve(__dirname, '../../.agents/skills');
|
|
313
|
+
const skillPath = path.join(skillsDir, skillName, 'SKILL.md');
|
|
314
|
+
// Try to load from local directory first
|
|
315
|
+
if (fs.existsSync(skillPath)) {
|
|
316
|
+
try {
|
|
317
|
+
return fs.readFileSync(skillPath, 'utf8');
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
this.warn(`Failed to read local skill file: ${error}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// If not found locally, try to fetch from GitHub
|
|
324
|
+
this.log(chalk.yellow(`Skill ${skillName} not found locally, fetching from GitHub...`));
|
|
325
|
+
return this.fetchSkillFromGitHub(skillName);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Saves conversation history to file
|
|
329
|
+
* @param history - Array of conversation turns to save
|
|
330
|
+
*/
|
|
331
|
+
saveConversationHistory(history) {
|
|
332
|
+
try {
|
|
333
|
+
const historyPath = this.getHistoryFilePath();
|
|
334
|
+
fs.writeFileSync(historyPath, JSON.stringify(history, null, 2), 'utf8');
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
this.warn(chalk.yellow(`Failed to save conversation history: ${error}`));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
308
340
|
}
|
package/oclif.manifest.json
CHANGED
|
@@ -181,13 +181,15 @@
|
|
|
181
181
|
"description": "Interact with DHTI using GitHub Copilot SDK with streaming responses",
|
|
182
182
|
"examples": [
|
|
183
183
|
"<%= config.bin %> <%= command.id %> --prompt \"Start the DHTI stack with langserve\"",
|
|
184
|
-
"<%= config.bin %> <%= command.id %> --file ./my-prompt.txt --model gpt-
|
|
184
|
+
"<%= config.bin %> <%= command.id %> --file ./my-prompt.txt --model gpt-5.2",
|
|
185
185
|
"<%= config.bin %> <%= command.id %> --prompt \"Generate a new elixir for patient risk assessment\" --skill elixir-generator",
|
|
186
|
+
"<%= config.bin %> <%= command.id %> --prompt \"Complex task\" --timeout 300 # Increase timeout for long-running requests",
|
|
186
187
|
"<%= config.bin %> <%= command.id %> --clear-history --prompt \"Start fresh conversation\"",
|
|
187
188
|
"<%= config.bin %> <%= command.id %> --clear-history # Clear history without starting new conversation"
|
|
188
189
|
],
|
|
189
190
|
"flags": {
|
|
190
191
|
"clear-history": {
|
|
192
|
+
"char": "c",
|
|
191
193
|
"description": "Clear conversation history and start a new session",
|
|
192
194
|
"name": "clear-history",
|
|
193
195
|
"allowNo": false,
|
|
@@ -206,7 +208,7 @@
|
|
|
206
208
|
},
|
|
207
209
|
"model": {
|
|
208
210
|
"char": "m",
|
|
209
|
-
"description": "Model to use for copilot-sdk interactions",
|
|
211
|
+
"description": "Model to use for copilot-sdk interactions. Supported models include: GPT-4.1 (default), GPT-5.1, GPT-5.2, GPT-5.3, o1-mini, o3-mini, o4-mini, Claude Haiku 4.5, Claude Opus 4.1/4.5/4.6, Claude Sonnet 3.5/3.7/4.5, Gemini 2.0 Flash, Gemini 2.5 Pro, Gemini 3 Flash/Pro, Grok Code Fast 1, Raptor mini. Model availability depends on your GitHub Copilot subscription. See https://docs.github.com/en/copilot/reference/ai-models/supported-models for details.",
|
|
210
212
|
"name": "model",
|
|
211
213
|
"default": "gpt-4.1",
|
|
212
214
|
"hasDynamicHelp": false,
|
|
@@ -232,6 +234,15 @@
|
|
|
232
234
|
"hasDynamicHelp": false,
|
|
233
235
|
"multiple": false,
|
|
234
236
|
"type": "option"
|
|
237
|
+
},
|
|
238
|
+
"timeout": {
|
|
239
|
+
"char": "t",
|
|
240
|
+
"description": "Timeout in seconds for model response (default: 180). Increase for complex prompts or slower models.",
|
|
241
|
+
"name": "timeout",
|
|
242
|
+
"default": 180,
|
|
243
|
+
"hasDynamicHelp": false,
|
|
244
|
+
"multiple": false,
|
|
245
|
+
"type": "option"
|
|
235
246
|
}
|
|
236
247
|
},
|
|
237
248
|
"hasDynamicHelp": false,
|
|
@@ -882,5 +893,5 @@
|
|
|
882
893
|
]
|
|
883
894
|
}
|
|
884
895
|
},
|
|
885
|
-
"version": "1.3.
|
|
896
|
+
"version": "1.3.3"
|
|
886
897
|
}
|