diff --git a/prt/api/init_db.php b/prt/api/init_db.php
new file mode 100644
index 0000000..2c3f0e5
--- /dev/null
+++ b/prt/api/init_db.php
@@ -0,0 +1,24 @@
+exec("ALTER TABLE prompt_items ADD COLUMN IF NOT EXISTS sort_order INT DEFAULT 0 AFTER current_version_id");
+
+ $sql = file_get_contents(__DIR__ . "/../schema.sql");
+
+ // Split combined SQL into individual queries for PDO if needed,
+ // but exec() can often handle multiple statements depending on driver config.
+ // For safer execution, we'll try to run it.
+ $pdo->exec($sql);
+
+ echo json_encode(["success" => true, "message" => "Database schema initialized successfully."]);
+} catch (Exception $e) {
+ http_response_code(500);
+ echo json_encode(["success" => false, "error" => $e->getMessage()]);
+}
+?>
diff --git a/prt/api/prompts.php b/prt/api/prompts.php
new file mode 100644
index 0000000..ed652cf
--- /dev/null
+++ b/prt/api/prompts.php
@@ -0,0 +1,105 @@
+query("SELECT * FROM prompt_categories ORDER BY COALESCE(parent_id, 0) ASC, sort_order ASC, name ASC")->fetchAll(PDO::FETCH_ASSOC);
+ $prompts = $pdo->query("SELECT * FROM prompt_items ORDER BY sort_order ASC, updated_at DESC")->fetchAll(PDO::FETCH_ASSOC);
+ $versions = $pdo->query("SELECT * FROM prompt_versions ORDER BY version_number DESC")->fetchAll(PDO::FETCH_ASSOC);
+ echo json_encode(['success' => true, 'categories' => $categories, 'prompts' => $prompts, 'versions' => $versions]);
+ break;
+
+ case 'move_category':
+ $input = json_decode(file_get_contents('php://input'), true);
+ // input: { id, parent_id, sort_order }
+ $stmt = $pdo->prepare("UPDATE prompt_categories SET parent_id = ?, sort_order = ? WHERE id = ?");
+ $stmt->execute([$input['parent_id'], $input['sort_order'], $input['id']]);
+ echo json_encode(['success' => true]);
+ break;
+
+ case 'move_prompt':
+ $input = json_decode(file_get_contents('php://input'), true);
+ // input: { id, category_id, sort_order }
+ $stmt = $pdo->prepare("UPDATE prompt_items SET category_id = ?, sort_order = ? WHERE id = ?");
+ $stmt->execute([$input['category_id'], $input['sort_order'], $input['id']]);
+ echo json_encode(['success' => true]);
+ break;
+
+ case 'save_category':
+ $input = json_decode(file_get_contents('php://input'), true);
+ if (isset($input['id']) && is_numeric($input['id'])) {
+ $stmt = $pdo->prepare("UPDATE prompt_categories SET name = ?, parent_id = ? WHERE id = ?");
+ $stmt->execute([$input['name'], $input['parent_id'], $input['id']]);
+ $id = $input['id'];
+ } else {
+ $stmt = $pdo->prepare("INSERT INTO prompt_categories (name, parent_id) VALUES (?, ?)");
+ $stmt->execute([$input['name'], $input['parent_id']]);
+ $id = $pdo->lastInsertId();
+ }
+ echo json_encode(['success' => true, 'id' => $id]);
+ break;
+
+ case 'delete_category':
+ $input = json_decode(file_get_contents('php://input'), true);
+ $stmt = $pdo->prepare("DELETE FROM prompt_categories WHERE id = ?");
+ $stmt->execute([$input['id']]);
+ echo json_encode(['success' => true]);
+ break;
+
+ case 'save_prompt':
+ $input = json_decode(file_get_contents('php://input'), true);
+ if (isset($input['id']) && is_numeric($input['id'])) {
+ $stmt = $pdo->prepare("UPDATE prompt_items SET name = ?, category_id = ?, description = ? WHERE id = ?");
+ $stmt->execute([$input['name'], $input['category_id'], $input['description'] ?? '', $input['id']]);
+ $id = $input['id'];
+ } else {
+ $stmt = $pdo->prepare("INSERT INTO prompt_items (name, category_id, description) VALUES (?, ?, ?)");
+ $stmt->execute([$input['name'], $input['category_id'], $input['description'] ?? '']);
+ $id = $pdo->lastInsertId();
+ }
+ echo json_encode(['success' => true, 'id' => $id]);
+ break;
+
+ case 'delete_prompt':
+ $input = json_decode(file_get_contents('php://input'), true);
+ $stmt = $pdo->prepare("DELETE FROM prompt_items WHERE id = ?");
+ $stmt->execute([$input['id']]);
+ echo json_encode(['success' => true]);
+ break;
+
+ case 'save_version':
+ $input = json_decode(file_get_contents('php://input'), true);
+
+ // Get next version number
+ $stmt = $pdo->prepare("SELECT MAX(version_number) as max_v FROM prompt_versions WHERE prompt_id = ?");
+ $stmt->execute([$input['prompt_id']]);
+ $row = $stmt->fetch();
+ $nextV = ($row['max_v'] ?? 0) + 1;
+
+ $stmt = $pdo->prepare("INSERT INTO prompt_versions (prompt_id, content, version_number, change_summary) VALUES (?, ?, ?, ?)");
+ $stmt->execute([$input['prompt_id'], $input['content'], $nextV, $input['change_summary']]);
+ $versionId = $pdo->lastInsertId();
+
+ // Update current_version_id in prompt_items
+ $stmt = $pdo->prepare("UPDATE prompt_items SET current_version_id = ?, updated_at = NOW() WHERE id = ?");
+ $stmt->execute([$versionId, $input['prompt_id']]);
+
+ echo json_encode(['success' => true, 'id' => $versionId, 'version_number' => $nextV]);
+ break;
+
+ default:
+ echo json_encode(['success' => false, 'error' => 'Invalid action']);
+ break;
+ }
+} catch (Exception $e) {
+ echo json_encode(['success' => false, 'error' => $e->getMessage()]);
+}
+?>
diff --git a/prt/index.php b/prt/index.php
new file mode 100644
index 0000000..682b7b3
--- /dev/null
+++ b/prt/index.php
@@ -0,0 +1,867 @@
+
+
+
+
+
+ SAM PRT | 프롬프트 연구 터미널
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
프롬프트 진화 과정
+
Research History
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
수행할 연구 프로젝트를 선택하세요
+
+ 사이드바에서 기존 프롬프트 과제를 선택하거나
+ 새로운 연구 분야를 추가하여 인공지능 연구를 시작하십시오.
+
+
+
+
+
+ AI 혁신 파트너
+ Codebridge-X
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
구체적인 프롬프트 과제명을 입력하세요
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/prt/ref/.gitignore b/prt/ref/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/prt/ref/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/prt/ref/App.tsx b/prt/ref/App.tsx
new file mode 100644
index 0000000..b916797
--- /dev/null
+++ b/prt/ref/App.tsx
@@ -0,0 +1,204 @@
+
+import React, { useState, useEffect, useCallback } from 'react';
+import Sidebar from './components/Sidebar';
+import PromptEditor from './components/PromptEditor';
+import { AppState, Category, Prompt, PromptVersion } from './types';
+import { generatePromptTitle } from './services/geminiService';
+
+const INITIAL_DATA: AppState = {
+ categories: [
+ { id: 'cat-1', name: '웹 프론트엔드', parentId: null },
+ { id: 'cat-2', name: 'AI 엔지니어링', parentId: null },
+ { id: 'cat-3', name: '클라우드 아키텍처', parentId: null },
+ ],
+ prompts: [
+ {
+ id: 'p-1',
+ categoryId: 'cat-2',
+ name: 'Gemini 최적화 로직',
+ description: '프롬프트 최적화를 위한 핵심 시스템 프롬프트 연구',
+ tags: ['ai', 'optimization'],
+ currentVersionId: 'v-1',
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString()
+ }
+ ],
+ versions: [
+ {
+ id: 'v-1',
+ promptId: 'p-1',
+ content: '당신은 세계적인 프롬프트 엔지니어입니다. 입력받은 프롬프트를 분석하여 논리적 구조, 제약 조건, 그리고 창의적인 페르소나를 강화하여 재작성하세요.',
+ versionNumber: 1,
+ changeSummary: '초기 연구 가설 설정',
+ createdAt: new Date().toISOString()
+ }
+ ],
+ selectedCategoryId: null,
+ selectedPromptId: 'p-1',
+};
+
+const App: React.FC = () => {
+ const [state, setState] = useState(() => {
+ const saved = localStorage.getItem('sam_prompt_state');
+ return saved ? JSON.parse(saved) : INITIAL_DATA;
+ });
+
+ useEffect(() => {
+ localStorage.setItem('sam_prompt_state', JSON.stringify(state));
+ }, [state]);
+
+ const handleSelectPrompt = (id: string) => {
+ setState(prev => ({ ...prev, selectedPromptId: id }));
+ };
+
+ const handleAddCategory = (parentId: string | null) => {
+ const name = prompt('새로운 연구 분야의 이름을 입력하세요 (예: DevOps, Mobile, Security):');
+ if (!name) return;
+
+ const newCategory: Category = {
+ id: `cat-${Date.now()}`,
+ name,
+ parentId
+ };
+
+ setState(prev => ({
+ ...prev,
+ categories: [...prev.categories, newCategory]
+ }));
+ };
+
+ const handleDeleteCategory = (id: string) => {
+ if (!confirm('정말로 이 분야의 모든 연구 내용을 삭제하시겠습니까?')) return;
+ setState(prev => ({
+ ...prev,
+ categories: prev.categories.filter(c => c.id !== id),
+ prompts: prev.prompts.filter(p => p.categoryId !== id),
+ selectedPromptId: prev.prompts.find(p => p.categoryId === id)?.id === prev.selectedPromptId ? null : prev.selectedPromptId
+ }));
+ };
+
+ const handleAddPrompt = async (categoryId: string) => {
+ const id = `p-${Date.now()}`;
+ const versionId = `v-${Date.now()}`;
+
+ const newPrompt: Prompt = {
+ id,
+ categoryId,
+ name: '제목 없는 연구 프로젝트',
+ description: '',
+ tags: [],
+ currentVersionId: versionId,
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ };
+
+ const newVersion: PromptVersion = {
+ id: versionId,
+ promptId: id,
+ content: '',
+ versionNumber: 1,
+ changeSummary: '신규 연구 프로젝트 시작',
+ createdAt: new Date().toISOString(),
+ };
+
+ setState(prev => ({
+ ...prev,
+ prompts: [...prev.prompts, newPrompt],
+ versions: [...prev.versions, newVersion],
+ selectedPromptId: id
+ }));
+ };
+
+ const handleSaveVersion = async (content: string, summary: string) => {
+ if (!state.selectedPromptId) return;
+
+ const currentPrompt = state.prompts.find(p => p.id === state.selectedPromptId);
+ if (!currentPrompt) return;
+
+ const currentVersions = state.versions.filter(v => v.promptId === state.selectedPromptId);
+ const nextVersionNumber = Math.max(0, ...currentVersions.map(v => v.versionNumber)) + 1;
+ const newVersionId = `v-${Date.now()}`;
+
+ // Auto-update name if it's still default
+ let updatedName = currentPrompt.name;
+ if (updatedName === '제목 없는 연구 프로젝트' && content.length > 10) {
+ updatedName = await generatePromptTitle(content);
+ }
+
+ const newVersion: PromptVersion = {
+ id: newVersionId,
+ promptId: state.selectedPromptId,
+ content,
+ versionNumber: nextVersionNumber,
+ changeSummary: summary,
+ createdAt: new Date().toISOString(),
+ };
+
+ setState(prev => ({
+ ...prev,
+ prompts: prev.prompts.map(p => p.id === state.selectedPromptId ? {
+ ...p,
+ name: updatedName,
+ currentVersionId: newVersionId,
+ updatedAt: new Date().toISOString()
+ } : p),
+ versions: [...prev.versions, newVersion]
+ }));
+ };
+
+ const handleDeletePrompt = (id: string) => {
+ if (!confirm('이 프롬프트 연구 기록을 삭제하시겠습니까?')) return;
+ setState(prev => ({
+ ...prev,
+ prompts: prev.prompts.filter(p => p.id !== id),
+ versions: prev.versions.filter(v => v.promptId !== id),
+ selectedPromptId: prev.selectedPromptId === id ? null : prev.selectedPromptId
+ }));
+ };
+
+ const selectedPrompt = state.prompts.find(p => p.id === state.selectedPromptId);
+ const selectedPromptVersions = state.versions.filter(v => v.promptId === state.selectedPromptId);
+ const selectedCategory = state.categories.find(c => c.id === selectedPrompt?.categoryId);
+
+ return (
+
+
+
+ {selectedPrompt ? (
+
+ ) : (
+
+
+
연구를 시작할 프롬프트를 선택하세요
+
+ 사이드바에서 기존 연구 과제를 선택하거나 새로운 분야를 추가하여 프롬프트의 진화 과정을 기록하십시오.
+
+
+ 혁신 파트너
+ Codebridge-X
+
+
+ )}
+
+ );
+};
+
+export default App;
diff --git a/prt/ref/README.md b/prt/ref/README.md
new file mode 100644
index 0000000..983f2bd
--- /dev/null
+++ b/prt/ref/README.md
@@ -0,0 +1,20 @@
+
+

