Initial commit: Obsidian Web Interface for Claude Code
- Full IDE with terminal integration using xterm.js - Session management with local and web sessions - HTML preview functionality - Multi-terminal support with session picker Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
236
services/tag-parser.js
Normal file
236
services/tag-parser.js
Normal file
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* Tag Parser for Dyad-style XML-like tags
|
||||
* Extracts operations from LLM responses
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parse all dyad-write tags from response
|
||||
* Format: <dyad-write path="file.ext">content</dyad-write>
|
||||
*/
|
||||
function getDyadWriteTags(response) {
|
||||
const tags = [];
|
||||
const regex = /<dyad-write\s+path="([^"]+)">([\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: <dyad-rename from="old.ext" to="new.ext">
|
||||
*/
|
||||
function getDyadRenameTags(response) {
|
||||
const tags = [];
|
||||
const regex = /<dyad-rename\s+from="([^"]+)"\s+to="([^"]+)">/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: <dyad-delete path="file.ext">
|
||||
*/
|
||||
function getDyadDeleteTags(response) {
|
||||
const tags = [];
|
||||
const regex = /<dyad-delete\s+path="([^"]+)">/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: <dyad-add-dependency packages="pkg1 pkg2">
|
||||
*/
|
||||
function getDyadAddDependencyTags(response) {
|
||||
const tags = [];
|
||||
const regex = /<dyad-add-dependency\s+packages="([^"]+)">/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: <dyad-command type="rebuild|restart|refresh">
|
||||
*/
|
||||
function getDyadCommandTags(response) {
|
||||
const tags = [];
|
||||
const regex = /<dyad-command\s+type="([^"]+)">/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: <dyad-chat-summary>summary text</dyad-chat-summary>
|
||||
*/
|
||||
function getDyadChatSummary(response) {
|
||||
const match = response.match(/<dyad-chat-summary>([\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(/<dyad-write\s+path="[^"]+">[\s\S]*?<\/dyad-write>/g, '[File operation]');
|
||||
|
||||
// Remove dyad-rename tags
|
||||
stripped = stripped.replace(/<dyad-rename\s+from="[^"]+"\s+to="[^"]+">/g, '[Rename operation]');
|
||||
|
||||
// Remove dyad-delete tags
|
||||
stripped = stripped.replace(/<dyad-delete\s+path="[^"]+">/g, '[Delete operation]');
|
||||
|
||||
// Remove dyad-add-dependency tags
|
||||
stripped = stripped.replace(/<dyad-add-dependency\s+packages="[^"]+">/g, '[Install packages]');
|
||||
|
||||
// Remove dyad-command tags
|
||||
stripped = stripped.replace(/<dyad-command\s+type="[^"]+">/g, '[Command]');
|
||||
|
||||
// Remove dyad-chat-summary tags
|
||||
stripped = stripped.replace(/<dyad-chat-summary>[\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
|
||||
};
|
||||
Reference in New Issue
Block a user