mirror of
https://github.com/coleam00/Archon.git
synced 2026-01-04 21:58:47 -05:00
- Update the Claude and other files based on the changes to the MCP Server.
- Implement Assignment type ahead, allow freeform assignee for flexibility.
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
||||
import { User, Bot, Code, Shield, CheckCircle } from 'lucide-react';
|
||||
|
||||
interface AssigneeTypeaheadInputProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
onKeyPress?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||
autoFocus?: boolean;
|
||||
}
|
||||
|
||||
// Default assignee options with icons
|
||||
const DEFAULT_ASSIGNEES = [
|
||||
{ value: 'User', icon: User, color: 'text-blue-500' },
|
||||
{ value: 'Archon', icon: Bot, color: 'text-pink-500' },
|
||||
{ value: 'AI IDE Agent', icon: Code, color: 'text-emerald-500' },
|
||||
{ value: 'IDE Agent', icon: Code, color: 'text-emerald-500' },
|
||||
{ value: 'prp-executor', icon: Shield, color: 'text-purple-500' },
|
||||
{ value: 'prp-validator', icon: CheckCircle, color: 'text-cyan-500' }
|
||||
];
|
||||
|
||||
export const AssigneeTypeaheadInput: React.FC<AssigneeTypeaheadInputProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
placeholder = 'Type or select assignee...',
|
||||
className = '',
|
||||
onKeyPress,
|
||||
autoFocus = false
|
||||
}) => {
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [highlightedIndex, setHighlightedIndex] = useState(0);
|
||||
const [filteredOptions, setFilteredOptions] = useState(DEFAULT_ASSIGNEES);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Update input value when prop changes
|
||||
useEffect(() => {
|
||||
setInputValue(value);
|
||||
}, [value]);
|
||||
|
||||
// Filter options based on input
|
||||
useEffect(() => {
|
||||
const filtered = inputValue.trim() === ''
|
||||
? DEFAULT_ASSIGNEES
|
||||
: DEFAULT_ASSIGNEES.filter(option =>
|
||||
option.value.toLowerCase().includes(inputValue.toLowerCase())
|
||||
);
|
||||
|
||||
// Add current input as an option if it's not in the default list and not empty
|
||||
if (inputValue.trim() && !DEFAULT_ASSIGNEES.find(opt => opt.value.toLowerCase() === inputValue.toLowerCase())) {
|
||||
filtered.push({
|
||||
value: inputValue,
|
||||
icon: User,
|
||||
color: 'text-gray-500'
|
||||
});
|
||||
}
|
||||
|
||||
setFilteredOptions(filtered);
|
||||
setHighlightedIndex(0);
|
||||
}, [inputValue]);
|
||||
|
||||
// Handle clicking outside to close dropdown
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(event.target as Node) &&
|
||||
inputRef.current &&
|
||||
!inputRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = e.target.value;
|
||||
setInputValue(newValue);
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
const handleInputFocus = () => {
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
const handleInputBlur = () => {
|
||||
// Delay to allow click on dropdown item
|
||||
setTimeout(() => {
|
||||
// Only trigger onChange if the value actually changed
|
||||
if (inputValue !== value) {
|
||||
onChange(inputValue);
|
||||
}
|
||||
setIsOpen(false);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const selectOption = useCallback((optionValue: string) => {
|
||||
setInputValue(optionValue);
|
||||
onChange(optionValue);
|
||||
setIsOpen(false);
|
||||
inputRef.current?.focus();
|
||||
}, [onChange]);
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (!isOpen && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
|
||||
setIsOpen(true);
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isOpen) return;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
setHighlightedIndex(prev =>
|
||||
prev < filteredOptions.length - 1 ? prev + 1 : 0
|
||||
);
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
setHighlightedIndex(prev =>
|
||||
prev > 0 ? prev - 1 : filteredOptions.length - 1
|
||||
);
|
||||
break;
|
||||
case 'Enter':
|
||||
e.preventDefault();
|
||||
if (filteredOptions[highlightedIndex]) {
|
||||
selectOption(filteredOptions[highlightedIndex].value);
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
setIsOpen(false);
|
||||
break;
|
||||
case 'Tab':
|
||||
if (filteredOptions[highlightedIndex]) {
|
||||
selectOption(filteredOptions[highlightedIndex].value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyPressWrapper = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
// Don't trigger the parent's Enter handler if dropdown is open
|
||||
if (e.key === 'Enter' && isOpen && filteredOptions.length > 0) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
onKeyPress?.(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
onFocus={handleInputFocus}
|
||||
onBlur={handleInputBlur}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyPress={handleKeyPressWrapper}
|
||||
placeholder={placeholder}
|
||||
className={className}
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
|
||||
{isOpen && filteredOptions.length > 0 && (
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className="absolute z-50 w-full mt-1 bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg max-h-60 overflow-auto"
|
||||
>
|
||||
{filteredOptions.map((option, index) => {
|
||||
const Icon = option.icon;
|
||||
const isHighlighted = index === highlightedIndex;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={option.value}
|
||||
onClick={() => selectOption(option.value)}
|
||||
className={`
|
||||
flex items-center gap-2 px-3 py-2 cursor-pointer transition-colors
|
||||
${isHighlighted
|
||||
? 'bg-cyan-100 dark:bg-cyan-900/30'
|
||||
: 'hover:bg-gray-100 dark:hover:bg-gray-800'
|
||||
}
|
||||
`}
|
||||
onMouseEnter={() => setHighlightedIndex(index)}
|
||||
>
|
||||
<Icon className={`w-4 h-4 ${option.color}`} />
|
||||
<span className="text-sm text-gray-700 dark:text-gray-300">
|
||||
{option.value}
|
||||
</span>
|
||||
{option.value === inputValue && (
|
||||
<span className="ml-auto text-xs text-gray-500 dark:text-gray-400">
|
||||
current
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import React, { memo, useCallback, useMemo, useState, useEffect, useRef } from '
|
||||
import { X } from 'lucide-react';
|
||||
import { Button } from '../ui/Button';
|
||||
import { ArchonLoadingSpinner } from '../animations/Animations';
|
||||
import { DebouncedInput, FeatureInput } from './TaskInputComponents';
|
||||
import { DebouncedInput, FeatureInput, AssigneeTypeaheadInput } from './TaskInputComponents';
|
||||
import type { Task } from './TaskTableView';
|
||||
|
||||
interface EditTaskModalProps {
|
||||
@@ -16,7 +16,15 @@ interface EditTaskModalProps {
|
||||
getTasksForPrioritySelection: (status: Task['status']) => Array<{value: number, label: string}>;
|
||||
}
|
||||
|
||||
const ASSIGNEE_OPTIONS = ['User', 'Archon', 'AI IDE Agent'] as const;
|
||||
// Assignee options - expanded to include all agent types
|
||||
const ASSIGNEE_OPTIONS = [
|
||||
'User',
|
||||
'Archon',
|
||||
'AI IDE Agent',
|
||||
'IDE Agent',
|
||||
'prp-executor',
|
||||
'prp-validator'
|
||||
] as const;
|
||||
|
||||
// Removed debounce utility - now using DebouncedInput component
|
||||
|
||||
@@ -82,10 +90,10 @@ export const EditTaskModal = memo(({
|
||||
setLocalTask(prev => prev ? { ...prev, task_order: parseInt(e.target.value) } : null);
|
||||
}, []);
|
||||
|
||||
const handleAssigneeChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const handleAssigneeChange = useCallback((value: string) => {
|
||||
setLocalTask(prev => prev ? {
|
||||
...prev,
|
||||
assignee: { name: e.target.value as 'User' | 'Archon' | 'AI IDE Agent', avatar: '' }
|
||||
assignee: { name: value, avatar: '' }
|
||||
} : null);
|
||||
}, []);
|
||||
|
||||
@@ -167,15 +175,12 @@ export const EditTaskModal = memo(({
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-gray-700 dark:text-gray-300 mb-1">Assignee</label>
|
||||
<select
|
||||
value={localTask?.assignee?.name || 'User'}
|
||||
<AssigneeTypeaheadInput
|
||||
value={localTask?.assignee?.name || 'User'}
|
||||
onChange={handleAssigneeChange}
|
||||
placeholder="Type or select assignee..."
|
||||
className="w-full bg-white/50 dark:bg-black/70 border border-gray-300 dark:border-gray-700 text-gray-700 dark:text-white rounded-md py-2 px-3 focus:outline-none focus:border-cyan-400 focus:shadow-[0_0_10px_rgba(34,211,238,0.2)] transition-all duration-300"
|
||||
>
|
||||
{ASSIGNEE_OPTIONS.map(option => (
|
||||
<option key={option} value={option}>{option}</option>
|
||||
))}
|
||||
</select>
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -169,4 +169,7 @@ export const FeatureInput = memo(({
|
||||
prevProps.projectFeatures === nextProps.projectFeatures;
|
||||
});
|
||||
|
||||
FeatureInput.displayName = 'FeatureInput';
|
||||
FeatureInput.displayName = 'FeatureInput';
|
||||
|
||||
// Re-export AssigneeTypeaheadInput for convenience
|
||||
export { AssigneeTypeaheadInput } from './AssigneeTypeaheadInput';
|
||||
@@ -7,6 +7,7 @@ import { projectService } from '../../services/projectService';
|
||||
import { ItemTypes, getAssigneeIcon, getAssigneeGlow, getOrderColor, getOrderGlow } from '../../lib/task-utils';
|
||||
import { DraggableTaskCard } from './DraggableTaskCard';
|
||||
import { copyToClipboard } from '../../utils/clipboard';
|
||||
import { AssigneeTypeaheadInput } from './TaskInputComponents';
|
||||
|
||||
export interface Task {
|
||||
id: string;
|
||||
@@ -79,7 +80,7 @@ const reorderTasks = (tasks: Task[], fromIndex: number, toIndex: number): Task[]
|
||||
interface EditableCellProps {
|
||||
value: string;
|
||||
onSave: (value: string) => void;
|
||||
type?: 'text' | 'textarea' | 'select';
|
||||
type?: 'text' | 'textarea' | 'select' | 'typeahead';
|
||||
options?: string[];
|
||||
placeholder?: string;
|
||||
isEditing: boolean;
|
||||
@@ -140,7 +141,37 @@ const EditableCell = ({
|
||||
|
||||
return (
|
||||
<div className="flex items-center w-full">
|
||||
{type === 'select' ? (
|
||||
{type === 'typeahead' ? (
|
||||
<div className="relative">
|
||||
<AssigneeTypeaheadInput
|
||||
value={editValue}
|
||||
onChange={(value) => {
|
||||
setEditValue(value);
|
||||
// Update the value but don't auto-save yet
|
||||
}}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
handleSave();
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
handleCancel();
|
||||
}
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
className="w-full bg-white/90 dark:bg-black/90 border border-cyan-300 dark:border-cyan-600 rounded px-2 py-1 text-sm focus:outline-none focus:border-cyan-500 focus:shadow-[0_0_5px_rgba(34,211,238,0.3)]"
|
||||
autoFocus
|
||||
/>
|
||||
{/* Save button for explicit save */}
|
||||
<button
|
||||
onClick={handleSave}
|
||||
className="absolute right-0 top-0 h-full px-2 text-cyan-600 hover:text-cyan-700 dark:text-cyan-400 dark:hover:text-cyan-300"
|
||||
title="Save (Enter)"
|
||||
>
|
||||
✓
|
||||
</button>
|
||||
</div>
|
||||
) : type === 'select' ? (
|
||||
<select
|
||||
value={editValue}
|
||||
onChange={(e) => {
|
||||
@@ -341,6 +372,7 @@ const DraggableTaskRow = ({
|
||||
<EditableCell
|
||||
value={task.assignee?.name || 'AI IDE Agent'}
|
||||
onSave={(value) => handleUpdateField('assignee', value || 'AI IDE Agent')}
|
||||
type="typeahead"
|
||||
isEditing={editingField === 'assignee'}
|
||||
onEdit={() => setEditingField('assignee')}
|
||||
onCancel={() => setEditingField(null)}
|
||||
@@ -513,12 +545,11 @@ const AddTaskRow = ({ onTaskCreate, tasks, statusFilter }: AddTaskRowProps) => {
|
||||
/>
|
||||
</td>
|
||||
<td className="p-3">
|
||||
<input
|
||||
type="text"
|
||||
<AssigneeTypeaheadInput
|
||||
value={newTask.assignee.name}
|
||||
onChange={(e) => setNewTask(prev => ({
|
||||
onChange={(value) => setNewTask(prev => ({
|
||||
...prev,
|
||||
assignee: { name: e.target.value || 'AI IDE Agent', avatar: '' }
|
||||
assignee: { name: value || 'AI IDE Agent', avatar: '' }
|
||||
}))}
|
||||
onKeyPress={handleKeyPress}
|
||||
placeholder="AI IDE Agent"
|
||||
|
||||
@@ -14,8 +14,15 @@ import { TaskTableView, Task } from './TaskTableView';
|
||||
import { TaskBoardView } from './TaskBoardView';
|
||||
import { EditTaskModal } from './EditTaskModal';
|
||||
|
||||
// Assignee utilities
|
||||
const ASSIGNEE_OPTIONS = ['User', 'Archon', 'AI IDE Agent'] as const;
|
||||
// Assignee utilities - expanded to include all agent types
|
||||
const ASSIGNEE_OPTIONS = [
|
||||
'User',
|
||||
'Archon',
|
||||
'AI IDE Agent',
|
||||
'IDE Agent',
|
||||
'prp-executor',
|
||||
'prp-validator'
|
||||
] as const;
|
||||
|
||||
// Delete confirmation modal component
|
||||
interface DeleteConfirmModalProps {
|
||||
@@ -140,6 +147,9 @@ export const TasksTab = ({
|
||||
const [taskToDelete, setTaskToDelete] = useState<Task | null>(null);
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||
|
||||
// Track local updates to prevent echo from WebSocket
|
||||
const [localUpdates, setLocalUpdates] = useState<Record<string, number>>({});
|
||||
|
||||
// Track recently deleted tasks to prevent race conditions
|
||||
const [recentlyDeletedIds, setRecentlyDeletedIds] = useState<Set<string>>(new Set());
|
||||
|
||||
@@ -164,6 +174,21 @@ export const TasksTab = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is an echo of a local update
|
||||
const localUpdateTime = localUpdates[updatedTask.id];
|
||||
if (localUpdateTime && Date.now() - localUpdateTime < 2000) {
|
||||
console.log('[Socket] Skipping echo update for locally updated task:', updatedTask.id);
|
||||
// Clean up the local update marker after the echo protection window
|
||||
setTimeout(() => {
|
||||
setLocalUpdates(prev => {
|
||||
const newUpdates = { ...prev };
|
||||
delete newUpdates[updatedTask.id];
|
||||
return newUpdates;
|
||||
});
|
||||
}, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip updates while modal is open for the same task to prevent conflicts
|
||||
if (isModalOpen && editingTask?.id === updatedTask.id) {
|
||||
console.log('[Socket] Skipping update for task being edited:', updatedTask.id);
|
||||
@@ -189,7 +214,7 @@ export const TasksTab = ({
|
||||
setTimeout(() => onTasksChange(updated), 0);
|
||||
return updated;
|
||||
});
|
||||
}, [onTasksChange, isModalOpen, editingTask?.id, recentlyDeletedIds]);
|
||||
}, [onTasksChange, isModalOpen, editingTask?.id, recentlyDeletedIds, localUpdates]);
|
||||
|
||||
const handleTaskCreated = useCallback((message: any) => {
|
||||
const newTask = message.data || message;
|
||||
@@ -534,6 +559,12 @@ export const TasksTab = ({
|
||||
return updated;
|
||||
});
|
||||
console.log(`[TasksTab] Optimistically updated UI for task ${taskId}`);
|
||||
|
||||
// Mark this update as local to prevent echo when socket update arrives
|
||||
setLocalUpdates(prev => ({
|
||||
...prev,
|
||||
[taskId]: Date.now()
|
||||
}));
|
||||
|
||||
try {
|
||||
// Then update the backend
|
||||
@@ -555,6 +586,13 @@ export const TasksTab = ({
|
||||
return updated;
|
||||
});
|
||||
|
||||
// Clear the local update marker
|
||||
setLocalUpdates(prev => {
|
||||
const newUpdates = { ...prev };
|
||||
delete newUpdates[taskId];
|
||||
return newUpdates;
|
||||
});
|
||||
|
||||
alert(`Failed to move task: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
};
|
||||
@@ -655,6 +693,25 @@ export const TasksTab = ({
|
||||
// Inline task update function
|
||||
const updateTaskInline = async (taskId: string, updates: Partial<Task>) => {
|
||||
console.log(`[TasksTab] Inline update for task ${taskId} with updates:`, updates);
|
||||
|
||||
// Store the original task for potential rollback
|
||||
const originalTask = tasks.find(t => t.id === taskId);
|
||||
|
||||
// Optimistically update the UI immediately
|
||||
setTasks(prevTasks =>
|
||||
prevTasks.map(task =>
|
||||
task.id === taskId
|
||||
? { ...task, ...updates }
|
||||
: task
|
||||
)
|
||||
);
|
||||
|
||||
// Mark this update as local to prevent echo when socket update arrives
|
||||
setLocalUpdates(prev => ({
|
||||
...prev,
|
||||
[taskId]: Date.now()
|
||||
}));
|
||||
|
||||
try {
|
||||
const updateData: Partial<UpdateTaskRequest> = {};
|
||||
|
||||
@@ -674,11 +731,25 @@ export const TasksTab = ({
|
||||
await projectService.updateTask(taskId, updateData);
|
||||
console.log(`[TasksTab] projectService.updateTask successful for ${taskId}.`);
|
||||
|
||||
// Don't update local state optimistically - let socket handle it
|
||||
console.log(`[TasksTab] Waiting for socket update for task ${taskId}.`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`[TasksTab] Failed to update task ${taskId} inline:`, error);
|
||||
|
||||
// Revert the optimistic update on error
|
||||
if (originalTask) {
|
||||
setTasks(prevTasks =>
|
||||
prevTasks.map(task =>
|
||||
task.id === taskId ? originalTask : task
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Clear the local update marker
|
||||
setLocalUpdates(prev => {
|
||||
const newUpdates = { ...prev };
|
||||
delete newUpdates[taskId];
|
||||
return newUpdates;
|
||||
});
|
||||
|
||||
alert(`Failed to update task: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ export const IDEGlobalRules = () => {
|
||||
|
||||
**MANDATORY: Always complete the full Archon specific task cycle before any coding:**
|
||||
|
||||
1. **Check Current Task** → \`archon:manage_task(action="get", task_id="...")\`
|
||||
1. **Check Current Task** → \`archon:get_task(task_id="...")\`
|
||||
2. **Research for Task** → \`archon:search_code_examples()\` + \`archon:perform_rag_query()\`
|
||||
3. **Implement the Task** → Write code based on research
|
||||
4. **Update Task Status** → \`archon:manage_task(action="update", task_id="...", update_fields={"status": "review"})\`
|
||||
5. **Get Next Task** → \`archon:manage_task(action="list", filter_by="status", filter_value="todo")\`
|
||||
4. **Update Task Status** → \`archon:update_task(task_id="...", status="review")\`
|
||||
5. **Get Next Task** → \`archon:list_tasks(filter_by="status", filter_value="todo")\`
|
||||
6. **Repeat Cycle**
|
||||
|
||||
**NEVER skip task updates with the Archon MCP server. NEVER code without checking current tasks first.**
|
||||
@@ -45,8 +45,7 @@ export const IDEGlobalRules = () => {
|
||||
|
||||
\`\`\`bash
|
||||
# Create project container
|
||||
archon:manage_project(
|
||||
action="create",
|
||||
archon:create_project(
|
||||
title="Descriptive Project Name",
|
||||
github_repo="github.com/user/repo-name"
|
||||
)
|
||||
@@ -60,7 +59,7 @@ archon:manage_project(
|
||||
# First, analyze existing codebase thoroughly
|
||||
# Read all major files, understand architecture, identify current state
|
||||
# Then create project container
|
||||
archon:manage_project(action="create", title="Existing Project Name")
|
||||
archon:create_project(title="Existing Project Name")
|
||||
|
||||
# Research current tech stack and create tasks for remaining work
|
||||
# Focus on what needs to be built, not what already exists
|
||||
@@ -70,7 +69,7 @@ archon:manage_project(action="create", title="Existing Project Name")
|
||||
|
||||
\`\`\`bash
|
||||
# Check existing project status
|
||||
archon:manage_task(action="list", filter_by="project", filter_value="[project_id]")
|
||||
archon:list_tasks(filter_by="project", filter_value="[project_id]")
|
||||
|
||||
# Pick up where you left off - no new project creation needed
|
||||
# Continue with standard development iteration workflow
|
||||
@@ -101,16 +100,14 @@ archon:search_code_examples(query="[specific feature] implementation", match_cou
|
||||
|
||||
\`\`\`bash
|
||||
# Get current project status
|
||||
archon:manage_task(
|
||||
action="list",
|
||||
archon:list_tasks(
|
||||
filter_by="project",
|
||||
filter_value="[project_id]",
|
||||
include_closed=false
|
||||
)
|
||||
|
||||
# Get next priority task
|
||||
archon:manage_task(
|
||||
action="list",
|
||||
archon:list_tasks(
|
||||
filter_by="status",
|
||||
filter_value="todo",
|
||||
project_id="[project_id]"
|
||||
@@ -150,15 +147,14 @@ archon:search_code_examples(
|
||||
|
||||
**1. Get Task Details:**
|
||||
\`\`\`bash
|
||||
archon:manage_task(action="get", task_id="[current_task_id]")
|
||||
archon:get_task(task_id="[current_task_id]")
|
||||
\`\`\`
|
||||
|
||||
**2. Update to In-Progress:**
|
||||
\`\`\`bash
|
||||
archon:manage_task(
|
||||
action="update",
|
||||
archon:update_task(
|
||||
task_id="[current_task_id]",
|
||||
update_fields={"status": "doing"}
|
||||
status="doing"
|
||||
)
|
||||
\`\`\`
|
||||
|
||||
@@ -170,10 +166,9 @@ archon:manage_task(
|
||||
**4. Complete Task:**
|
||||
- When you complete a task mark it under review so that the user can confirm and test.
|
||||
\`\`\`bash
|
||||
archon:manage_task(
|
||||
action="update",
|
||||
archon:update_task(
|
||||
task_id="[current_task_id]",
|
||||
update_fields={"status": "review"}
|
||||
status="review"
|
||||
)
|
||||
\`\`\`
|
||||
|
||||
@@ -225,7 +220,7 @@ archon:search_code_examples(query="PostgreSQL connection pooling Node.js", match
|
||||
**Start of each coding session:**
|
||||
|
||||
1. Check available sources: \`archon:get_available_sources()\`
|
||||
2. Review project status: \`archon:manage_task(action="list", filter_by="project", filter_value="...")\`
|
||||
2. Review project status: \`archon:list_tasks(filter_by="project", filter_value="...")\`
|
||||
3. Identify next priority task: Find highest \`task_order\` in "todo" status
|
||||
4. Conduct task-specific research
|
||||
5. Begin implementation
|
||||
@@ -247,17 +242,15 @@ archon:search_code_examples(query="PostgreSQL connection pooling Node.js", match
|
||||
**Status Update Examples:**
|
||||
\`\`\`bash
|
||||
# Move to review when implementation complete but needs testing
|
||||
archon:manage_task(
|
||||
action="update",
|
||||
archon:update_task(
|
||||
task_id="...",
|
||||
update_fields={"status": "review"}
|
||||
status="review"
|
||||
)
|
||||
|
||||
# Complete task after review passes
|
||||
archon:manage_task(
|
||||
action="update",
|
||||
archon:update_task(
|
||||
task_id="...",
|
||||
update_fields={"status": "done"}
|
||||
status="done"
|
||||
)
|
||||
\`\`\`
|
||||
|
||||
@@ -291,8 +284,7 @@ archon:manage_task(
|
||||
archon:get_project_features(project_id="...")
|
||||
|
||||
# Create tasks aligned with features
|
||||
archon:manage_task(
|
||||
action="create",
|
||||
archon:create_task(
|
||||
project_id="...",
|
||||
title="...",
|
||||
feature="Authentication", # Align with project features
|
||||
|
||||
@@ -6,8 +6,8 @@ export const UITaskStatusSchema = z.enum(['backlog', 'in-progress', 'review', 'c
|
||||
export const TaskPrioritySchema = z.enum(['low', 'medium', 'high', 'critical']);
|
||||
export const ProjectColorSchema = z.enum(['cyan', 'purple', 'pink', 'blue', 'orange', 'green']);
|
||||
|
||||
// Assignee schema - simplified to predefined options
|
||||
export const AssigneeSchema = z.enum(['User', 'Archon', 'AI IDE Agent']);
|
||||
// Assignee schema - allow any string value (backend no longer restricts this)
|
||||
export const AssigneeSchema = z.string();
|
||||
|
||||
// Project schemas
|
||||
export const CreateProjectSchema = z.object({
|
||||
|
||||
Reference in New Issue
Block a user