fix: use csv-parse library for proper CSV handling in manifest generation
This commit is contained in:
parent
5d470b2de3
commit
2fc1abe396
|
|
@ -2,6 +2,7 @@ const path = require('node:path');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const yaml = require('yaml');
|
const yaml = require('yaml');
|
||||||
const crypto = require('node:crypto');
|
const crypto = require('node:crypto');
|
||||||
|
const csv = require('csv-parse/sync');
|
||||||
const { getSourcePath, getModulePath } = require('../../../lib/project-root');
|
const { getSourcePath, getModulePath } = require('../../../lib/project-root');
|
||||||
|
|
||||||
// Load package.json for version info
|
// Load package.json for version info
|
||||||
|
|
@ -783,30 +784,23 @@ class ManifestGenerator {
|
||||||
*/
|
*/
|
||||||
async writeAgentManifest(cfgDir) {
|
async writeAgentManifest(cfgDir) {
|
||||||
const csvPath = path.join(cfgDir, 'agent-manifest.csv');
|
const csvPath = path.join(cfgDir, 'agent-manifest.csv');
|
||||||
|
const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
|
||||||
|
|
||||||
// Read existing manifest to preserve entries
|
// Read existing manifest to preserve entries
|
||||||
const existingEntries = new Map();
|
const existingEntries = new Map();
|
||||||
if (await fs.pathExists(csvPath)) {
|
if (await fs.pathExists(csvPath)) {
|
||||||
const content = await fs.readFile(csvPath, 'utf8');
|
const content = await fs.readFile(csvPath, 'utf8');
|
||||||
const lines = content.split('\n').filter((line) => line.trim());
|
const records = csv.parse(content, {
|
||||||
|
columns: true,
|
||||||
// Skip header
|
skip_empty_lines: true,
|
||||||
for (let i = 1; i < lines.length; i++) {
|
});
|
||||||
const line = lines[i];
|
for (const record of records) {
|
||||||
if (line) {
|
existingEntries.set(`${record.module}:${record.name}`, record);
|
||||||
// Parse CSV (simple parsing assuming no commas in quoted fields)
|
|
||||||
const parts = line.split('","');
|
|
||||||
if (parts.length >= 11) {
|
|
||||||
const name = parts[0].replace(/^"/, '');
|
|
||||||
const module = parts[8];
|
|
||||||
existingEntries.set(`${module}:${name}`, line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create CSV header with persona fields
|
// Create CSV header with persona fields
|
||||||
let csv = 'name,displayName,title,icon,role,identity,communicationStyle,principles,module,path\n';
|
let csvContent = 'name,displayName,title,icon,role,identity,communicationStyle,principles,module,path\n';
|
||||||
|
|
||||||
// Combine existing and new agents, preferring new data for duplicates
|
// Combine existing and new agents, preferring new data for duplicates
|
||||||
const allAgents = new Map();
|
const allAgents = new Map();
|
||||||
|
|
@ -819,18 +813,38 @@ class ManifestGenerator {
|
||||||
// Add/update new agents
|
// Add/update new agents
|
||||||
for (const agent of this.agents) {
|
for (const agent of this.agents) {
|
||||||
const key = `${agent.module}:${agent.name}`;
|
const key = `${agent.module}:${agent.name}`;
|
||||||
allAgents.set(
|
allAgents.set(key, {
|
||||||
key,
|
name: agent.name,
|
||||||
`"${agent.name}","${agent.displayName}","${agent.title}","${agent.icon}","${agent.role}","${agent.identity}","${agent.communicationStyle}","${agent.principles}","${agent.module}","${agent.path}"`,
|
displayName: agent.displayName,
|
||||||
);
|
title: agent.title,
|
||||||
|
icon: agent.icon,
|
||||||
|
role: agent.role,
|
||||||
|
identity: agent.identity,
|
||||||
|
communicationStyle: agent.communicationStyle,
|
||||||
|
principles: agent.principles,
|
||||||
|
module: agent.module,
|
||||||
|
path: agent.path,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write all agents
|
// Write all agents
|
||||||
for (const [, value] of allAgents) {
|
for (const [, record] of allAgents) {
|
||||||
csv += value + '\n';
|
const row = [
|
||||||
|
escapeCsv(record.name),
|
||||||
|
escapeCsv(record.displayName),
|
||||||
|
escapeCsv(record.title),
|
||||||
|
escapeCsv(record.icon),
|
||||||
|
escapeCsv(record.role),
|
||||||
|
escapeCsv(record.identity),
|
||||||
|
escapeCsv(record.communicationStyle),
|
||||||
|
escapeCsv(record.principles),
|
||||||
|
escapeCsv(record.module),
|
||||||
|
escapeCsv(record.path),
|
||||||
|
].join(',');
|
||||||
|
csvContent += row + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.writeFile(csvPath, csv);
|
await fs.writeFile(csvPath, csvContent);
|
||||||
return csvPath;
|
return csvPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -840,30 +854,23 @@ class ManifestGenerator {
|
||||||
*/
|
*/
|
||||||
async writeTaskManifest(cfgDir) {
|
async writeTaskManifest(cfgDir) {
|
||||||
const csvPath = path.join(cfgDir, 'task-manifest.csv');
|
const csvPath = path.join(cfgDir, 'task-manifest.csv');
|
||||||
|
const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
|
||||||
|
|
||||||
// Read existing manifest to preserve entries
|
// Read existing manifest to preserve entries
|
||||||
const existingEntries = new Map();
|
const existingEntries = new Map();
|
||||||
if (await fs.pathExists(csvPath)) {
|
if (await fs.pathExists(csvPath)) {
|
||||||
const content = await fs.readFile(csvPath, 'utf8');
|
const content = await fs.readFile(csvPath, 'utf8');
|
||||||
const lines = content.split('\n').filter((line) => line.trim());
|
const records = csv.parse(content, {
|
||||||
|
columns: true,
|
||||||
// Skip header
|
skip_empty_lines: true,
|
||||||
for (let i = 1; i < lines.length; i++) {
|
});
|
||||||
const line = lines[i];
|
for (const record of records) {
|
||||||
if (line) {
|
existingEntries.set(`${record.module}:${record.name}`, record);
|
||||||
// Parse CSV (simple parsing assuming no commas in quoted fields)
|
|
||||||
const parts = line.split('","');
|
|
||||||
if (parts.length >= 6) {
|
|
||||||
const name = parts[0].replace(/^"/, '');
|
|
||||||
const module = parts[3];
|
|
||||||
existingEntries.set(`${module}:${name}`, line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create CSV header with standalone column
|
// Create CSV header with standalone column
|
||||||
let csv = 'name,displayName,description,module,path,standalone\n';
|
let csvContent = 'name,displayName,description,module,path,standalone\n';
|
||||||
|
|
||||||
// Combine existing and new tasks
|
// Combine existing and new tasks
|
||||||
const allTasks = new Map();
|
const allTasks = new Map();
|
||||||
|
|
@ -876,15 +883,30 @@ class ManifestGenerator {
|
||||||
// Add/update new tasks
|
// Add/update new tasks
|
||||||
for (const task of this.tasks) {
|
for (const task of this.tasks) {
|
||||||
const key = `${task.module}:${task.name}`;
|
const key = `${task.module}:${task.name}`;
|
||||||
allTasks.set(key, `"${task.name}","${task.displayName}","${task.description}","${task.module}","${task.path}","${task.standalone}"`);
|
allTasks.set(key, {
|
||||||
|
name: task.name,
|
||||||
|
displayName: task.displayName,
|
||||||
|
description: task.description,
|
||||||
|
module: task.module,
|
||||||
|
path: task.path,
|
||||||
|
standalone: task.standalone,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write all tasks
|
// Write all tasks
|
||||||
for (const [, value] of allTasks) {
|
for (const [, record] of allTasks) {
|
||||||
csv += value + '\n';
|
const row = [
|
||||||
|
escapeCsv(record.name),
|
||||||
|
escapeCsv(record.displayName),
|
||||||
|
escapeCsv(record.description),
|
||||||
|
escapeCsv(record.module),
|
||||||
|
escapeCsv(record.path),
|
||||||
|
escapeCsv(record.standalone),
|
||||||
|
].join(',');
|
||||||
|
csvContent += row + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.writeFile(csvPath, csv);
|
await fs.writeFile(csvPath, csvContent);
|
||||||
return csvPath;
|
return csvPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -894,30 +916,23 @@ class ManifestGenerator {
|
||||||
*/
|
*/
|
||||||
async writeToolManifest(cfgDir) {
|
async writeToolManifest(cfgDir) {
|
||||||
const csvPath = path.join(cfgDir, 'tool-manifest.csv');
|
const csvPath = path.join(cfgDir, 'tool-manifest.csv');
|
||||||
|
const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
|
||||||
|
|
||||||
// Read existing manifest to preserve entries
|
// Read existing manifest to preserve entries
|
||||||
const existingEntries = new Map();
|
const existingEntries = new Map();
|
||||||
if (await fs.pathExists(csvPath)) {
|
if (await fs.pathExists(csvPath)) {
|
||||||
const content = await fs.readFile(csvPath, 'utf8');
|
const content = await fs.readFile(csvPath, 'utf8');
|
||||||
const lines = content.split('\n').filter((line) => line.trim());
|
const records = csv.parse(content, {
|
||||||
|
columns: true,
|
||||||
// Skip header
|
skip_empty_lines: true,
|
||||||
for (let i = 1; i < lines.length; i++) {
|
});
|
||||||
const line = lines[i];
|
for (const record of records) {
|
||||||
if (line) {
|
existingEntries.set(`${record.module}:${record.name}`, record);
|
||||||
// Parse CSV (simple parsing assuming no commas in quoted fields)
|
|
||||||
const parts = line.split('","');
|
|
||||||
if (parts.length >= 6) {
|
|
||||||
const name = parts[0].replace(/^"/, '');
|
|
||||||
const module = parts[3];
|
|
||||||
existingEntries.set(`${module}:${name}`, line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create CSV header with standalone column
|
// Create CSV header with standalone column
|
||||||
let csv = 'name,displayName,description,module,path,standalone\n';
|
let csvContent = 'name,displayName,description,module,path,standalone\n';
|
||||||
|
|
||||||
// Combine existing and new tools
|
// Combine existing and new tools
|
||||||
const allTools = new Map();
|
const allTools = new Map();
|
||||||
|
|
@ -930,15 +945,30 @@ class ManifestGenerator {
|
||||||
// Add/update new tools
|
// Add/update new tools
|
||||||
for (const tool of this.tools) {
|
for (const tool of this.tools) {
|
||||||
const key = `${tool.module}:${tool.name}`;
|
const key = `${tool.module}:${tool.name}`;
|
||||||
allTools.set(key, `"${tool.name}","${tool.displayName}","${tool.description}","${tool.module}","${tool.path}","${tool.standalone}"`);
|
allTools.set(key, {
|
||||||
|
name: tool.name,
|
||||||
|
displayName: tool.displayName,
|
||||||
|
description: tool.description,
|
||||||
|
module: tool.module,
|
||||||
|
path: tool.path,
|
||||||
|
standalone: tool.standalone,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write all tools
|
// Write all tools
|
||||||
for (const [, value] of allTools) {
|
for (const [, record] of allTools) {
|
||||||
csv += value + '\n';
|
const row = [
|
||||||
|
escapeCsv(record.name),
|
||||||
|
escapeCsv(record.displayName),
|
||||||
|
escapeCsv(record.description),
|
||||||
|
escapeCsv(record.module),
|
||||||
|
escapeCsv(record.path),
|
||||||
|
escapeCsv(record.standalone),
|
||||||
|
].join(',');
|
||||||
|
csvContent += row + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.writeFile(csvPath, csv);
|
await fs.writeFile(csvPath, csvContent);
|
||||||
return csvPath;
|
return csvPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue