prjct-cli 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/agentic/command-executor.ts +26 -4
- package/core/agentic/index.ts +1 -0
- package/core/agentic/template-executor.ts +261 -0
- package/core/commands/workflow.ts +28 -6
- package/core/schemas/state.ts +43 -1
- package/core/services/agent-service.ts +36 -45
- package/core/storage/state-storage.ts +259 -1
- package/core/types/agentic.ts +6 -0
- package/dist/bin/prjct.mjs +14378 -0
- package/dist/core/infrastructure/command-installer.js +499 -0
- package/dist/core/infrastructure/editors-config.js +157 -0
- package/dist/core/infrastructure/setup.js +934 -0
- package/dist/core/utils/version.js +142 -0
- package/package.json +1 -1
- package/templates/agentic/orchestrator.md +144 -45
- package/templates/agentic/task-fragmentation.md +323 -0
|
@@ -18,6 +18,7 @@ import chainOfThought from './chain-of-thought'
|
|
|
18
18
|
import memorySystem from './memory-system'
|
|
19
19
|
import groundTruth from './ground-truth'
|
|
20
20
|
import planMode from './plan-mode'
|
|
21
|
+
import templateExecutor from './template-executor'
|
|
21
22
|
|
|
22
23
|
import type {
|
|
23
24
|
ExecutionResult,
|
|
@@ -160,13 +161,25 @@ export class CommandExecutor {
|
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
// 3. AGENTIC:
|
|
164
|
+
// 3. AGENTIC: Template-first execution
|
|
165
|
+
// Claude decides agent assignment via templates - no hardcoded routing
|
|
166
|
+
const taskDescription = (params.task as string) || (params.description as string) || ''
|
|
167
|
+
const agenticExecContext = await templateExecutor.buildContext(
|
|
168
|
+
commandName,
|
|
169
|
+
taskDescription,
|
|
170
|
+
projectPath
|
|
171
|
+
)
|
|
172
|
+
const agenticInfo = templateExecutor.buildAgenticPrompt(agenticExecContext)
|
|
173
|
+
|
|
164
174
|
// Build context with agent routing info for Claude delegation
|
|
165
175
|
const context: PromptContext = {
|
|
166
176
|
...metadataContext,
|
|
167
|
-
agentsPath:
|
|
168
|
-
agentRoutingPath:
|
|
177
|
+
agentsPath: agenticExecContext.paths.agentsDir,
|
|
178
|
+
agentRoutingPath: agenticExecContext.paths.agentRouting,
|
|
179
|
+
orchestratorPath: agenticExecContext.paths.orchestrator,
|
|
180
|
+
taskFragmentationPath: agenticExecContext.paths.taskFragmentation,
|
|
169
181
|
agenticDelegation: true,
|
|
182
|
+
agenticMode: true,
|
|
170
183
|
}
|
|
171
184
|
|
|
172
185
|
// 6. Load state with filtered context
|
|
@@ -214,7 +227,10 @@ export class CommandExecutor {
|
|
|
214
227
|
)
|
|
215
228
|
|
|
216
229
|
// Log agentic mode
|
|
217
|
-
console.log(`🤖
|
|
230
|
+
console.log(`🤖 Template-first execution: Claude reads templates and decides`)
|
|
231
|
+
if (agenticInfo.requiresOrchestration) {
|
|
232
|
+
console.log(` → Orchestration: ${agenticExecContext.paths.orchestrator}`)
|
|
233
|
+
}
|
|
218
234
|
|
|
219
235
|
// Record successful attempt
|
|
220
236
|
loopDetector.recordSuccess(commandName, loopContext)
|
|
@@ -229,8 +245,14 @@ export class CommandExecutor {
|
|
|
229
245
|
state,
|
|
230
246
|
prompt,
|
|
231
247
|
agenticDelegation: true,
|
|
248
|
+
agenticMode: true,
|
|
249
|
+
agenticExecContext,
|
|
250
|
+
agenticPrompt: agenticInfo.prompt,
|
|
251
|
+
requiresOrchestration: agenticInfo.requiresOrchestration,
|
|
232
252
|
agentsPath: context.agentsPath as string,
|
|
233
253
|
agentRoutingPath: context.agentRoutingPath as string,
|
|
254
|
+
orchestratorPath: agenticExecContext.paths.orchestrator,
|
|
255
|
+
taskFragmentationPath: agenticExecContext.paths.taskFragmentation,
|
|
234
256
|
reasoning,
|
|
235
257
|
groundTruth: groundTruthResult,
|
|
236
258
|
learnedPatterns,
|
package/core/agentic/index.ts
CHANGED
|
@@ -88,6 +88,7 @@ export {
|
|
|
88
88
|
// Tool and template management
|
|
89
89
|
export { default as toolRegistry } from './tool-registry'
|
|
90
90
|
export { default as templateLoader } from './template-loader'
|
|
91
|
+
export { default as templateExecutor, TemplateExecutor } from './template-executor'
|
|
91
92
|
|
|
92
93
|
// ============ Utilities ============
|
|
93
94
|
// Chain of thought, services
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Executor
|
|
3
|
+
*
|
|
4
|
+
* Executes templates as the entry point for agentic command execution.
|
|
5
|
+
* TypeScript provides infrastructure (paths, reads/writes).
|
|
6
|
+
* Claude takes all agentic decisions via templates.
|
|
7
|
+
*
|
|
8
|
+
* @module agentic/template-executor
|
|
9
|
+
* @version 1.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from 'fs/promises'
|
|
13
|
+
import path from 'path'
|
|
14
|
+
import os from 'os'
|
|
15
|
+
import configManager from '../infrastructure/config-manager'
|
|
16
|
+
import pathManager from '../infrastructure/path-manager'
|
|
17
|
+
import { isNotFoundError } from '../types/fs'
|
|
18
|
+
|
|
19
|
+
// =============================================================================
|
|
20
|
+
// Types
|
|
21
|
+
// =============================================================================
|
|
22
|
+
|
|
23
|
+
export interface TemplateExecutionContext {
|
|
24
|
+
projectPath: string
|
|
25
|
+
projectId: string
|
|
26
|
+
globalPath: string
|
|
27
|
+
command: string
|
|
28
|
+
args: string
|
|
29
|
+
|
|
30
|
+
// Paths for Claude (not content - Claude reads what it needs)
|
|
31
|
+
paths: {
|
|
32
|
+
orchestrator: string
|
|
33
|
+
agentRouting: string
|
|
34
|
+
taskFragmentation: string
|
|
35
|
+
commandTemplate: string
|
|
36
|
+
repoAnalysis: string
|
|
37
|
+
agentsDir: string
|
|
38
|
+
skillsDir: string
|
|
39
|
+
stateJson: string
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface AgenticPromptInfo {
|
|
44
|
+
prompt: string
|
|
45
|
+
context: TemplateExecutionContext
|
|
46
|
+
requiresOrchestration: boolean
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Commands that require orchestration (task routing, fragmentation)
|
|
50
|
+
const ORCHESTRATED_COMMANDS = ['task', 'done', 'ship', 'resume', 'bug', 'enrich']
|
|
51
|
+
|
|
52
|
+
// Commands that do NOT need orchestration
|
|
53
|
+
const SIMPLE_COMMANDS = ['init', 'sync', 'pause', 'next', 'dash', 'history', 'undo', 'redo']
|
|
54
|
+
|
|
55
|
+
// =============================================================================
|
|
56
|
+
// Template Executor Class
|
|
57
|
+
// =============================================================================
|
|
58
|
+
|
|
59
|
+
export class TemplateExecutor {
|
|
60
|
+
/**
|
|
61
|
+
* Get npm root for templates path
|
|
62
|
+
*/
|
|
63
|
+
private async getNpmRoot(): Promise<string> {
|
|
64
|
+
// Get the templates path relative to this module
|
|
65
|
+
// In production, templates are in the npm package
|
|
66
|
+
const moduleDir = path.dirname(require.resolve('prjct-cli/package.json'))
|
|
67
|
+
return moduleDir
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get project ID from local config
|
|
72
|
+
*/
|
|
73
|
+
private async getProjectId(projectPath: string): Promise<string> {
|
|
74
|
+
return configManager.getProjectId(projectPath)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Build execution context with paths only
|
|
79
|
+
* Claude reads files agentically - we don't inject content
|
|
80
|
+
*/
|
|
81
|
+
async buildContext(
|
|
82
|
+
command: string,
|
|
83
|
+
args: string,
|
|
84
|
+
projectPath: string
|
|
85
|
+
): Promise<TemplateExecutionContext> {
|
|
86
|
+
const projectId = await this.getProjectId(projectPath)
|
|
87
|
+
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
88
|
+
|
|
89
|
+
// Get templates directory - use local path during development
|
|
90
|
+
let templatesDir: string
|
|
91
|
+
try {
|
|
92
|
+
const npmRoot = await this.getNpmRoot()
|
|
93
|
+
templatesDir = path.join(npmRoot, 'templates')
|
|
94
|
+
} catch {
|
|
95
|
+
// Fallback to local templates during development
|
|
96
|
+
templatesDir = path.join(__dirname, '..', '..', 'templates')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
projectPath,
|
|
101
|
+
projectId,
|
|
102
|
+
globalPath,
|
|
103
|
+
command,
|
|
104
|
+
args,
|
|
105
|
+
paths: {
|
|
106
|
+
orchestrator: path.join(templatesDir, 'agentic', 'orchestrator.md'),
|
|
107
|
+
agentRouting: path.join(templatesDir, 'agentic', 'agent-routing.md'),
|
|
108
|
+
taskFragmentation: path.join(templatesDir, 'agentic', 'task-fragmentation.md'),
|
|
109
|
+
commandTemplate: path.join(templatesDir, 'commands', `${command}.md`),
|
|
110
|
+
repoAnalysis: path.join(globalPath, 'analysis', 'repo-analysis.json'),
|
|
111
|
+
agentsDir: path.join(globalPath, 'agents'),
|
|
112
|
+
skillsDir: path.join(os.homedir(), '.claude', 'skills'),
|
|
113
|
+
stateJson: path.join(globalPath, 'storage', 'state.json'),
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Check if a command requires orchestration
|
|
120
|
+
*/
|
|
121
|
+
requiresOrchestration(command: string): boolean {
|
|
122
|
+
if (ORCHESTRATED_COMMANDS.includes(command)) return true
|
|
123
|
+
if (SIMPLE_COMMANDS.includes(command)) return false
|
|
124
|
+
// Default: assume orchestration needed for unknown commands
|
|
125
|
+
return true
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Check if agents exist for the project
|
|
130
|
+
*/
|
|
131
|
+
async hasAgents(projectPath: string): Promise<boolean> {
|
|
132
|
+
try {
|
|
133
|
+
const projectId = await this.getProjectId(projectPath)
|
|
134
|
+
const agentsDir = path.join(pathManager.getGlobalProjectPath(projectId), 'agents')
|
|
135
|
+
const files = await fs.readdir(agentsDir)
|
|
136
|
+
return files.some(f => f.endsWith('.md'))
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (isNotFoundError(error)) return false
|
|
139
|
+
return false
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get list of available agent names
|
|
145
|
+
*/
|
|
146
|
+
async getAvailableAgents(projectPath: string): Promise<string[]> {
|
|
147
|
+
try {
|
|
148
|
+
const projectId = await this.getProjectId(projectPath)
|
|
149
|
+
const agentsDir = path.join(pathManager.getGlobalProjectPath(projectId), 'agents')
|
|
150
|
+
const files = await fs.readdir(agentsDir)
|
|
151
|
+
return files
|
|
152
|
+
.filter(f => f.endsWith('.md'))
|
|
153
|
+
.map(f => f.replace('.md', ''))
|
|
154
|
+
} catch {
|
|
155
|
+
return []
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Build prompt that tells Claude to execute templates agentically
|
|
161
|
+
*/
|
|
162
|
+
buildAgenticPrompt(context: TemplateExecutionContext): AgenticPromptInfo {
|
|
163
|
+
const requiresOrchestration = this.requiresOrchestration(context.command)
|
|
164
|
+
|
|
165
|
+
const prompt = `
|
|
166
|
+
## Agentic Execution Mode
|
|
167
|
+
|
|
168
|
+
You are executing a prjct command. Follow the template-first approach.
|
|
169
|
+
|
|
170
|
+
### Context
|
|
171
|
+
- Command: ${context.command}
|
|
172
|
+
- Args: ${context.args}
|
|
173
|
+
- Project: ${context.projectPath}
|
|
174
|
+
- Project ID: ${context.projectId}
|
|
175
|
+
|
|
176
|
+
### Paths (Read as needed)
|
|
177
|
+
- Orchestrator: ${context.paths.orchestrator}
|
|
178
|
+
- Agent Routing: ${context.paths.agentRouting}
|
|
179
|
+
- Task Fragmentation: ${context.paths.taskFragmentation}
|
|
180
|
+
- Command Template: ${context.paths.commandTemplate}
|
|
181
|
+
- Repo Analysis: ${context.paths.repoAnalysis}
|
|
182
|
+
- Agents Directory: ${context.paths.agentsDir}
|
|
183
|
+
- Skills Directory: ${context.paths.skillsDir}
|
|
184
|
+
- State JSON: ${context.paths.stateJson}
|
|
185
|
+
|
|
186
|
+
### Instructions
|
|
187
|
+
|
|
188
|
+
1. **Read the command template** (${context.paths.commandTemplate})
|
|
189
|
+
|
|
190
|
+
2. **Check if orchestration is needed**
|
|
191
|
+
- This command ${requiresOrchestration ? 'REQUIRES' : 'does NOT require'} orchestration
|
|
192
|
+
${requiresOrchestration ? `
|
|
193
|
+
3. **Orchestration steps:**
|
|
194
|
+
- Read: ${context.paths.orchestrator}
|
|
195
|
+
- Read: ${context.paths.repoAnalysis} to understand project technologies
|
|
196
|
+
- Analyze the task: "${context.args}"
|
|
197
|
+
- Determine which domains are ACTUALLY relevant based on:
|
|
198
|
+
a) What the task requires
|
|
199
|
+
b) What technologies exist in this project
|
|
200
|
+
c) What agents are available in ${context.paths.agentsDir}
|
|
201
|
+
|
|
202
|
+
- **IMPORTANTE**: Los agentes en ${context.paths.agentsDir} YA son específicos del proyecto
|
|
203
|
+
(fueron generados durante p. sync con las tecnologías reales)
|
|
204
|
+
|
|
205
|
+
- SIEMPRE usar el especialista si existe para el dominio
|
|
206
|
+
- Solo usar generalista si NO existe agente para ese dominio
|
|
207
|
+
|
|
208
|
+
- Check if task should be fragmented (read: ${context.paths.taskFragmentation})
|
|
209
|
+
- If agents loaded, check their skills and load from ${context.paths.skillsDir}
|
|
210
|
+
` : `
|
|
211
|
+
3. **Simple execution:**
|
|
212
|
+
- Execute the command template directly
|
|
213
|
+
- No agent routing needed
|
|
214
|
+
`}
|
|
215
|
+
|
|
216
|
+
4. **Execute the command template** with full context
|
|
217
|
+
|
|
218
|
+
5. **Return results**
|
|
219
|
+
|
|
220
|
+
### Agentic Decision Making
|
|
221
|
+
|
|
222
|
+
YOU decide:
|
|
223
|
+
- Whether to run orchestration (based on command type)
|
|
224
|
+
- Which domains the task involves (frontend, backend, database, etc.)
|
|
225
|
+
- Whether to fragment the task into subtasks
|
|
226
|
+
- Which specialist agents to delegate to
|
|
227
|
+
|
|
228
|
+
ALWAYS:
|
|
229
|
+
- Use specialist agents when they exist (they're already project-specific)
|
|
230
|
+
- Delegate subtasks to the appropriate specialist via Task tool
|
|
231
|
+
- Let specialists handle their domain (they have the project patterns)
|
|
232
|
+
- Generate and store summaries when subtasks complete
|
|
233
|
+
|
|
234
|
+
ONLY use generalist when:
|
|
235
|
+
- No specialist agent exists for that domain
|
|
236
|
+
- Task is completely outside project scope
|
|
237
|
+
- Extreme edge case
|
|
238
|
+
|
|
239
|
+
### Subtask Management
|
|
240
|
+
|
|
241
|
+
When fragmenting tasks:
|
|
242
|
+
1. Store subtasks in state.json under currentTask.subtasks
|
|
243
|
+
2. Track progress: currentSubtaskIndex, subtaskProgress
|
|
244
|
+
3. Each completed subtask generates a summary
|
|
245
|
+
4. Pass summary to next agent for context handoff
|
|
246
|
+
`
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
prompt,
|
|
250
|
+
context,
|
|
251
|
+
requiresOrchestration
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// =============================================================================
|
|
257
|
+
// Singleton Export
|
|
258
|
+
// =============================================================================
|
|
259
|
+
|
|
260
|
+
export const templateExecutor = new TemplateExecutor()
|
|
261
|
+
export default templateExecutor
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
* Core task management - Write-Through Architecture
|
|
4
4
|
*
|
|
5
5
|
* Uses storage layer: JSON (source) → MD (context) → Event (sync)
|
|
6
|
+
*
|
|
7
|
+
* AGENTIC: Uses template-executor for Claude-driven decisions.
|
|
8
|
+
* TypeScript provides infrastructure; Claude decides via templates.
|
|
6
9
|
*/
|
|
7
10
|
|
|
8
11
|
import type { CommandResult, ProjectContext } from '../types'
|
|
@@ -15,6 +18,7 @@ import {
|
|
|
15
18
|
out
|
|
16
19
|
} from './base'
|
|
17
20
|
import { stateStorage, queueStorage } from '../storage'
|
|
21
|
+
import { templateExecutor } from '../agentic/template-executor'
|
|
18
22
|
|
|
19
23
|
export class WorkflowCommands extends PrjctCommandsBase {
|
|
20
24
|
/**
|
|
@@ -32,9 +36,12 @@ export class WorkflowCommands extends PrjctCommandsBase {
|
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
if (task) {
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const
|
|
39
|
+
// AGENTIC: Build execution context for Claude to decide
|
|
40
|
+
const execContext = await templateExecutor.buildContext('task', task, projectPath)
|
|
41
|
+
const agenticInfo = templateExecutor.buildAgenticPrompt(execContext)
|
|
42
|
+
|
|
43
|
+
// Get available agents for context
|
|
44
|
+
const availableAgents = await templateExecutor.getAvailableAgents(projectPath)
|
|
38
45
|
|
|
39
46
|
// Write-through: JSON → MD → Event
|
|
40
47
|
await stateStorage.startTask(projectId, {
|
|
@@ -43,14 +50,29 @@ export class WorkflowCommands extends PrjctCommandsBase {
|
|
|
43
50
|
sessionId: generateUUID()
|
|
44
51
|
})
|
|
45
52
|
|
|
46
|
-
|
|
53
|
+
// AGENTIC: Log that Claude will decide via templates
|
|
54
|
+
const agentsList = availableAgents.length > 0
|
|
55
|
+
? availableAgents.join(', ')
|
|
56
|
+
: 'none (run p. sync)'
|
|
57
|
+
|
|
58
|
+
console.log(`🤖 Agentic mode: Claude will read templates and decide`)
|
|
59
|
+
out.done(`${task} [specialists: ${agentsList}]`)
|
|
47
60
|
|
|
48
61
|
await this.logToMemory(projectPath, 'task_started', {
|
|
49
62
|
task,
|
|
50
|
-
|
|
63
|
+
agenticMode: true,
|
|
64
|
+
availableAgents,
|
|
51
65
|
timestamp: dateHelper.getTimestamp(),
|
|
52
66
|
})
|
|
53
|
-
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
success: true,
|
|
70
|
+
task,
|
|
71
|
+
agenticMode: true,
|
|
72
|
+
availableAgents,
|
|
73
|
+
execContext,
|
|
74
|
+
agenticPrompt: agenticInfo.prompt,
|
|
75
|
+
}
|
|
54
76
|
} else {
|
|
55
77
|
// Read from storage (JSON is source of truth)
|
|
56
78
|
const currentTask = await stateStorage.getCurrentTask(projectId)
|
package/core/schemas/state.ts
CHANGED
|
@@ -17,15 +17,53 @@ import { z } from 'zod'
|
|
|
17
17
|
export const PrioritySchema = z.enum(['low', 'medium', 'high', 'critical'])
|
|
18
18
|
export const TaskTypeSchema = z.enum(['feature', 'bug', 'improvement', 'chore'])
|
|
19
19
|
export const TaskSectionSchema = z.enum(['active', 'backlog', 'previously_active'])
|
|
20
|
-
export const TaskStatusSchema = z.enum(['pending', 'in_progress', 'completed', 'blocked', 'paused'])
|
|
20
|
+
export const TaskStatusSchema = z.enum(['pending', 'in_progress', 'completed', 'blocked', 'paused', 'failed'])
|
|
21
21
|
export const ActivityTypeSchema = z.enum(['task_completed', 'feature_shipped', 'idea_captured', 'session_started'])
|
|
22
22
|
|
|
23
|
+
// Subtask summary for context handoff between agents
|
|
24
|
+
export const SubtaskSummarySchema = z.object({
|
|
25
|
+
title: z.string(),
|
|
26
|
+
description: z.string(),
|
|
27
|
+
filesChanged: z.array(z.object({
|
|
28
|
+
path: z.string(),
|
|
29
|
+
action: z.enum(['created', 'modified', 'deleted']),
|
|
30
|
+
})),
|
|
31
|
+
whatWasDone: z.array(z.string()),
|
|
32
|
+
outputForNextAgent: z.string().optional(),
|
|
33
|
+
notes: z.string().optional(),
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// Subtask schema for task fragmentation
|
|
37
|
+
export const SubtaskSchema = z.object({
|
|
38
|
+
id: z.string(), // subtask-xxx
|
|
39
|
+
description: z.string(),
|
|
40
|
+
domain: z.string(), // frontend, backend, database, testing, etc.
|
|
41
|
+
agent: z.string(), // agent file name (e.g., "frontend.md")
|
|
42
|
+
status: TaskStatusSchema,
|
|
43
|
+
dependsOn: z.array(z.string()), // IDs of dependent subtasks
|
|
44
|
+
startedAt: z.string().optional(), // ISO8601
|
|
45
|
+
completedAt: z.string().optional(), // ISO8601
|
|
46
|
+
output: z.string().optional(), // Brief output description
|
|
47
|
+
summary: SubtaskSummarySchema.optional(), // Full summary for context handoff
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Subtask progress tracking
|
|
51
|
+
export const SubtaskProgressSchema = z.object({
|
|
52
|
+
completed: z.number(),
|
|
53
|
+
total: z.number(),
|
|
54
|
+
percentage: z.number(),
|
|
55
|
+
})
|
|
56
|
+
|
|
23
57
|
export const CurrentTaskSchema = z.object({
|
|
24
58
|
id: z.string(), // task_xxxxxxxx
|
|
25
59
|
description: z.string(),
|
|
26
60
|
startedAt: z.string(), // ISO8601
|
|
27
61
|
sessionId: z.string(), // sess_xxxxxxxx
|
|
28
62
|
featureId: z.string().optional(), // feat_xxxxxxxx
|
|
63
|
+
// Subtask tracking for fragmented tasks
|
|
64
|
+
subtasks: z.array(SubtaskSchema).optional(),
|
|
65
|
+
currentSubtaskIndex: z.number().optional(),
|
|
66
|
+
subtaskProgress: SubtaskProgressSchema.optional(),
|
|
29
67
|
})
|
|
30
68
|
|
|
31
69
|
export const PreviousTaskSchema = z.object({
|
|
@@ -99,6 +137,10 @@ export type TaskSection = z.infer<typeof TaskSectionSchema>
|
|
|
99
137
|
export type TaskStatus = z.infer<typeof TaskStatusSchema>
|
|
100
138
|
export type ActivityType = z.infer<typeof ActivityTypeSchema>
|
|
101
139
|
|
|
140
|
+
export type Subtask = z.infer<typeof SubtaskSchema>
|
|
141
|
+
export type SubtaskSummary = z.infer<typeof SubtaskSummarySchema>
|
|
142
|
+
export type SubtaskProgress = z.infer<typeof SubtaskProgressSchema>
|
|
143
|
+
|
|
102
144
|
export type CurrentTask = z.infer<typeof CurrentTaskSchema>
|
|
103
145
|
export type PreviousTask = z.infer<typeof PreviousTaskSchema>
|
|
104
146
|
export type StateJson = z.infer<typeof StateJsonSchema>
|
|
@@ -68,8 +68,13 @@ export class AgentService {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
* Assign agent for a task
|
|
72
|
-
*
|
|
71
|
+
* Assign agent for a task - AGENTIC APPROACH
|
|
72
|
+
*
|
|
73
|
+
* NO keyword matching. Returns available agents and context
|
|
74
|
+
* for Claude to make the decision via templates/orchestrator.md
|
|
75
|
+
*
|
|
76
|
+
* The agents in {agentsDir} are already project-specific
|
|
77
|
+
* (generated during p. sync with real technologies)
|
|
73
78
|
*/
|
|
74
79
|
async assignForTask(
|
|
75
80
|
task: string,
|
|
@@ -81,67 +86,53 @@ export class AgentService {
|
|
|
81
86
|
const agents = await this.agentRouter.getAgentNames()
|
|
82
87
|
|
|
83
88
|
if (agents.length === 0) {
|
|
89
|
+
// No agents available - suggest running sync
|
|
84
90
|
return {
|
|
85
|
-
agent:
|
|
91
|
+
agent: null, // Claude decides - don't default to generalist
|
|
86
92
|
routing: {
|
|
87
|
-
confidence:
|
|
88
|
-
reason: 'No specialized agents available',
|
|
93
|
+
confidence: 0,
|
|
94
|
+
reason: 'No specialized agents available. Run "p. sync" to generate agents.',
|
|
89
95
|
availableAgents: [],
|
|
90
96
|
},
|
|
97
|
+
_agenticNote: 'AGENTIC: Claude reads orchestrator.md and decides how to proceed',
|
|
91
98
|
}
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
//
|
|
95
|
-
// Claude
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (taskLower.includes(nameLower) || nameLower.includes('general')) {
|
|
102
|
-
bestMatch = agentName
|
|
103
|
-
break
|
|
104
|
-
}
|
|
105
|
-
// Common domain keywords
|
|
106
|
-
if (
|
|
107
|
-
(nameLower.includes('fe') || nameLower.includes('frontend')) &&
|
|
108
|
-
(taskLower.includes('ui') ||
|
|
109
|
-
taskLower.includes('component') ||
|
|
110
|
-
taskLower.includes('react'))
|
|
111
|
-
) {
|
|
112
|
-
bestMatch = agentName
|
|
113
|
-
break
|
|
114
|
-
}
|
|
115
|
-
if (
|
|
116
|
-
(nameLower.includes('be') || nameLower.includes('backend')) &&
|
|
117
|
-
(taskLower.includes('api') ||
|
|
118
|
-
taskLower.includes('server') ||
|
|
119
|
-
taskLower.includes('database'))
|
|
120
|
-
) {
|
|
121
|
-
bestMatch = agentName
|
|
122
|
-
break
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
await this.agentRouter.logUsage(task, bestMatch, projectPath)
|
|
101
|
+
// AGENTIC: No keyword matching here
|
|
102
|
+
// Claude reads the orchestrator template and decides based on:
|
|
103
|
+
// 1. Task analysis (what domains are involved)
|
|
104
|
+
// 2. Available agents (from agents directory)
|
|
105
|
+
// 3. Whether to fragment into subtasks
|
|
106
|
+
//
|
|
107
|
+
// The TypeScript code just provides the list of available agents
|
|
127
108
|
|
|
128
109
|
return {
|
|
129
|
-
agent:
|
|
110
|
+
agent: null, // Claude decides via templates
|
|
130
111
|
routing: {
|
|
131
|
-
confidence: 0
|
|
132
|
-
reason: '
|
|
112
|
+
confidence: 0, // Claude determines confidence
|
|
113
|
+
reason: 'AGENTIC: Claude will analyze task and select appropriate specialist agents',
|
|
133
114
|
availableAgents: agents,
|
|
134
115
|
},
|
|
135
|
-
_agenticNote:
|
|
116
|
+
_agenticNote: `
|
|
117
|
+
AGENTIC EXECUTION:
|
|
118
|
+
- Read: templates/agentic/orchestrator.md
|
|
119
|
+
- Analyze task: "${task}"
|
|
120
|
+
- Available specialists: ${agents.join(', ')}
|
|
121
|
+
- Claude decides which agent(s) to use
|
|
122
|
+
- Always prefer specialists over generalist
|
|
123
|
+
- Fragment complex tasks into subtasks
|
|
124
|
+
`,
|
|
136
125
|
}
|
|
137
126
|
} catch (_error) {
|
|
138
127
|
// Agent routing unavailable - expected for new projects
|
|
139
128
|
return {
|
|
140
|
-
agent:
|
|
129
|
+
agent: null,
|
|
141
130
|
routing: {
|
|
142
|
-
confidence:
|
|
143
|
-
reason: 'Agent routing unavailable',
|
|
131
|
+
confidence: 0,
|
|
132
|
+
reason: 'Agent routing unavailable - run "p. sync" first',
|
|
133
|
+
availableAgents: [],
|
|
144
134
|
},
|
|
135
|
+
_agenticNote: 'AGENTIC: Suggest running p. sync to generate agents',
|
|
145
136
|
}
|
|
146
137
|
}
|
|
147
138
|
}
|