prjct-cli 0.20.0 → 0.21.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.
Files changed (236) hide show
  1. package/CHANGELOG.md +24 -6
  2. package/CLAUDE.md +56 -15
  3. package/README.md +5 -6
  4. package/bin/prjct +59 -42
  5. package/bin/prjct.ts +60 -0
  6. package/core/__tests__/agentic/memory-system.test.ts +18 -3
  7. package/core/__tests__/agentic/plan-mode.test.ts +55 -26
  8. package/core/__tests__/agentic/prompt-builder.test.ts +6 -6
  9. package/core/__tests__/utils/project-commands.test.ts +72 -0
  10. package/core/agentic/agent-router.ts +3 -12
  11. package/core/agentic/command-executor.ts +372 -3
  12. package/core/agentic/context-builder.ts +7 -27
  13. package/core/agentic/ground-truth.ts +604 -5
  14. package/core/agentic/index.ts +180 -0
  15. package/core/agentic/loop-detector.ts +418 -4
  16. package/core/agentic/memory-system.ts +857 -3
  17. package/core/agentic/plan-mode.ts +491 -4
  18. package/core/agentic/prompt-builder.ts +44 -65
  19. package/core/agentic/services.ts +13 -5
  20. package/core/agentic/skill-loader.ts +112 -0
  21. package/core/agentic/smart-context.ts +37 -122
  22. package/core/agentic/template-loader.ts +79 -122
  23. package/core/agentic/tool-registry.ts +5 -11
  24. package/core/agents/index.ts +1 -1
  25. package/core/agents/performance.ts +4 -2
  26. package/core/bus/bus.ts +262 -0
  27. package/core/bus/index.ts +3 -313
  28. package/core/commands/analysis.ts +5 -5
  29. package/core/commands/analytics.ts +11 -11
  30. package/core/commands/base.ts +33 -209
  31. package/core/commands/cleanup.ts +148 -0
  32. package/core/commands/command-data.ts +346 -0
  33. package/core/commands/commands.ts +216 -0
  34. package/core/commands/design.ts +83 -0
  35. package/core/commands/index.ts +13 -207
  36. package/core/commands/maintenance.ts +52 -473
  37. package/core/commands/planning.ts +3 -3
  38. package/core/commands/register.ts +104 -0
  39. package/core/commands/registry.ts +441 -0
  40. package/core/commands/setup.ts +25 -9
  41. package/core/commands/shipping.ts +48 -11
  42. package/core/commands/snapshots.ts +299 -0
  43. package/core/commands/workflow.ts +2 -2
  44. package/core/constants/index.ts +254 -4
  45. package/core/domain/agent-loader.ts +5 -6
  46. package/core/domain/task-stack.ts +555 -4
  47. package/core/errors.ts +127 -1
  48. package/core/events/events.ts +87 -0
  49. package/core/events/index.ts +4 -138
  50. package/core/index.ts +15 -23
  51. package/core/infrastructure/agent-detector.ts +126 -201
  52. package/core/infrastructure/author-detector.ts +99 -171
  53. package/core/infrastructure/command-installer.ts +476 -4
  54. package/core/infrastructure/config-manager.ts +41 -37
  55. package/core/infrastructure/path-manager.ts +59 -9
  56. package/core/infrastructure/permission-manager.ts +286 -0
  57. package/core/integrations/notion/client.ts +323 -0
  58. package/core/integrations/notion/index.ts +43 -0
  59. package/core/integrations/notion/setup.ts +230 -0
  60. package/core/integrations/notion/sync.ts +311 -0
  61. package/core/integrations/notion/templates.ts +234 -0
  62. package/core/outcomes/analyzer.ts +7 -41
  63. package/core/outcomes/index.ts +1 -1
  64. package/core/outcomes/recorder.ts +1 -1
  65. package/core/plugin/builtin/notion.ts +178 -0
  66. package/core/{plugins → plugin/builtin}/webhook.ts +6 -22
  67. package/core/plugin/loader.ts +5 -5
  68. package/core/plugin/registry.ts +2 -2
  69. package/core/schemas/ideas.ts +85 -54
  70. package/core/schemas/index.ts +14 -33
  71. package/core/schemas/permissions.ts +177 -0
  72. package/core/schemas/project.ts +39 -12
  73. package/core/schemas/roadmap.ts +94 -59
  74. package/core/schemas/schemas.ts +39 -0
  75. package/core/schemas/shipped.ts +87 -60
  76. package/core/schemas/state.ts +110 -70
  77. package/core/server/index.ts +21 -0
  78. package/core/server/routes.ts +165 -0
  79. package/core/server/server.ts +136 -0
  80. package/core/server/sse.ts +135 -0
  81. package/core/services/agent-service.ts +170 -0
  82. package/core/services/breakdown-service.ts +126 -0
  83. package/core/services/index.ts +21 -0
  84. package/core/services/memory-service.ts +108 -0
  85. package/core/services/project-service.ts +146 -0
  86. package/core/services/skill-service.ts +253 -0
  87. package/core/session/compaction.ts +257 -0
  88. package/core/session/index.ts +20 -8
  89. package/core/{infrastructure/session-manager/migration.ts → session/log-migration.ts} +9 -9
  90. package/core/{infrastructure/session-manager/session-manager.ts → session/session-log-manager.ts} +27 -26
  91. package/core/session/{session-manager.ts → task-session-manager.ts} +7 -4
  92. package/core/session/utils.ts +1 -1
  93. package/core/storage/ideas-storage.ts +10 -26
  94. package/core/storage/index.ts +14 -162
  95. package/core/storage/queue-storage.ts +13 -11
  96. package/core/storage/shipped-storage.ts +4 -17
  97. package/core/storage/state-storage.ts +35 -43
  98. package/core/storage/storage-manager.ts +42 -52
  99. package/core/storage/storage.ts +160 -0
  100. package/core/sync/auth-config.ts +1 -8
  101. package/core/sync/index.ts +17 -10
  102. package/core/sync/oauth-handler.ts +1 -6
  103. package/core/sync/sync-client.ts +6 -34
  104. package/core/sync/sync-manager.ts +11 -40
  105. package/core/types/agentic.ts +577 -0
  106. package/core/types/agents.ts +145 -0
  107. package/core/types/bus.ts +82 -0
  108. package/core/types/commands.ts +366 -0
  109. package/core/types/config.ts +70 -0
  110. package/core/types/core.ts +96 -0
  111. package/core/types/domain.ts +71 -0
  112. package/core/types/events.ts +42 -0
  113. package/core/types/fs.ts +56 -0
  114. package/core/types/index.ts +396 -500
  115. package/core/types/infrastructure.ts +196 -0
  116. package/core/types/integrations.ts +57 -0
  117. package/core/{agentic/memory-system/types.ts → types/memory.ts} +33 -8
  118. package/core/{outcomes/types.ts → types/outcomes.ts} +53 -8
  119. package/core/types/plugin.ts +25 -0
  120. package/core/types/server.ts +54 -0
  121. package/core/types/services.ts +65 -0
  122. package/core/types/session.ts +135 -0
  123. package/core/types/storage.ts +148 -0
  124. package/core/types/sync.ts +121 -0
  125. package/core/types/task.ts +72 -0
  126. package/core/types/template.ts +24 -0
  127. package/core/types/utils.ts +90 -0
  128. package/core/utils/cache.ts +195 -0
  129. package/core/utils/collection-filters.ts +245 -0
  130. package/core/utils/date-helper.ts +1 -5
  131. package/core/utils/file-helper.ts +20 -10
  132. package/core/utils/jsonl-helper.ts +5 -8
  133. package/core/utils/markdown-builder.ts +277 -0
  134. package/core/utils/project-commands.ts +132 -0
  135. package/core/utils/runtime.ts +119 -0
  136. package/dist/bin/prjct.mjs +12568 -0
  137. package/package.json +13 -8
  138. package/scripts/build.js +106 -0
  139. package/scripts/postinstall.js +50 -8
  140. package/templates/agentic/subagent-generation.md +1 -1
  141. package/templates/commands/init.md +43 -0
  142. package/templates/commands/notion-setup.md +191 -0
  143. package/templates/commands/serve.md +118 -0
  144. package/templates/commands/ship.md +13 -2
  145. package/templates/commands/skill.md +110 -0
  146. package/templates/commands/sync.md +1 -1
  147. package/templates/commands/test.md +23 -4
  148. package/templates/mcp-config.json +28 -0
  149. package/templates/permissions/default.jsonc +60 -0
  150. package/templates/permissions/permissive.jsonc +49 -0
  151. package/templates/permissions/strict.jsonc +62 -0
  152. package/templates/skills/code-review.md +47 -0
  153. package/templates/skills/debug.md +61 -0
  154. package/templates/skills/refactor.md +47 -0
  155. package/templates/subagents/domain/devops.md +1 -1
  156. package/templates/subagents/domain/testing.md +6 -10
  157. package/templates/subagents/workflow/prjct-shipper.md +16 -7
  158. package/templates/tools/bash.txt +22 -0
  159. package/templates/tools/edit.txt +18 -0
  160. package/templates/tools/glob.txt +19 -0
  161. package/templates/tools/grep.txt +21 -0
  162. package/templates/tools/read.txt +14 -0
  163. package/templates/tools/task.txt +20 -0
  164. package/templates/tools/webfetch.txt +16 -0
  165. package/templates/tools/websearch.txt +18 -0
  166. package/templates/tools/write.txt +17 -0
  167. package/core/agentic/command-executor/command-executor.ts +0 -312
  168. package/core/agentic/command-executor/index.ts +0 -16
  169. package/core/agentic/command-executor/status-signal.ts +0 -38
  170. package/core/agentic/command-executor/types.ts +0 -79
  171. package/core/agentic/ground-truth/index.ts +0 -76
  172. package/core/agentic/ground-truth/types.ts +0 -33
  173. package/core/agentic/ground-truth/utils.ts +0 -48
  174. package/core/agentic/ground-truth/verifiers/analyze.ts +0 -54
  175. package/core/agentic/ground-truth/verifiers/done.ts +0 -75
  176. package/core/agentic/ground-truth/verifiers/feature.ts +0 -70
  177. package/core/agentic/ground-truth/verifiers/index.ts +0 -37
  178. package/core/agentic/ground-truth/verifiers/init.ts +0 -52
  179. package/core/agentic/ground-truth/verifiers/now.ts +0 -57
  180. package/core/agentic/ground-truth/verifiers/ship.ts +0 -85
  181. package/core/agentic/ground-truth/verifiers/spec.ts +0 -45
  182. package/core/agentic/ground-truth/verifiers/sync.ts +0 -47
  183. package/core/agentic/ground-truth/verifiers.ts +0 -6
  184. package/core/agentic/loop-detector/error-analysis.ts +0 -97
  185. package/core/agentic/loop-detector/hallucination.ts +0 -71
  186. package/core/agentic/loop-detector/index.ts +0 -41
  187. package/core/agentic/loop-detector/loop-detector.ts +0 -222
  188. package/core/agentic/loop-detector/types.ts +0 -66
  189. package/core/agentic/memory-system/history.ts +0 -53
  190. package/core/agentic/memory-system/index.ts +0 -192
  191. package/core/agentic/memory-system/patterns.ts +0 -156
  192. package/core/agentic/memory-system/semantic-memories.ts +0 -278
  193. package/core/agentic/memory-system/session.ts +0 -21
  194. package/core/agentic/plan-mode/approval.ts +0 -57
  195. package/core/agentic/plan-mode/constants.ts +0 -44
  196. package/core/agentic/plan-mode/index.ts +0 -28
  197. package/core/agentic/plan-mode/plan-mode.ts +0 -407
  198. package/core/agentic/plan-mode/types.ts +0 -193
  199. package/core/agents/types.ts +0 -126
  200. package/core/command-registry/categories.ts +0 -23
  201. package/core/command-registry/commands.ts +0 -15
  202. package/core/command-registry/core-commands.ts +0 -344
  203. package/core/command-registry/index.ts +0 -158
  204. package/core/command-registry/optional-commands.ts +0 -163
  205. package/core/command-registry/setup-commands.ts +0 -83
  206. package/core/command-registry/types.ts +0 -59
  207. package/core/command-registry.ts +0 -9
  208. package/core/commands/types.ts +0 -185
  209. package/core/commands.ts +0 -11
  210. package/core/constants/formats.ts +0 -187
  211. package/core/context-sync.ts +0 -18
  212. package/core/data/index.ts +0 -27
  213. package/core/data/md-base-manager.ts +0 -203
  214. package/core/data/md-ideas-manager.ts +0 -155
  215. package/core/data/md-queue-manager.ts +0 -180
  216. package/core/data/md-shipped-manager.ts +0 -90
  217. package/core/data/md-state-manager.ts +0 -137
  218. package/core/domain/task-stack/index.ts +0 -19
  219. package/core/domain/task-stack/parser.ts +0 -86
  220. package/core/domain/task-stack/storage.ts +0 -123
  221. package/core/domain/task-stack/task-stack.ts +0 -340
  222. package/core/domain/task-stack/types.ts +0 -51
  223. package/core/infrastructure/command-installer/command-installer.ts +0 -327
  224. package/core/infrastructure/command-installer/global-config.ts +0 -136
  225. package/core/infrastructure/command-installer/index.ts +0 -25
  226. package/core/infrastructure/command-installer/types.ts +0 -41
  227. package/core/infrastructure/session-manager/index.ts +0 -23
  228. package/core/infrastructure/session-manager/types.ts +0 -45
  229. package/core/infrastructure/session-manager.ts +0 -8
  230. package/core/serializers/ideas-serializer.ts +0 -187
  231. package/core/serializers/index.ts +0 -36
  232. package/core/serializers/queue-serializer.ts +0 -210
  233. package/core/serializers/shipped-serializer.ts +0 -108
  234. package/core/serializers/state-serializer.ts +0 -136
  235. package/core/session/types.ts +0 -29
  236. /package/core/infrastructure/{agents/claude-agent.ts → claude-agent.ts} +0 -0
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Agentic Module - Central exports for agentic capabilities
3
+ *
4
+ * Organized into logical groups:
5
+ * - Memory: Pattern learning, semantic memories, history
6
+ * - Planning: Plan mode, approval flows
7
+ * - Execution: Command executor, loop detection
8
+ * - Context: Context building, smart context, prompt generation
9
+ * - Routing: Agent router, ground truth verification
10
+ * - Tools: Tool registry, template loader
11
+ */
12
+
13
+ // ============ Memory ============
14
+ // Pattern learning, semantic memories, session history
15
+ export {
16
+ default as memorySystem,
17
+ MemorySystem,
18
+ CachedStore,
19
+ PatternStore,
20
+ SemanticMemories,
21
+ HistoryStore,
22
+ SessionStore,
23
+ MEMORY_TAGS,
24
+ } from './memory-system'
25
+
26
+ // ============ Planning ============
27
+ // Plan mode for complex tasks
28
+ export {
29
+ default as planMode,
30
+ PlanMode,
31
+ generateApprovalPrompt,
32
+ PLAN_STATUS,
33
+ PLAN_REQUIRED_COMMANDS,
34
+ DESTRUCTIVE_COMMANDS,
35
+ PLANNING_TOOLS,
36
+ } from './plan-mode'
37
+
38
+ // ============ Execution ============
39
+ // Command execution, loop detection
40
+ export {
41
+ default as commandExecutor,
42
+ CommandExecutor,
43
+ signalStart,
44
+ signalEnd,
45
+ } from './command-executor'
46
+
47
+ export {
48
+ default as loopDetector,
49
+ LoopDetector,
50
+ HALLUCINATION_PATTERNS,
51
+ detectHallucination,
52
+ getHallucinationSuggestion,
53
+ isSimilarError,
54
+ analyzeErrorPattern,
55
+ generateEscalationMessage,
56
+ generateSuggestion,
57
+ } from './loop-detector'
58
+
59
+ // ============ Context ============
60
+ // Context building and prompt generation
61
+ export { default as contextBuilder } from './context-builder'
62
+ export { default as smartContext } from './smart-context'
63
+ export { default as promptBuilder } from './prompt-builder'
64
+
65
+ // ============ Routing ============
66
+ // Agent routing and ground truth verification
67
+ export { default as AgentRouter } from './agent-router'
68
+ export {
69
+ default as groundTruth,
70
+ verify,
71
+ prepareCommand,
72
+ requiresVerification,
73
+ verifiers,
74
+ verifyDone,
75
+ verifyShip,
76
+ verifyFeature,
77
+ verifyNow,
78
+ verifyInit,
79
+ verifySync,
80
+ verifyAnalyze,
81
+ verifySpec,
82
+ formatDuration,
83
+ escapeRegex,
84
+ formatWarnings,
85
+ } from './ground-truth'
86
+
87
+ // ============ Tools ============
88
+ // Tool and template management
89
+ export { default as toolRegistry } from './tool-registry'
90
+ export { default as templateLoader } from './template-loader'
91
+
92
+ // ============ Utilities ============
93
+ // Chain of thought, services
94
+ export { default as chainOfThought } from './chain-of-thought'
95
+ export { default as services } from './services'
96
+
97
+ // ============ Types ============
98
+ // All types re-exported from ../types (canonical source)
99
+ export type {
100
+ // Memory types
101
+ Memory,
102
+ MemoryTag,
103
+ MemoryDatabase,
104
+ HistoryEntry,
105
+ HistoryEventType,
106
+ Decision,
107
+ Workflow,
108
+ Preference,
109
+ Patterns,
110
+ MemoryContext,
111
+ MemoryContextParams,
112
+ // Plan Mode types
113
+ PlanParams,
114
+ GatheredInfo,
115
+ GatheredInfoType,
116
+ GatheredFileData,
117
+ GatheredAnalysisData,
118
+ ProposedPlan,
119
+ PlanStepDefinition,
120
+ PlanStep,
121
+ PlanStepResult,
122
+ PlanStatus,
123
+ Plan,
124
+ PlanAnalysis,
125
+ ApprovalPrompt,
126
+ ApprovalOption,
127
+ ApprovalContext,
128
+ ChangedFile,
129
+ ApprovalOperation,
130
+ // Execution types
131
+ ExecutionResult,
132
+ SimpleExecutionResult,
133
+ ExecutionToolsFn,
134
+ // Loop Detector types
135
+ ErrorEntry,
136
+ AttemptRecord,
137
+ ErrorPattern,
138
+ EscalationInfo,
139
+ AttemptResult,
140
+ AttemptInfo,
141
+ HallucinationPattern,
142
+ HallucinationResult,
143
+ OutputAnalysis,
144
+ // Ground Truth types
145
+ GroundTruthContext,
146
+ VerificationResult,
147
+ Verifier,
148
+ // Tool registry types
149
+ ToolFunction,
150
+ ToolRegistryInterface,
151
+ // Agent router types
152
+ Agent,
153
+ AssignmentContext,
154
+ // Context builder types
155
+ ContextPaths,
156
+ ProjectContext,
157
+ ContextState,
158
+ // Smart context types
159
+ ContextDomain,
160
+ FullContext,
161
+ FilteredContext,
162
+ AgentInfo,
163
+ FeatureInfo,
164
+ PatternInfo,
165
+ StackInfo,
166
+ FilterMetrics,
167
+ DomainAnalysis,
168
+ // Prompt builder types
169
+ Frontmatter,
170
+ Template,
171
+ LearnedPatterns,
172
+ ThinkBlock,
173
+ PlanInfo,
174
+ // Chain of Thought types
175
+ ChainOfThoughtContext,
176
+ ChainOfThoughtState,
177
+ ReasoningStep,
178
+ ReasoningResult,
179
+ ChainOfThoughtResult,
180
+ } from '../types'
@@ -1,8 +1,422 @@
1
1
  /**
2
- * Loop Detection & User Escalation
3
- * Re-exports from loop-detector/index.ts for backwards compatibility.
2
+ * Loop Detector
3
+ * Detects and prevents execution loops and hallucinations.
4
+ *
5
+ * OPTIMIZATION (P2.4): Loop Detection
6
+ * - Track attempt counts per command/error type
7
+ * - Auto-escalate after 3 failed attempts
8
+ * - Provide specific help based on error patterns
9
+ *
10
+ * ANTI-HALLUCINATION: Detects contradictory/impossible outputs
11
+ * - Patterns that indicate Claude is hallucinating (saying file exists when it doesn't)
12
+ * - Contradictory statements in same response
13
+ * - Completing tasks that were never started
14
+ *
15
+ * Source: Augment Code pattern
16
+ * "If you notice yourself going around in circles... ask the user for help"
4
17
  */
