@orxataguy/tyr 1.0.30 → 1.0.32
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 +49 -10
- package/package.json +1 -1
- package/src/core/Container.ts +4 -1
- package/src/core/Kernel.ts +2 -0
- package/src/core/sys/chat.ts +73 -0
- package/src/core/sys/help.ts +1 -0
- package/src/index.ts +1 -0
- package/src/lib/AIContextManager.ts +1004 -27
- package/src/lib/AIVendorManager.ts +474 -25
- package/src/lib/ChatManager.ts +880 -0
- package/src/lib/PromptTemplateManager.ts +112 -1
- package/src/lib/TokenManager.ts +30 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AIContextManager } from './AIContextManager.js';
|
|
1
|
+
import { AIContextManager, AGENT_TOOLS_DESCRIPTION, SEARCH_REPLACE_FORMAT_INSTRUCTIONS } from './AIContextManager.js';
|
|
2
2
|
import { AIMessage } from './AIVendorManager.js';
|
|
3
3
|
import { Logger } from '../core/Logger.js';
|
|
4
4
|
import { TyrError } from '../core/TyrError.js';
|
|
@@ -8,6 +8,62 @@ export interface PromptTemplate {
|
|
|
8
8
|
user: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
const CODE_SYSTEM_PROMPT =
|
|
12
|
+
"You are a senior software engineer working inside an existing project. You are given the project's " +
|
|
13
|
+
'AGENTS.md (architecture and conventions) and its directory tree. Fulfil the user\'s request by ' +
|
|
14
|
+
'creating new files and/or modifying existing ones as needed, following the project\'s existing ' +
|
|
15
|
+
'conventions (naming, structure, style, imports) exactly as described in AGENTS.md. Regardless of the ' +
|
|
16
|
+
"language used elsewhere in the project's existing code or comments, or the language of the user's " +
|
|
17
|
+
'request, write ALL comments you add or rewrite (inline comments, JSDoc/docblocks, etc.) in English.\n\n' +
|
|
18
|
+
'Before writing any new file that is supposed to follow an existing convention or pattern (e.g. "a ' +
|
|
19
|
+
'command file", "a service class", any structure AGENTS.md only names without fully specifying), do ' +
|
|
20
|
+
'not guess its exact shape from the name alone: find and read at least one real existing example of ' +
|
|
21
|
+
'that pattern in the project first (list_directory / read_file / search_dependency_usage can help you ' +
|
|
22
|
+
'locate one), and follow its structure precisely. If the pattern depends on a framework or library ' +
|
|
23
|
+
'installed in the project and you are not certain of its exact API, do not assume — use ' +
|
|
24
|
+
'read_dependency_manifest to locate it in node_modules and see its entry points, then ' +
|
|
25
|
+
'list_dependency_files / read_dependency_file to read its real source or type definitions before you ' +
|
|
26
|
+
'write code against it.\n\n' +
|
|
27
|
+
SEARCH_REPLACE_FORMAT_INSTRUCTIONS +
|
|
28
|
+
'\n\n' + AGENT_TOOLS_DESCRIPTION +
|
|
29
|
+
" Do not assume the content of an existing file you need to modify that hasn't already been shown to " +
|
|
30
|
+
'you: request it with read_file before writing a SEARCH block against it — a SEARCH snippet that does ' +
|
|
31
|
+
"not match the file's real current content will simply fail to apply and that edit will be skipped.";
|
|
32
|
+
|
|
33
|
+
const PROJECT_DESCRIBE_SYSTEM_PROMPT =
|
|
34
|
+
'You are a senior software architect writing a comprehensive AGENTS.md context file for an AI coding ' +
|
|
35
|
+
"assistant that will work on this project. You are given the project's README (when present) — treat " +
|
|
36
|
+
"it as a helpful guide to the project's intent and terminology, but not as the only source of truth, " +
|
|
37
|
+
'since it may be incomplete or outdated — and the project file tree, for structural grounding only ' +
|
|
38
|
+
'(an accurate tree will be appended to the document automatically after your response, so do not ' +
|
|
39
|
+
'reproduce or reformat it yourself). Everything else about the codebase — file contents, the ' +
|
|
40
|
+
"project's real dependencies, how they're used — is NOT included up front: request it yourself with " +
|
|
41
|
+
'the available tools before writing. Produce a thorough, well-structured Markdown document that: ' +
|
|
42
|
+
'describes the overall purpose of the project; walks through every directory and file, explaining ' +
|
|
43
|
+
'what it does and what it is responsible for; documents the public API surface (exported classes, ' +
|
|
44
|
+
'functions, types) of each module; and explains how the pieces fit together, so a newcomer AI ' +
|
|
45
|
+
'assistant can safely make changes without missing context. Be exhaustive and specific to this ' +
|
|
46
|
+
"codebase — do not include generic advice. Write the entire document in English, regardless of the " +
|
|
47
|
+
"language used in the project's source code, comments, filenames, README, or file contents.\n\n" +
|
|
48
|
+
AGENT_TOOLS_DESCRIPTION +
|
|
49
|
+
' When you have everything you need, respond ONLY with the final Markdown document, with no additional ' +
|
|
50
|
+
'commentary or mentions of the tools you used.';
|
|
51
|
+
|
|
52
|
+
const FILE_DESCRIBE_SYSTEM_PROMPT =
|
|
53
|
+
'You are a senior software architect writing an AGENTS.md-style context file for a single source ' +
|
|
54
|
+
'file, meant to be read by an AI coding assistant before it edits that file. You may also be given ' +
|
|
55
|
+
"the project's README and/or its existing AGENTS.md for overall context — use them to align " +
|
|
56
|
+
'terminology and to note how this file fits into the broader project, without repeating what they ' +
|
|
57
|
+
'already cover. Produce a thorough, well-structured Markdown document that: describes what the file ' +
|
|
58
|
+
'does end-to-end; documents every exported class, function, type and constant, what it is ' +
|
|
59
|
+
"responsible for, and how it is meant to be used; and calls out any non-obvious behaviour, edge " +
|
|
60
|
+
'cases or invariants a future editor needs to know. Be exhaustive and specific to this file — do not ' +
|
|
61
|
+
'include generic advice. Write the entire document in English, regardless of the language used in ' +
|
|
62
|
+
"the file's own code, comments, or the project's README/AGENTS.md.\n\n" +
|
|
63
|
+
AGENT_TOOLS_DESCRIPTION +
|
|
64
|
+
' When you have everything you need, respond ONLY with the final Markdown document, with no additional ' +
|
|
65
|
+
'commentary or mentions of the tools you used.';
|
|
66
|
+
|
|
11
67
|
const DEFAULT_TEMPLATES: Record<string, PromptTemplate> = {
|
|
12
68
|
'analyze-bug': {
|
|
13
69
|
system:
|
|
@@ -23,6 +79,13 @@ const DEFAULT_TEMPLATES: Record<string, PromptTemplate> = {
|
|
|
23
79
|
'JSDoc comments on public methods. Output only the TypeScript code for the file, no commentary.',
|
|
24
80
|
user: 'Command name: {{name}}\nRequested behaviour:\n{{description}}',
|
|
25
81
|
},
|
|
82
|
+
'generate-code': {
|
|
83
|
+
system:
|
|
84
|
+
'You are a senior engineer. Follow the ' +
|
|
85
|
+
'existing conventions exactly: KISS, SOLID, DRY. Only make simple comments on the functions to give context if is necessary ' +
|
|
86
|
+
'Output only the code for the file, no commentary.',
|
|
87
|
+
user: 'Command name: {{name}}\nRequested behaviour:\n{{description}}',
|
|
88
|
+
},
|
|
26
89
|
'explain-code': {
|
|
27
90
|
system:
|
|
28
91
|
'You are a senior software engineer. Explain what the given code does clearly and ' +
|
|
@@ -36,6 +99,25 @@ const DEFAULT_TEMPLATES: Record<string, PromptTemplate> = {
|
|
|
36
99
|
'provide the full refactored code.',
|
|
37
100
|
user: 'Code:\n```\n{{code}}\n```\n\nGoal:\n{{goal}}',
|
|
38
101
|
},
|
|
102
|
+
// Used by the ai:code command. AGENTS.md is prepended automatically by build() (via
|
|
103
|
+
// AIContextManager.getContext) — {{tree}} is the only extra context seeded up front; the
|
|
104
|
+
// agent pulls in any specific file it needs itself via AGENT_TOOLS (see AIContextManager).
|
|
105
|
+
'ai-code': {
|
|
106
|
+
system: CODE_SYSTEM_PROMPT,
|
|
107
|
+
user: '# Project structure\n```\n{{tree}}\n```\n\n# Task\n{{task}}',
|
|
108
|
+
},
|
|
109
|
+
// Used by the ai:describe command when generating AGENTS.md for a whole project. Intentionally
|
|
110
|
+
// built with buildStandalone() rather than build(): forcing a guidelines file to exist before
|
|
111
|
+
// we've generated one yet would be circular.
|
|
112
|
+
'ai-describe-project': {
|
|
113
|
+
system: PROJECT_DESCRIBE_SYSTEM_PROMPT,
|
|
114
|
+
user: '{{readme}}\n\n# Project structure\n```\n{{tree}}\n```',
|
|
115
|
+
},
|
|
116
|
+
// Used by the ai:describe command when describing a single file.
|
|
117
|
+
'ai-describe-file': {
|
|
118
|
+
system: FILE_DESCRIBE_SYSTEM_PROMPT,
|
|
119
|
+
user: 'File path (relative to the project): {{path}}\nFile content:\n```\n{{content}}\n```\n\n{{context}}',
|
|
120
|
+
},
|
|
39
121
|
};
|
|
40
122
|
|
|
41
123
|
/**
|
|
@@ -128,6 +210,35 @@ export class PromptTemplateManager {
|
|
|
128
210
|
|
|
129
211
|
return [systemMessage, ...contextMessages, userMessage];
|
|
130
212
|
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @method buildStandalone
|
|
216
|
+
* @description Like build(), but does NOT prepend the project's guidelines file — use this
|
|
217
|
+
* when the template itself is what generates that file (e.g. 'ai-describe-project', the very
|
|
218
|
+
* first run on a project with no AGENTS.md yet), where calling getContext() would force a
|
|
219
|
+
* separate, redundant guidelines generation first.
|
|
220
|
+
* @param {string} templateName - Name of a registered template (see listTemplates()).
|
|
221
|
+
* @param {Record<string,string>} vars - Values for the template's {{placeholders}}.
|
|
222
|
+
* @returns {AIMessage[]} Ordered messages: [system, user].
|
|
223
|
+
* @example
|
|
224
|
+
* const messages = prompts.buildStandalone('ai-describe-project', { tree, readme });
|
|
225
|
+
* const result = await aiContext.runDescribeAgent(projectDir, messages, tokens);
|
|
226
|
+
*/
|
|
227
|
+
public buildStandalone(templateName: string, vars: Record<string, string>): AIMessage[] {
|
|
228
|
+
const template = this.templates[templateName];
|
|
229
|
+
if (!template) {
|
|
230
|
+
throw new TyrError(
|
|
231
|
+
`Unknown prompt template: '${templateName}'`,
|
|
232
|
+
null,
|
|
233
|
+
`Available templates: ${this.listTemplates().join(', ')}`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return [
|
|
238
|
+
{ role: 'system', content: template.system },
|
|
239
|
+
{ role: 'user', content: this.fill(template.user, vars) },
|
|
240
|
+
];
|
|
241
|
+
}
|
|
131
242
|
}
|
|
132
243
|
|
|
133
244
|
export const PromptTemplateManagerTests = {
|
package/src/lib/TokenManager.ts
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { homedir } from 'os';
|
|
4
4
|
|
|
5
|
-
import { AIMessage } from './AIVendorManager.js';
|
|
5
|
+
import { AIMessage, AIContentBlock } from './AIVendorManager.js';
|
|
6
6
|
import { Logger } from '../core/Logger.js';
|
|
7
7
|
import { TyrError } from '../core/TyrError.js';
|
|
8
8
|
import { getEnvInt } from '../core/util/getenv.js';
|
|
@@ -56,6 +56,34 @@ export class TokenManager {
|
|
|
56
56
|
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* @method contentToText
|
|
61
|
+
* @description Flattens an AIMessage's content — a plain string, or an AIContentBlock[] as
|
|
62
|
+
* used by the tool-use agent loop (text / tool_use / tool_result / image blocks) — into a
|
|
63
|
+
* single string for estimation purposes. Non-text blocks contribute an approximate stand-in
|
|
64
|
+
* length (JSON-stringified input/content) rather than being ignored, so a message full of
|
|
65
|
+
* large tool_result blocks isn't silently under-counted.
|
|
66
|
+
* @param {string | AIContentBlock[]} content - The raw content of an AIMessage.
|
|
67
|
+
* @returns {string} Flattened text.
|
|
68
|
+
* @example
|
|
69
|
+
* const text = tokens_.contentToText(message.content);
|
|
70
|
+
*/
|
|
71
|
+
public contentToText(content: string | AIContentBlock[]): string {
|
|
72
|
+
if (typeof content === 'string') return content;
|
|
73
|
+
|
|
74
|
+
return content
|
|
75
|
+
.map((block) => {
|
|
76
|
+
switch (block.type) {
|
|
77
|
+
case 'text': return block.text;
|
|
78
|
+
case 'tool_use': return JSON.stringify(block.input ?? {});
|
|
79
|
+
case 'tool_result': return block.content;
|
|
80
|
+
case 'image': return ''; // billed very differently from text; excluded from the char-based heuristic
|
|
81
|
+
default: return '';
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
.join('\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
59
87
|
/**
|
|
60
88
|
* @method estimateMessagesTokens
|
|
61
89
|
* @description Estimates the total token cost of a full list of prompt messages.
|
|
@@ -66,7 +94,7 @@ export class TokenManager {
|
|
|
66
94
|
*/
|
|
67
95
|
public estimateMessagesTokens(messages: AIMessage[]): number {
|
|
68
96
|
return messages.reduce(
|
|
69
|
-
(total, m) => total + this.estimateTokens(m.content) + PER_MESSAGE_OVERHEAD_TOKENS,
|
|
97
|
+
(total, m) => total + this.estimateTokens(this.contentToText(m.content)) + PER_MESSAGE_OVERHEAD_TOKENS,
|
|
70
98
|
0
|
|
71
99
|
);
|
|
72
100
|
}
|