prjct-cli 0.45.0 → 0.45.3
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 +75 -0
- package/bin/prjct.ts +117 -10
- package/core/__tests__/agentic/memory-system.test.ts +39 -26
- package/core/__tests__/agentic/plan-mode.test.ts +64 -46
- package/core/__tests__/agentic/prompt-builder.test.ts +14 -14
- package/core/__tests__/services/project-index.test.ts +353 -0
- package/core/__tests__/types/fs.test.ts +3 -3
- package/core/__tests__/utils/date-helper.test.ts +10 -10
- package/core/__tests__/utils/output.test.ts +9 -6
- package/core/__tests__/utils/project-commands.test.ts +5 -6
- package/core/agentic/agent-router.ts +9 -10
- package/core/agentic/chain-of-thought.ts +16 -4
- package/core/agentic/command-executor.ts +66 -40
- package/core/agentic/context-builder.ts +8 -5
- package/core/agentic/ground-truth.ts +15 -9
- package/core/agentic/index.ts +145 -152
- package/core/agentic/loop-detector.ts +40 -11
- package/core/agentic/memory-system.ts +98 -35
- package/core/agentic/orchestrator-executor.ts +135 -71
- package/core/agentic/plan-mode.ts +46 -16
- package/core/agentic/prompt-builder.ts +108 -42
- package/core/agentic/services.ts +10 -9
- package/core/agentic/skill-loader.ts +9 -15
- package/core/agentic/smart-context.ts +129 -79
- package/core/agentic/template-executor.ts +13 -12
- package/core/agentic/template-loader.ts +7 -4
- package/core/agentic/tool-registry.ts +16 -13
- package/core/agents/index.ts +1 -1
- package/core/agents/performance.ts +10 -27
- package/core/ai-tools/formatters.ts +8 -6
- package/core/ai-tools/generator.ts +4 -4
- package/core/ai-tools/index.ts +1 -1
- package/core/ai-tools/registry.ts +21 -11
- package/core/bus/bus.ts +23 -16
- package/core/bus/index.ts +2 -2
- package/core/cli/linear.ts +3 -5
- package/core/cli/start.ts +28 -25
- package/core/commands/analysis.ts +58 -39
- package/core/commands/analytics.ts +52 -44
- package/core/commands/base.ts +15 -13
- package/core/commands/cleanup.ts +6 -13
- package/core/commands/command-data.ts +28 -4
- package/core/commands/commands.ts +57 -24
- package/core/commands/context.ts +4 -4
- package/core/commands/design.ts +3 -10
- package/core/commands/index.ts +5 -8
- package/core/commands/maintenance.ts +7 -4
- package/core/commands/planning.ts +179 -56
- package/core/commands/register.ts +13 -9
- package/core/commands/registry.ts +15 -14
- package/core/commands/setup.ts +26 -14
- package/core/commands/shipping.ts +11 -16
- package/core/commands/snapshots.ts +16 -32
- package/core/commands/uninstall.ts +541 -0
- package/core/commands/workflow.ts +24 -28
- package/core/constants/index.ts +10 -22
- package/core/context/generator.ts +82 -33
- package/core/context-tools/files-tool.ts +18 -19
- package/core/context-tools/imports-tool.ts +13 -33
- package/core/context-tools/index.ts +29 -54
- package/core/context-tools/recent-tool.ts +16 -22
- package/core/context-tools/signatures-tool.ts +17 -26
- package/core/context-tools/summary-tool.ts +20 -22
- package/core/context-tools/token-counter.ts +25 -20
- package/core/context-tools/types.ts +5 -5
- package/core/domain/agent-generator.ts +7 -5
- package/core/domain/agent-loader.ts +2 -2
- package/core/domain/analyzer.ts +19 -16
- package/core/domain/architecture-generator.ts +6 -3
- package/core/domain/context-estimator.ts +3 -4
- package/core/domain/snapshot-manager.ts +25 -22
- package/core/domain/task-stack.ts +24 -14
- package/core/errors.ts +1 -1
- package/core/events/events.ts +2 -4
- package/core/events/index.ts +1 -2
- package/core/index.ts +28 -16
- package/core/infrastructure/agent-detector.ts +3 -3
- package/core/infrastructure/ai-provider.ts +23 -20
- package/core/infrastructure/author-detector.ts +16 -10
- package/core/infrastructure/capability-installer.ts +2 -2
- package/core/infrastructure/claude-agent.ts +6 -6
- package/core/infrastructure/command-installer.ts +22 -17
- package/core/infrastructure/config-manager.ts +18 -14
- package/core/infrastructure/editors-config.ts +8 -4
- package/core/infrastructure/path-manager.ts +8 -6
- package/core/infrastructure/permission-manager.ts +20 -17
- package/core/infrastructure/setup.ts +42 -38
- package/core/infrastructure/update-checker.ts +5 -5
- package/core/integrations/issue-tracker/enricher.ts +8 -19
- package/core/integrations/issue-tracker/index.ts +2 -2
- package/core/integrations/issue-tracker/manager.ts +15 -15
- package/core/integrations/issue-tracker/types.ts +5 -22
- package/core/integrations/jira/client.ts +67 -59
- package/core/integrations/jira/index.ts +11 -14
- package/core/integrations/jira/mcp-adapter.ts +5 -10
- package/core/integrations/jira/service.ts +10 -10
- package/core/integrations/linear/client.ts +27 -18
- package/core/integrations/linear/index.ts +9 -12
- package/core/integrations/linear/service.ts +11 -11
- package/core/integrations/linear/sync.ts +8 -8
- package/core/outcomes/analyzer.ts +5 -18
- package/core/outcomes/index.ts +2 -2
- package/core/outcomes/recorder.ts +3 -3
- package/core/plugin/builtin/webhook.ts +19 -15
- package/core/plugin/hooks.ts +29 -21
- package/core/plugin/index.ts +7 -7
- package/core/plugin/loader.ts +19 -19
- package/core/plugin/registry.ts +12 -23
- package/core/schemas/agents.ts +1 -1
- package/core/schemas/analysis.ts +1 -1
- package/core/schemas/enriched-task.ts +62 -49
- package/core/schemas/ideas.ts +13 -13
- package/core/schemas/index.ts +17 -27
- package/core/schemas/issues.ts +40 -25
- package/core/schemas/metrics.ts +25 -25
- package/core/schemas/outcomes.ts +70 -62
- package/core/schemas/permissions.ts +15 -12
- package/core/schemas/prd.ts +27 -14
- package/core/schemas/project.ts +3 -3
- package/core/schemas/roadmap.ts +47 -34
- package/core/schemas/schemas.ts +3 -4
- package/core/schemas/shipped.ts +3 -3
- package/core/schemas/state.ts +43 -29
- package/core/server/index.ts +5 -6
- package/core/server/routes-extended.ts +68 -72
- package/core/server/routes.ts +3 -3
- package/core/server/server.ts +31 -26
- package/core/services/agent-generator.ts +237 -0
- package/core/services/agent-service.ts +2 -2
- package/core/services/breakdown-service.ts +2 -4
- package/core/services/context-generator.ts +299 -0
- package/core/services/context-selector.ts +420 -0
- package/core/services/doctor-service.ts +426 -0
- package/core/services/file-categorizer.ts +448 -0
- package/core/services/file-scorer.ts +270 -0
- package/core/services/git-analyzer.ts +267 -0
- package/core/services/index.ts +27 -10
- package/core/services/memory-service.ts +3 -4
- package/core/services/project-index.ts +911 -0
- package/core/services/project-service.ts +4 -4
- package/core/services/skill-installer.ts +14 -17
- package/core/services/skill-lock.ts +3 -3
- package/core/services/skill-service.ts +12 -6
- package/core/services/stack-detector.ts +245 -0
- package/core/services/sync-service.ts +87 -345
- package/core/services/watch-service.ts +294 -0
- package/core/session/compaction.ts +23 -31
- package/core/session/index.ts +11 -5
- package/core/session/log-migration.ts +3 -3
- package/core/session/metrics.ts +19 -14
- package/core/session/session-log-manager.ts +12 -17
- package/core/session/task-session-manager.ts +25 -25
- package/core/session/utils.ts +1 -1
- package/core/storage/ideas-storage.ts +41 -57
- package/core/storage/index-storage.ts +514 -0
- package/core/storage/index.ts +41 -17
- package/core/storage/metrics-storage.ts +39 -34
- package/core/storage/queue-storage.ts +35 -45
- package/core/storage/shipped-storage.ts +17 -20
- package/core/storage/state-storage.ts +50 -30
- package/core/storage/storage-manager.ts +6 -6
- package/core/storage/storage.ts +18 -15
- package/core/sync/auth-config.ts +3 -3
- package/core/sync/index.ts +13 -19
- package/core/sync/oauth-handler.ts +3 -3
- package/core/sync/sync-client.ts +4 -9
- package/core/sync/sync-manager.ts +12 -14
- package/core/types/commands.ts +42 -7
- package/core/types/index.ts +284 -305
- package/core/types/integrations.ts +3 -3
- package/core/types/storage.ts +14 -14
- package/core/types/utils.ts +3 -3
- package/core/utils/agent-stream.ts +3 -1
- package/core/utils/animations.ts +14 -11
- package/core/utils/branding.ts +7 -7
- package/core/utils/cache.ts +1 -3
- package/core/utils/collection-filters.ts +3 -15
- package/core/utils/date-helper.ts +2 -7
- package/core/utils/file-helper.ts +13 -8
- package/core/utils/jsonl-helper.ts +13 -10
- package/core/utils/keychain.ts +4 -8
- package/core/utils/logger.ts +1 -1
- package/core/utils/next-steps.ts +3 -3
- package/core/utils/output.ts +58 -11
- package/core/utils/project-commands.ts +6 -6
- package/core/utils/project-credentials.ts +5 -12
- package/core/utils/runtime.ts +2 -2
- package/core/utils/session-helper.ts +3 -4
- package/core/utils/version.ts +3 -3
- package/core/wizard/index.ts +13 -0
- package/core/wizard/onboarding.ts +633 -0
- package/core/workflow/state-machine.ts +7 -7
- package/dist/bin/prjct.mjs +18755 -15574
- package/dist/core/infrastructure/command-installer.js +86 -79
- package/dist/core/infrastructure/editors-config.js +6 -6
- package/dist/core/infrastructure/setup.js +246 -225
- package/dist/core/utils/version.js +9 -9
- package/package.json +11 -12
- package/scripts/build.js +3 -3
- package/scripts/postinstall.js +2 -2
- package/templates/mcp-config.json +6 -1
- package/templates/permissions/permissive.jsonc +1 -1
- package/templates/permissions/strict.jsonc +5 -9
- package/templates/global/docs/agents.md +0 -88
- package/templates/global/docs/architecture.md +0 -103
- package/templates/global/docs/commands.md +0 -96
- package/templates/global/docs/validation.md +0 -95
|
@@ -11,43 +11,42 @@
|
|
|
11
11
|
* @version 3.3
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import fs from 'fs/promises'
|
|
15
|
-
import path from 'path'
|
|
14
|
+
import fs from 'node:fs/promises'
|
|
15
|
+
import path from 'node:path'
|
|
16
16
|
import pathManager from '../infrastructure/path-manager'
|
|
17
|
-
import { isNotFoundError } from '../types/fs'
|
|
18
17
|
import { generateUUID } from '../schemas'
|
|
18
|
+
import { isNotFoundError } from '../types/fs'
|
|
19
19
|
import { getTimestamp, getTodayKey } from '../utils/date-helper'
|
|
20
|
-
import { appendJsonLine, getLastJsonLines } from '../utils/jsonl-helper'
|
|
21
20
|
import { ensureDir } from '../utils/file-helper'
|
|
21
|
+
import { appendJsonLine, getLastJsonLines } from '../utils/jsonl-helper'
|
|
22
22
|
|
|
23
23
|
// Re-export types from canonical location
|
|
24
24
|
export type {
|
|
25
|
-
|
|
26
|
-
MemoryTag,
|
|
27
|
-
MemoryDatabase,
|
|
25
|
+
Decision,
|
|
28
26
|
HistoryEntry,
|
|
29
27
|
HistoryEventType,
|
|
30
|
-
|
|
31
|
-
Workflow,
|
|
32
|
-
Preference,
|
|
33
|
-
Patterns,
|
|
28
|
+
Memory,
|
|
34
29
|
MemoryContext,
|
|
35
30
|
MemoryContextParams,
|
|
31
|
+
MemoryDatabase,
|
|
32
|
+
MemoryTag,
|
|
33
|
+
Patterns,
|
|
34
|
+
Preference,
|
|
35
|
+
Workflow,
|
|
36
36
|
} from '../types/memory'
|
|
37
37
|
|
|
38
38
|
export { MEMORY_TAGS } from '../types/memory'
|
|
39
39
|
|
|
40
40
|
import type {
|
|
41
|
-
Memory,
|
|
42
|
-
MemoryTag,
|
|
43
|
-
MemoryDatabase,
|
|
44
41
|
HistoryEntry,
|
|
45
42
|
HistoryEventType,
|
|
46
|
-
|
|
47
|
-
Workflow,
|
|
48
|
-
Preference,
|
|
49
|
-
Patterns,
|
|
43
|
+
Memory,
|
|
50
44
|
MemoryContext,
|
|
45
|
+
MemoryDatabase,
|
|
46
|
+
MemoryTag,
|
|
47
|
+
Patterns,
|
|
48
|
+
Preference,
|
|
49
|
+
Workflow,
|
|
51
50
|
} from '../types/memory'
|
|
52
51
|
|
|
53
52
|
import { MEMORY_TAGS } from '../types/memory'
|
|
@@ -240,10 +239,19 @@ export class HistoryStore {
|
|
|
240
239
|
const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
|
|
241
240
|
const day = getTodayKey()
|
|
242
241
|
|
|
243
|
-
return path.join(
|
|
242
|
+
return path.join(
|
|
243
|
+
pathManager.getGlobalProjectPath(projectId),
|
|
244
|
+
'memory',
|
|
245
|
+
'sessions',
|
|
246
|
+
yearMonth,
|
|
247
|
+
`${day}.jsonl`
|
|
248
|
+
)
|
|
244
249
|
}
|
|
245
250
|
|
|
246
|
-
async appendHistory(
|
|
251
|
+
async appendHistory(
|
|
252
|
+
projectId: string,
|
|
253
|
+
entry: Record<string, unknown> & { type: HistoryEventType }
|
|
254
|
+
): Promise<void> {
|
|
247
255
|
const sessionPath = this._getSessionPath(projectId)
|
|
248
256
|
await ensureDir(path.dirname(sessionPath))
|
|
249
257
|
|
|
@@ -294,7 +302,12 @@ export class PatternStore extends CachedStore<Patterns> {
|
|
|
294
302
|
return this.save(projectId)
|
|
295
303
|
}
|
|
296
304
|
|
|
297
|
-
async recordDecision(
|
|
305
|
+
async recordDecision(
|
|
306
|
+
projectId: string,
|
|
307
|
+
key: string,
|
|
308
|
+
value: string,
|
|
309
|
+
context: string = ''
|
|
310
|
+
): Promise<void> {
|
|
298
311
|
const patterns = await this.load(projectId)
|
|
299
312
|
const now = getTimestamp()
|
|
300
313
|
|
|
@@ -333,7 +346,10 @@ export class PatternStore extends CachedStore<Patterns> {
|
|
|
333
346
|
await this.save(projectId)
|
|
334
347
|
}
|
|
335
348
|
|
|
336
|
-
async getDecision(
|
|
349
|
+
async getDecision(
|
|
350
|
+
projectId: string,
|
|
351
|
+
key: string
|
|
352
|
+
): Promise<{ value: string; confidence: string } | null> {
|
|
337
353
|
const patterns = await this.load(projectId)
|
|
338
354
|
const decision = patterns.decisions[key]
|
|
339
355
|
|
|
@@ -348,7 +364,11 @@ export class PatternStore extends CachedStore<Patterns> {
|
|
|
348
364
|
return decision !== null
|
|
349
365
|
}
|
|
350
366
|
|
|
351
|
-
async recordWorkflow(
|
|
367
|
+
async recordWorkflow(
|
|
368
|
+
projectId: string,
|
|
369
|
+
workflowName: string,
|
|
370
|
+
pattern: Record<string, unknown>
|
|
371
|
+
): Promise<void> {
|
|
352
372
|
const patterns = await this.load(projectId)
|
|
353
373
|
const now = getTimestamp()
|
|
354
374
|
|
|
@@ -381,7 +401,11 @@ export class PatternStore extends CachedStore<Patterns> {
|
|
|
381
401
|
await this.save(projectId)
|
|
382
402
|
}
|
|
383
403
|
|
|
384
|
-
async getPreference(
|
|
404
|
+
async getPreference(
|
|
405
|
+
projectId: string,
|
|
406
|
+
key: string,
|
|
407
|
+
defaultValue: unknown = null
|
|
408
|
+
): Promise<unknown> {
|
|
385
409
|
const patterns = await this.load(projectId)
|
|
386
410
|
return patterns.preferences[key]?.value ?? defaultValue
|
|
387
411
|
}
|
|
@@ -391,7 +415,8 @@ export class PatternStore extends CachedStore<Patterns> {
|
|
|
391
415
|
|
|
392
416
|
return {
|
|
393
417
|
decisions: Object.keys(patterns.decisions).length,
|
|
394
|
-
learnedDecisions: Object.values(patterns.decisions).filter((d) => d.confidence !== 'low')
|
|
418
|
+
learnedDecisions: Object.values(patterns.decisions).filter((d) => d.confidence !== 'low')
|
|
419
|
+
.length,
|
|
395
420
|
workflows: Object.keys(patterns.workflows).length,
|
|
396
421
|
preferences: Object.keys(patterns.preferences).length,
|
|
397
422
|
}
|
|
@@ -535,7 +560,11 @@ export class SemanticMemories extends CachedStore<MemoryDatabase> {
|
|
|
535
560
|
return true
|
|
536
561
|
}
|
|
537
562
|
|
|
538
|
-
async findByTags(
|
|
563
|
+
async findByTags(
|
|
564
|
+
projectId: string,
|
|
565
|
+
tags: string[],
|
|
566
|
+
matchAll: boolean = false
|
|
567
|
+
): Promise<Memory[]> {
|
|
539
568
|
const db = await this.load(projectId)
|
|
540
569
|
const parsedTags = this._coerceTags(tags)
|
|
541
570
|
|
|
@@ -556,11 +585,16 @@ export class SemanticMemories extends CachedStore<MemoryDatabase> {
|
|
|
556
585
|
const queryLower = query.toLowerCase()
|
|
557
586
|
|
|
558
587
|
return db.memories.filter(
|
|
559
|
-
(m) =>
|
|
588
|
+
(m) =>
|
|
589
|
+
m.title.toLowerCase().includes(queryLower) || m.content.toLowerCase().includes(queryLower)
|
|
560
590
|
)
|
|
561
591
|
}
|
|
562
592
|
|
|
563
|
-
async getRelevantMemories(
|
|
593
|
+
async getRelevantMemories(
|
|
594
|
+
projectId: string,
|
|
595
|
+
context: MemoryContext,
|
|
596
|
+
limit: number = 5
|
|
597
|
+
): Promise<Memory[]> {
|
|
564
598
|
const db = await this.load(projectId)
|
|
565
599
|
|
|
566
600
|
const scored = db.memories.map((memory) => {
|
|
@@ -625,7 +659,12 @@ export class SemanticMemories extends CachedStore<MemoryDatabase> {
|
|
|
625
659
|
return keywords.filter((k) => k.length > 2 && !stopWords.includes(k))
|
|
626
660
|
}
|
|
627
661
|
|
|
628
|
-
async autoRemember(
|
|
662
|
+
async autoRemember(
|
|
663
|
+
projectId: string,
|
|
664
|
+
decisionType: string,
|
|
665
|
+
value: string,
|
|
666
|
+
context: string = ''
|
|
667
|
+
): Promise<void> {
|
|
629
668
|
const tagMap: Record<string, string[]> = {
|
|
630
669
|
commit_footer: [MEMORY_TAGS.COMMIT_STYLE],
|
|
631
670
|
branch_naming: [MEMORY_TAGS.BRANCH_NAMING],
|
|
@@ -736,11 +775,20 @@ export class MemorySystem {
|
|
|
736
775
|
return this._semanticMemories.searchMemories(projectId, query)
|
|
737
776
|
}
|
|
738
777
|
|
|
739
|
-
getRelevantMemories(
|
|
778
|
+
getRelevantMemories(
|
|
779
|
+
projectId: string,
|
|
780
|
+
context: MemoryContext,
|
|
781
|
+
limit?: number
|
|
782
|
+
): Promise<Memory[]> {
|
|
740
783
|
return this._semanticMemories.getRelevantMemories(projectId, context, limit)
|
|
741
784
|
}
|
|
742
785
|
|
|
743
|
-
autoRemember(
|
|
786
|
+
autoRemember(
|
|
787
|
+
projectId: string,
|
|
788
|
+
decisionType: string,
|
|
789
|
+
value: string,
|
|
790
|
+
context?: string
|
|
791
|
+
): Promise<void> {
|
|
744
792
|
return this._semanticMemories.autoRemember(projectId, decisionType, value, context)
|
|
745
793
|
}
|
|
746
794
|
|
|
@@ -784,7 +832,10 @@ export class MemorySystem {
|
|
|
784
832
|
return this._patternStore.recordDecision(projectId, key, value, context)
|
|
785
833
|
}
|
|
786
834
|
|
|
787
|
-
getDecision(
|
|
835
|
+
getDecision(
|
|
836
|
+
projectId: string,
|
|
837
|
+
key: string
|
|
838
|
+
): Promise<{ value: string; confidence: string } | null> {
|
|
788
839
|
return this._patternStore.getDecision(projectId, key)
|
|
789
840
|
}
|
|
790
841
|
|
|
@@ -792,7 +843,11 @@ export class MemorySystem {
|
|
|
792
843
|
return this._patternStore.hasPattern(projectId, key)
|
|
793
844
|
}
|
|
794
845
|
|
|
795
|
-
recordWorkflow(
|
|
846
|
+
recordWorkflow(
|
|
847
|
+
projectId: string,
|
|
848
|
+
workflowName: string,
|
|
849
|
+
pattern: Record<string, unknown>
|
|
850
|
+
): Promise<void> {
|
|
796
851
|
return this._patternStore.recordWorkflow(projectId, workflowName, pattern)
|
|
797
852
|
}
|
|
798
853
|
|
|
@@ -816,7 +871,10 @@ export class MemorySystem {
|
|
|
816
871
|
// TIER 3: History
|
|
817
872
|
// ===========================================================================
|
|
818
873
|
|
|
819
|
-
appendHistory(
|
|
874
|
+
appendHistory(
|
|
875
|
+
projectId: string,
|
|
876
|
+
entry: Record<string, unknown> & { type: HistoryEventType }
|
|
877
|
+
): Promise<void> {
|
|
820
878
|
return this._historyStore.appendHistory(projectId, entry)
|
|
821
879
|
}
|
|
822
880
|
|
|
@@ -838,7 +896,12 @@ export class MemorySystem {
|
|
|
838
896
|
return null
|
|
839
897
|
}
|
|
840
898
|
|
|
841
|
-
async learnDecision(
|
|
899
|
+
async learnDecision(
|
|
900
|
+
projectId: string,
|
|
901
|
+
key: string,
|
|
902
|
+
value: string,
|
|
903
|
+
context: string = ''
|
|
904
|
+
): Promise<void> {
|
|
842
905
|
this.setSession(`decision:${key}`, value)
|
|
843
906
|
await this.recordDecision(projectId, key, value, context)
|
|
844
907
|
await this.appendHistory(projectId, { type: 'decision', key, value, context })
|
|
@@ -13,20 +13,15 @@
|
|
|
13
13
|
* @version 1.0.0
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import fs from 'fs/promises'
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import pathManager from '../infrastructure/path-manager'
|
|
16
|
+
import fs from 'node:fs/promises'
|
|
17
|
+
import os from 'node:os'
|
|
18
|
+
import path from 'node:path'
|
|
20
19
|
import configManager from '../infrastructure/config-manager'
|
|
20
|
+
import pathManager from '../infrastructure/path-manager'
|
|
21
21
|
import { stateStorage } from '../storage'
|
|
22
|
+
import type { LoadedAgent, LoadedSkill, OrchestratorContext, OrchestratorSubtask } from '../types'
|
|
22
23
|
import { isNotFoundError } from '../types/fs'
|
|
23
24
|
import { parseFrontmatter } from './template-loader'
|
|
24
|
-
import type {
|
|
25
|
-
OrchestratorContext,
|
|
26
|
-
LoadedAgent,
|
|
27
|
-
LoadedSkill,
|
|
28
|
-
OrchestratorSubtask,
|
|
29
|
-
} from '../types'
|
|
30
25
|
|
|
31
26
|
// =============================================================================
|
|
32
27
|
// Domain Detection Keywords
|
|
@@ -38,49 +33,139 @@ import type {
|
|
|
38
33
|
*/
|
|
39
34
|
const DOMAIN_KEYWORDS: Record<string, string[]> = {
|
|
40
35
|
database: [
|
|
41
|
-
'database',
|
|
42
|
-
'
|
|
43
|
-
'
|
|
36
|
+
'database',
|
|
37
|
+
'db',
|
|
38
|
+
'sql',
|
|
39
|
+
'query',
|
|
40
|
+
'table',
|
|
41
|
+
'schema',
|
|
42
|
+
'migration',
|
|
43
|
+
'postgres',
|
|
44
|
+
'mysql',
|
|
45
|
+
'sqlite',
|
|
46
|
+
'mongo',
|
|
47
|
+
'redis',
|
|
48
|
+
'prisma',
|
|
49
|
+
'drizzle',
|
|
50
|
+
'orm',
|
|
51
|
+
'model',
|
|
52
|
+
'entity',
|
|
53
|
+
'repository',
|
|
54
|
+
'data layer',
|
|
55
|
+
'persist',
|
|
44
56
|
],
|
|
45
57
|
backend: [
|
|
46
|
-
'api',
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
58
|
+
'api',
|
|
59
|
+
'endpoint',
|
|
60
|
+
'route',
|
|
61
|
+
'server',
|
|
62
|
+
'controller',
|
|
63
|
+
'service',
|
|
64
|
+
'middleware',
|
|
65
|
+
'auth',
|
|
66
|
+
'authentication',
|
|
67
|
+
'authorization',
|
|
68
|
+
'jwt',
|
|
69
|
+
'oauth',
|
|
70
|
+
'rest',
|
|
71
|
+
'graphql',
|
|
72
|
+
'trpc',
|
|
73
|
+
'express',
|
|
74
|
+
'fastify',
|
|
75
|
+
'hono',
|
|
76
|
+
'nest',
|
|
77
|
+
'validation',
|
|
78
|
+
'business logic',
|
|
50
79
|
],
|
|
51
80
|
frontend: [
|
|
52
|
-
'ui',
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
81
|
+
'ui',
|
|
82
|
+
'component',
|
|
83
|
+
'page',
|
|
84
|
+
'form',
|
|
85
|
+
'button',
|
|
86
|
+
'input',
|
|
87
|
+
'modal',
|
|
88
|
+
'dialog',
|
|
89
|
+
'react',
|
|
90
|
+
'vue',
|
|
91
|
+
'svelte',
|
|
92
|
+
'angular',
|
|
93
|
+
'next',
|
|
94
|
+
'nuxt',
|
|
95
|
+
'solid',
|
|
96
|
+
'css',
|
|
97
|
+
'style',
|
|
98
|
+
'tailwind',
|
|
99
|
+
'layout',
|
|
100
|
+
'responsive',
|
|
101
|
+
'animation',
|
|
102
|
+
'hook',
|
|
103
|
+
'state',
|
|
104
|
+
'context',
|
|
105
|
+
'redux',
|
|
106
|
+
'zustand',
|
|
107
|
+
'jotai',
|
|
56
108
|
],
|
|
57
109
|
testing: [
|
|
58
|
-
'test',
|
|
59
|
-
'
|
|
60
|
-
'
|
|
110
|
+
'test',
|
|
111
|
+
'spec',
|
|
112
|
+
'unit',
|
|
113
|
+
'integration',
|
|
114
|
+
'e2e',
|
|
115
|
+
'jest',
|
|
116
|
+
'vitest',
|
|
117
|
+
'playwright',
|
|
118
|
+
'cypress',
|
|
119
|
+
'mocha',
|
|
120
|
+
'chai',
|
|
121
|
+
'mock',
|
|
122
|
+
'stub',
|
|
123
|
+
'fixture',
|
|
124
|
+
'coverage',
|
|
125
|
+
'assertion',
|
|
61
126
|
],
|
|
62
127
|
devops: [
|
|
63
|
-
'docker',
|
|
64
|
-
'
|
|
65
|
-
'
|
|
128
|
+
'docker',
|
|
129
|
+
'kubernetes',
|
|
130
|
+
'k8s',
|
|
131
|
+
'ci',
|
|
132
|
+
'cd',
|
|
133
|
+
'pipeline',
|
|
134
|
+
'deploy',
|
|
135
|
+
'github actions',
|
|
136
|
+
'vercel',
|
|
137
|
+
'aws',
|
|
138
|
+
'gcp',
|
|
139
|
+
'azure',
|
|
140
|
+
'terraform',
|
|
141
|
+
'nginx',
|
|
142
|
+
'caddy',
|
|
143
|
+
'env',
|
|
144
|
+
'environment',
|
|
145
|
+
'config',
|
|
146
|
+
'secret',
|
|
66
147
|
],
|
|
67
148
|
uxui: [
|
|
68
|
-
'design',
|
|
69
|
-
'
|
|
70
|
-
'
|
|
149
|
+
'design',
|
|
150
|
+
'ux',
|
|
151
|
+
'user experience',
|
|
152
|
+
'accessibility',
|
|
153
|
+
'a11y',
|
|
154
|
+
'color',
|
|
155
|
+
'typography',
|
|
156
|
+
'spacing',
|
|
157
|
+
'prototype',
|
|
158
|
+
'wireframe',
|
|
159
|
+
'figma',
|
|
160
|
+
'user flow',
|
|
161
|
+
'interaction',
|
|
71
162
|
],
|
|
72
163
|
}
|
|
73
164
|
|
|
74
165
|
/**
|
|
75
166
|
* Domain dependency order - earlier domains should complete first
|
|
76
167
|
*/
|
|
77
|
-
const DOMAIN_DEPENDENCY_ORDER = [
|
|
78
|
-
'database',
|
|
79
|
-
'backend',
|
|
80
|
-
'frontend',
|
|
81
|
-
'testing',
|
|
82
|
-
'devops',
|
|
83
|
-
]
|
|
168
|
+
const DOMAIN_DEPENDENCY_ORDER = ['database', 'backend', 'frontend', 'testing', 'devops']
|
|
84
169
|
|
|
85
170
|
// =============================================================================
|
|
86
171
|
// Orchestrator Executor Class
|
|
@@ -108,11 +193,7 @@ export class OrchestratorExecutor {
|
|
|
108
193
|
const repoAnalysis = await this.loadRepoAnalysis(globalPath)
|
|
109
194
|
|
|
110
195
|
// Step 3: Detect domains from task + project context
|
|
111
|
-
const { domains, primary } = await this.detectDomains(
|
|
112
|
-
taskDescription,
|
|
113
|
-
projectId,
|
|
114
|
-
repoAnalysis
|
|
115
|
-
)
|
|
196
|
+
const { domains, primary } = await this.detectDomains(taskDescription, projectId, repoAnalysis)
|
|
116
197
|
|
|
117
198
|
// Step 4: Load agents for detected domains
|
|
118
199
|
const agents = await this.loadAgents(domains, projectId)
|
|
@@ -126,12 +207,7 @@ export class OrchestratorExecutor {
|
|
|
126
207
|
// Step 7: Create subtasks if fragmentation is required
|
|
127
208
|
let subtasks: OrchestratorSubtask[] | null = null
|
|
128
209
|
if (requiresFragmentation && command === 'task') {
|
|
129
|
-
subtasks = await this.createSubtasks(
|
|
130
|
-
taskDescription,
|
|
131
|
-
domains,
|
|
132
|
-
agents,
|
|
133
|
-
projectId
|
|
134
|
-
)
|
|
210
|
+
subtasks = await this.createSubtasks(taskDescription, domains, agents, projectId)
|
|
135
211
|
}
|
|
136
212
|
|
|
137
213
|
return {
|
|
@@ -229,10 +305,8 @@ export class OrchestratorExecutor {
|
|
|
229
305
|
.filter(([domain]) => {
|
|
230
306
|
// Check if agent exists for this domain
|
|
231
307
|
return availableAgents.some(
|
|
232
|
-
agent =>
|
|
233
|
-
agent === domain ||
|
|
234
|
-
agent.includes(domain) ||
|
|
235
|
-
domain.includes(agent.replace('.md', ''))
|
|
308
|
+
(agent) =>
|
|
309
|
+
agent === domain || agent.includes(domain) || domain.includes(agent.replace('.md', ''))
|
|
236
310
|
)
|
|
237
311
|
})
|
|
238
312
|
.sort((a, b) => b[1] - a[1]) // Sort by score descending
|
|
@@ -256,7 +330,7 @@ export class OrchestratorExecutor {
|
|
|
256
330
|
try {
|
|
257
331
|
const agentsDir = path.join(globalPath, 'agents')
|
|
258
332
|
const files = await fs.readdir(agentsDir)
|
|
259
|
-
return files.filter(f => f.endsWith('.md')).map(f => f.replace('.md', ''))
|
|
333
|
+
return files.filter((f) => f.endsWith('.md')).map((f) => f.replace('.md', ''))
|
|
260
334
|
} catch {
|
|
261
335
|
return []
|
|
262
336
|
}
|
|
@@ -275,11 +349,7 @@ export class OrchestratorExecutor {
|
|
|
275
349
|
|
|
276
350
|
for (const domain of domains) {
|
|
277
351
|
// Try exact match first, then variations
|
|
278
|
-
const possibleNames = [
|
|
279
|
-
`${domain}.md`,
|
|
280
|
-
`${domain}-agent.md`,
|
|
281
|
-
`prjct-${domain}.md`,
|
|
282
|
-
]
|
|
352
|
+
const possibleNames = [`${domain}.md`, `${domain}-agent.md`, `prjct-${domain}.md`]
|
|
283
353
|
|
|
284
354
|
for (const fileName of possibleNames) {
|
|
285
355
|
const filePath = path.join(agentsDir, fileName)
|
|
@@ -297,10 +367,7 @@ export class OrchestratorExecutor {
|
|
|
297
367
|
|
|
298
368
|
// Found one, no need to try other variations
|
|
299
369
|
break
|
|
300
|
-
} catch {
|
|
301
|
-
// Try next variation
|
|
302
|
-
continue
|
|
303
|
-
}
|
|
370
|
+
} catch {}
|
|
304
371
|
}
|
|
305
372
|
}
|
|
306
373
|
|
|
@@ -320,12 +387,12 @@ export class OrchestratorExecutor {
|
|
|
320
387
|
const frontmatter = { ...parsed.frontmatter }
|
|
321
388
|
if (typeof frontmatter.skills === 'string') {
|
|
322
389
|
// Handle comma-separated string
|
|
323
|
-
frontmatter.skills = (frontmatter.skills as string).split(',').map(s => s.trim())
|
|
390
|
+
frontmatter.skills = (frontmatter.skills as string).split(',').map((s) => s.trim())
|
|
324
391
|
}
|
|
325
392
|
|
|
326
393
|
return {
|
|
327
394
|
frontmatter: frontmatter as { skills?: string[]; [key: string]: unknown },
|
|
328
|
-
body: parsed.content
|
|
395
|
+
body: parsed.content,
|
|
329
396
|
}
|
|
330
397
|
}
|
|
331
398
|
|
|
@@ -438,11 +505,11 @@ export class OrchestratorExecutor {
|
|
|
438
505
|
// Create subtask for each domain
|
|
439
506
|
const subtasks: OrchestratorSubtask[] = sortedDomains.map((domain, index) => {
|
|
440
507
|
// Find the agent for this domain
|
|
441
|
-
const agent = agents.find(a => a.domain === domain)
|
|
508
|
+
const agent = agents.find((a) => a.domain === domain)
|
|
442
509
|
const agentFile = agent ? `${agent.name}.md` : `${domain}.md`
|
|
443
510
|
|
|
444
511
|
// Determine dependencies - each subtask depends on previous ones
|
|
445
|
-
const dependsOn = sortedDomains.slice(0, index).map((
|
|
512
|
+
const dependsOn = sortedDomains.slice(0, index).map((_d, i) => `subtask-${i + 1}`)
|
|
446
513
|
|
|
447
514
|
return {
|
|
448
515
|
id: `subtask-${index + 1}`,
|
|
@@ -458,7 +525,7 @@ export class OrchestratorExecutor {
|
|
|
458
525
|
// Store subtasks in state.json via state storage
|
|
459
526
|
await stateStorage.createSubtasks(
|
|
460
527
|
projectId,
|
|
461
|
-
subtasks.map(st => ({
|
|
528
|
+
subtasks.map((st) => ({
|
|
462
529
|
id: st.id,
|
|
463
530
|
description: st.description,
|
|
464
531
|
domain: st.domain,
|
|
@@ -473,10 +540,7 @@ export class OrchestratorExecutor {
|
|
|
473
540
|
/**
|
|
474
541
|
* Generate a domain-specific subtask description
|
|
475
542
|
*/
|
|
476
|
-
private generateSubtaskDescription(
|
|
477
|
-
fullTask: string,
|
|
478
|
-
domain: string
|
|
479
|
-
): string {
|
|
543
|
+
private generateSubtaskDescription(fullTask: string, domain: string): string {
|
|
480
544
|
const domainDescriptions: Record<string, string> = {
|
|
481
545
|
database: 'Set up data layer: schema, models, migrations',
|
|
482
546
|
backend: 'Implement API: routes, controllers, services, validation',
|
|
@@ -8,22 +8,34 @@
|
|
|
8
8
|
* Pattern from: Devin AI, Windsurf, Kiro
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { generateUUID } from '../schemas'
|
|
11
12
|
import type {
|
|
12
|
-
|
|
13
|
+
ApprovalContext,
|
|
14
|
+
ApprovalPrompt,
|
|
13
15
|
GatheredInfo,
|
|
14
|
-
ProposedPlan,
|
|
15
|
-
PlanStep,
|
|
16
|
-
PlanStepResult,
|
|
17
|
-
PlanStatus,
|
|
18
16
|
Plan,
|
|
19
17
|
PlanAnalysis,
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
PlanParams,
|
|
19
|
+
PlanStatus,
|
|
20
|
+
PlanStep,
|
|
21
|
+
PlanStepResult,
|
|
22
|
+
ProposedPlan,
|
|
22
23
|
} from '../types'
|
|
23
|
-
import { generateUUID } from '../schemas'
|
|
24
24
|
import { getTimestamp } from '../utils/date-helper'
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
DESTRUCTIVE_COMMANDS,
|
|
28
|
+
PLAN_REQUIRED_COMMANDS,
|
|
29
|
+
PLAN_STATUS,
|
|
30
|
+
PLANNING_TOOLS,
|
|
31
|
+
} from '../constants'
|
|
32
|
+
|
|
33
|
+
import {
|
|
34
|
+
DESTRUCTIVE_COMMANDS,
|
|
35
|
+
PLAN_REQUIRED_COMMANDS,
|
|
36
|
+
PLAN_STATUS,
|
|
37
|
+
PLANNING_TOOLS,
|
|
38
|
+
} from '../constants'
|
|
27
39
|
|
|
28
40
|
// =============================================================================
|
|
29
41
|
// Approval
|
|
@@ -32,7 +44,10 @@ import { PLAN_STATUS, PLAN_REQUIRED_COMMANDS, DESTRUCTIVE_COMMANDS, PLANNING_TOO
|
|
|
32
44
|
/**
|
|
33
45
|
* Generate approval prompt for destructive commands
|
|
34
46
|
*/
|
|
35
|
-
export function generateApprovalPrompt(
|
|
47
|
+
export function generateApprovalPrompt(
|
|
48
|
+
commandName: string,
|
|
49
|
+
context: ApprovalContext
|
|
50
|
+
): ApprovalPrompt {
|
|
36
51
|
const prompts: Record<string, ApprovalPrompt> = {
|
|
37
52
|
ship: {
|
|
38
53
|
title: 'Ship Confirmation',
|
|
@@ -51,7 +66,10 @@ export function generateApprovalPrompt(commandName: string, context: ApprovalCon
|
|
|
51
66
|
cleanup: {
|
|
52
67
|
title: 'Cleanup Confirmation',
|
|
53
68
|
message: 'This will delete files/code. Continue?',
|
|
54
|
-
details: [
|
|
69
|
+
details: [
|
|
70
|
+
`Files to delete: ${context.filesToDelete?.length || 0}`,
|
|
71
|
+
`Code to remove: ${context.linesOfCode || 0} lines`,
|
|
72
|
+
],
|
|
55
73
|
options: [
|
|
56
74
|
{ key: 'y', label: 'Yes, cleanup', action: 'approve' },
|
|
57
75
|
{ key: 'n', label: 'No, cancel', action: 'reject' },
|
|
@@ -218,7 +236,10 @@ export class PlanMode {
|
|
|
218
236
|
/**
|
|
219
237
|
* Propose a plan for user approval
|
|
220
238
|
*/
|
|
221
|
-
proposePlan(
|
|
239
|
+
proposePlan(
|
|
240
|
+
projectId: string,
|
|
241
|
+
proposedPlan: ProposedPlan
|
|
242
|
+
): ReturnType<typeof this.formatPlanForApproval> | null {
|
|
222
243
|
const plan = this.getActivePlan(projectId)
|
|
223
244
|
if (!plan) return null
|
|
224
245
|
|
|
@@ -350,7 +371,10 @@ export class PlanMode {
|
|
|
350
371
|
/**
|
|
351
372
|
* Mark current step as complete
|
|
352
373
|
*/
|
|
353
|
-
completeStep(
|
|
374
|
+
completeStep(
|
|
375
|
+
projectId: string,
|
|
376
|
+
result: PlanStepResult = { success: true }
|
|
377
|
+
): ReturnType<typeof this.getNextStep> {
|
|
354
378
|
const plan = this.getActivePlan(projectId)
|
|
355
379
|
if (!plan || plan.status !== PLAN_STATUS.EXECUTING) {
|
|
356
380
|
return null
|
|
@@ -370,7 +394,10 @@ export class PlanMode {
|
|
|
370
394
|
/**
|
|
371
395
|
* Mark step as failed
|
|
372
396
|
*/
|
|
373
|
-
failStep(
|
|
397
|
+
failStep(
|
|
398
|
+
projectId: string,
|
|
399
|
+
error: string
|
|
400
|
+
): { failed: boolean; step: number; error: string; options: string[] } | null {
|
|
374
401
|
const plan = this.getActivePlan(projectId)
|
|
375
402
|
if (!plan) return null
|
|
376
403
|
|
|
@@ -461,7 +488,10 @@ export class PlanMode {
|
|
|
461
488
|
[PLAN_STATUS.ABORTED]: '🛑',
|
|
462
489
|
}
|
|
463
490
|
|
|
464
|
-
const lines = [
|
|
491
|
+
const lines = [
|
|
492
|
+
`${statusEmoji[plan.status] || '📋'} Plan: ${plan.command}`,
|
|
493
|
+
`Status: ${plan.status}`,
|
|
494
|
+
]
|
|
465
495
|
|
|
466
496
|
if (plan.status === PLAN_STATUS.EXECUTING) {
|
|
467
497
|
const progress = Math.round((plan.currentStep / plan.steps.length) * 100)
|