UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

112 lines (108 loc) 5.21 kB
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; import React from 'react'; import { ErrorMessage, SuccessMessage } from '../../../components/message-box.js'; /** * Handles /schedule start — enters scheduler mode. * Returns true if handled. */ export async function handleScheduleStart(commandParts, options) { if (commandParts[0] !== 'schedule' || commandParts[1] !== 'start') { return false; } const { onEnterSchedulerMode, onCommandComplete } = options; if (onEnterSchedulerMode) { onEnterSchedulerMode(); onCommandComplete?.(); } else { options.onAddToChatQueue(React.createElement(ErrorMessage, { key: `schedule-error-${options.getNextComponentKey()}`, message: 'Scheduler mode is not available.', })); onCommandComplete?.(); } return true; } /** * Creates a markdown file with frontmatter template and asks the AI to help write it. * Shared logic for /schedule create and /commands create. */ async function handleFileCreate(fileName, dirName, entityName, aiPrompt, options) { const { onAddToChatQueue, onHandleChatMessage, onCommandComplete, getNextComponentKey, } = options; if (!fileName) { onAddToChatQueue(React.createElement(ErrorMessage, { key: `${entityName}-create-error-${getNextComponentKey()}`, message: `Usage: /${entityName} create <name>\nExample: /${entityName} create ${entityName === 'schedule' ? 'deps-update' : 'review-code'}`, })); onCommandComplete?.(); return true; } const safeName = fileName.endsWith('.md') ? fileName : `${fileName}.md`; const targetDir = join(process.cwd(), '.nanocoder', dirName); const filePath = join(targetDir, safeName); if (existsSync(filePath)) { onAddToChatQueue(React.createElement(ErrorMessage, { key: `${entityName}-create-exists-${getNextComponentKey()}`, message: `${entityName === 'schedule' ? 'Schedule' : 'Command'} file already exists: .nanocoder/${dirName}/${safeName}`, })); onCommandComplete?.(); return true; } mkdirSync(targetDir, { recursive: true }); const template = `--- description: ${safeName.replace(/\.md$/, '')} ${entityName === 'schedule' ? 'scheduled command' : 'custom command'} --- `; writeFileSync(filePath, template, 'utf-8'); onAddToChatQueue(React.createElement(SuccessMessage, { key: `${entityName}-created-${getNextComponentKey()}`, message: `Created ${entityName} file: .nanocoder/${dirName}/${safeName}`, hideBox: true, })); const commandBaseName = safeName.replace(/\.md$/, ''); await onHandleChatMessage(aiPrompt(safeName, commandBaseName)); return true; } /** * Handles /schedule create — creates the schedule file and prompts the AI to help write it. * Returns true if handled. */ export async function handleScheduleCreate(commandParts, options) { if (commandParts[0] !== 'schedule' || commandParts[1] !== 'create') { return false; } return handleFileCreate(commandParts[2], 'schedules', 'schedule', safeName => `I just created a new schedule command file at .nanocoder/schedules/${safeName}. Help me write the content for this scheduled task. Ask me what I want this scheduled job to do, then write the markdown prompt into the file using the write_file tool. The file should contain a clear prompt that instructs the AI agent what to do when this schedule runs. Keep the YAML frontmatter at the top with the description field.`, options); } /** * Handles /commands create — creates the command file and prompts the AI to help write it. * Returns true if handled. */ export async function handleCommandCreate(commandParts, options) { if ((commandParts[0] !== 'commands' && commandParts[0] !== 'custom-commands') || commandParts[1] !== 'create') { return false; } return handleFileCreate(commandParts[2], 'commands', 'commands', (safeName, commandBaseName) => `I just created a new custom command file at .nanocoder/commands/${safeName}. Help me write the content for this command. Ask me what I want this command to do, then write the markdown prompt into the file using the write_file tool. The file should contain a clear prompt that instructs the AI what to do when this command is invoked via /${commandBaseName}. Keep the YAML frontmatter at the top. Here is an example of the frontmatter format with all available fields: --- description: Generate unit tests for a file aliases: [test, unittest] parameters: [filename] tags: [testing, quality] triggers: [write tests, unit test] estimated-tokens: 2000 resources: true category: testing version: 1.0.0 author: user examples: - /gen-tests src/utils.ts - /gen-tests lib/parser.ts references: [docs/testing-guide.md] dependencies: [lint] --- Generate comprehensive unit tests for {{filename}}... All fields are optional except description. Use whichever fields are appropriate for the user's needs. Parameters defined here can be used as {{param}} placeholders in the prompt body.`, options); } //# sourceMappingURL=create-handler.js.map