/**
* Tag Parser for Dyad-style XML-like tags
* Extracts operations from LLM responses
*/
/**
* Parse all dyad-write tags from response
* Format: content
*/
function getDyadWriteTags(response) {
const tags = [];
const regex = /([\s\S]*?)<\/dyad-write>/g;
let match;
while ((match = regex.exec(response)) !== null) {
tags.push({
type: 'write',
path: match[1],
content: match[2].trim()
});
}
return tags;
}
/**
* Parse all dyad-rename tags from response
* Format:
*/
function getDyadRenameTags(response) {
const tags = [];
const regex = //g;
let match;
while ((match = regex.exec(response)) !== null) {
tags.push({
type: 'rename',
from: match[1],
to: match[2]
});
}
return tags;
}
/**
* Parse all dyad-delete tags from response
* Format:
*/
function getDyadDeleteTags(response) {
const tags = [];
const regex = //g;
let match;
while ((match = regex.exec(response)) !== null) {
tags.push({
type: 'delete',
path: match[1]
});
}
return tags;
}
/**
* Parse all dyad-add-dependency tags from response
* Format:
*/
function getDyadAddDependencyTags(response) {
const tags = [];
const regex = //g;
let match;
while ((match = regex.exec(response)) !== null) {
tags.push({
type: 'add-dependency',
packages: match[1].split(' ').filter(p => p.trim())
});
}
return tags;
}
/**
* Parse all dyad-command tags from response
* Format:
*/
function getDyadCommandTags(response) {
const tags = [];
const regex = //g;
let match;
while ((match = regex.exec(response)) !== null) {
tags.push({
type: 'command',
command: match[1]
});
}
return tags;
}
/**
* Parse all dyad-chat-summary tags from response
* Format: summary text
*/
function getDyadChatSummary(response) {
const match = response.match(/([\s\S]*?)<\/dyad-chat-summary>/);
if (match) {
return {
type: 'chat-summary',
summary: match[1].trim()
};
}
return null;
}
/**
* Extract all tags from response in order
* Returns structured object with all tag types
*/
function extractAllTags(response) {
return {
writes: getDyadWriteTags(response),
renames: getDyadRenameTags(response),
deletes: getDyadDeleteTags(response),
dependencies: getDyadAddDependencyTags(response),
commands: getDyadCommandTags(response),
chatSummary: getDyadChatSummary(response)
};
}
/**
* Check if response contains any actionable tags
*/
function hasActionableTags(response) {
const tags = extractAllTags(response);
return (
tags.writes.length > 0 ||
tags.renames.length > 0 ||
tags.deletes.length > 0 ||
tags.dependencies.length > 0 ||
tags.commands.length > 0
);
}
/**
* Strip all tags from response for display purposes
*/
function stripTags(response) {
let stripped = response;
// Remove dyad-write tags
stripped = stripped.replace(/[\s\S]*?<\/dyad-write>/g, '[File operation]');
// Remove dyad-rename tags
stripped = stripped.replace(//g, '[Rename operation]');
// Remove dyad-delete tags
stripped = stripped.replace(//g, '[Delete operation]');
// Remove dyad-add-dependency tags
stripped = stripped.replace(//g, '[Install packages]');
// Remove dyad-command tags
stripped = stripped.replace(//g, '[Command]');
// Remove dyad-chat-summary tags
stripped = stripped.replace(/[\s\S]*?<\/dyad-chat-summary>/g, '');
return stripped;
}
/**
* Generate a summary of operations for approval UI
*/
function generateOperationSummary(tags) {
const operations = [];
tags.deletes.forEach(tag => {
operations.push({
type: 'delete',
description: `Delete ${tag.path}`,
tag
});
});
tags.renames.forEach(tag => {
operations.push({
type: 'rename',
description: `Rename ${tag.from} → ${tag.to}`,
tag
});
});
tags.writes.forEach(tag => {
operations.push({
type: 'write',
description: `Create/update ${tag.path}`,
tag
});
});
tags.dependencies.forEach(tag => {
operations.push({
type: 'install',
description: `Install packages: ${tag.packages.join(', ')}`,
tag
});
});
tags.commands.forEach(tag => {
operations.push({
type: 'command',
description: `Execute command: ${tag.command}`,
tag
});
});
return operations;
}
module.exports = {
getDyadWriteTags,
getDyadRenameTags,
getDyadDeleteTags,
getDyadAddDependencyTags,
getDyadCommandTags,
getDyadChatSummary,
extractAllTags,
hasActionableTags,
stripTags,
generateOperationSummary
};