mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-03 08:46:46 +00:00
regen skills
This commit is contained in:
2
.github/scripts/doc-agent/package-lock.json
generated
vendored
2
.github/scripts/doc-agent/package-lock.json
generated
vendored
@@ -20,7 +20,7 @@
|
||||
"typescript": "^5.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
"node": ">=20.12"
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk": {
|
||||
|
||||
46
.github/scripts/doc-agent/src/regen-skills.ts
vendored
46
.github/scripts/doc-agent/src/regen-skills.ts
vendored
@@ -2,12 +2,16 @@
|
||||
* Regen-skills mode: rebuild a module's skill file from ai.md inputs.
|
||||
*
|
||||
* For a given module (e.g. `protocol`, `ledger`, `consensus`), collect all
|
||||
* `.ai.md` files under the matching source paths and ask the agent to
|
||||
* produce an updated `docs/skills/<module>.md`.
|
||||
* `.ai.md` files under the matching source paths and ask the Agent SDK to
|
||||
* write an updated `docs/skills/<module>.md`.
|
||||
*
|
||||
* The agent writes the file via the `Write` tool rather than returning the
|
||||
* skill content as text. This avoids hitting the per-turn output token
|
||||
* limit on large modules (which previously truncated several skill files).
|
||||
*/
|
||||
|
||||
import { existsSync, readdirSync, statSync } from 'node:fs';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join, relative, resolve } from 'node:path';
|
||||
import { query } from '@anthropic-ai/claude-agent-sdk';
|
||||
import { MODEL, MODULE_SKILL_MAP, PROMPTS_DIR, SKILLS_DIR, XRPLD_ROOT } from './config.js';
|
||||
@@ -60,16 +64,17 @@ async function loadAiFiles(absPaths: readonly string[]): Promise<AiFile[]> {
|
||||
* Regenerate the skill file for a given module name.
|
||||
*
|
||||
* @param moduleName - The skill file name without extension (e.g. "protocol",
|
||||
* "ledger"). Must match a key in the MODULE_SKILL_MAP value set.
|
||||
* "ledger"). Must match a value in MODULE_SKILL_MAP.
|
||||
*/
|
||||
export async function regenSkills(moduleName: string): Promise<void> {
|
||||
const skillFile = `${moduleName}.md`;
|
||||
const prefixes = prefixesForSkill(skillFile);
|
||||
|
||||
if (prefixes.length === 0) {
|
||||
throw new Error(
|
||||
`Unknown module: ${moduleName}. Valid modules: ${Array.from(new Set(Object.values(MODULE_SKILL_MAP).filter((v): v is string => v !== null))).join(', ')}`,
|
||||
const known = Array.from(
|
||||
new Set(Object.values(MODULE_SKILL_MAP).filter((v): v is string => v !== null)),
|
||||
);
|
||||
throw new Error(`Unknown module: ${moduleName}. Valid modules: ${known.join(', ')}`);
|
||||
}
|
||||
|
||||
console.log(`Regenerating skill: ${skillFile}`);
|
||||
@@ -84,6 +89,7 @@ export async function regenSkills(moduleName: string): Promise<void> {
|
||||
|
||||
const aiFiles = await loadAiFiles(aiPaths);
|
||||
const skillPath = resolve(SKILLS_DIR, skillFile);
|
||||
const skillRelPath = relative(XRPLD_ROOT, skillPath);
|
||||
const existingSkill = existsSync(skillPath)
|
||||
? await readFile(skillPath, 'utf8')
|
||||
: '(no existing skill file — create a new one)';
|
||||
@@ -94,7 +100,11 @@ export async function regenSkills(moduleName: string): Promise<void> {
|
||||
.map((f) => `\n### \`${f.sourcePath}\`\n\n${f.content}`)
|
||||
.join('\n\n---\n');
|
||||
|
||||
const userPrompt = `Regenerate the skill file: \`docs/skills/${skillFile}\`
|
||||
const userPrompt = `Regenerate the skill file at: \`${skillRelPath}\`
|
||||
|
||||
Use the **Write** tool to write the new content to that path. Do NOT return
|
||||
the skill content in your message — write it directly to the file. This
|
||||
avoids hitting per-turn output token limits.
|
||||
|
||||
## Existing skill content
|
||||
|
||||
@@ -104,27 +114,31 @@ ${existingSkill}
|
||||
|
||||
${aiBlocks}
|
||||
|
||||
Produce the new complete skill file content as your final message.`;
|
||||
When you have written the file, respond with a brief one-line confirmation.`;
|
||||
|
||||
let response = '';
|
||||
const result = query({
|
||||
prompt: userPrompt,
|
||||
options: {
|
||||
model: MODEL,
|
||||
systemPrompt,
|
||||
cwd: XRPLD_ROOT,
|
||||
allowedTools: ['Read', 'Glob', 'Grep'],
|
||||
allowedTools: ['Write', 'Read', 'Glob', 'Grep'],
|
||||
permissionMode: 'acceptEdits',
|
||||
},
|
||||
});
|
||||
|
||||
let wroteFile = false;
|
||||
for await (const message of result) {
|
||||
if (message.type === 'assistant') {
|
||||
const content = message.message?.content;
|
||||
if (Array.isArray(content)) {
|
||||
for (const block of content) {
|
||||
if (block.type === 'text') {
|
||||
response += block.text;
|
||||
if (block.type === 'tool_use' && block.name === 'Write') {
|
||||
const input = block.input as { file_path?: string } | undefined;
|
||||
if (input?.file_path !== undefined) {
|
||||
wroteFile = true;
|
||||
console.log(` Agent wrote: ${input.file_path}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,12 +149,10 @@ Produce the new complete skill file content as your final message.`;
|
||||
}
|
||||
}
|
||||
|
||||
const trimmed = response.trim();
|
||||
if (trimmed.length === 0) {
|
||||
console.error(' Agent returned empty response — skill file not updated.');
|
||||
if (!wroteFile) {
|
||||
console.error(' Agent did not call Write — skill file not updated.');
|
||||
return;
|
||||
}
|
||||
|
||||
await writeFile(skillPath, `${trimmed}\n`);
|
||||
console.log(` Wrote: ${relative(XRPLD_ROOT, skillPath)}`);
|
||||
console.log(` Wrote: ${skillRelPath}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user