pit-manager
Version:
Centralized prompt management system for Human Behavior AI agents
241 lines • 10 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.PromptTracker = void 0;
const fs = __importStar(require("fs/promises"));
const fsSync = __importStar(require("fs"));
const path = __importStar(require("path"));
const crypto = __importStar(require("crypto"));
const yaml = __importStar(require("js-yaml"));
class PromptTracker {
promptsDir;
executionsDir;
templates = new Map();
sharedPromptsDir;
constructor(repoPath = '.pit') {
// Prompts are tracked in the prompts directory created by pit init
this.promptsDir = path.join(repoPath, 'prompts');
this.executionsDir = path.join(repoPath, 'executions');
// Also check for shared prompts in parent directory
const parentPrompts = path.join(path.dirname(repoPath), 'prompts');
if (fsSync.existsSync(parentPrompts)) {
this.sharedPromptsDir = parentPrompts;
}
}
async init() {
await fs.mkdir(this.promptsDir, { recursive: true });
await fs.mkdir(this.executionsDir, { recursive: true });
await this.loadTemplates();
}
async loadTemplates() {
try {
// Load from .pit/prompts directory
const files = await fs.readdir(this.promptsDir);
for (const file of files) {
if (file.endsWith('.json')) {
const content = await fs.readFile(path.join(this.promptsDir, file), 'utf-8');
const template = JSON.parse(content);
this.templates.set(template.id, template);
}
else if (file.endsWith('.md')) {
const template = await this.loadMarkdownTemplate(path.join(this.promptsDir, file));
if (template) {
this.templates.set(template.id, template);
}
}
}
// Load from shared prompts directory if it exists
if (this.sharedPromptsDir) {
const sharedFiles = await fs.readdir(this.sharedPromptsDir);
for (const file of sharedFiles) {
if (file.endsWith('.md')) {
const template = await this.loadMarkdownTemplate(path.join(this.sharedPromptsDir, file));
if (template) {
this.templates.set(template.id, template);
}
}
}
}
}
catch (error) {
console.error('Failed to load prompt templates:', error);
}
}
async loadMarkdownTemplate(filepath) {
try {
const content = await fs.readFile(filepath, 'utf-8');
// Parse YAML frontmatter
if (content.startsWith('---\n')) {
const parts = content.split('---\n', 3);
if (parts.length >= 3) {
const metadata = yaml.load(parts[1]);
const promptContent = parts[2].trim();
return {
id: metadata.id || path.basename(filepath, '.md'),
name: metadata.name || metadata.id,
version: metadata.version || '1.0.0',
content: promptContent,
variables: metadata.variables || [],
metadata: metadata
};
}
}
}
catch (error) {
console.error(`Error loading markdown template ${filepath}:`, error);
}
return null;
}
async saveTemplate(template) {
const filename = `${template.id}.json`;
const filepath = path.join(this.promptsDir, filename);
template.metadata.modified = new Date();
template.version = this.generateVersion(template);
await fs.writeFile(filepath, JSON.stringify(template, null, 2));
this.templates.set(template.id, template);
return template.version;
}
async trackExecution(execution, responseContent, responseMetadata) {
const executionId = execution.executionId || this.generateExecutionId();
const filename = `${executionId}.json`;
const filepath = path.join(this.executionsDir, filename);
// Create cross-language compatible format
const executionData = {
version: '1.0',
prompt: {
id: executionId,
templateId: execution.templateId,
templateVersion: execution.templateVersion,
language: execution.language || 'typescript',
content: {
template: '', // Not stored to save space
variables: Object.keys(execution.variables),
expanded: execution.expandedPrompt
},
execution: {
timestamp: execution.timestamp.toISOString(),
provider: execution.provider,
model: execution.model,
responseHash: execution.responseHash,
variables: execution.variables
}
},
response: responseContent ? {
content: responseContent,
metadata: responseMetadata || {}
} : undefined
};
await fs.writeFile(filepath, JSON.stringify(executionData, null, 2));
return executionId;
}
async getTemplate(id) {
return this.templates.get(id) || null;
}
async getTemplateHistory(id) {
// In a real implementation, this would retrieve from version control
const current = this.templates.get(id);
return current ? [current] : [];
}
async expandTemplate(templateId, variables) {
const template = this.templates.get(templateId);
if (!template) {
throw new Error(`Template ${templateId} not found`);
}
let expanded = template.content;
for (const [key, value] of Object.entries(variables)) {
const placeholder = `{{${key}}}`;
expanded = expanded.replace(new RegExp(placeholder, 'g'), String(value));
}
return expanded;
}
async diffTemplates(id1, version1, id2, version2) {
// Simple diff implementation - in production, use a proper diff library
const template1 = await this.getTemplate(id1);
const template2 = await this.getTemplate(id2);
if (!template1 || !template2) {
throw new Error('One or both templates not found');
}
const lines1 = template1.content.split('\n');
const lines2 = template2.content.split('\n');
let diff = `--- ${id1} (${version1})\n+++ ${id2} (${version2})\n`;
const maxLines = Math.max(lines1.length, lines2.length);
for (let i = 0; i < maxLines; i++) {
if (lines1[i] !== lines2[i]) {
if (lines1[i] !== undefined) {
diff += `- ${lines1[i]}\n`;
}
if (lines2[i] !== undefined) {
diff += `+ ${lines2[i]}\n`;
}
}
}
return diff;
}
generateVersion(template) {
const hash = crypto.createHash('sha256');
hash.update(template.content);
hash.update(JSON.stringify(template.variables));
return hash.digest('hex').substring(0, 8);
}
generateExecutionId() {
return `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
async getExecutionsByTemplate(templateId) {
const executions = [];
try {
const files = await fs.readdir(this.executionsDir);
for (const file of files) {
if (file.endsWith('.json')) {
const content = await fs.readFile(path.join(this.executionsDir, file), 'utf-8');
const data = JSON.parse(content);
// Handle cross-language format
if (data.prompt?.templateId === templateId) {
executions.push(data);
}
}
}
}
catch (error) {
console.error('Failed to load executions:', error);
}
return executions.sort((a, b) => {
const timeA = new Date(a.prompt?.execution?.timestamp || 0).getTime();
const timeB = new Date(b.prompt?.execution?.timestamp || 0).getTime();
return timeB - timeA;
});
}
}
exports.PromptTracker = PromptTracker;
//# sourceMappingURL=prompt-tracker.js.map