{"repo": "breck7/scrollhub", "files": {"Agents.js": {"8ede78b3c1d132cd84cf1ce106292cec48274722": {"t": 1785745993.258011, "n": 6542, "h": "b86a40fdcd60e4e3", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolders, agent, whatKind, domainSuffix) {\n this.userPrompt = userPrompt\n this.existingFolders = existingFolders\n this.agent = agent\n this.what = whatKind\n this.domainSuffix = \".\" + domainSuffix.replace(/^\\./, \"\")\n this.systemPrompt = this.makePrompt(userPrompt, domainSuffix)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n makePrompt(userPrompt, domainSuffix) {\n const domainExpression = `(domain${domainSuffix} here)`\n const domainPrompt = `First suggest a short, memorable domain name ending in ${domainSuffix} that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n${domainExpression}`\n let basePrompt = fs.readFileSync(path.join(__dirname, \"prompts\", this.what + \".scroll\"), \"utf8\")\n basePrompt = basePrompt.replaceAll(\"USER_PROMPT\", userPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_PROMPT\", domainPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_EXPRESSION\", domainExpression)\n return basePrompt\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n // sometimes LLMs are returning junk with spaces and newlines etc\n suggestedDomain = suggestedDomain.split(\" \")[0].split(\"\\n\")[0]\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n const { domainSuffix } = this\n // Ensure the suggested domain ends with domainSuffix\n if (!suggestedDomain.endsWith(domainSuffix)) suggestedDomain = suggestedDomain.replace(domainSuffix, \"\") + domainSuffix\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingFolders[finalDomain]) {\n const baseName = suggestedDomain.replace(domainSuffix, \"\")\n finalDomain = `${baseName}${counter}${domainSuffix}`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\n\nPrompt: ${this.what}\nAgent: ${this.agent.name}\nModel: ${this.agent.model}\n\n## User prompt\n${this.userPrompt}\n\n## System prompt\n${this.systemPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"claude\"\n model = \"claude-3-5-sonnet-20241022\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: this.model,\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n model = \"deepseek-chat\"\n name = \"deepseek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: this.getMessages(prompt),\n model: this.model\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n\n getMessages(prompt) {\n return [{ role: \"system\", content: prompt.systemPrompt }]\n }\n}\n\nclass DeepSeekReasoner extends DeepSeek {\n model = \"deepseek-reasoner\"\n name = \"deepseekreasoner\"\n\n getMessages(prompt) {\n return [\n { role: \"system\", content: prompt.systemPrompt },\n { role: \"user\", content: prompt.userPrompt }\n ]\n }\n}\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n const availableAgents = \"deepseek claude\".split(\" \")\n availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const AgentClasses = { claude: [Claude], deepseek: [DeepSeek, DeepSeekReasoner] }\n const agentConstructors = AgentClasses[name]\n agentConstructors.forEach(con => {\n const agent = new con(apiKey, hubFolder)\n this.agents[agent.name] = agent\n })\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingFolders, agentName, promptTemplate, domainSuffix) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingFolders, agent, promptTemplate, domainSuffix)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingFolders) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingFolders)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "30bb4725f3c678933f1ef0f1f96d55f6324ae6b1": {"t": 1785758602.7272491, "n": 6542, "h": "b86a40fdcd60e4e3", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolders, agent, whatKind, domainSuffix) {\n this.userPrompt = userPrompt\n this.existingFolders = existingFolders\n this.agent = agent\n this.what = whatKind\n this.domainSuffix = \".\" + domainSuffix.replace(/^\\./, \"\")\n this.systemPrompt = this.makePrompt(userPrompt, domainSuffix)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n makePrompt(userPrompt, domainSuffix) {\n const domainExpression = `(domain${domainSuffix} here)`\n const domainPrompt = `First suggest a short, memorable domain name ending in ${domainSuffix} that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n${domainExpression}`\n let basePrompt = fs.readFileSync(path.join(__dirname, \"prompts\", this.what + \".scroll\"), \"utf8\")\n basePrompt = basePrompt.replaceAll(\"USER_PROMPT\", userPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_PROMPT\", domainPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_EXPRESSION\", domainExpression)\n return basePrompt\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n // sometimes LLMs are returning junk with spaces and newlines etc\n suggestedDomain = suggestedDomain.split(\" \")[0].split(\"\\n\")[0]\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n const { domainSuffix } = this\n // Ensure the suggested domain ends with domainSuffix\n if (!suggestedDomain.endsWith(domainSuffix)) suggestedDomain = suggestedDomain.replace(domainSuffix, \"\") + domainSuffix\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingFolders[finalDomain]) {\n const baseName = suggestedDomain.replace(domainSuffix, \"\")\n finalDomain = `${baseName}${counter}${domainSuffix}`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\n\nPrompt: ${this.what}\nAgent: ${this.agent.name}\nModel: ${this.agent.model}\n\n## User prompt\n${this.userPrompt}\n\n## System prompt\n${this.systemPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"claude\"\n model = \"claude-3-5-sonnet-20241022\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: this.model,\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n model = \"deepseek-chat\"\n name = \"deepseek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: this.getMessages(prompt),\n model: this.model\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n\n getMessages(prompt) {\n return [{ role: \"system\", content: prompt.systemPrompt }]\n }\n}\n\nclass DeepSeekReasoner extends DeepSeek {\n model = \"deepseek-reasoner\"\n name = \"deepseekreasoner\"\n\n getMessages(prompt) {\n return [\n { role: \"system\", content: prompt.systemPrompt },\n { role: \"user\", content: prompt.userPrompt }\n ]\n }\n}\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n const availableAgents = \"deepseek claude\".split(\" \")\n availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const AgentClasses = { claude: [Claude], deepseek: [DeepSeek, DeepSeekReasoner] }\n const agentConstructors = AgentClasses[name]\n agentConstructors.forEach(con => {\n const agent = new con(apiKey, hubFolder)\n this.agents[agent.name] = agent\n })\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingFolders, agentName, promptTemplate, domainSuffix) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingFolders, agent, promptTemplate, domainSuffix)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingFolders) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingFolders)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "ee5f2fa74a22202baf595b69a20c5f6271bf8d72": {"t": 1785758603.8621836, "n": 6404, "h": "6b2f2844a146fa1c", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolders, agent, whatKind, domainSuffix) {\n this.userPrompt = userPrompt\n this.existingFolders = existingFolders\n this.agent = agent\n this.what = whatKind\n this.domainSuffix = \".\" + domainSuffix.replace(/^\\./, \"\")\n this.systemPrompt = this.makePrompt(userPrompt, domainSuffix)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n makePrompt(userPrompt, domainSuffix) {\n const domainExpression = `(domain${domainSuffix} here)`\n const domainPrompt = `First suggest a short, memorable domain name ending in ${domainSuffix} that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n${domainExpression}`\n let basePrompt = fs.readFileSync(path.join(__dirname, \"prompts\", this.what + \".scroll\"), \"utf8\")\n basePrompt = basePrompt.replaceAll(\"USER_PROMPT\", userPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_PROMPT\", domainPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_EXPRESSION\", domainExpression)\n return basePrompt\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n const { domainSuffix } = this\n // Ensure the suggested domain ends with domainSuffix\n if (!suggestedDomain.endsWith(domainSuffix)) suggestedDomain = suggestedDomain.replace(domainSuffix, \"\") + domainSuffix\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingFolders[finalDomain]) {\n const baseName = suggestedDomain.replace(domainSuffix, \"\")\n finalDomain = `${baseName}${counter}${domainSuffix}`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\n\nPrompt: ${this.what}\nAgent: ${this.agent.name}\nModel: ${this.agent.model}\n\n## User prompt\n${this.userPrompt}\n\n## System prompt\n${this.systemPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"claude\"\n model = \"claude-3-5-sonnet-20241022\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: this.model,\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n model = \"deepseek-chat\"\n name = \"deepseek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: this.getMessages(prompt),\n model: this.model\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n\n getMessages(prompt) {\n return [{ role: \"system\", content: prompt.systemPrompt }]\n }\n}\n\nclass DeepSeekReasoner extends DeepSeek {\n model = \"deepseek-reasoner\"\n name = \"deepseekreasoner\"\n\n getMessages(prompt) {\n return [\n { role: \"system\", content: prompt.systemPrompt },\n { role: \"user\", content: prompt.userPrompt }\n ]\n }\n}\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n const availableAgents = \"deepseek claude\".split(\" \")\n availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const AgentClasses = { claude: [Claude], deepseek: [DeepSeek, DeepSeekReasoner] }\n const agentConstructors = AgentClasses[name]\n agentConstructors.forEach(con => {\n const agent = new con(apiKey, hubFolder)\n this.agents[agent.name] = agent\n })\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingFolders, agentName, promptTemplate, domainSuffix) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingFolders, agent, promptTemplate, domainSuffix)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingFolders) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingFolders)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "3ee5347bbd2c8b6db92c61e3be22cee720795c2d": {"t": 1785758605.090785, "n": 6404, "h": "f26434591fb51373", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolders, agent, whatKind, domainSuffix) {\n this.userPrompt = userPrompt\n this.existingFolders = existingFolders\n this.agent = agent\n this.what = whatKind\n this.domainSuffix = \".\" + domainSuffix.replace(/^\\./, \"\")\n this.systemPrompt = this.makePrompt(userPrompt, domainSuffix)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n makePrompt(userPrompt, domainSuffix) {\n const domainExpression = `(domain${domainSuffix} here)`\n const domainPrompt = `First suggest a short, memorable domain name ending in ${domainSuffix} that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n${domainExpression}`\n let basePrompt = fs.readFileSync(path.join(__dirname, \"prompts\", this.what + \".scroll\"), \"utf8\")\n basePrompt = basePrompt.replaceAll(\"USER_PROMPT\", userPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_PROMPT\", domainPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_EXPRESSION\", domainExpression)\n return basePrompt\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n const { domainSuffix } = this\n // Ensure the suggested domain ends with domainSuffix\n if (!suggestedDomain.endsWith(domainSuffix)) suggestedDomain = suggestedDomain.replace(domainSuffix, \"\") + domainSuffix\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingFolders[finalDomain]) {\n const baseName = suggestedDomain.replace(domainSuffix, \"\")\n finalDomain = `${baseName}${counter}${domainSuffix}`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\n\nPrompt: ${this.what}\nAgent: ${this.agent.name}\nModel: ${this.agent.model}\n\n## User prompt\n${this.userPrompt}\n\n## System prompt\n${this.systemPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"claude\"\n model = \"claude-3-5-sonnet-20241022\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: this.model,\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n model = \"deepseek-chat\"\n name = \"deepseek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: this.getMessages(prompt),\n model: this.model\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n\n getMessages(prompt) {\n return [{ role: \"system\", content: prompt.systemPrompt }]\n }\n}\n\nclass DeepSeekReasoner extends DeepSeek {\n model = \"deepseek-reasoner\"\n name = \"deepseekreasoner\"\n\n getMessages(prompt) {\n return [\n { role: \"system\", content: prompt.systemPrompt },\n { role: \"user\", content: prompt.userPrompt }\n ]\n }\n}\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n const availableAgents = \"claude deepseek\".split(\" \")\n availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const AgentClasses = { claude: [Claude], deepseek: [DeepSeek, DeepSeekReasoner] }\n const agentConstructors = AgentClasses[name]\n agentConstructors.forEach(con => {\n const agent = new con(apiKey, hubFolder)\n this.agents[agent.name] = agent\n })\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingFolders, agentName, promptTemplate, domainSuffix) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingFolders, agent, promptTemplate, domainSuffix)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingFolders) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingFolders)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "9621c3451ac3f5379f5037210347acc2732b6633": {"t": 1785758606.5246475, "n": 6101, "h": "a81f36a695d6bdef", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolders, agent, whatKind, domainSuffix) {\n this.userPrompt = userPrompt\n this.existingFolders = existingFolders\n this.agent = agent\n this.what = whatKind\n this.domainSuffix = \".\" + domainSuffix.replace(/^\\./, \"\")\n this.systemPrompt = this.makePrompt(userPrompt, domainSuffix)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n makePrompt(userPrompt, domainSuffix) {\n const domainExpression = `(domain${domainSuffix} here)`\n const domainPrompt = `First suggest a short, memorable domain name ending in ${domainSuffix} that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n${domainExpression}`\n let basePrompt = fs.readFileSync(path.join(__dirname, \"prompts\", this.what + \".scroll\"), \"utf8\")\n basePrompt = basePrompt.replaceAll(\"USER_PROMPT\", userPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_PROMPT\", domainPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_EXPRESSION\", domainExpression)\n return basePrompt\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n const { domainSuffix } = this\n // Ensure the suggested domain ends with domainSuffix\n if (!suggestedDomain.endsWith(domainSuffix)) suggestedDomain = suggestedDomain.replace(domainSuffix, \"\") + domainSuffix\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingFolders[finalDomain]) {\n const baseName = suggestedDomain.replace(domainSuffix, \"\")\n finalDomain = `${baseName}${counter}${domainSuffix}`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n model = \"deepseek-chat\"\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: this.model\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass DeepSeekReasoner extends DeepSeek {\n model = \"deepseek-reasoner\"\n name = \"DeepSeekReasoner\"\n}\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const AgentClasses = { claude: [Claude], deepseek: [DeepSeek, DeepSeekReasoner] }\n const agentConstructors = AgentClasses[name]\n agentConstructors.forEach(con => {\n const agent = new con(apiKey, hubFolder)\n return (this.agents[agent.name] = agent)\n })\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingFolders, agentName, promptTemplate, domainSuffix) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingFolders, agent, promptTemplate, domainSuffix)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingFolders) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingFolders)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "6c46c22b6fb649155aa56d8fdc5e994f58dc4c38": {"t": 1785758607.8267262, "n": 5940, "h": "2250b2075d6d0fcf", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolders, agent, whatKind, domainSuffix) {\n this.userPrompt = userPrompt\n this.existingFolders = existingFolders\n this.agent = agent\n this.what = whatKind\n this.domainSuffix = \".\" + domainSuffix.replace(/^\\./, \"\")\n this.systemPrompt = this.makePrompt(userPrompt, domainSuffix)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n makePrompt(userPrompt, domainSuffix) {\n const domainExpression = `(domain${domainSuffix} here)`\n const domainPrompt = `First suggest a short, memorable domain name ending in ${domainSuffix} that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n${domainExpression}`\n let basePrompt = fs.readFileSync(path.join(__dirname, \"prompts\", this.what + \".scroll\"), \"utf8\")\n basePrompt = basePrompt.replaceAll(\"USER_PROMPT\", userPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_PROMPT\", domainPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_EXPRESSION\", domainExpression)\n return basePrompt\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n const { domainSuffix } = this\n // Ensure the suggested domain ends with domainSuffix\n if (!suggestedDomain.endsWith(domainSuffix)) suggestedDomain = suggestedDomain.replace(domainSuffix, \"\") + domainSuffix\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingFolders[finalDomain]) {\n const baseName = suggestedDomain.replace(domainSuffix, \"\")\n finalDomain = `${baseName}${counter}${domainSuffix}`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingFolders, agentName, promptTemplate, domainSuffix) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingFolders, agent, promptTemplate, domainSuffix)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingFolders) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingFolders)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "effe2da7e3655639e7999b4f3729e6ff80a254bb": {"t": 1785758609.0260496, "n": 5941, "h": "0506fe7360afcec7", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolders, agent, whatKind, domainSuffix) {\n this.userPrompt = userPrompt\n this.existingFolders = existingFolders\n this.agent = agent\n this.what = whatKind\n this.domainSuffix = \".\" + domainSuffix.replace(/^\\./, \"\")\n this.systemPrompt = this.makePrompt(userPrompt, domainSuffix)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n makePrompt(userPrompt, domainSuffix) {\n const domainExpression = `(domain${domainSuffix} here)`\n const domainPrompt = `First suggest a short, memorable domain name ending in ${domainSuffix} that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n${domainExpression}`\n let basePrompt = fs.readFileSync(path.join(__dirname, \"prompts\", this.what + \".scroll\"), \"utf8\")\n basePrompt = basePrompt.replaceAll(\"USER_PROMPT\", userPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_PROMPT\", domainPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_EXPRESSION\", domainExpression)\n return basePrompt\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n const { domainSuffix } = this\n // Ensure the suggested domain ends with domainSuffix\n if (!suggestedDomain.endsWith(domainSuffix)) suggestedDomain = suggestedDomain.replace(domainSuffix, \"\") + domainSuffix\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingFolders.[finalDomain]) {\n const baseName = suggestedDomain.replace(domainSuffix, \"\")\n finalDomain = `${baseName}${counter}${domainSuffix}`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingFolders, agentName, promptTemplate, domainSuffix) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingFolders, agent, promptTemplate, domainSuffix)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingFolders) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingFolders)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "92287b6d965d25024a5a1d315ba9ca9d1266c5cd": {"t": 1785758610.4955676, "n": 5945, "h": "8085fc272a521c32", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolderNames, agent, whatKind, domainSuffix) {\n this.userPrompt = userPrompt\n this.existingNames = existingFolderNames\n this.agent = agent\n this.what = whatKind\n this.domainSuffix = \".\" + domainSuffix.replace(/^\\./, \"\")\n this.systemPrompt = this.makePrompt(userPrompt, domainSuffix)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n makePrompt(userPrompt, domainSuffix) {\n const domainExpression = `(domain${domainSuffix} here)`\n const domainPrompt = `First suggest a short, memorable domain name ending in ${domainSuffix} that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n${domainExpression}`\n let basePrompt = fs.readFileSync(path.join(__dirname, \"prompts\", this.what + \".scroll\"), \"utf8\")\n basePrompt = basePrompt.replaceAll(\"USER_PROMPT\", userPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_PROMPT\", domainPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_EXPRESSION\", domainExpression)\n return basePrompt\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n const { domainSuffix } = this\n // Ensure the suggested domain ends with domainSuffix\n if (!suggestedDomain.endsWith(domainSuffix)) suggestedDomain = suggestedDomain.replace(domainSuffix, \"\") + domainSuffix\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(domainSuffix, \"\")\n finalDomain = `${baseName}${counter}${domainSuffix}`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName, promptTemplate, domainSuffix) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingNames, agent, promptTemplate, domainSuffix)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "e32af53cfc3f3f3227db7c35d277e22edcf51f25": {"t": 1785758611.785209, "n": 5941, "h": "64463430376b8bc0", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolderNames, agent, whatKind, domainSuffix) {\n this.userPrompt = userPrompt\n this.existingNames = existingFolderNames\n this.agent = agent\n this.what = whatKind\n this.domainSuffix = \".\" + domainSuffix.replace(/^\\./, \"\")\n this.systemPrompt = this.makePrompt(userPrompt, domainSuffix)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n makePrompt(userPrompt, domainSuffix) {\n const domainExpression = `(domain${domainSuffix} here)`\n const domainPrompt = `First suggest a short, memorable domain name ending in ${domainSuffix} that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n${domainExpression}`\n let basePrompt = fs.readFileSync(path.join(__dirname, \"prompts\", this.what + \".scroll\"), \"utf8\")\n basePrompt = basePrompt.replaceAll(\"USER_PROMPT\", userPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_PROMPT\", domainPrompt)\n basePrompt = basePrompt.replaceAll(\"DOMAIN_EXPRESSION\", domainSuffix)\n return basePrompt\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n const { domainSuffix } = this\n // Ensure the suggested domain ends with domainSuffix\n if (!suggestedDomain.endsWith(domainSuffix)) suggestedDomain = suggestedDomain.replace(domainSuffix, \"\") + domainSuffix\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(domainSuffix, \"\")\n finalDomain = `${baseName}${counter}${domainSuffix}`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName, promptTemplate, domainSuffix) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingNames, agent, promptTemplate, domainSuffix)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "56ca6b082dbe1cb8f75aa4567383fbcf40056b08": {"t": 1785758614.010363, "n": 5932, "h": "f8842c1657e6aae5", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolderNames, agent, whatKind, domainSuffix) {\n this.userPrompt = userPrompt\n this.existingNames = existingFolderNames\n this.agent = agent\n this.what = whatKind\n this.domainSuffix = \".\" + domainSuffix.replace(/^\\./, \"\")\n this.systemPrompt = this.makePrompt(userPrompt, domainSuffix)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n makePrompt(userPrompt, domainSuffix) {\n const domainExpression = `(domain${domainSuffix} here)`\n const domainPrompt = `First suggest a short, memorable domain name ending in ${domainSuffix} that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n${domainExpression}`\n let basePrompt = fs.readFileSync(path.join(__dirname, \"prompts\", this.what + \".scroll\"), \"utf8\")\n basePrompt = basePrompt.replace(\"USER_PROMPT\", userPrompt)\n basePrompt = basePrompt.replace(\"DOMAIN_PROMPT\", domainPrompt)\n basePrompt = basePrompt.replace(\"DOMAIN_EXPRESSION\", domainSuffix)\n return basePrompt\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n const { domainSuffix } = this\n // Ensure the suggested domain ends with domainSuffix\n if (!suggestedDomain.endsWith(domainSuffix)) suggestedDomain = suggestedDomain.replace(domainSuffix, \"\") + domainSuffix\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(domainSuffix, \"\")\n finalDomain = `${baseName}${counter}${domainSuffix}`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName, promptTemplate, domainSuffix) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingNames, agent, promptTemplate, domainSuffix)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "fc173bf3909410a938249e98249fe84f42628311": {"t": 1785758616.0065377, "n": 5948, "h": "792c7ec4346115d2", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolderNames, agent, whatKind, domainSuffix) {\n this.userPrompt = userPrompt\n this.existingNames = existingFolderNames\n this.agent = agent\n this.what = whatKind\n this.domainSuffix = domainSuffix\n this.systemPrompt = this.makePrompt(userPrompt, domainSuffix)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n makePrompt(userPrompt, domainSuffix) {\n domainSuffix = \".\" + domainSuffix.replace(/^\\./, \"\")\n const domainExpression = `(domain${domainSuffix} here)`\n const domainPrompt = `First suggest a short, memorable domain name ending in ${domainSuffix} that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n${domainExpression}`\n let basePrompt = fs.readFileSync(path.join(__dirname, \"prompts\", this.what + \".scroll\"), \"utf8\")\n basePrompt = basePrompt.replace(\"USER_PROMPT\", userPrompt)\n basePrompt = basePrompt.replace(\"DOMAIN_PROMPT\", domainPrompt)\n basePrompt = basePrompt.replace(\"DOMAIN_EXPRESSION\", domainSuffix)\n return basePrompt\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName, promptTemplate, domainSuffix) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingNames, agent, promptTemplate, domainSuffix)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "5e2023c153e881344ac5cb0512a54f7034b2a848": {"t": 1785758617.3280773, "n": 5249, "h": "4c570e487687e4ca", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass FolderPrompt {\n constructor(userPrompt, existingFolderNames, agent, whatKind) {\n this.userPrompt = userPrompt\n this.existingNames = existingFolderNames\n this.agent = agent\n this.what = whatKind\n this.systemPrompt = fs.readFileSync(path.join(__dirname, \"prompts\", whatKind + \".scroll\"), \"utf8\").replace(\"USER_PROMPT\", userPrompt)\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName, promptTemplate = \"website\") {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new FolderPrompt(userPrompt, existingNames, agent, promptTemplate)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "cffd2495fb4f5c4db6ae9af514852a9a7938f0c1": {"t": 1785758619.3932886, "n": 13199, "h": "84e8ae2b41fe98ec", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames, agent, what) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n this.agent = agent\n this.what = what\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n}\n\nclass DatabasePrompt extends SimpleCreationPrompt {\n get systemPrompt() {\n return `You are an expert web, scroll, and knowledge base developer. Create a knowledge base using ScrollSets based on this request: \"${this.userPrompt}\"\n\nScrollSets use the Particles, Parsers, and Scroll stack to define measures, and then encode concepts using those measures.\n\nFor example:\n\n---measures.parsers---\nidParser\n extends abstractIdParser\norganismParser\n extends abstractStringMeasureParser\n description The organism name mainly associated with the organelle such as human, plant, whale, etc.\ndiameterParser\n extends abstractIntegerMeasureParser\n description The diameter of the organelle in nanometers\nlowParser\n extends abstractIntegerMeasureParser\n description For cells that have this kind of organelle, how many are usually found on the low end?\nmedianParser\n extends abstractIntegerMeasureParser\n description For cells that have this kind of organelle, how many are usually found in the median?\nhighParser\n extends abstractIntegerMeasureParser\n description For cells that have this kind of organelle, how many are usually found on the high end?\n\n---concepts.scroll---\nmeasures.parsers\n\nid Mitochondria\norganism human\ndiameter 1000\nlow 200\nmedian 500\nhigh 2000\n\nid Chloroplast\norganism plant\ndiameter 6000\nlow 20\nmedian 40\nhigh 100\n\nid Nucleus\norganism human\ndiameter 6000\nlow 1\nmedian 1\nhigh 2\n\n---\n\nRequirements:\n- Create 5 - 7 measures. The most important things about this topic.\n- Write 5 concepts. The most important concepts in this topic.\n- Build a nice homepage iterating over the data.\n- Use only Scroll, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---measures.parsers---\n(put measures here)\n---concepts.scroll---\n(put concepts here)\n---index.scroll---\ntitle (database title here)\nheader.scroll\n(content here)\nconcepts.scroll\nconcepts\n printTable\nfooter.scroll\n---header.scroll---\nimportOnly\nbuildHtml\nhomeButton\nmetaTags\neditButton\neditBaseUrl /edit.html?folderName=(domain.scroll.pub here)&fileName=\nmetaTags\nstyle.css\nscript.js\n---footer.scroll---\nimportOnly\ncenter\nscrollVersionLink\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n}\n\nclass BlogCreationPrompt extends SimpleCreationPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a blog based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Write 2 blog posts. Keep them short. Intelligent. Data backed. Witty.\n- Use only Scroll, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\ntitle (blog title here)\nheader.scroll\nprintSnippets All\nfooter.scroll\n---(firstPostPermalinkHere).scroll---\ntitle (first post title here)\ntags All\nheader.scroll\n(first post content here)\nfooter.scroll\n---(secondPostPermalinkHere).scroll---\ntitle (second post title here)\ntags All\nheader.scroll\n(second post content here)\nfooter.scroll\n---header.scroll---\nimportOnly\nbuildHtml\nbuildTxt\nhomeButton\nleftRightButtons\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton\neditBaseUrl /edit.html?folderName=(domain.scroll.pub here)&fileName=\nmetaTags\nstyle.css\nscript.js\ncontainer\nprintTitle\n---feed.scroll---\nbuildRss\nprintFeed All\n---footer.scroll---\nimportOnly\ncenter\nscrollVersionLink\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n}\n\nclass SlideshowCreationPrompt extends SimpleCreationPrompt {\n get systemPrompt() {\n return `You are an expert design agency tasked with helping someone create a slideshow. Create a slideshow based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Make the slideshow inspirational, concise, witty.\n- The scroll slideshow keyword automatically injects javascript to handle the showing/hiding of slide content, navigation, urls, etc. You just need to add content and style.\n- Use mostly Scroll, but you can also use CSS, HTML and JavaScript (NO frameworks, NO external dependencies)\n- Make it mobile-responsive\n- 5-10 slides\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved or confidential or any of that mumbo jumbo.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nBelow is the Scroll Parser that implements the Slideshow. This should give you the class names for the styles.\nslideshowParser\n // Left and right arrows navigate.\n description Slideshow widget. *** delimits slides.\n extends abstractScrollWithRequirementsParser\n string copyFromExternal .jquery-3.7.1.min.js .slideshow.js\n example\n slideshow\n Why did the cow cross the road?\n ***\n Because it wanted to go to the MOOOO-vies.\n ***\n THE END\n ****\n javascript\n buildHtml() {\n return \\`
\\`\n }\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this slideshow. Then provide the files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildTxt\nbuildHtml\ntitle (slideshow title for meta tags)\nmetaTags\nstyle.css\nscript.js\nslideshow\n***\n\n(first slide)\n\n***\n\n(slides here, delimited by ***)\n\n***\n\n(Final slide)\n\n****\n---style.css---\n(CSS content here)\n---script.js---\n(Any JavaScript content here, if needed)\n---end---`\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hub) {\n this.hubFolder = hub.hubFolder\n this.config = hub.config\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.config.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n availablePrompts = {\n website: SimpleCreationPrompt,\n blog: BlogCreationPrompt,\n slideshow: SlideshowCreationPrompt,\n database: DatabasePrompt\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName, promptTemplate = \"website\") {\n const agent = this.agents[agentName] || this.allAgents[0]\n const promptToUse = this.availablePrompts[promptTemplate]\n const prompt = new promptToUse(userPrompt, existingNames, agent, promptTemplate)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "41b3d9e1880410daee62f45f5f82b7fed352705d": {"t": 1785758622.7586555, "n": 13314, "h": "92092012ab00c4f6", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames, agent, what) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n this.agent = agent\n this.what = what\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n}\n\nclass DatabasePrompt extends SimpleCreationPrompt {\n get systemPrompt() {\n return `You are an expert web, scroll, and knowledge base developer. Create a knowledge base using ScrollSets based on this request: \"${this.userPrompt}\"\n\nScrollSets use the Particles, Parsers, and Scroll stack to define measures, and then encode concepts using those measures.\n\nFor example:\n\n---measures.parsers---\nidParser\n extends abstractIdParser\norganismParser\n extends abstractStringMeasureParser\n description The organism name mainly associated with the organelle such as human, plant, whale, etc.\ndiameterParser\n extends abstractIntegerMeasureParser\n description The diameter of the organelle in nanometers\nlowParser\n extends abstractIntegerMeasureParser\n description For cells that have this kind of organelle, how many are usually found on the low end?\nmedianParser\n extends abstractIntegerMeasureParser\n description For cells that have this kind of organelle, how many are usually found in the median?\nhighParser\n extends abstractIntegerMeasureParser\n description For cells that have this kind of organelle, how many are usually found on the high end?\n\n---concepts.scroll---\nmeasures.parsers\n\nid Mitochondria\norganism human\ndiameter 1000\nlow 200\nmedian 500\nhigh 2000\n\nid Chloroplast\norganism plant\ndiameter 6000\nlow 20\nmedian 40\nhigh 100\n\nid Nucleus\norganism human\ndiameter 6000\nlow 1\nmedian 1\nhigh 2\n\n---\n\nRequirements:\n- Create 5 - 7 measures. The most important things about this topic.\n- Write 5 concepts. The most important concepts in this topic.\n- Build a nice homepage iterating over the data.\n- Use only Scroll, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---measures.parsers---\n(put measures here)\n---concepts.scroll---\n(put concepts here)\n---index.scroll---\ntitle (database title here)\nheader.scroll\n(content here)\nconcepts.scroll\nconcepts\n printTable\nfooter.scroll\n---header.scroll---\nimportOnly\nbuildHtml\nhomeButton\nmetaTags\neditButton\neditBaseUrl /edit.html?folderName=(domain.scroll.pub here)&fileName=\nmetaTags\nstyle.css\nscript.js\n---footer.scroll---\nimportOnly\ncenter\nscrollVersionLink\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n}\n\nclass BlogCreationPrompt extends SimpleCreationPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a blog based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Write 2 blog posts. Keep them short. Intelligent. Data backed. Witty.\n- Use only Scroll, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\ntitle (blog title here)\nheader.scroll\nprintSnippets All\nfooter.scroll\n---(firstPostPermalinkHere).scroll---\ntitle (first post title here)\ntags All\nheader.scroll\n(first post content here)\nfooter.scroll\n---(secondPostPermalinkHere).scroll---\ntitle (second post title here)\ntags All\nheader.scroll\n(second post content here)\nfooter.scroll\n---header.scroll---\nimportOnly\nbuildHtml\nbuildTxt\nhomeButton\nleftRightButtons\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton\neditBaseUrl /edit.html?folderName=(domain.scroll.pub here)&fileName=\nmetaTags\nstyle.css\nscript.js\ncontainer\nprintTitle\n---feed.scroll---\nbuildRss\nprintFeed All\n---footer.scroll---\nimportOnly\ncenter\nscrollVersionLink\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n}\n\nclass SlideshowCreationPrompt extends SimpleCreationPrompt {\n get systemPrompt() {\n return `You are an expert design agency tasked with helping someone create a slideshow. Create a slideshow based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Make the slideshow inspirational, concise, witty.\n- The scroll slideshow keyword automatically injects javascript to handle the showing/hiding of slide content, navigation, urls, etc. You just need to add content and style.\n- Use mostly Scroll, but you can also use CSS, HTML and JavaScript (NO frameworks, NO external dependencies)\n- Make it mobile-responsive\n- 5-10 slides\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved or confidential or any of that mumbo jumbo.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nBelow is the Scroll Parser that implements the Slideshow. This should give you the class names for the styles.\nslideshowParser\n // Left and right arrows navigate.\n description Slideshow widget. *** delimits slides.\n extends abstractScrollWithRequirementsParser\n string copyFromExternal .jquery-3.7.1.min.js .slideshow.js\n example\n slideshow\n Why did the cow cross the road?\n ***\n Because it wanted to go to the MOOOO-vies.\n ***\n THE END\n ****\n javascript\n buildHtml() {\n return \\`
\\`\n }\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this slideshow. Then provide the files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildTxt\nbuildHtml\ntitle (slideshow title for meta tags)\nmetaTags\nstyle.css\nscript.js\nslideshow\n***\n\n(first slide)\n\n***\n\n(slides here, delimited by ***)\n\n***\n\n(Final slide)\n\n****\n---style.css---\n(CSS content here)\n---script.js---\n(Any JavaScript content here, if needed)\n---end---`\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hubFolder) {\n this.hubFolder = hubFolder\n this.agents = {}\n const keyPath = path.join(hubFolder, `keys.txt`)\n this.keyFile = fs.existsSync(keyPath) ? Particle.fromDisk(keyPath) : new Particle()\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.keyFile.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n availablePrompts = {\n website: SimpleCreationPrompt,\n blog: BlogCreationPrompt,\n slideshow: SlideshowCreationPrompt,\n database: DatabasePrompt\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName, promptTemplate = \"website\") {\n const agent = this.agents[agentName] || this.allAgents[0]\n const promptToUse = this.availablePrompts[promptTemplate]\n const prompt = new promptToUse(userPrompt, existingNames, agent, promptTemplate)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "bf49cffdae6c9280a08f0401c886acb738eb8f8d": {"t": 1785758623.9751096, "n": 10558, "h": "f903067748a97f03", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames, agent, what) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n this.agent = agent\n this.what = what\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n}\n\nclass BlogCreationPrompt extends SimpleCreationPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a blog based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Write 2 blog posts. Keep them short. Intelligent. Data backed. Witty.\n- Use only Scroll, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\ntitle (blog title here)\nheader.scroll\nprintSnippets All\nfooter.scroll\n---(firstPostPermalinkHere).scroll---\ntitle (first post title here)\ntags All\nheader.scroll\n(first post content here)\nfooter.scroll\n---(secondPostPermalinkHere).scroll---\ntitle (second post title here)\ntags All\nheader.scroll\n(second post content here)\nfooter.scroll\n---header.scroll---\nimportOnly\nbuildHtml\nbuildTxt\nhomeButton\nleftRightButtons\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton\neditBaseUrl /edit.html?folderName=(domain.scroll.pub here)&fileName=\nmetaTags\nstyle.css\nscript.js\ncontainer\nprintTitle\n---feed.scroll---\nbuildRss\nprintFeed All\n---footer.scroll---\nimportOnly\ncenter\nscrollVersionLink\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n}\n\nclass SlideshowCreationPrompt extends SimpleCreationPrompt {\n get systemPrompt() {\n return `You are an expert design agency tasked with helping someone create a slideshow. Create a slideshow based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Make the slideshow inspirational, concise, witty.\n- The scroll slideshow keyword automatically injects javascript to handle the showing/hiding of slide content, navigation, urls, etc. You just need to add content and style.\n- Use mostly Scroll, but you can also use CSS, HTML and JavaScript (NO frameworks, NO external dependencies)\n- Make it mobile-responsive\n- 5-10 slides\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved or confidential or any of that mumbo jumbo.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nBelow is the Scroll Parser that implements the Slideshow. This should give you the class names for the styles.\nslideshowParser\n // Left and right arrows navigate.\n description Slideshow widget. *** delimits slides.\n extends abstractScrollWithRequirementsParser\n string copyFromExternal .jquery-3.7.1.min.js .slideshow.js\n example\n slideshow\n Why did the cow cross the road?\n ***\n Because it wanted to go to the MOOOO-vies.\n ***\n THE END\n ****\n javascript\n buildHtml() {\n return \\`
\\`\n }\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this slideshow. Then provide the files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildTxt\nbuildHtml\ntitle (slideshow title for meta tags)\nmetaTags\nstyle.css\nscript.js\nslideshow\n***\n\n(first slide)\n\n***\n\n(slides here, delimited by ***)\n\n***\n\n(Final slide)\n\n****\n---style.css---\n(CSS content here)\n---script.js---\n(Any JavaScript content here, if needed)\n---end---`\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hubFolder) {\n this.hubFolder = hubFolder\n this.agents = {}\n const keyPath = path.join(hubFolder, `keys.txt`)\n this.keyFile = fs.existsSync(keyPath) ? Particle.fromDisk(keyPath) : new Particle()\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.keyFile.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n availablePrompts = {\n website: SimpleCreationPrompt,\n blog: BlogCreationPrompt,\n slideshow: SlideshowCreationPrompt\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName, promptTemplate = \"website\") {\n const agent = this.agents[agentName] || this.allAgents[0]\n const promptToUse = this.availablePrompts[promptTemplate]\n const prompt = new promptToUse(userPrompt, existingNames, agent, promptTemplate)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "34419bc38ebfd5f73bc8dc35cf6cc3303f8810e1": {"t": 1785758625.2202337, "n": 9681, "h": "838d2b4c1ef18355", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames, agent, what) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n this.agent = agent\n this.what = what\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n}\n\nclass BlogCreationPrompt extends SimpleCreationPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a blog based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Write 2 blog posts. Keep them short. Intelligent. Data backed. Witty.\n- Use only Scroll, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\ntitle (blog title here)\nheader.scroll\nprintSnippets All\nfooter.scroll\n---(firstPostPermalinkHere).scroll---\ntitle (first post title here)\ntags All\nheader.scroll\n(first post content here)\nfooter.scroll\n---(secondPostPermalinkHere).scroll---\ntitle (second post title here)\ntags All\nheader.scroll\n(second post content here)\nfooter.scroll\n---header.scroll---\nimportOnly\nbuildHtml\nbuildTxt\nhomeButton\nleftRightButtons\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton\neditBaseUrl /edit.html?folderName=(domain.scroll.pub here)&fileName=\nmetaTags\nstyle.css\nscript.js\ncontainer\nprintTitle\n---feed.scroll---\nbuildRss\nprintFeed All\n---footer.scroll---\nimportOnly\ncenter\nscrollVersionLink\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n}\n\nclass SlideshowCreationPrompt extends SimpleCreationPrompt {\n get systemPrompt() {\n return `You are an expert design agency tasked with helping someone create a slideshow. Create a slideshow based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Make the slideshow inspirational, concise, witty.\n- The scroll slideshow keyword automatically injects javascript to handle the showing/hiding of slide content, navigation, urls, etc. You just need to add content and style.\n- Use mostly Scroll, but you can also use CSS, HTML and JavaScript (NO frameworks, NO external dependencies)\n- Make it mobile-responsive\n- 5-10 slides\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved or confidential or any of that mumbo jumbo.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this slideshow. Then provide the files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildTxt\nbuildHtml\ntitle (slideshow title for meta tags)\nmetaTags\nstyle.css\nscript.js\nslideshow\n***\n\n(first slide)\n\n***\n\n(slides here, delimited by ***)\n\n***\n\n(Final slide)\n\n****\n---style.css---\n(CSS content here)\n---script.js---\n(Any JavaScript content here, if needed)\n---end---`\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hubFolder) {\n this.hubFolder = hubFolder\n this.agents = {}\n const keyPath = path.join(hubFolder, `keys.txt`)\n this.keyFile = fs.existsSync(keyPath) ? Particle.fromDisk(keyPath) : new Particle()\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.keyFile.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n availablePrompts = {\n website: SimpleCreationPrompt,\n blog: BlogCreationPrompt,\n slideshow: SlideshowCreationPrompt\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName, promptTemplate = \"website\") {\n const agent = this.agents[agentName] || this.allAgents[0]\n const promptToUse = this.availablePrompts[promptTemplate]\n const prompt = new promptToUse(userPrompt, existingNames, agent, promptTemplate)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "fc3b50038778dca66b443bc79d54a96c2d4ea1e4": {"t": 1785758626.576284, "n": 8218, "h": "bd2319fda95982cc", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames, agent, what) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n this.agent = agent\n this.what = what\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\n\\n${this.what} generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass BlogCreationPrompt extends SimpleCreationPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a blog based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Write 2 blog posts. Keep them short. Intelligent. Data backed. Witty.\n- Use only Scroll, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\ntitle (blog title here)\nheader.scroll\nprintSnippets All\nfooter.scroll\n---(firstPostPermalinkHere).scroll---\ntitle (first post title here)\ntags All\nheader.scroll\n(first post content here)\nfooter.scroll\n---(secondPostPermalinkHere).scroll---\ntitle (second post title here)\ntags All\nheader.scroll\n(second post content here)\nfooter.scroll\n---header.scroll---\nimportOnly\nbuildHtml\nbuildTxt\nhomeButton\nleftRightButtons\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton\neditBaseUrl /edit.html?folderName=(domain.scroll.pub here)&fileName=\nmetaTags\nstyle.css\nscript.js\ncontainer\nprintTitle\n---feed.scroll---\nbuildRss\nprintFeed All\n---footer.scroll---\nimportOnly\ncenter\nscrollVersionLink\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hubFolder) {\n this.hubFolder = hubFolder\n this.agents = {}\n const keyPath = path.join(hubFolder, `keys.txt`)\n this.keyFile = fs.existsSync(keyPath) ? Particle.fromDisk(keyPath) : new Particle()\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.keyFile.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n availablePrompts = {\n website: SimpleCreationPrompt,\n blog: BlogCreationPrompt\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName, promptTemplate = \"website\") {\n const agent = this.agents[agentName] || this.allAgents[0]\n const promptToUse = this.availablePrompts[promptTemplate]\n const prompt = new promptToUse(userPrompt, existingNames, agent, promptTemplate)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "3e64cd6365a243e42e1a00dfca9e924e395ea1b4": {"t": 1785758627.737772, "n": 6278, "h": "56964583401f94a8", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\nconst { Particle } = require(\"scrollsdk/products/Particle.js\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames, agent) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n this.agent = agent\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\nWebsite generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hubFolder) {\n this.hubFolder = hubFolder\n this.agents = {}\n const keyPath = path.join(hubFolder, `keys.txt`)\n this.keyFile = fs.existsSync(keyPath) ? Particle.fromDisk(keyPath) : new Particle()\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const apiKey = this.keyFile.get(name)\n if (!apiKey) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n } else {\n console.log(`${name} agent loaded.`)\n }\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames, agent)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "dce121db8d5594e88def355ee161abd8a672d131": {"t": 1785758628.9234462, "n": 6107, "h": "47ed238efca02ca6", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames, agent) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n this.agent = agent\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\nWebsite generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hubFolder) {\n this.hubFolder = hubFolder\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const keyPath = path.join(hubFolder, `${name}.txt`)\n if (!fs.existsSync(keyPath)) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n }\n const apiKey = fs.readFileSync(keyPath, \"utf8\").trim()\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames, agent)\n if (!agent) throw new Error(`Agent ${agentName} not found. Is API key set?`)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "a98727922d5b652096021cae62fd999abb14c932": {"t": 1785758630.332391, "n": 6026, "h": "14fc5993aef641bf", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames, agent) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n this.agent = agent\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n if (!suggestedDomain) suggestedDomain = \"error\"\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\nWebsite generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hubFolder) {\n this.hubFolder = hubFolder\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const keyPath = path.join(hubFolder, `${name}.txt`)\n if (!fs.existsSync(keyPath)) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n }\n const apiKey = fs.readFileSync(keyPath, \"utf8\").trim()\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames, agent)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "9914ab14e4fbe3d67e66f121c62dba6a50d691ff": {"t": 1785758631.519054, "n": 5973, "h": "5f92b40f284e1a92", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames, agent) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n this.agent = agent\n }\n setResponse(response) {\n this.response = response\n return this\n }\n\n setDebugLog(completion) {\n this.completion = completion\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\nWebsite generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {\n constructor(apiKey, hubFolder) {\n this.apiKey = apiKey\n this.hubFolder = hubFolder\n }\n}\n\nclass Claude extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new Anthropic({\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n get client() {\n if (!this._client)\n this._client = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey: this.apiKey\n })\n return this._client\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.client.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n prompt.setDebugLog(completion)\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(hubFolder) {\n this.hubFolder = hubFolder\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { hubFolder } = this\n const keyPath = path.join(hubFolder, `${name}.txt`)\n if (!fs.existsSync(keyPath)) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n }\n const apiKey = fs.readFileSync(keyPath, \"utf8\").trim()\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey, hubFolder)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames, agent)\n await agent.do(prompt)\n return prompt\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "1e1fc42a4c946ffbd6b1467e02b2097d7841405b": {"t": 1785758632.871865, "n": 5660, "h": "0479c7f06b0d6014", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames, agent) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n this.agent = agent\n }\n setResponse(response) {\n this.response = response\n return this\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\nWebsite generated by ${this.agent.name} from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {}\n\nclass Claude extends AbstractAgent {\n constructor(apiKey) {\n super(apiKey)\n this.client = new Anthropic({\n apiKey\n })\n }\n name = \"Claude\"\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n constructor(apiKey) {\n super(apiKey)\n this.openai = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey\n })\n }\n name = \"DeepSeek\"\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.openai.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(keyFolder) {\n this.keyFolder = keyFolder\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { keyFolder } = this\n const keyPath = path.join(keyFolder, `${name}.txt`)\n if (!fs.existsSync(keyPath)) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n }\n const apiKey = fs.readFileSync(keyPath, \"utf8\").trim()\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames, agent)\n await agent.do(prompt)\n return prompt.parsedResponse\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "bc552ef64faafc84a275befd4dabd1982081c498": {"t": 1785758634.289355, "n": 5563, "h": "af82e43d9d9de96f", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n }\n setResponse(response) {\n this.response = response\n return this\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\nWebsite generated from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {}\n\nclass Claude extends AbstractAgent {\n constructor(apiKey) {\n super(apiKey)\n this.client = new Anthropic({\n apiKey\n })\n }\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n constructor(apiKey) {\n super(apiKey)\n this.openai = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey\n })\n }\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.openai.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(keyFolder) {\n this.keyFolder = keyFolder\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { keyFolder } = this\n const keyPath = path.join(keyFolder, `${name}.txt`)\n if (!fs.existsSync(keyPath)) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n }\n const apiKey = fs.readFileSync(keyPath, \"utf8\").trim()\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames, agentName) {\n const agent = this.agents[agentName] || this.allAgents[0]\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt.parsedResponse\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}, "34551e8db16b11a528f486bc2c4b42e4ee854e83": {"t": 1785758635.5492885, "n": 5551, "h": "b0b5e26bc2a80d3b", "c": "const { Anthropic } = require(\"@anthropic-ai/sdk\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\nconst OpenAI = require(\"openai\")\n\nclass AbstractPrompt {\n constructor(userPrompt, existingNames) {\n this.userPrompt = userPrompt\n this.existingNames = existingNames\n }\n setResponse(response) {\n this.response = response\n return this\n }\n}\n\nclass SimpleCreationPrompt extends AbstractPrompt {\n get systemPrompt() {\n return `You are an expert web developer. Create a website based on this request: \"${this.userPrompt}\"\n\nRequirements:\n- Use only Scroll, vanilla HTML, CSS, and JavaScript (NO frameworks, NO external dependencies)\n- Create clean, semantic HTML5\n- Make it mobile-responsive\n- Follow modern best practices and accessibility guidelines\n- Keep it simple but professional\n- Include basic SEO meta tags using Scroll\n- Use only relative links and no external resources\n- Do not put a copyright symbol or all rights reserved in the footer.\n- Make it beautiful. Dazzling. Advanced used of CSS.\n\nFirst suggest a short, memorable domain name ending in .scroll.pub that represents this website. Then provide the website files. Use this exact format:\n\n---domain---\n(domain.scroll.pub here)\n---index.scroll---\nbuildHtml\nbaseUrl https://(domain.scroll.pub here)\nmetaTags\neditButton /edit.html\ntitle (Title here)\nstyle.css\nbody.html\nscript.js\n---body.html---\n(HTML body content here)\n---style.css---\n(CSS content here)\n---script.js---\n(JavaScript content here)\n---end---`\n }\n\n get parsedResponse() {\n const { response } = this\n const files = {}\n\n let currentFile = null\n let currentContent = []\n let suggestedDomain = \"\"\n\n for (const line of response.split(\"\\n\")) {\n if (line.startsWith(\"---\") && line.endsWith(\"---\")) {\n if (currentFile === \"domain\" && currentContent.length > 0) {\n suggestedDomain = currentContent.join(\"\").trim()\n } else if (currentFile && currentContent.length > 0) {\n files[currentFile] = currentContent.join(\"\\n\")\n }\n currentContent = []\n const fileName = line.replace(/---/g, \"\")\n if (fileName === \"end\") break\n currentFile = fileName\n } else if (currentFile) {\n currentContent.push(line)\n }\n }\n\n // Ensure the suggested domain ends with .scroll.pub\n if (!suggestedDomain.endsWith(\".scroll.pub\")) {\n suggestedDomain = suggestedDomain.replace(/\\.scroll\\.pub.*$/, \"\") + \".scroll.pub\"\n }\n\n // If domain is taken, add numbers until we find a free one\n let finalDomain = suggestedDomain\n let counter = 1\n while (this.existingNames.includes(finalDomain)) {\n const baseName = suggestedDomain.replace(\".scroll.pub\", \"\")\n finalDomain = `${baseName}${counter}.scroll.pub`\n counter++\n }\n\n // Add a default README\n files[\"readme.scroll\"] = `# ${finalDomain}\\nWebsite generated from prompt: ${this.userPrompt}`\n\n return {\n folderName: finalDomain,\n files\n }\n }\n}\n\nclass AbstractAgent {}\n\nclass Claude extends AbstractAgent {\n constructor(apiKey) {\n super(apiKey)\n this.client = new Anthropic({\n apiKey\n })\n }\n async do(prompt) {\n console.log(\"Sending prompt to claude\")\n const { client } = this\n // Call Claude API\n const completion = await client.messages.create({\n model: \"claude-3-5-sonnet-20241022\",\n max_tokens: 4000,\n temperature: 0.7,\n messages: [{ role: \"user\", content: prompt.systemPrompt }]\n })\n\n // Parse Claude's response into domain and files\n const response = completion.content[0].text\n return prompt.setResponse(response)\n }\n}\n\nclass OpenAIAgent extends AbstractAgent {}\n\nclass DeepSeek extends AbstractAgent {\n constructor(apiKey) {\n super(apiKey)\n this.openai = new OpenAI({\n baseURL: \"https://api.deepseek.com\",\n apiKey\n })\n }\n async do(prompt) {\n console.log(\"Sending prompt to deepseek\")\n const completion = await this.openai.chat.completions.create({\n messages: [{ role: \"system\", content: prompt.systemPrompt }],\n model: \"deepseek-chat\"\n })\n const response = completion.choices[0].message.content\n return prompt.setResponse(response)\n }\n}\n\nconst AgentClasses = { claude: Claude, deepseek: DeepSeek, openai: OpenAIAgent }\n\nclass Agents {\n constructor(keyFolder) {\n this.keyFolder = keyFolder\n this.agents = {}\n this.availableAgents.forEach(agent => this.loadAgent(agent))\n }\n\n availableAgents = \"claude deepseek\".split(\" \")\n\n loadAgent(name) {\n const { keyFolder } = this\n const keyPath = path.join(keyFolder, `${name}.txt`)\n if (!fs.existsSync(keyPath)) {\n console.log(`No ${name} API key found. Skipping ${name} agent`)\n return\n }\n const apiKey = fs.readFileSync(keyPath, \"utf8\").trim()\n const agentConstructor = AgentClasses[name]\n this.agents[name] = new agentConstructor(apiKey)\n }\n\n get allAgents() {\n return Object.values(this.agents)\n }\n\n async createFolderNameAndFilesFromPrompt(userPrompt, existingNames) {\n const agent = this.agents.claude || this.agents.deepseek\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt.parsedResponse\n }\n\n // todo: wire this up\n async createMultipleFoldersFromPrompt(userPrompt, existingNames) {\n return await Promise.all(\n this.allAgents.map(async agent => {\n const prompt = new SimpleCreationPrompt(userPrompt, existingNames)\n await agent.do(prompt)\n return prompt\n })\n )\n }\n}\n\nmodule.exports = { Agents }\n"}}}}