+
+
+# Run and deploy your AI Studio app
+
+This contains everything you need to run your app locally.
+
+View your app in AI Studio: https://ai.studio/apps/drive/1Z2kAlFwh0UXFVckjV9KPZLXtpLoCfhoV
+
+## Run Locally
+
+**Prerequisites:** Node.js
+
+
+1. Install dependencies:
+ `npm install`
+2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
+3. Run the app:
+ `npm run dev`
diff --git a/prt/ref/components/Icons.tsx b/prt/ref/components/Icons.tsx
new file mode 100644
index 0000000..3c20703
--- /dev/null
+++ b/prt/ref/components/Icons.tsx
@@ -0,0 +1,38 @@
+
+import React from 'react';
+
+export const FolderIcon = ({ className = "w-5 h-5" }) => (
+
+);
+
+export const FileIcon = ({ className = "w-5 h-5" }) => (
+
+);
+
+export const PlusIcon = ({ className = "w-5 h-5" }) => (
+
+);
+
+export const HistoryIcon = ({ className = "w-5 h-5" }) => (
+
+);
+
+export const SparklesIcon = ({ className = "w-5 h-5" }) => (
+
+);
+
+export const TrashIcon = ({ className = "w-5 h-5" }) => (
+
+ );
diff --git a/prt/ref/components/PromptEditor.tsx b/prt/ref/components/PromptEditor.tsx
new file mode 100644
index 0000000..e732b9c
--- /dev/null
+++ b/prt/ref/components/PromptEditor.tsx
@@ -0,0 +1,176 @@
+
+import React, { useState, useEffect } from 'react';
+import { Prompt, PromptVersion, Category } from '../types';
+import { SparklesIcon, HistoryIcon, TrashIcon } from './Icons';
+import { optimizePrompt } from '../services/geminiService';
+
+interface PromptEditorProps {
+ prompt: Prompt;
+ versions: PromptVersion[];
+ categoryName: string;
+ onSave: (content: string, summary: string) => void;
+ onDelete: (id: string) => void;
+}
+
+const PromptEditor: React.FC = ({ prompt, versions, categoryName, onSave, onDelete }) => {
+ const currentVersion = versions.find(v => v.id === prompt.currentVersionId);
+ const [content, setContent] = useState(currentVersion?.content || '');
+ const [summary, setSummary] = useState('');
+ const [isOptimizing, setIsOptimizing] = useState(false);
+ const [showHistory, setShowHistory] = useState(false);
+
+ useEffect(() => {
+ setContent(currentVersion?.content || '');
+ setSummary('');
+ }, [prompt.id, prompt.currentVersionId, currentVersion]);
+
+ const handleOptimize = async () => {
+ if (!content.trim()) return;
+ setIsOptimizing(true);
+ const optimized = await optimizePrompt(content, categoryName);
+ setContent(optimized);
+ setIsOptimizing(false);
+ };
+
+ const handleSave = () => {
+ if (!content.trim()) return;
+ onSave(content, summary || `${versions.length + 1}차 고도화 진행`);
+ };
+
+ return (
+
+ {/* Header */}
+
+
+
+
+
+ {categoryName}
+ /
+ 진화 단계 v{currentVersion?.versionNumber || 1}
+
+
{prompt.name}
+
+
+
+
+
+
+
+
+
+
+ {/* Editor Area */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setSummary(e.target.value)}
+ placeholder="이번 연구 단계의 핵심 개선 내용을 기록하세요 (변경 요약)..."
+ className="w-full pl-12 pr-4 py-3.5 bg-white border border-slate-200 rounded-2xl text-sm font-bold transition-all focus:ring-4 focus:ring-indigo-50/50 focus:border-indigo-300 outline-none"
+ />
+
+
+
+
+
+
+
+ {/* Version History Sidebar */}
+ {showHistory && (
+
+
+
프롬프트 진화 히스토리
+
+
+
+ {[...versions].sort((a, b) => b.versionNumber - a.versionNumber).map((v, idx) => (
+
+ {/* Connection line */}
+ {idx < versions.length - 1 && (
+
+ )}
+ {/* Circle */}
+
+
+
+
+ 버전 {v.versionNumber}
+ {new Date(v.createdAt).toLocaleDateString()}
+
+
"{v.changeSummary}"
+
+
+
+ ))}
+
+
+ )}
+
+
+ );
+};
+
+export default PromptEditor;
diff --git a/prt/ref/components/Sidebar.tsx b/prt/ref/components/Sidebar.tsx
new file mode 100644
index 0000000..4072a37
--- /dev/null
+++ b/prt/ref/components/Sidebar.tsx
@@ -0,0 +1,142 @@
+
+import React, { useState } from 'react';
+import { Category, Prompt } from '../types';
+import { FolderIcon, FileIcon, PlusIcon, TrashIcon } from './Icons';
+
+interface SidebarProps {
+ categories: Category[];
+ prompts: Prompt[];
+ onSelectPrompt: (id: string) => void;
+ onAddCategory: (parentId: string | null) => void;
+ onAddPrompt: (categoryId: string) => void;
+ onDeleteCategory: (id: string) => void;
+ selectedPromptId: string | null;
+}
+
+const Sidebar: React.FC = ({
+ categories,
+ prompts,
+ onSelectPrompt,
+ onAddCategory,
+ onAddPrompt,
+ onDeleteCategory,
+ selectedPromptId
+}) => {
+ const [expanded, setExpanded] = useState>({});
+
+ const toggleExpand = (id: string) => {
+ setExpanded(prev => ({ ...prev, [id]: !prev[id] }));
+ };
+
+ const renderCategory = (category: Category, depth: number = 0) => {
+ const categoryPrompts = prompts.filter(p => p.categoryId === category.id);
+ const isExpanded = expanded[category.id];
+
+ return (
+
+
toggleExpand(category.id)}
+ >
+
+
+
+
+
+
{category.name}
+
+
+
+
+
+
+
+ {isExpanded && (
+
+ {categoryPrompts.map(prompt => (
+
onSelectPrompt(prompt.id)}
+ className={`flex items-center space-x-3 px-4 py-2 my-0.5 cursor-pointer rounded-lg text-sm transition-all duration-200
+ ${selectedPromptId === prompt.id
+ ? 'bg-indigo-600 text-white shadow-md shadow-indigo-100'
+ : 'hover:bg-indigo-50 text-slate-500 hover:text-indigo-600'}
+ `}
+ style={{ marginLeft: `${depth * 4}px` }}
+ >
+
+ {prompt.name}
+
+ ))}
+
+ )}
+
+ );
+ };
+
+ return (
+
+ );
+};
+
+export default Sidebar;
diff --git a/prt/ref/index.html b/prt/ref/index.html
new file mode 100644
index 0000000..81b8dee
--- /dev/null
+++ b/prt/ref/index.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+ SAM 프롬프트 관리 | Codebridge-X
+
+
+
+
+
+
+
+
+
+
+
diff --git a/prt/ref/index.tsx b/prt/ref/index.tsx
new file mode 100644
index 0000000..aaa0c6e
--- /dev/null
+++ b/prt/ref/index.tsx
@@ -0,0 +1,16 @@
+
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import App from './App';
+
+const rootElement = document.getElementById('root');
+if (!rootElement) {
+ throw new Error("Could not find root element to mount to");
+}
+
+const root = ReactDOM.createRoot(rootElement);
+root.render(
+
+
+
+);
diff --git a/prt/ref/metadata.json b/prt/ref/metadata.json
new file mode 100644
index 0000000..2ee97f7
--- /dev/null
+++ b/prt/ref/metadata.json
@@ -0,0 +1,6 @@
+
+{
+ "name": "SAM 프롬프트 관리",
+ "description": "Codebridge-X의 전문 프롬프트 진화 및 버전 관리 시스템. 코딩 각 분야의 프롬프트를 체계적으로 연구하고 기록합니다.",
+ "requestFramePermissions": []
+}
diff --git a/prt/ref/package.json b/prt/ref/package.json
new file mode 100644
index 0000000..d2e94b9
--- /dev/null
+++ b/prt/ref/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "sam-프롬프트-관리",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^19.2.3",
+ "react-dom": "^19.2.3",
+ "@google/genai": "^1.34.0"
+ },
+ "devDependencies": {
+ "@types/node": "^22.14.0",
+ "@vitejs/plugin-react": "^5.0.0",
+ "typescript": "~5.8.2",
+ "vite": "^6.2.0"
+ }
+}
diff --git a/prt/ref/services/geminiService.ts b/prt/ref/services/geminiService.ts
new file mode 100644
index 0000000..907bac4
--- /dev/null
+++ b/prt/ref/services/geminiService.ts
@@ -0,0 +1,50 @@
+
+import { GoogleGenAI } from "@google/genai";
+
+const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
+
+export const optimizePrompt = async (originalPrompt: string, field: string): Promise => {
+ try {
+ const response = await ai.models.generateContent({
+ model: 'gemini-3-flash-preview',
+ contents: `You are an expert Prompt Engineer specializing in the ${field} field.
+ Refine the following AI prompt to be more effective, professional, and clear.
+ Maintain the original intent but improve structure, context, and constraints.
+ Please provide the optimized prompt in the same language as the original.
+
+ Original Prompt:
+ "${originalPrompt}"
+
+ Optimized Prompt (Provide only the text of the prompt):`,
+ config: {
+ temperature: 0.7,
+ topP: 0.95,
+ }
+ });
+
+ return response.text || originalPrompt;
+ } catch (error) {
+ console.error("Gemini optimization failed:", error);
+ return originalPrompt;
+ }
+};
+
+export const generatePromptTitle = async (content: string): Promise => {
+ try {
+ const response = await ai.models.generateContent({
+ model: 'gemini-3-flash-preview',
+ contents: `다음 AI 프롬프트를 요약하여 3-5단어 내외의 짧고 명확한 한국어 제목으로 만들어주세요:
+
+ "${content}"
+
+ 제목:`,
+ config: {
+ maxOutputTokens: 20
+ }
+ });
+
+ return response.text?.trim() || "새 프롬프트";
+ } catch (error) {
+ return "새 프롬프트";
+ }
+ };
diff --git a/prt/ref/tsconfig.json b/prt/ref/tsconfig.json
new file mode 100644
index 0000000..2c6eed5
--- /dev/null
+++ b/prt/ref/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "experimentalDecorators": true,
+ "useDefineForClassFields": false,
+ "module": "ESNext",
+ "lib": [
+ "ES2022",
+ "DOM",
+ "DOM.Iterable"
+ ],
+ "skipLibCheck": true,
+ "types": [
+ "node"
+ ],
+ "moduleResolution": "bundler",
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "allowJs": true,
+ "jsx": "react-jsx",
+ "paths": {
+ "@/*": [
+ "./*"
+ ]
+ },
+ "allowImportingTsExtensions": true,
+ "noEmit": true
+ }
+}
\ No newline at end of file
diff --git a/prt/ref/types.ts b/prt/ref/types.ts
new file mode 100644
index 0000000..e268f55
--- /dev/null
+++ b/prt/ref/types.ts
@@ -0,0 +1,35 @@
+
+export interface PromptVersion {
+ id: string;
+ promptId: string;
+ content: string;
+ versionNumber: number;
+ changeSummary: string;
+ createdAt: string;
+}
+
+export interface Prompt {
+ id: string;
+ categoryId: string;
+ name: string;
+ description: string;
+ tags: string[];
+ currentVersionId: string;
+ createdAt: string;
+ updatedAt: string;
+}
+
+export interface Category {
+ id: string;
+ name: string;
+ parentId: string | null;
+ children?: Category[];
+}
+
+export interface AppState {
+ categories: Category[];
+ prompts: Prompt[];
+ versions: PromptVersion[];
+ selectedCategoryId: string | null;
+ selectedPromptId: string | null;
+}