agent-files-watcher
Version:
A comprehensive CLI tool and MCP server for synchronizing AI agent documentation using a partials-to-templates architecture
316 lines (315 loc) • 12 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.toolImplementations = exports.toolSchemas = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const yaml = __importStar(require("js-yaml"));
const child_process_1 = require("child_process");
const builder_1 = require("./builder");
const zod_1 = require("zod");
// Simplified schema for tools - we only need the directory paths
const ToolsConfigSchema = zod_1.z.object({
partialsDir: zod_1.z.string(),
templatesDir: zod_1.z.string(),
});
// Global state for watcher process
let watcherProcess = null;
// Function to get config paths
function getConfigPaths() {
const rootDir = process.cwd();
const configPath = path.join(rootDir, ".agent-instructions.yaml");
if (!fs.existsSync(configPath)) {
// Fallback to default if config doesn't exist
console.warn("Warning: .agent-instructions.yaml not found. Using default 'docs' directory.");
return {
partialsDir: path.join(rootDir, "docs/agent-partials"),
templatesDir: path.join(rootDir, "docs/agent-templates"),
};
}
try {
const config = yaml.load(fs.readFileSync(configPath, "utf8"));
const validatedConfig = ToolsConfigSchema.parse(config);
return {
partialsDir: path.join(rootDir, validatedConfig.partialsDir),
templatesDir: path.join(rootDir, validatedConfig.templatesDir),
};
}
catch (error) {
console.warn(`Warning: Error reading config file: ${error instanceof Error ? error.message : "Unknown error"}. Using default 'docs' directory.`);
return {
partialsDir: path.join(rootDir, "docs/agent-partials"),
templatesDir: path.join(rootDir, "docs/agent-templates"),
};
}
}
exports.toolSchemas = [
{
name: "list_partials",
description: "Lists all available content partials.",
inputSchema: { type: "object", properties: {} },
},
{
name: "read_partial",
description: "Reads the content of a specific partial.",
inputSchema: {
type: "object",
properties: {
partial_name: {
type: "string",
description: "The name of the partial to read.",
},
},
required: ["partial_name"],
},
},
{
name: "update_partial",
description: "Overwrites the content of a specific partial file.",
inputSchema: {
type: "object",
properties: {
partial_name: {
type: "string",
description: "The name of the partial to update.",
},
new_content: {
type: "string",
description: "The new, complete content for the partial.",
},
},
required: ["partial_name", "new_content"],
},
},
{
name: "build_context_files",
description: "Triggers the build process to regenerate all physical agent files from the partials.",
inputSchema: { type: "object", properties: {} },
},
{
name: "create_new_partial",
description: "Creates a new partial file.",
inputSchema: {
type: "object",
properties: {
partial_name: {
type: "string",
description: "The name of the partial to create.",
},
content: {
type: "string",
description: "The content of the partial to create.",
},
},
required: ["partial_name", "content"],
},
},
{
name: "list_templates",
description: "Lists all available templates.",
inputSchema: { type: "object", properties: {} },
},
{
name: "read_template",
description: "Reads the content of a specific template.",
inputSchema: {
type: "object",
properties: {
template_name: {
type: "string",
description: "The name of the template to read.",
},
},
required: ["template_name"],
},
},
{
name: "update_template",
description: "Overwrites the content of a specific template file.",
inputSchema: {
type: "object",
properties: {
template_name: {
type: "string",
description: "The name of the template to update.",
},
new_content: {
type: "string",
description: "The new, complete content for the template.",
},
},
required: ["template_name", "new_content"],
},
},
{
name: "start_watcher",
description: "Starts the agent-context watcher to monitor file changes and auto-rebuild context files.",
inputSchema: { type: "object", properties: {} },
},
{
name: "stop_watcher",
description: "Stops the currently running agent-context watcher process.",
inputSchema: { type: "object", properties: {} },
},
];
exports.toolImplementations = {
list_partials: () => {
const { partialsDir } = getConfigPaths();
return fs.readdirSync(partialsDir);
},
read_partial: ({ partial_name }) => {
const { partialsDir } = getConfigPaths();
const partialPath = path.join(partialsDir, partial_name);
return fs.readFileSync(partialPath, "utf8");
},
update_partial: ({ partial_name, new_content, }) => {
const { partialsDir } = getConfigPaths();
const partialPath = path.join(partialsDir, partial_name);
fs.writeFileSync(partialPath, new_content);
return { success: true };
},
build_context_files: async () => {
// Run build in current working directory
await (0, builder_1.runBuild)();
return { success: true };
},
create_new_partial: ({ partial_name, content, }) => {
const { partialsDir } = getConfigPaths();
const partialPath = path.join(partialsDir, partial_name);
fs.writeFileSync(partialPath, content);
return { success: true };
},
list_templates: () => {
const { templatesDir } = getConfigPaths();
return fs.readdirSync(templatesDir);
},
read_template: ({ template_name }) => {
const { templatesDir } = getConfigPaths();
const templatePath = path.join(templatesDir, template_name);
return fs.readFileSync(templatePath, "utf8");
},
update_template: ({ template_name, new_content, }) => {
const { templatesDir } = getConfigPaths();
const templatePath = path.join(templatesDir, template_name);
fs.writeFileSync(templatePath, new_content);
return { success: true };
},
start_watcher: () => {
if (watcherProcess && !watcherProcess.killed) {
return { success: false, message: "Watcher is already running" };
}
try {
// Get the path to the current binary
const executablePath = process.execPath; // Path to node
const scriptPath = path.resolve(__dirname, "../index.js");
// Check if compiled version exists, otherwise use ts-node for development
const isBuilt = fs.existsSync(scriptPath);
if (isBuilt) {
// Use the built version
watcherProcess = (0, child_process_1.spawn)(executablePath, [scriptPath, "watch"], {
stdio: "pipe",
detached: false,
});
}
else {
// Use ts-node for development
const tsNodePath = path.resolve(process.cwd(), "node_modules/.bin/ts-node");
const srcScriptPath = path.resolve(__dirname, "../index.ts");
if (fs.existsSync(tsNodePath)) {
watcherProcess = (0, child_process_1.spawn)(tsNodePath, [srcScriptPath, "watch"], {
stdio: "pipe",
detached: false,
});
}
else {
// Fallback to npx ts-node
watcherProcess = (0, child_process_1.spawn)("npx", ["ts-node", srcScriptPath, "watch"], {
stdio: "pipe",
detached: false,
});
}
}
watcherProcess.stdout?.on("data", (data) => {
console.log(`Watcher: ${data}`);
});
watcherProcess.stderr?.on("data", (data) => {
console.error(`Watcher: ${data}`);
});
watcherProcess.on("close", (code) => {
console.error(`Watcher process exited with code ${code}`);
watcherProcess = null;
});
watcherProcess.on("error", (error) => {
console.error(`Watcher process error: ${error.message}`);
watcherProcess = null;
});
return {
success: true,
message: "Watcher started successfully",
pid: watcherProcess.pid,
};
}
catch (error) {
return {
success: false,
message: `Failed to start watcher: ${error instanceof Error ? error.message : "Unknown error"}`,
};
}
},
stop_watcher: () => {
if (!watcherProcess || watcherProcess.killed) {
return {
success: false,
message: "No watcher process is currently running",
};
}
try {
watcherProcess.kill("SIGTERM");
// Give it a moment to gracefully shutdown, then force kill if needed
setTimeout(() => {
if (watcherProcess && !watcherProcess.killed) {
watcherProcess.kill("SIGKILL");
}
}, 5000);
return { success: true, message: "Watcher stop signal sent" };
}
catch (error) {
return {
success: false,
message: `Failed to stop watcher: ${error instanceof Error ? error.message : "Unknown error"}`,
};
}
}
};