prjct-cli 0.15.0 → 0.17.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/CHANGELOG.md +51 -0
- package/bin/dev.js +0 -1
- package/bin/serve.js +19 -20
- package/core/agentic/agent-router.ts +79 -14
- package/core/agentic/command-executor/command-executor.ts +2 -74
- package/core/agentic/services.ts +0 -48
- package/core/agentic/template-loader.ts +35 -1
- package/core/commands/base.ts +96 -77
- package/core/commands/planning.ts +13 -2
- package/core/commands/setup.ts +3 -85
- package/core/errors.ts +209 -0
- package/core/infrastructure/config-manager.ts +22 -5
- package/core/infrastructure/setup.ts +5 -50
- package/core/storage/storage-manager.ts +42 -6
- package/core/utils/logger.ts +19 -12
- package/package.json +2 -4
- package/templates/agentic/subagent-generation.md +109 -0
- package/templates/commands/setup.md +18 -3
- package/templates/commands/sync.md +74 -13
- package/templates/mcp-config.json +20 -1
- package/templates/subagents/domain/backend.md +105 -0
- package/templates/subagents/domain/database.md +118 -0
- package/templates/subagents/domain/devops.md +148 -0
- package/templates/subagents/domain/frontend.md +99 -0
- package/templates/subagents/domain/testing.md +169 -0
- package/templates/subagents/workflow/prjct-planner.md +158 -0
- package/templates/subagents/workflow/prjct-shipper.md +179 -0
- package/templates/subagents/workflow/prjct-workflow.md +98 -0
- package/bin/generate-views.js +0 -209
- package/bin/migrate-to-json.js +0 -742
- package/core/agentic/context-filter.ts +0 -365
- package/core/agentic/parallel-tools.ts +0 -165
- package/core/agentic/response-templates.ts +0 -164
- package/core/agentic/semantic-compression.ts +0 -273
- package/core/agentic/think-blocks.ts +0 -202
- package/core/agentic/validation-rules.ts +0 -313
- package/core/domain/agent-matcher.ts +0 -130
- package/core/domain/agent-validator.ts +0 -250
- package/core/domain/architect-session.ts +0 -315
- package/core/domain/product-standards.ts +0 -106
- package/core/domain/smart-cache.ts +0 -167
- package/core/domain/task-analyzer.ts +0 -296
- package/core/infrastructure/legacy-installer-detector/cleanup.ts +0 -216
- package/core/infrastructure/legacy-installer-detector/detection.ts +0 -95
- package/core/infrastructure/legacy-installer-detector/index.ts +0 -171
- package/core/infrastructure/legacy-installer-detector/migration.ts +0 -87
- package/core/infrastructure/legacy-installer-detector/types.ts +0 -42
- package/core/infrastructure/legacy-installer-detector.ts +0 -7
- package/core/infrastructure/migrator/file-operations.ts +0 -125
- package/core/infrastructure/migrator/index.ts +0 -288
- package/core/infrastructure/migrator/project-scanner.ts +0 -90
- package/core/infrastructure/migrator/reports.ts +0 -117
- package/core/infrastructure/migrator/types.ts +0 -124
- package/core/infrastructure/migrator/validation.ts +0 -94
- package/core/infrastructure/migrator/version-migration.ts +0 -117
- package/core/infrastructure/migrator.ts +0 -10
- package/core/infrastructure/uuid-migration.ts +0 -750
- package/templates/commands/migrate-all.md +0 -96
- package/templates/commands/migrate.md +0 -140
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.16.0] - 2025-12-15
|
|
4
|
+
|
|
5
|
+
### Dead Code Cleanup
|
|
6
|
+
|
|
7
|
+
Aggressive removal of ~6,600 lines of unused/legacy code for a cleaner, leaner codebase.
|
|
8
|
+
|
|
9
|
+
- **Domain Modules Removed (6 files)**
|
|
10
|
+
- `agent-matcher.ts` - Replaced by template-based routing
|
|
11
|
+
- `product-standards.ts` - Unused domain standards
|
|
12
|
+
- `smart-cache.ts`, `task-analyzer.ts`, `agent-validator.ts`, `architect-session.ts`
|
|
13
|
+
|
|
14
|
+
- **Agentic Modules Removed (6 files)**
|
|
15
|
+
- `semantic-compression.ts` - Exported but never called
|
|
16
|
+
- `response-templates.ts`, `think-blocks.ts`, `parallel-tools.ts`
|
|
17
|
+
- `context-filter.ts`, `validation-rules.ts`
|
|
18
|
+
|
|
19
|
+
- **Legacy Migration Code Removed**
|
|
20
|
+
- `legacy-installer-detector/` directory (curl-to-npm migration)
|
|
21
|
+
- `migrator/` directory (version migrations)
|
|
22
|
+
- `uuid-migration.ts` (750 lines)
|
|
23
|
+
- `bin/migrate-to-json.js`, `bin/generate-views.js`
|
|
24
|
+
- `/p:migrate`, `/p:migrate-all` command templates
|
|
25
|
+
|
|
26
|
+
- **Code Cleanup**
|
|
27
|
+
- Simplified `services.ts` facade (removed 6 unused exports)
|
|
28
|
+
- Simplified `setup.ts` (removed migration steps)
|
|
29
|
+
- Removed deprecated methods from `base.ts`
|
|
30
|
+
- Cleaned `command-executor.ts` (removed dead module usage)
|
|
31
|
+
|
|
32
|
+
- **Impact**
|
|
33
|
+
- Lines removed: ~6,600
|
|
34
|
+
- Files deleted: ~25
|
|
35
|
+
- Build: Passing
|
|
36
|
+
- Tests: 131 pass
|
|
37
|
+
|
|
38
|
+
## [0.15.1] - 2025-12-15
|
|
39
|
+
|
|
40
|
+
### MCP Setup Documentation
|
|
41
|
+
|
|
42
|
+
Enhanced setup template with MCP server installation documentation.
|
|
43
|
+
|
|
44
|
+
- **Setup Template Updates**
|
|
45
|
+
- Added MCP server installation step documentation
|
|
46
|
+
- Context7 library documentation lookup integration
|
|
47
|
+
- Updated success output examples
|
|
48
|
+
|
|
49
|
+
- **MCP Config Enhancements**
|
|
50
|
+
- Added description field for Context7 server
|
|
51
|
+
- Added usage guidelines with when/tools/examples
|
|
52
|
+
- Better documentation for resolve-library-id and get-library-docs
|
|
53
|
+
|
|
3
54
|
## [0.15.0] - 2025-12-15
|
|
4
55
|
|
|
5
56
|
### Template Optimization - Aggressive Consolidation
|
package/bin/dev.js
CHANGED
package/bin/serve.js
CHANGED
|
@@ -138,7 +138,6 @@ if (sharedCheck.needed || webCheck.needed) {
|
|
|
138
138
|
const sharedInstall = spawnSync('npm', ['install'], {
|
|
139
139
|
cwd: sharedDir,
|
|
140
140
|
stdio: 'inherit',
|
|
141
|
-
shell: true,
|
|
142
141
|
})
|
|
143
142
|
|
|
144
143
|
if (sharedInstall.status !== 0) {
|
|
@@ -151,7 +150,6 @@ if (sharedCheck.needed || webCheck.needed) {
|
|
|
151
150
|
const sharedBuild = spawnSync('npm', ['run', 'build'], {
|
|
152
151
|
cwd: sharedDir,
|
|
153
152
|
stdio: 'inherit',
|
|
154
|
-
shell: true,
|
|
155
153
|
})
|
|
156
154
|
|
|
157
155
|
if (sharedBuild.status !== 0) {
|
|
@@ -167,7 +165,6 @@ if (sharedCheck.needed || webCheck.needed) {
|
|
|
167
165
|
const webInstall = spawnSync('npm', ['install'], {
|
|
168
166
|
cwd: webDir,
|
|
169
167
|
stdio: 'inherit',
|
|
170
|
-
shell: true,
|
|
171
168
|
})
|
|
172
169
|
|
|
173
170
|
if (webInstall.status !== 0) {
|
|
@@ -189,6 +186,7 @@ if (sharedCheck.needed || webCheck.needed) {
|
|
|
189
186
|
function isPrjctWebProcess(pid) {
|
|
190
187
|
try {
|
|
191
188
|
if (process.platform === 'win32') {
|
|
189
|
+
// Windows: wmic needs shell for proper command parsing
|
|
192
190
|
const result = spawnSync('wmic', ['process', 'where', `processid=${pid}`, 'get', 'commandline'], {
|
|
193
191
|
shell: true,
|
|
194
192
|
encoding: 'utf8',
|
|
@@ -197,10 +195,9 @@ function isPrjctWebProcess(pid) {
|
|
|
197
195
|
} else {
|
|
198
196
|
// macOS / Linux - check process command line
|
|
199
197
|
const result = spawnSync('ps', ['-p', pid, '-o', 'command='], {
|
|
200
|
-
shell: true,
|
|
201
198
|
encoding: 'utf8',
|
|
202
199
|
})
|
|
203
|
-
const cmd = result.stdout
|
|
200
|
+
const cmd = result.stdout?.trim() || ''
|
|
204
201
|
return cmd.includes('server.ts') || cmd.includes('prjct') || cmd.includes('next')
|
|
205
202
|
}
|
|
206
203
|
} catch {
|
|
@@ -214,9 +211,10 @@ function isPrjctWebProcess(pid) {
|
|
|
214
211
|
function getPortPids(portToCheck) {
|
|
215
212
|
try {
|
|
216
213
|
if (process.platform === 'win32') {
|
|
217
|
-
|
|
214
|
+
// Windows: netstat works without shell
|
|
215
|
+
const result = spawnSync('netstat', ['-ano'], { encoding: 'utf8' })
|
|
218
216
|
const pids = []
|
|
219
|
-
const lines = result.stdout.split('\n')
|
|
217
|
+
const lines = (result.stdout || '').split('\n')
|
|
220
218
|
for (const line of lines) {
|
|
221
219
|
if (line.includes(`:${portToCheck}`) && line.includes('LISTENING')) {
|
|
222
220
|
const parts = line.trim().split(/\s+/)
|
|
@@ -226,11 +224,11 @@ function getPortPids(portToCheck) {
|
|
|
226
224
|
}
|
|
227
225
|
return pids
|
|
228
226
|
} else {
|
|
227
|
+
// macOS / Linux
|
|
229
228
|
const result = spawnSync('lsof', ['-ti', `:${portToCheck}`], {
|
|
230
|
-
shell: true,
|
|
231
229
|
encoding: 'utf8',
|
|
232
230
|
})
|
|
233
|
-
return result.stdout.trim().split('\n').filter(Boolean)
|
|
231
|
+
return (result.stdout || '').trim().split('\n').filter(Boolean)
|
|
234
232
|
}
|
|
235
233
|
} catch {
|
|
236
234
|
return []
|
|
@@ -244,9 +242,11 @@ function killPids(pids) {
|
|
|
244
242
|
for (const pid of pids) {
|
|
245
243
|
try {
|
|
246
244
|
if (process.platform === 'win32') {
|
|
247
|
-
|
|
245
|
+
// Windows: taskkill works without shell
|
|
246
|
+
spawnSync('taskkill', ['/F', '/PID', pid])
|
|
248
247
|
} else {
|
|
249
|
-
|
|
248
|
+
// Unix: kill works without shell
|
|
249
|
+
spawnSync('kill', ['-9', pid])
|
|
250
250
|
}
|
|
251
251
|
} catch {
|
|
252
252
|
// Ignore individual kill errors
|
|
@@ -258,13 +258,14 @@ function killPids(pids) {
|
|
|
258
258
|
* Open URL in browser
|
|
259
259
|
*/
|
|
260
260
|
function openBrowser(url) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
261
|
+
if (process.platform === 'darwin') {
|
|
262
|
+
spawn('open', [url], { detached: true, stdio: 'ignore' }).unref()
|
|
263
|
+
} else if (process.platform === 'win32') {
|
|
264
|
+
// Windows 'start' is a shell builtin, requires shell: true
|
|
265
|
+
spawn('cmd', ['/c', 'start', '', url], { detached: true, stdio: 'ignore' }).unref()
|
|
266
|
+
} else {
|
|
267
|
+
spawn('xdg-open', [url], { detached: true, stdio: 'ignore' }).unref()
|
|
268
|
+
}
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
// Check if port is in use and handle accordingly
|
|
@@ -324,7 +325,6 @@ if (!fs.existsSync(nextDir)) {
|
|
|
324
325
|
const buildResult = spawnSync('npm', ['run', 'build'], {
|
|
325
326
|
cwd: webDir,
|
|
326
327
|
stdio: 'inherit',
|
|
327
|
-
shell: true,
|
|
328
328
|
})
|
|
329
329
|
if (buildResult.status !== 0) {
|
|
330
330
|
console.error('❌ Build failed')
|
|
@@ -337,7 +337,6 @@ if (!fs.existsSync(nextDir)) {
|
|
|
337
337
|
const web = spawn('npm', ['run', 'start'], {
|
|
338
338
|
cwd: webDir,
|
|
339
339
|
stdio: 'inherit',
|
|
340
|
-
shell: true,
|
|
341
340
|
env: { ...process.env, PORT: port, NODE_ENV: 'production' },
|
|
342
341
|
})
|
|
343
342
|
|
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
* Agent Router
|
|
3
3
|
* Orchestrates agent loading and context building for Claude delegation.
|
|
4
4
|
*
|
|
5
|
+
* Loads agents from two locations:
|
|
6
|
+
* 1. {projectPath}/.claude/agents/ - Claude Code sub-agents (PRIMARY, per-project)
|
|
7
|
+
* 2. ~/.prjct-cli/projects/{id}/agents/ - Legacy agents (fallback)
|
|
8
|
+
*
|
|
9
|
+
* Claude Code sub-agents are prioritized as they're more specific to the project.
|
|
10
|
+
*
|
|
5
11
|
* @module agentic/agent-router
|
|
6
|
-
* @version
|
|
12
|
+
* @version 3.0.0
|
|
7
13
|
*/
|
|
8
14
|
|
|
9
15
|
import fs from 'fs/promises'
|
|
@@ -14,6 +20,7 @@ import pathManager from '../infrastructure/path-manager'
|
|
|
14
20
|
interface Agent {
|
|
15
21
|
name: string
|
|
16
22
|
content: string
|
|
23
|
+
source: 'claude-code' | 'legacy'
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
interface AssignmentContext {
|
|
@@ -30,29 +37,42 @@ interface AssignmentContext {
|
|
|
30
37
|
*/
|
|
31
38
|
class AgentRouter {
|
|
32
39
|
projectId: string | null = null
|
|
33
|
-
|
|
40
|
+
projectPath: string | null = null
|
|
41
|
+
legacyAgentsPath: string | null = null
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get path to Claude Code sub-agents directory
|
|
45
|
+
*/
|
|
46
|
+
getClaudeCodeAgentsPath(): string | null {
|
|
47
|
+
if (!this.projectPath) return null
|
|
48
|
+
return path.join(this.projectPath, '.claude', 'agents')
|
|
49
|
+
}
|
|
34
50
|
|
|
35
51
|
/**
|
|
36
52
|
* Initialize router with project context
|
|
37
53
|
*/
|
|
38
54
|
async initialize(projectPath: string): Promise<void> {
|
|
39
55
|
this.projectId = await configManager.getProjectId(projectPath)
|
|
40
|
-
this.
|
|
56
|
+
this.projectPath = projectPath
|
|
57
|
+
this.legacyAgentsPath = pathManager.getPath(this.projectId!, 'agents')
|
|
41
58
|
}
|
|
42
59
|
|
|
43
60
|
/**
|
|
44
|
-
* Load
|
|
61
|
+
* Load agents from a specific directory
|
|
45
62
|
*/
|
|
46
|
-
async
|
|
63
|
+
private async loadAgentsFromPath(
|
|
64
|
+
agentsPath: string,
|
|
65
|
+
source: 'claude-code' | 'legacy'
|
|
66
|
+
): Promise<Agent[]> {
|
|
47
67
|
try {
|
|
48
|
-
const files = await fs.readdir(
|
|
68
|
+
const files = await fs.readdir(agentsPath)
|
|
49
69
|
const agents: Agent[] = []
|
|
50
70
|
|
|
51
71
|
for (const file of files) {
|
|
52
72
|
if (file.endsWith('.md')) {
|
|
53
73
|
const name = file.replace('.md', '')
|
|
54
|
-
const content = await fs.readFile(path.join(
|
|
55
|
-
agents.push({ name, content })
|
|
74
|
+
const content = await fs.readFile(path.join(agentsPath, file), 'utf-8')
|
|
75
|
+
agents.push({ name, content, source })
|
|
56
76
|
}
|
|
57
77
|
}
|
|
58
78
|
|
|
@@ -62,6 +82,33 @@ class AgentRouter {
|
|
|
62
82
|
}
|
|
63
83
|
}
|
|
64
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Load all available agents from both Claude Code and legacy locations
|
|
87
|
+
* Claude Code sub-agents take priority over legacy agents with same name
|
|
88
|
+
*/
|
|
89
|
+
async loadAvailableAgents(): Promise<Agent[]> {
|
|
90
|
+
const agentMap = new Map<string, Agent>()
|
|
91
|
+
|
|
92
|
+
// Load legacy agents first (lower priority)
|
|
93
|
+
if (this.legacyAgentsPath) {
|
|
94
|
+
const legacyAgents = await this.loadAgentsFromPath(this.legacyAgentsPath, 'legacy')
|
|
95
|
+
for (const agent of legacyAgents) {
|
|
96
|
+
agentMap.set(agent.name, agent)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Load Claude Code sub-agents (higher priority - overwrites legacy)
|
|
101
|
+
const claudeCodePath = this.getClaudeCodeAgentsPath()
|
|
102
|
+
if (claudeCodePath) {
|
|
103
|
+
const claudeCodeAgents = await this.loadAgentsFromPath(claudeCodePath, 'claude-code')
|
|
104
|
+
for (const agent of claudeCodeAgents) {
|
|
105
|
+
agentMap.set(agent.name, agent)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return Array.from(agentMap.values())
|
|
110
|
+
}
|
|
111
|
+
|
|
65
112
|
/**
|
|
66
113
|
* Get list of available agent names
|
|
67
114
|
*/
|
|
@@ -72,15 +119,33 @@ class AgentRouter {
|
|
|
72
119
|
|
|
73
120
|
/**
|
|
74
121
|
* Load a specific agent by name
|
|
122
|
+
* Checks Claude Code sub-agents first, then falls back to legacy
|
|
75
123
|
*/
|
|
76
124
|
async loadAgent(name: string): Promise<Agent | null> {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
125
|
+
// Try Claude Code sub-agents first (higher priority)
|
|
126
|
+
const claudeCodePath = this.getClaudeCodeAgentsPath()
|
|
127
|
+
if (claudeCodePath) {
|
|
128
|
+
try {
|
|
129
|
+
const filePath = path.join(claudeCodePath, `${name}.md`)
|
|
130
|
+
const content = await fs.readFile(filePath, 'utf-8')
|
|
131
|
+
return { name, content, source: 'claude-code' }
|
|
132
|
+
} catch {
|
|
133
|
+
// Not found in Claude Code path, try legacy
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Fall back to legacy agents
|
|
138
|
+
if (this.legacyAgentsPath) {
|
|
139
|
+
try {
|
|
140
|
+
const filePath = path.join(this.legacyAgentsPath, `${name}.md`)
|
|
141
|
+
const content = await fs.readFile(filePath, 'utf-8')
|
|
142
|
+
return { name, content, source: 'legacy' }
|
|
143
|
+
} catch {
|
|
144
|
+
// Not found
|
|
145
|
+
}
|
|
83
146
|
}
|
|
147
|
+
|
|
148
|
+
return null
|
|
84
149
|
}
|
|
85
150
|
|
|
86
151
|
/**
|
|
@@ -9,15 +9,10 @@ import templateLoader from '../template-loader'
|
|
|
9
9
|
import contextBuilder from '../context-builder'
|
|
10
10
|
import promptBuilder from '../prompt-builder'
|
|
11
11
|
import toolRegistry from '../tool-registry'
|
|
12
|
-
import { validate, formatError } from '../validation-rules'
|
|
13
12
|
import loopDetector from '../loop-detector'
|
|
14
13
|
import chainOfThought from '../chain-of-thought'
|
|
15
|
-
import semanticCompression from '../semantic-compression'
|
|
16
|
-
import responseTemplates from '../response-templates'
|
|
17
14
|
import memorySystem from '../memory-system'
|
|
18
15
|
import groundTruth from '../ground-truth'
|
|
19
|
-
import thinkBlocks from '../think-blocks'
|
|
20
|
-
import parallelTools from '../parallel-tools'
|
|
21
16
|
import planMode from '../plan-mode'
|
|
22
17
|
import { signalStart, signalEnd } from './status-signal'
|
|
23
18
|
import type { ExecutionResult, SimpleExecutionResult, ExecutionToolsFn } from './types'
|
|
@@ -71,18 +66,6 @@ export class CommandExecutor {
|
|
|
71
66
|
// 2. Build METADATA context only (lazy loading - no file reads yet)
|
|
72
67
|
const metadataContext = await contextBuilder.build(projectPath, params)
|
|
73
68
|
|
|
74
|
-
// 2.5. VALIDATE: Pre-flight checks with specific errors
|
|
75
|
-
const validation = await validate(commandName, metadataContext as unknown as Parameters<typeof validate>[1])
|
|
76
|
-
if (!validation.valid) {
|
|
77
|
-
this.signalEnd()
|
|
78
|
-
return {
|
|
79
|
-
success: false,
|
|
80
|
-
error: formatError(validation),
|
|
81
|
-
validation,
|
|
82
|
-
isValidationError: true,
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
69
|
// 2.55. P3.4 PLAN MODE: Check if command requires planning
|
|
87
70
|
const requiresPlanning = planMode.requiresPlanning(commandName)
|
|
88
71
|
const isDestructive = planMode.isDestructive(commandName)
|
|
@@ -112,29 +95,6 @@ export class CommandExecutor {
|
|
|
112
95
|
}
|
|
113
96
|
}
|
|
114
97
|
|
|
115
|
-
// 2.7. THINK BLOCKS (P3.1): Dynamic reasoning based on triggers
|
|
116
|
-
let thinkBlock = null
|
|
117
|
-
const preThinkState =
|
|
118
|
-
groundTruthResult?.actual || (await contextBuilder.loadStateForCommand(metadataContext, commandName))
|
|
119
|
-
const thinkTrigger = thinkBlocks.detectTrigger(
|
|
120
|
-
commandName,
|
|
121
|
-
metadataContext as unknown as Parameters<typeof thinkBlocks.detectTrigger>[1],
|
|
122
|
-
preThinkState as Parameters<typeof thinkBlocks.detectTrigger>[2]
|
|
123
|
-
)
|
|
124
|
-
if (thinkTrigger) {
|
|
125
|
-
thinkBlock = await thinkBlocks.generate(
|
|
126
|
-
thinkTrigger,
|
|
127
|
-
commandName,
|
|
128
|
-
metadataContext as unknown as Parameters<typeof thinkBlocks.generate>[2],
|
|
129
|
-
preThinkState as Parameters<typeof thinkBlocks.generate>[3]
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
// Log think block if in debug mode
|
|
133
|
-
if (process.env.PRJCT_DEBUG === 'true') {
|
|
134
|
-
console.log(thinkBlocks.format(thinkBlock, true))
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
98
|
// 2.8. CHAIN OF THOUGHT: Reasoning for critical commands
|
|
139
99
|
let reasoning = null
|
|
140
100
|
if (chainOfThought.requiresReasoning(commandName)) {
|
|
@@ -164,29 +124,7 @@ export class CommandExecutor {
|
|
|
164
124
|
}
|
|
165
125
|
|
|
166
126
|
// 6. Load state with filtered context
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
// 6.5. SEMANTIC COMPRESSION: Compress state for reduced token usage
|
|
170
|
-
const compressedState: Record<string, unknown> = {}
|
|
171
|
-
for (const [key, content] of Object.entries(rawState)) {
|
|
172
|
-
if (content) {
|
|
173
|
-
const compressed = semanticCompression.compress(content, key)
|
|
174
|
-
compressedState[key] = {
|
|
175
|
-
raw: content,
|
|
176
|
-
summary: compressed.summary,
|
|
177
|
-
compressed,
|
|
178
|
-
}
|
|
179
|
-
} else {
|
|
180
|
-
compressedState[key] = { raw: null, summary: 'Empty', compressed: null }
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Use compressed summaries for prompt, keep raw for tool execution
|
|
185
|
-
const state = {
|
|
186
|
-
...rawState,
|
|
187
|
-
_compressed: compressedState,
|
|
188
|
-
_compressionMetrics: semanticCompression.getMetrics(),
|
|
189
|
-
}
|
|
127
|
+
const state = await contextBuilder.loadState(metadataContext)
|
|
190
128
|
|
|
191
129
|
// 7. MEMORY: Load learned patterns AND relevant memories for this command
|
|
192
130
|
let learnedPatterns = null
|
|
@@ -224,7 +162,7 @@ export class CommandExecutor {
|
|
|
224
162
|
state,
|
|
225
163
|
null,
|
|
226
164
|
learnedPatterns,
|
|
227
|
-
|
|
165
|
+
null,
|
|
228
166
|
relevantMemories,
|
|
229
167
|
planInfo
|
|
230
168
|
)
|
|
@@ -248,19 +186,9 @@ export class CommandExecutor {
|
|
|
248
186
|
agentsPath: context.agentsPath as string,
|
|
249
187
|
agentRoutingPath: context.agentRoutingPath as string,
|
|
250
188
|
reasoning,
|
|
251
|
-
thinkBlock,
|
|
252
189
|
groundTruth: groundTruthResult,
|
|
253
|
-
compressionMetrics: state._compressionMetrics,
|
|
254
190
|
learnedPatterns,
|
|
255
191
|
relevantMemories,
|
|
256
|
-
formatResponse: (data: unknown) => responseTemplates.format(commandName, data as Parameters<typeof responseTemplates.format>[1]),
|
|
257
|
-
formatThinkBlock: (verbose: boolean) => thinkBlocks.format(thinkBlock, verbose),
|
|
258
|
-
parallel: {
|
|
259
|
-
execute: (toolCalls: unknown[]) => parallelTools.execute(toolCalls as Parameters<typeof parallelTools.execute>[0]),
|
|
260
|
-
readAll: (paths: string[]) => parallelTools.readAll(paths),
|
|
261
|
-
canParallelize: (tools: string[]) => parallelTools.canParallelize(tools),
|
|
262
|
-
getMetrics: () => parallelTools.getMetrics(),
|
|
263
|
-
},
|
|
264
192
|
memory: {
|
|
265
193
|
create: (memory: unknown) =>
|
|
266
194
|
memorySystem.createMemory(metadataContext.projectId!, memory as Parameters<typeof memorySystem.createMemory>[1]),
|
package/core/agentic/services.ts
CHANGED
|
@@ -29,14 +29,8 @@ import toolRegistry from './tool-registry'
|
|
|
29
29
|
import loopDetector from './loop-detector'
|
|
30
30
|
import memorySystem from './memory-system'
|
|
31
31
|
import groundTruth from './ground-truth'
|
|
32
|
-
import semanticCompression from './semantic-compression'
|
|
33
|
-
import responseTemplates from './response-templates'
|
|
34
32
|
import chainOfThought from './chain-of-thought'
|
|
35
|
-
import thinkBlocks from './think-blocks'
|
|
36
|
-
import parallelTools from './parallel-tools'
|
|
37
33
|
import planMode from './plan-mode'
|
|
38
|
-
import contextFilter from './context-filter'
|
|
39
|
-
import validationRules from './validation-rules'
|
|
40
34
|
import agentRouter from './agent-router'
|
|
41
35
|
import smartContext from './smart-context'
|
|
42
36
|
import { mdManagers } from '../data'
|
|
@@ -90,54 +84,18 @@ export interface AgenticServices {
|
|
|
90
84
|
*/
|
|
91
85
|
truth: typeof groundTruth
|
|
92
86
|
|
|
93
|
-
/**
|
|
94
|
-
* Semantic compression - compresses context to fit token limits
|
|
95
|
-
* @see semanticCompression
|
|
96
|
-
*/
|
|
97
|
-
compression: typeof semanticCompression
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Response templates - generates formatted responses
|
|
101
|
-
* @see responseTemplates
|
|
102
|
-
*/
|
|
103
|
-
responses: typeof responseTemplates
|
|
104
|
-
|
|
105
87
|
/**
|
|
106
88
|
* Chain of thought - adds reasoning to responses
|
|
107
89
|
* @see chainOfThought
|
|
108
90
|
*/
|
|
109
91
|
reasoning: typeof chainOfThought
|
|
110
92
|
|
|
111
|
-
/**
|
|
112
|
-
* Think blocks - generates think block content
|
|
113
|
-
* @see thinkBlocks
|
|
114
|
-
*/
|
|
115
|
-
thinking: typeof thinkBlocks
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Parallel tools - executes tools in parallel
|
|
119
|
-
* @see parallelTools
|
|
120
|
-
*/
|
|
121
|
-
parallel: typeof parallelTools
|
|
122
|
-
|
|
123
93
|
/**
|
|
124
94
|
* Plan mode - handles planning workflow
|
|
125
95
|
* @see planMode
|
|
126
96
|
*/
|
|
127
97
|
planning: typeof planMode
|
|
128
98
|
|
|
129
|
-
/**
|
|
130
|
-
* Context filter - filters context for relevance
|
|
131
|
-
* @see contextFilter
|
|
132
|
-
*/
|
|
133
|
-
filter: typeof contextFilter
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Validation rules - validates command inputs
|
|
137
|
-
* @see validationRules
|
|
138
|
-
*/
|
|
139
|
-
validation: typeof validationRules
|
|
140
|
-
|
|
141
99
|
/**
|
|
142
100
|
* Agent router - routes tasks to appropriate agents
|
|
143
101
|
* @see agentRouter
|
|
@@ -187,14 +145,8 @@ export const services: AgenticServices = {
|
|
|
187
145
|
loops: loopDetector,
|
|
188
146
|
memory: memorySystem,
|
|
189
147
|
truth: groundTruth,
|
|
190
|
-
compression: semanticCompression,
|
|
191
|
-
responses: responseTemplates,
|
|
192
148
|
reasoning: chainOfThought,
|
|
193
|
-
thinking: thinkBlocks,
|
|
194
|
-
parallel: parallelTools,
|
|
195
149
|
planning: planMode,
|
|
196
|
-
filter: contextFilter,
|
|
197
|
-
validation: validationRules,
|
|
198
150
|
router: agentRouter,
|
|
199
151
|
smartContext: smartContext,
|
|
200
152
|
data: mdManagers,
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import fs from 'fs/promises'
|
|
10
10
|
import path from 'path'
|
|
11
|
+
import { TemplateError } from '../errors'
|
|
11
12
|
|
|
12
13
|
interface Frontmatter {
|
|
13
14
|
name?: string
|
|
@@ -24,14 +25,42 @@ interface ParsedTemplate {
|
|
|
24
25
|
/**
|
|
25
26
|
* Loads command templates from templates/commands/ with caching.
|
|
26
27
|
* Parses YAML-like frontmatter for metadata extraction.
|
|
28
|
+
* Uses LRU cache with size limit to prevent memory leaks.
|
|
27
29
|
*/
|
|
28
30
|
class TemplateLoader {
|
|
29
31
|
templatesDir: string
|
|
30
32
|
cache: Map<string, ParsedTemplate>
|
|
33
|
+
cacheOrder: string[] // Track access order for LRU eviction
|
|
34
|
+
maxCacheSize: number
|
|
31
35
|
|
|
32
36
|
constructor() {
|
|
33
37
|
this.templatesDir = path.join(__dirname, '..', '..', 'templates', 'commands')
|
|
34
38
|
this.cache = new Map()
|
|
39
|
+
this.cacheOrder = []
|
|
40
|
+
this.maxCacheSize = 50 // More than enough for all commands
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Update LRU order - move key to end (most recently used)
|
|
45
|
+
*/
|
|
46
|
+
private updateLruOrder(key: string): void {
|
|
47
|
+
const index = this.cacheOrder.indexOf(key)
|
|
48
|
+
if (index > -1) {
|
|
49
|
+
this.cacheOrder.splice(index, 1)
|
|
50
|
+
}
|
|
51
|
+
this.cacheOrder.push(key)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Evict least recently used entry if cache exceeds max size
|
|
56
|
+
*/
|
|
57
|
+
private evictLru(): void {
|
|
58
|
+
while (this.cache.size >= this.maxCacheSize && this.cacheOrder.length > 0) {
|
|
59
|
+
const oldest = this.cacheOrder.shift()
|
|
60
|
+
if (oldest) {
|
|
61
|
+
this.cache.delete(oldest)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
35
64
|
}
|
|
36
65
|
|
|
37
66
|
/**
|
|
@@ -40,6 +69,7 @@ class TemplateLoader {
|
|
|
40
69
|
async load(commandName: string): Promise<ParsedTemplate> {
|
|
41
70
|
// Check cache first
|
|
42
71
|
if (this.cache.has(commandName)) {
|
|
72
|
+
this.updateLruOrder(commandName)
|
|
43
73
|
return this.cache.get(commandName)!
|
|
44
74
|
}
|
|
45
75
|
|
|
@@ -49,12 +79,16 @@ class TemplateLoader {
|
|
|
49
79
|
const rawContent = await fs.readFile(templatePath, 'utf-8')
|
|
50
80
|
const parsed = this.parseFrontmatter(rawContent)
|
|
51
81
|
|
|
82
|
+
// Evict LRU if needed before adding
|
|
83
|
+
this.evictLru()
|
|
84
|
+
|
|
52
85
|
// Cache result
|
|
53
86
|
this.cache.set(commandName, parsed)
|
|
87
|
+
this.cacheOrder.push(commandName)
|
|
54
88
|
|
|
55
89
|
return parsed
|
|
56
90
|
} catch {
|
|
57
|
-
throw
|
|
91
|
+
throw TemplateError.notFound(commandName)
|
|
58
92
|
}
|
|
59
93
|
}
|
|
60
94
|
|