5
18
 
6
- import loopDetector from './loop-detector/index'
7
- export * from './loop-detector/index'
19
+ import type {
20
+ ErrorEntry,
21
+ AttemptRecord,
22
+ ErrorPattern,
23
+ EscalationInfo,
24
+ AttemptResult,
25
+ AttemptInfo,
26
+ HallucinationPattern,
27
+ HallucinationResult,
28
+ OutputAnalysis,
29
+ } from '../types'
30
+
31
+ // =============================================================================
32
+ // Constants
33
+ // =============================================================================
34
+
35
+ /**
36
+ * ANTI-HALLUCINATION: Patterns that indicate Claude may be hallucinating
37
+ * These patterns detect contradictory or impossible statements
38
+ */
39
+ export const HALLUCINATION_PATTERNS: HallucinationPattern[] = [
40
+ // Contradictory file operations
41
+ { pattern: /file.*not found.*created/i, type: 'contradiction', description: 'Claims file created but also not found' },
42
+ { pattern: /created.*but.*error/i, type: 'contradiction', description: 'Claims success but also error' },
43
+ { pattern: /successfully.*failed/i, type: 'contradiction', description: 'Contradictory success/failure' },
44
+
45
+ // Impossible task states
46
+ {
47
+ pattern: /already.*completed.*completing/i,
48
+ type: 'state',
49
+ description: 'Completing already-completed task',
50
+ },
51
+ { pattern: /no task.*marking complete/i, type: 'state', description: 'Completing non-existent task' },
52
+ { pattern: /no.*active.*done with/i, type: 'state', description: 'Finishing task that doesnt exist' },
53
+
54
+ // Invented data
55
+ {
56
+ pattern: /version.*updated.*no package/i,
57
+ type: 'invented',
58
+ description: 'Version update without package.json',
59
+ },
60
+ { pattern: /committed.*nothing to commit/i, type: 'invented', description: 'Commit without changes' },
61
+ { pattern: /pushed.*no remote/i, type: 'invented', description: 'Push without remote' },
62
+ ]
63
+
64
+ // =============================================================================
65
+ // Hallucination Detection
66
+ // =============================================================================
67
+
68
+ /**
69
+ * Get suggestion for handling detected hallucination
70
+ */
71
+ export function getHallucinationSuggestion(type: string): string {
72
+ const suggestions: Record<string, string> = {
73
+ contradiction: 'Verify file/resource state before reporting. Use Read tool to check actual state.',
74
+ state: 'Check current task state from now.md before assuming completion.',
75
+ invented: 'Verify prerequisites exist (package.json, git remote) before claiming actions.',
76
+ }
77
+ return suggestions[type] || 'Verify actual state before proceeding.'
78
+ }
79
+
80
+ /**
81
+ * Detect potential hallucination patterns in output
82
+ */
83
+ export function detectHallucination(output: string): HallucinationResult {
84
+ if (!output || typeof output !== 'string') {
85
+ return { detected: false }
86
+ }
87
+
88
+ for (const { pattern, type, description } of HALLUCINATION_PATTERNS) {
89
+ if (pattern.test(output)) {
90
+ return {
91
+ detected: true,
92
+ type,
93
+ pattern: pattern.source,
94
+ description,
95
+ message: `Potential hallucination detected: ${description}`,
96
+ suggestion: getHallucinationSuggestion(type),
97
+ }
98
+ }
99
+ }
100
+
101
+ return { detected: false }
102
+ }
103
+
104
+ // =============================================================================
105
+ // Error Analysis
106
+ // =============================================================================
107
+
108
+ /**
109
+ * Check if two errors are similar
110
+ */
111
+ export function isSimilarError(error1: string, error2: string): boolean {
112
+ if (!error1 || !error2) return false
113
+
114
+ // Normalize errors
115
+ const normalize = (e: string) =>
116
+ e
117
+ .toLowerCase()
118
+ .replace(/[0-9]+/g, 'N') // Replace numbers
119
+ .replace(/['"`]/g, '') // Remove quotes
120
+ .replace(/\s+/g, ' ') // Normalize whitespace
121
+ .trim()
122
+
123
+ return normalize(error1) === normalize(error2)
124
+ }
125
+
126
+ /**
127
+ * Analyze error pattern from history
128
+ */
129
+ export function analyzeErrorPattern(errors: ErrorEntry[]): ErrorPattern {
130
+ if (!errors || errors.length === 0) {
131
+ return { type: 'unknown', description: 'No error information' }
132
+ }
133
+
134
+ const lastError = errors[errors.length - 1]?.message?.toLowerCase() || ''
135
+
136
+ // Detect common patterns (ORDER MATTERS - more specific patterns first)
137
+ if (lastError.includes('permission') || lastError.includes('access denied')) {
138
+ return { type: 'permission', description: 'File or directory permission issue' }
139
+ }
140
+ if (lastError.includes('not found') || lastError.includes('no such file')) {
141
+ return { type: 'not_found', description: 'File or resource not found' }
142
+ }
143
+ if (lastError.includes('syntax') || lastError.includes('parse')) {
144
+ return { type: 'syntax', description: 'Syntax or parsing error' }
145
+ }
146
+ if (lastError.includes('timeout') || lastError.includes('timed out')) {
147
+ return { type: 'timeout', description: 'Operation timed out' }
148
+ }
149
+ if (lastError.includes('network') || lastError.includes('connection')) {
150
+ return { type: 'network', description: 'Network or connection issue' }
151
+ }
152
+ // Config pattern MUST be checked before validation (since "invalid config" contains both)
153
+ if (lastError.includes('config') || lastError.includes('configuration')) {
154
+ return { type: 'config', description: 'Configuration issue' }
155
+ }
156
+ if (lastError.includes('validation') || lastError.includes('invalid')) {
157
+ return { type: 'validation', description: 'Validation failed' }
158
+ }
159
+
160
+ return { type: 'unknown', description: 'Unrecognized error pattern' }
161
+ }
162
+
163
+ /**
164
+ * Generate user-friendly escalation message
165
+ */
166
+ export function generateEscalationMessage(command: string, errorPattern: ErrorPattern, maxAttempts: number): string {
167
+ const messages: Record<string, string> = {
168
+ permission: `I've tried ${command} ${maxAttempts} times but keep hitting permission issues.`,
169
+ not_found: `After ${maxAttempts} attempts, I still can't find the required file or resource.`,
170
+ syntax: `I'm encountering repeated syntax errors with ${command}.`,
171
+ timeout: `The operation keeps timing out after ${maxAttempts} attempts.`,
172
+ network: `Network issues are preventing ${command} from completing.`,
173
+ validation: `Validation keeps failing for ${command}.`,
174
+ config: `There seems to be a configuration issue affecting ${command}.`,
175
+ unknown: `I've tried ${command} ${maxAttempts} times without success.`,
176
+ }
177
+
178
+ return messages[errorPattern.type] || messages.unknown
179
+ }
180
+
181
+ /**
182
+ * Generate actionable suggestion based on error pattern
183
+ */
184
+ export function generateSuggestion(errorPattern: ErrorPattern): string {
185
+ const suggestions: Record<string, string> = {
186
+ permission: 'Check file permissions. Try: chmod -R u+w ~/.prjct-cli/',
187
+ not_found: 'Verify the file path exists. Run /p:init if project not initialized.',
188
+ syntax: 'Check the file format. There may be invalid JSON or markdown.',
189
+ timeout: 'Check your network connection or try again in a moment.',
190
+ network: 'Verify internet connection and try again.',
191
+ validation: 'Review the input parameters and try with different values.',
192
+ config: 'Check .prjct/prjct.config.json for issues. Try /p:init to reinitialize.',
193
+ unknown: 'Can you check the issue manually and provide more context?',
194
+ }
195
+
196
+ return suggestions[errorPattern.type] || suggestions.unknown
197
+ }
198
+
199
+ // =============================================================================
200
+ // Loop Detector Class
201
+ // =============================================================================
202
+
203
+ export class LoopDetector {
204
+ private _attempts: Map<string, AttemptRecord>
205
+ private _errorPatterns: Map<string, unknown>
206
+ maxAttempts: number
207
+ sessionTimeout: number
208
+
209
+ constructor() {
210
+ // Track attempts per command session
211
+ this._attempts = new Map()
212
+
213
+ // Track error patterns
214
+ this._errorPatterns = new Map()
215
+
216
+ // Configuration
217
+ this.maxAttempts = 3
218
+ this.sessionTimeout = 5 * 60 * 1000 // 5 minutes
219
+ }
220
+
221
+ /**
222
+ * Generate a unique key for tracking attempts
223
+ */
224
+ private _getKey(command: string, context: string = ''): string {
225
+ return `${command}:${context}`.toLowerCase()
226
+ }
227
+
228
+ /**
229
+ * Record an attempt for a command
230
+ */
231
+ recordAttempt(command: string, context: string = '', result: AttemptResult = {}): AttemptInfo {
232
+ const key = this._getKey(command, context)
233
+ const now = Date.now()
234
+
235
+ // Get or create attempt record
236
+ let record = this._attempts.get(key)
237
+
238
+ if (!record || now - record.lastAttempt > this.sessionTimeout) {
239
+ // New session or timed out
240
+ record = {
241
+ command,
242
+ context,
243
+ attempts: 0,
244
+ errors: [],
245
+ firstAttempt: now,
246
+ lastAttempt: now,
247
+ success: false,
248
+ }
249
+ }
250
+
251
+ // Update record
252
+ record.attempts++
253
+ record.lastAttempt = now
254
+ record.success = result.success || false
255
+
256
+ if (result.error) {
257
+ record.errors.push({
258
+ message: result.error,
259
+ timestamp: now,
260
+ })
261
+ }
262
+
263
+ this._attempts.set(key, record)
264
+
265
+ return {
266
+ attemptNumber: record.attempts,
267
+ isLooping: this.isLooping(command, context),
268
+ shouldEscalate: this.shouldEscalate(command, context),
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Check if a command is in a loop (repeated failures)
274
+ */
275
+ isLooping(command: string, context: string = ''): boolean {
276
+ const key = this._getKey(command, context)
277
+ const record = this._attempts.get(key)
278
+
279
+ if (!record) return false
280
+
281
+ // Check if multiple failures with same error
282
+ if (record.attempts >= 2 && !record.success) {
283
+ const recentErrors = record.errors.slice(-3)
284
+ if (recentErrors.length >= 2) {
285
+ // Check if errors are similar
286
+ const firstError = recentErrors[0]?.message || ''
287
+ const sameError = recentErrors.every((e) => isSimilarError(e.message, firstError))
288
+ return sameError
289
+ }
290
+ }
291
+
292
+ return false
293
+ }
294
+
295
+ /**
296
+ * Check if we should escalate to user
297
+ */
298
+ shouldEscalate(command: string, context: string = ''): boolean {
299
+ const key = this._getKey(command, context)
300
+ const record = this._attempts.get(key)
301
+
302
+ if (!record) return false
303
+
304
+ // Escalate after max attempts without success
305
+ return record.attempts >= this.maxAttempts && !record.success
306
+ }
307
+
308
+ /**
309
+ * Get escalation message for user
310
+ */
311
+ getEscalationInfo(command: string, context: string = ''): EscalationInfo | null {
312
+ const key = this._getKey(command, context)
313
+ const record = this._attempts.get(key)
314
+
315
+ if (!record) {
316
+ return null
317
+ }
318
+
319
+ // Analyze error pattern
320
+ const errorPattern = analyzeErrorPattern(record.errors)
321
+
322
+ return {
323
+ status: 'BLOCKED',
324
+ command,
325
+ context,
326
+ attempts: record.attempts,
327
+ duration: record.lastAttempt - record.firstAttempt,
328
+ errorPattern,
329
+ message: generateEscalationMessage(command, errorPattern, this.maxAttempts),
330
+ suggestion: generateSuggestion(errorPattern),
331
+ lastError: record.errors[record.errors.length - 1]?.message || null,
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Mark a command as successful (resets tracking)
337
+ */
338
+ recordSuccess(command: string, context: string = ''): void {
339
+ const key = this._getKey(command, context)
340
+ const record = this._attempts.get(key)
341
+
342
+ if (record) {
343
+ record.success = true
344
+ record.attempts = 0
345
+ record.errors = []
346
+ this._attempts.set(key, record)
347
+ }
348
+ }
349
+
350
+ /**
351
+ * Clear all tracking for a command
352
+ */
353
+ clearTracking(command: string, context: string = ''): void {
354
+ const key = this._getKey(command, context)
355
+ this._attempts.delete(key)
356
+ }
357
+
358
+ /**
359
+ * Clear all tracking data
360
+ */
361
+ clearAll(): void {
362
+ this._attempts.clear()
363
+ this._errorPatterns.clear()
364
+ }
365
+
366
+ /**
367
+ * Get statistics for debugging
368
+ */
369
+ getStats(): { activeTracking: number; commands: Record<string, unknown> } {
370
+ const stats: { activeTracking: number; commands: Record<string, unknown> } = {
371
+ activeTracking: this._attempts.size,
372
+ commands: {},
373
+ }
374
+
375
+ for (const [key, record] of this._attempts) {
376
+ stats.commands[key] = {
377
+ attempts: record.attempts,
378
+ success: record.success,
379
+ errorCount: record.errors.length,
380
+ }
381
+ }
382
+
383
+ return stats
384
+ }
385
+
386
+ /**
387
+ * ANTI-HALLUCINATION: Detect potential hallucination patterns in output
388
+ */
389
+ detectHallucination(output: string) {
390
+ return detectHallucination(output)
391
+ }
392
+
393
+ /**
394
+ * Analyze output and record if hallucination detected
395
+ */
396
+ analyzeOutput(command: string, output: string): OutputAnalysis {
397
+ const hallucination = this.detectHallucination(output)
398
+
399
+ if (hallucination.detected) {
400
+ // Record as a special type of error
401
+ this.recordAttempt(command, 'hallucination', {
402
+ success: false,
403
+ error: `HALLUCINATION: ${hallucination.description}`,
404
+ })
405
+
406
+ return {
407
+ ...hallucination,
408
+ shouldBlock: true,
409
+ action: 'VERIFY_STATE',
410
+ }
411
+ }
412
+
413
+ return { detected: false, shouldBlock: false }
414
+ }
415
+ }
416
+
417
+ // =============================================================================
418
+ // Default Export
419
+ // =============================================================================
420
+
421
+ const loopDetector = new LoopDetector()
8
422
  export default loopDetector