@polymorphism-tech/morph-spec 4.8.19 → 4.10.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 (214) hide show
  1. package/CLAUDE.md +21 -0
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +44 -55
  4. package/bin/task-manager.js +133 -20
  5. package/bin/validate.js +67 -33
  6. package/claude-plugin.json +1 -1
  7. package/docs/CHEATSHEET.md +201 -203
  8. package/docs/QUICKSTART.md +2 -2
  9. package/framework/CLAUDE.md +99 -77
  10. package/framework/agents.json +734 -182
  11. package/framework/commands/commit.md +166 -0
  12. package/framework/commands/morph-apply.md +13 -2
  13. package/framework/commands/morph-archive.md +8 -2
  14. package/framework/commands/morph-infra.md +6 -0
  15. package/framework/commands/morph-preflight.md +6 -0
  16. package/framework/commands/morph-proposal.md +56 -7
  17. package/framework/commands/morph-status.md +6 -0
  18. package/framework/commands/morph-troubleshoot.md +6 -0
  19. package/framework/hooks/claude-code/notification/approval-reminder.js +3 -2
  20. package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
  21. package/framework/hooks/claude-code/post-tool-use/dispatch.js +155 -32
  22. package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +78 -0
  23. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +8 -17
  24. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +16 -3
  25. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +4 -3
  26. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +4 -3
  27. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +60 -0
  28. package/framework/hooks/claude-code/session-start/inject-morph-context.js +124 -2
  29. package/framework/hooks/claude-code/session-start/post-compact-restore.js +41 -0
  30. package/framework/hooks/claude-code/statusline.py +76 -30
  31. package/framework/hooks/claude-code/stop/validate-completion.js +2 -15
  32. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +23 -5
  33. package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
  34. package/framework/hooks/shared/activity-logger.js +0 -24
  35. package/framework/hooks/shared/compact-restore.js +100 -0
  36. package/framework/hooks/shared/dispatch-helpers.js +116 -0
  37. package/framework/hooks/shared/phase-utils.js +12 -5
  38. package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
  39. package/framework/hooks/shared/stale-task-reset.js +57 -0
  40. package/framework/hooks/shared/state-reader.js +29 -5
  41. package/framework/hooks/shared/worktree-helpers.js +53 -0
  42. package/framework/phases.json +69 -14
  43. package/framework/rules/morph-workflow.md +88 -86
  44. package/framework/skills/level-0-meta/mcp-registry.json +86 -51
  45. package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/SKILL.md +14 -17
  46. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
  47. package/framework/skills/level-0-meta/{code-review → morph-code-review}/SKILL.md +2 -2
  48. package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/SKILL.md +163 -163
  49. package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/SKILL.md +9 -9
  50. package/framework/skills/level-0-meta/morph-init/SKILL.md +77 -12
  51. package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/SKILL.md +62 -15
  52. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +5 -5
  53. package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
  54. package/framework/skills/level-0-meta/{simulation-checklist → morph-simulation-checklist}/SKILL.md +1 -1
  55. package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/SKILL.md +2 -2
  56. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/SKILL.md +3 -4
  57. package/framework/skills/level-0-meta/{tool-usage-guide → morph-tool-usage-guide}/references/tools-per-phase.md +7 -7
  58. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/SKILL.md +2 -2
  59. package/framework/skills/level-0-meta/{verification-before-completion → morph-verification-before-completion}/scripts/check-phase-outputs.mjs +2 -2
  60. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +238 -0
  61. package/framework/skills/level-1-workflows/{phase-codebase-analysis → morph-phase-codebase-analysis}/SKILL.md +3 -3
  62. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +507 -0
  63. package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/SKILL.md +168 -27
  64. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +50 -0
  65. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +45 -0
  66. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +47 -0
  67. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +254 -0
  68. package/framework/skills/level-1-workflows/{phase-setup → morph-phase-setup}/SKILL.md +50 -3
  69. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/SKILL.md +48 -11
  70. package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/scripts/validate-tasks.mjs +3 -3
  71. package/framework/skills/level-1-workflows/{phase-uiux → morph-phase-uiux}/SKILL.md +46 -11
  72. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +97 -0
  73. package/framework/standards/STANDARDS.json +640 -88
  74. package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
  75. package/framework/standards/integration/mcp/mcp-tools.md +25 -7
  76. package/framework/templates/REGISTRY.json +1825 -1909
  77. package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
  78. package/framework/templates/docs/onboarding.md +3 -7
  79. package/package.json +2 -7
  80. package/src/commands/agents/dispatch-agents.js +104 -6
  81. package/src/commands/mcp/mcp-setup.js +39 -2
  82. package/src/commands/phase/phase-reset.js +74 -0
  83. package/src/commands/project/doctor.js +34 -51
  84. package/src/commands/project/init.js +1 -1
  85. package/src/commands/project/status.js +2 -2
  86. package/src/commands/project/update.js +381 -365
  87. package/src/commands/project/worktree.js +154 -0
  88. package/src/commands/scope/escalate.js +215 -0
  89. package/src/commands/state/advance-phase.js +132 -68
  90. package/src/commands/state/approve.js +2 -2
  91. package/src/commands/state/index.js +7 -8
  92. package/src/commands/state/phase-runner.js +1 -1
  93. package/src/commands/state/state.js +61 -6
  94. package/src/commands/task/expand.js +100 -0
  95. package/src/commands/tasks/task.js +78 -99
  96. package/src/commands/templates/template-render.js +93 -173
  97. package/src/commands/trust/trust.js +26 -21
  98. package/src/core/paths/output-schema.js +19 -3
  99. package/src/core/state/phase-state-machine.js +7 -4
  100. package/src/core/state/state-manager.js +32 -57
  101. package/src/core/workflows/workflow-detector.js +9 -87
  102. package/src/lib/detectors/claude-config-detector.js +93 -347
  103. package/src/lib/detectors/design-system-detector.js +189 -189
  104. package/src/lib/detectors/index.js +155 -57
  105. package/src/lib/generators/context-generator.js +2 -2
  106. package/src/lib/installers/mcp-installer.js +37 -5
  107. package/src/lib/phase-chain/phase-validator.js +336 -0
  108. package/src/lib/scope/impact-analyzer.js +106 -0
  109. package/src/lib/stack/stack-profile.js +88 -0
  110. package/src/lib/tasks/task-classifier.js +16 -0
  111. package/src/lib/tasks/task-parser.js +1 -1
  112. package/src/lib/tasks/test-runner.js +77 -0
  113. package/src/lib/trust/trust-manager.js +32 -144
  114. package/src/lib/validators/shared/emit-validator-dispatch.js +64 -0
  115. package/src/lib/validators/spec-validator.js +58 -4
  116. package/src/lib/validators/validation-runner.js +23 -11
  117. package/src/scripts/setup-infra.js +255 -224
  118. package/src/utils/agents-installer.js +34 -14
  119. package/src/utils/banner.js +1 -1
  120. package/src/utils/claude-settings-manager.js +1 -1
  121. package/src/utils/file-copier.js +1 -1
  122. package/src/utils/hooks-installer.js +272 -8
  123. package/framework/hooks/dev/check-sync-health.js +0 -117
  124. package/framework/hooks/dev/guard-version-numbers.js +0 -57
  125. package/framework/hooks/dev/sync-standards-registry.js +0 -60
  126. package/framework/hooks/dev/sync-template-registry.js +0 -60
  127. package/framework/hooks/dev/validate-skill-format.js +0 -70
  128. package/framework/hooks/dev/validate-standard-format.js +0 -73
  129. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +0 -190
  130. package/framework/skills/level-1-workflows/phase-design/SKILL.md +0 -366
  131. package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
  132. package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
  133. package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
  134. package/framework/workflows/configs/design-impl.json +0 -49
  135. package/framework/workflows/configs/express.json +0 -45
  136. package/framework/workflows/configs/fast-track.json +0 -42
  137. package/framework/workflows/configs/full-morph.json +0 -79
  138. package/framework/workflows/configs/fusion.json +0 -39
  139. package/framework/workflows/configs/long-running.json +0 -33
  140. package/framework/workflows/configs/spec-only.json +0 -43
  141. package/framework/workflows/configs/ui-refresh.json +0 -49
  142. package/framework/workflows/configs/zero-touch.json +0 -82
  143. package/src/commands/project/index.js +0 -8
  144. package/src/commands/project/monitor.js +0 -295
  145. package/src/commands/project/tutorial.js +0 -115
  146. package/src/commands/state/validate-phase.js +0 -238
  147. package/src/commands/templates/generate-contracts.js +0 -445
  148. package/src/core/index.js +0 -10
  149. package/src/core/orchestrator.js +0 -171
  150. package/src/core/registry/command-registry.js +0 -28
  151. package/src/core/registry/index.js +0 -8
  152. package/src/core/registry/validator-registry.js +0 -204
  153. package/src/core/state/index.js +0 -8
  154. package/src/core/templates/index.js +0 -9
  155. package/src/core/templates/template-data-sources.js +0 -325
  156. package/src/core/templates/template-validator.js +0 -296
  157. package/src/core/workflows/index.js +0 -7
  158. package/src/generator/config-generator.js +0 -206
  159. package/src/generator/templates/config.json.template +0 -40
  160. package/src/generator/templates/project.md.template +0 -67
  161. package/src/lib/agents/micro-agent-factory.js +0 -161
  162. package/src/lib/analysis/complexity-analyzer.js +0 -441
  163. package/src/lib/analysis/index.js +0 -7
  164. package/src/lib/analytics/analytics-engine.js +0 -345
  165. package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
  166. package/src/lib/checkpoints/index.js +0 -7
  167. package/src/lib/context/context-bundler.js +0 -241
  168. package/src/lib/context/context-optimizer.js +0 -212
  169. package/src/lib/context/context-tracker.js +0 -273
  170. package/src/lib/context/core-four-tracker.js +0 -201
  171. package/src/lib/context/mcp-optimizer.js +0 -200
  172. package/src/lib/detectors/config-detector.js +0 -223
  173. package/src/lib/detectors/standards-generator.js +0 -335
  174. package/src/lib/detectors/structure-detector.js +0 -275
  175. package/src/lib/execution/fusion-executor.js +0 -304
  176. package/src/lib/execution/parallel-executor.js +0 -270
  177. package/src/lib/hooks/stop-hook-executor.js +0 -286
  178. package/src/lib/hops/hop-composer.js +0 -221
  179. package/src/lib/monitor/agent-resolver.js +0 -144
  180. package/src/lib/monitor/renderer.js +0 -230
  181. package/src/lib/orchestration/index.js +0 -7
  182. package/src/lib/orchestration/team-orchestrator.js +0 -404
  183. package/src/lib/phase-chain/eligibility-checker.js +0 -243
  184. package/src/lib/threads/thread-coordinator.js +0 -238
  185. package/src/lib/threads/thread-manager.js +0 -317
  186. package/src/lib/tracking/artifact-trail.js +0 -202
  187. package/src/sanitizer/context-sanitizer.js +0 -221
  188. package/src/sanitizer/patterns.js +0 -163
  189. package/src/scanner/project-scanner.js +0 -242
  190. package/src/ui/diff-display.js +0 -91
  191. package/src/ui/interactive-wizard.js +0 -96
  192. package/src/ui/user-review.js +0 -211
  193. package/src/ui/wizard-questions.js +0 -188
  194. package/src/utils/color-utils.js +0 -70
  195. package/src/utils/process-handler.js +0 -97
  196. package/src/writer/file-writer.js +0 -86
  197. /package/framework/skills/level-0-meta/{brainstorming → morph-brainstorming}/references/proposal-example.md +0 -0
  198. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-example.md +0 -0
  199. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/references/review-guidelines.md +0 -0
  200. /package/framework/skills/level-0-meta/{code-review → morph-code-review}/scripts/scan-csharp.mjs +0 -0
  201. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/references/review-example-nextjs.md +0 -0
  202. /package/framework/skills/level-0-meta/{code-review-nextjs → morph-code-review-nextjs}/scripts/scan-nextjs.mjs +0 -0
  203. /package/framework/skills/level-0-meta/{frontend-review → morph-frontend-review}/scripts/scan-accessibility.mjs +0 -0
  204. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-dev-server.mjs +0 -0
  205. /package/framework/skills/level-0-meta/{post-implementation → morph-post-implementation}/scripts/detect-stack.mjs +0 -0
  206. /package/framework/skills/level-0-meta/{terminal-title → morph-terminal-title}/scripts/set_title.sh +0 -0
  207. /package/framework/skills/level-1-workflows/{phase-clarify → morph-phase-clarify}/references/clarifications-example.md +0 -0
  208. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/architecture-analysis-guide.md +0 -0
  209. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-authoring-guide.md +0 -0
  210. /package/framework/skills/level-1-workflows/{phase-design → morph-phase-design}/references/spec-example.md +0 -0
  211. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/recap-example.md +0 -0
  212. /package/framework/skills/level-1-workflows/{phase-implement → morph-phase-implement}/references/vsa-implementation-guide.md +0 -0
  213. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/task-planning-patterns.md +0 -0
  214. /package/framework/skills/level-1-workflows/{phase-tasks → morph-phase-tasks}/references/tasks-example.md +0 -0
@@ -1,345 +0,0 @@
1
- /**
2
- * Analytics Engine — Metrics collection and JSONL storage
3
- *
4
- * Records events to append-only JSONL files:
5
- * - .morph/analytics/threads-log.jsonl
6
- * - .morph/analytics/context-log.jsonl
7
- * - .morph/analytics/trust-log.jsonl
8
- *
9
- * Provides aggregation, ASCII chart generation, and 30-day dashboards.
10
- * Auto-prunes entries older than 90 days.
11
- */
12
-
13
- import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from 'fs';
14
- import { join } from 'path';
15
-
16
- const ANALYTICS_DIR = join(process.cwd(), '.morph/analytics');
17
- const THREADS_LOG = join(ANALYTICS_DIR, 'threads-log.jsonl');
18
- const CONTEXT_LOG = join(ANALYTICS_DIR, 'context-log.jsonl');
19
- const TRUST_LOG = join(ANALYTICS_DIR, 'trust-log.jsonl');
20
- const PRUNE_DAYS = 90;
21
- const DASHBOARD_DAYS = 30;
22
-
23
- // ============================================================================
24
- // Internal Helpers
25
- // ============================================================================
26
-
27
- function ensureAnalyticsDir() {
28
- if (!existsSync(ANALYTICS_DIR)) {
29
- mkdirSync(ANALYTICS_DIR, { recursive: true });
30
- }
31
- }
32
-
33
- function appendJSONL(filePath, record) {
34
- ensureAnalyticsDir();
35
- appendFileSync(filePath, JSON.stringify(record) + '\n', 'utf8');
36
- }
37
-
38
- function readJSONL(filePath) {
39
- if (!existsSync(filePath)) return [];
40
-
41
- const lines = readFileSync(filePath, 'utf8').trim().split('\n').filter(Boolean);
42
- const records = [];
43
-
44
- for (const line of lines) {
45
- try {
46
- records.push(JSON.parse(line));
47
- } catch {
48
- // Skip malformed lines
49
- }
50
- }
51
- return records;
52
- }
53
-
54
- function pruneJSONL(filePath) {
55
- if (!existsSync(filePath)) return;
56
-
57
- const cutoff = new Date();
58
- cutoff.setDate(cutoff.getDate() - PRUNE_DAYS);
59
-
60
- const records = readJSONL(filePath);
61
- const fresh = records.filter(r => {
62
- const ts = new Date(r.timestamp || r.createdAt || 0);
63
- return ts > cutoff;
64
- });
65
-
66
- if (fresh.length < records.length) {
67
- writeFileSync(filePath, fresh.map(r => JSON.stringify(r)).join('\n') + '\n', 'utf8');
68
- }
69
- }
70
-
71
- function recentRecords(records, days = DASHBOARD_DAYS) {
72
- const cutoff = new Date();
73
- cutoff.setDate(cutoff.getDate() - days);
74
- return records.filter(r => {
75
- const ts = new Date(r.timestamp || r.createdAt || 0);
76
- return ts > cutoff;
77
- });
78
- }
79
-
80
- // ============================================================================
81
- // Event Recording
82
- // ============================================================================
83
-
84
- /**
85
- * Record a thread analytics event
86
- * @param {Object} event
87
- * @param {string} event.type - Event type (thread_created|thread_completed|thread_failed|checkpoint_passed|etc.)
88
- * @param {string} event.feature - Feature name
89
- * @param {string} [event.threadId] - Thread ID
90
- * @param {string} [event.agent] - Agent name
91
- * @param {Object} [event.data] - Additional event data
92
- */
93
- export function recordEvent(event) {
94
- const record = {
95
- ...event,
96
- timestamp: new Date().toISOString()
97
- };
98
-
99
- // Route to appropriate log
100
- if (event.type?.startsWith('context_') || event.type === 'token_usage') {
101
- appendJSONL(CONTEXT_LOG, record);
102
- } else if (event.type?.startsWith('trust_')) {
103
- appendJSONL(TRUST_LOG, record);
104
- } else {
105
- appendJSONL(THREADS_LOG, record);
106
- }
107
- }
108
-
109
- /**
110
- * Record a context event (token usage, optimization, etc.)
111
- * @param {Object} event
112
- */
113
- export function recordContextEvent(event) {
114
- recordEvent({ ...event, type: event.type || 'context_usage' });
115
- }
116
-
117
- /**
118
- * Record a trust level change event
119
- * @param {string} feature
120
- * @param {string} level - new trust level
121
- * @param {string} reason
122
- */
123
- export function recordTrustEvent(feature, level, reason = '') {
124
- appendJSONL(TRUST_LOG, {
125
- timestamp: new Date().toISOString(),
126
- type: 'trust_level_changed',
127
- feature,
128
- level,
129
- reason
130
- });
131
- }
132
-
133
- /**
134
- * Record auto-approval event (gate approved by trust)
135
- * @param {string} feature
136
- * @param {string} gate - 'design' | 'tasks' | 'proposal'
137
- * @param {string} trustLevel
138
- * @param {number} passRate
139
- */
140
- export function recordAutoApproval(feature, gate, trustLevel, passRate) {
141
- appendJSONL(TRUST_LOG, {
142
- timestamp: new Date().toISOString(),
143
- type: 'trust_auto_approved',
144
- feature,
145
- gate,
146
- trustLevel,
147
- passRate,
148
- timeSavedMinutes: 5 // estimated minutes saved per auto-approval
149
- });
150
- }
151
-
152
- /**
153
- * Get trust progression for a feature
154
- * @param {string} featureName
155
- * @returns {Array} Trust level history
156
- */
157
- export function getTrustProgression(featureName) {
158
- const events = readJSONL(TRUST_LOG)
159
- .filter(e => e.feature === featureName && e.type === 'trust_level_changed')
160
- .sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
161
- return events;
162
- }
163
-
164
- // ============================================================================
165
- // Feature Analytics
166
- // ============================================================================
167
-
168
- /**
169
- * Get analytics for a specific feature
170
- * @param {string} feature - Feature name
171
- * @returns {Object} Feature analytics
172
- */
173
- export function getFeatureAnalytics(feature) {
174
- const allThreadEvents = readJSONL(THREADS_LOG).filter(r => r.feature === feature);
175
- const allContextEvents = readJSONL(CONTEXT_LOG).filter(r => r.feature === feature);
176
-
177
- const threadsByStatus = allThreadEvents
178
- .filter(e => e.type === 'thread_completed' || e.type === 'thread_failed')
179
- .reduce((acc, e) => {
180
- const status = e.type === 'thread_completed' ? 'completed' : 'failed';
181
- acc[status] = (acc[status] || 0) + 1;
182
- return acc;
183
- }, {});
184
-
185
- const checkpoints = allThreadEvents.filter(e =>
186
- e.type === 'checkpoint_passed' || e.type === 'checkpoint_failed'
187
- );
188
- const checkpointPassRate = checkpoints.length > 0
189
- ? Math.round(checkpoints.filter(e => e.type === 'checkpoint_passed').length / checkpoints.length * 100)
190
- : 100;
191
-
192
- const totalDuration = allThreadEvents
193
- .filter(e => e.type === 'thread_completed' && e.data?.duration)
194
- .reduce((sum, e) => sum + e.data.duration, 0);
195
-
196
- const toolCallEvents = allThreadEvents.filter(e => e.type === 'tool_call');
197
- const tokenEvents = allContextEvents.filter(e => e.data?.tokensUsed);
198
- const totalTokens = tokenEvents.reduce((sum, e) => sum + (e.data.tokensUsed || 0), 0);
199
-
200
- return {
201
- feature,
202
- threads: threadsByStatus,
203
- checkpointPassRate,
204
- totalDuration,
205
- totalToolCalls: toolCallEvents.length,
206
- totalTokensUsed: totalTokens,
207
- optimizationEvents: allContextEvents.filter(e => e.type === 'context_optimized').length
208
- };
209
- }
210
-
211
- /**
212
- * Get project-wide analytics dashboard (30-day summary)
213
- * @returns {Object} Project analytics
214
- */
215
- export function getProjectAnalytics() {
216
- const allThreadEvents = recentRecords(readJSONL(THREADS_LOG));
217
- const allContextEvents = recentRecords(readJSONL(CONTEXT_LOG));
218
- const allTrustEvents = recentRecords(readJSONL(TRUST_LOG));
219
-
220
- const features = [...new Set(allThreadEvents.map(e => e.feature).filter(Boolean))];
221
-
222
- const completedThreads = allThreadEvents.filter(e => e.type === 'thread_completed');
223
- const failedThreads = allThreadEvents.filter(e => e.type === 'thread_failed');
224
- const checkpoints = allThreadEvents.filter(e =>
225
- e.type === 'checkpoint_passed' || e.type === 'checkpoint_failed'
226
- );
227
-
228
- const parallelEvents = allThreadEvents.filter(e => e.data?.type === 'parallel');
229
- const avgParallel = parallelEvents.length > 0
230
- ? parallelEvents.reduce((sum, e) => sum + (e.data?.concurrency || 1), 0) / parallelEvents.length
231
- : 1;
232
-
233
- const autoApprovalEvents = allTrustEvents.filter(e => e.type === 'trust_auto_approved');
234
- const avgTokens = allContextEvents.filter(e => e.data?.tokensUsed).length > 0
235
- ? Math.round(allContextEvents.reduce((sum, e) => sum + (e.data?.tokensUsed || 0), 0) / allContextEvents.filter(e => e.data?.tokensUsed).length)
236
- : 0;
237
-
238
- return {
239
- period: `${DASHBOARD_DAYS} days`,
240
- features: features.length,
241
- threads: {
242
- total: completedThreads.length + failedThreads.length,
243
- completed: completedThreads.length,
244
- failed: failedThreads.length
245
- },
246
- checkpoints: {
247
- total: checkpoints.length,
248
- passed: checkpoints.filter(e => e.type === 'checkpoint_passed').length,
249
- passRate: checkpoints.length > 0
250
- ? Math.round(checkpoints.filter(e => e.type === 'checkpoint_passed').length / checkpoints.length * 100)
251
- : 100
252
- },
253
- parallelization: {
254
- avgConcurrency: Math.round(avgParallel * 10) / 10,
255
- parallelThreads: parallelEvents.length
256
- },
257
- trust: {
258
- autoApprovals: autoApprovalEvents.length,
259
- trustChanges: allTrustEvents.filter(e => e.type === 'trust_level_changed').length
260
- },
261
- context: {
262
- avgTokensPerSession: avgTokens,
263
- optimizations: allContextEvents.filter(e => e.type === 'context_optimized').length
264
- }
265
- };
266
- }
267
-
268
- // ============================================================================
269
- // ASCII Charts
270
- // ============================================================================
271
-
272
- /**
273
- * Generate an ASCII bar chart
274
- * @param {Object} data - { label: value }
275
- * @param {Object} [opts]
276
- * @param {number} [opts.width=40] - Chart width in chars
277
- * @param {string} [opts.title] - Chart title
278
- * @returns {string} ASCII chart string
279
- */
280
- export function generateAsciiChart(data, { width = 40, title = '' } = {}) {
281
- const entries = Object.entries(data);
282
- if (entries.length === 0) return ' (no data)';
283
-
284
- const maxValue = Math.max(...entries.map(([, v]) => v), 1);
285
- const maxLabelLen = Math.max(...entries.map(([k]) => k.length));
286
- const lines = [];
287
-
288
- if (title) {
289
- lines.push(` ${title}`);
290
- lines.push(' ' + '─'.repeat(width + maxLabelLen + 5));
291
- }
292
-
293
- for (const [label, value] of entries) {
294
- const barLen = Math.round((value / maxValue) * width);
295
- const bar = '█'.repeat(barLen);
296
- const paddedLabel = label.padStart(maxLabelLen);
297
- lines.push(` ${paddedLabel} │${bar} ${value}`);
298
- }
299
-
300
- return lines.join('\n');
301
- }
302
-
303
- /**
304
- * Generate thread timeline ASCII chart
305
- * @param {Array} events - Array of thread events with timestamps
306
- * @param {string} label - Chart label
307
- * @returns {string} ASCII timeline
308
- */
309
- export function generateTimelineChart(events, label = 'Timeline') {
310
- if (events.length === 0) return ' (no data)';
311
-
312
- const now = new Date();
313
- const days = Array.from({ length: 7 }, (_, i) => {
314
- const d = new Date(now);
315
- d.setDate(d.getDate() - (6 - i));
316
- return d.toLocaleDateString('en-US', { weekday: 'short' });
317
- });
318
-
319
- const counts = new Array(7).fill(0);
320
- for (const event of events) {
321
- const ts = new Date(event.timestamp);
322
- const daysAgo = Math.floor((now - ts) / (1000 * 60 * 60 * 24));
323
- if (daysAgo < 7) {
324
- counts[6 - daysAgo]++;
325
- }
326
- }
327
-
328
- const data = {};
329
- days.forEach((d, i) => { data[d] = counts[i]; });
330
-
331
- return generateAsciiChart(data, { title: label });
332
- }
333
-
334
- // ============================================================================
335
- // Maintenance
336
- // ============================================================================
337
-
338
- /**
339
- * Prune all analytics logs (remove entries older than 90 days)
340
- */
341
- export function pruneAnalytics() {
342
- pruneJSONL(THREADS_LOG);
343
- pruneJSONL(CONTEXT_LOG);
344
- pruneJSONL(TRUST_LOG);
345
- }
@@ -1,298 +0,0 @@
1
- import { readFileSync, existsSync } from 'fs';
2
- import { join } from 'path';
3
- import { execSync } from 'child_process';
4
- import chalk from 'chalk';
5
- import { executeStopHooks, isMaxRetriesExceeded, incrementRetryCount } from '../hooks/stop-hook-executor.js';
6
- import { recordEvent } from '../analytics/analytics-engine.js';
7
- import { getFeature } from '../../core/state/state-manager.js';
8
-
9
- /**
10
- * Checkpoint Hooks - Automated validation orchestration
11
- *
12
- * Runs validators, tests, and linters at checkpoint milestones (every N tasks).
13
- * Blocks progress if critical validations fail.
14
- */
15
-
16
- /**
17
- * Load LLM interaction configuration
18
- * @returns {Object} Configuration object
19
- */
20
- function loadLLMInteractionConfig() {
21
- const configPath = join(process.cwd(), '.morph/config/llm-interaction.json');
22
-
23
- if (!existsSync(configPath)) {
24
- // Return defaults if config doesn't exist
25
- return {
26
- checkpoints: {
27
- frequency: 3,
28
- autoValidate: true,
29
- validators: {
30
- enabled: ['architecture', 'packages', 'design-system', 'security'],
31
- optional: []
32
- },
33
- hooks: {
34
- runTests: false,
35
- runLinters: true,
36
- buildCheck: false
37
- },
38
- onFailure: {
39
- blockProgress: true,
40
- maxRetries: 3,
41
- escalateAfter: 3
42
- }
43
- }
44
- };
45
- }
46
-
47
- return JSON.parse(readFileSync(configPath, 'utf8'));
48
- }
49
-
50
- /**
51
- * Run a single validator
52
- * @param {string} validatorName - Validator to run (architecture, packages, etc.)
53
- * @param {string} featureName - Feature being validated
54
- * @returns {Promise<Object>} Validation result
55
- */
56
- async function runValidator(validatorName, featureName) {
57
- try {
58
- // Use existing validate command
59
- const result = execSync(
60
- `node bin/validate.js ${validatorName} --feature=${featureName} --json`,
61
- { encoding: 'utf8', stdio: 'pipe' }
62
- );
63
-
64
- const parsed = JSON.parse(result);
65
- return {
66
- validator: validatorName,
67
- passed: parsed.errors === 0,
68
- errors: parsed.errors || 0,
69
- warnings: parsed.warnings || 0,
70
- details: parsed.issues || []
71
- };
72
- } catch (error) {
73
- return {
74
- validator: validatorName,
75
- passed: false,
76
- errors: 1,
77
- warnings: 0,
78
- details: [{ message: error.message, severity: 'error' }]
79
- };
80
- }
81
- }
82
-
83
- /**
84
- * Run tests if configured
85
- * @param {string} featureName - Feature being tested
86
- * @returns {Promise<Object>} Test result
87
- */
88
- async function runTests(featureName) {
89
- try {
90
- execSync('npm test --passWithNoTests', { encoding: 'utf8', stdio: 'pipe' });
91
- return {
92
- validator: 'tests',
93
- passed: true,
94
- errors: 0,
95
- warnings: 0,
96
- details: []
97
- };
98
- } catch (error) {
99
- return {
100
- validator: 'tests',
101
- passed: false,
102
- errors: 1,
103
- warnings: 0,
104
- details: [{ message: 'Test suite failed', severity: 'error' }]
105
- };
106
- }
107
- }
108
-
109
- /**
110
- * Run linters if configured
111
- * @param {string} featureName - Feature being linted
112
- * @returns {Promise<Object>} Linter result
113
- */
114
- async function runLinters(featureName) {
115
- try {
116
- // Check if eslint exists
117
- if (existsSync(join(process.cwd(), 'node_modules/.bin/eslint'))) {
118
- execSync('npm run lint --if-present', { encoding: 'utf8', stdio: 'pipe' });
119
- }
120
-
121
- return {
122
- validator: 'linters',
123
- passed: true,
124
- errors: 0,
125
- warnings: 0,
126
- details: []
127
- };
128
- } catch (error) {
129
- return {
130
- validator: 'linters',
131
- passed: false,
132
- errors: 0,
133
- warnings: 1,
134
- details: [{ message: 'Linting warnings detected', severity: 'warning' }]
135
- };
136
- }
137
- }
138
-
139
- /**
140
- * Run checkpoint hooks - orchestrates all validation
141
- * @param {string} featureName - Feature name
142
- * @param {number} checkpointNum - Checkpoint number (1, 2, 3, etc.)
143
- * @returns {Promise<Object>} Checkpoint result
144
- */
145
- export async function runCheckpointHooks(featureName, checkpointNum) {
146
- const config = loadLLMInteractionConfig();
147
- const checkpointConfig = config.checkpoints;
148
-
149
- if (!checkpointConfig.autoValidate) {
150
- return {
151
- passed: true,
152
- skipped: true,
153
- message: 'Auto-validation disabled in config'
154
- };
155
- }
156
-
157
- console.log(`\n🔍 Running CHECKPOINT ${checkpointNum} for feature: ${featureName}`);
158
- console.log('━'.repeat(60));
159
-
160
- const results = [];
161
-
162
- // 1. Run enabled validators
163
- console.log('\n📋 Running validators...');
164
- for (const validatorName of checkpointConfig.validators.enabled) {
165
- process.stdout.write(` • ${validatorName}... `);
166
- const result = await runValidator(validatorName, featureName);
167
- results.push(result);
168
-
169
- if (result.passed) {
170
- console.log('✅ PASSED');
171
- } else {
172
- console.log(`❌ FAILED (${result.errors} errors, ${result.warnings} warnings)`);
173
- }
174
- }
175
-
176
- // 2. Run tests (if configured)
177
- if (checkpointConfig.hooks.runTests) {
178
- console.log('\n🧪 Running tests...');
179
- process.stdout.write(' • test suite... ');
180
- const testResult = await runTests(featureName);
181
- results.push(testResult);
182
-
183
- if (testResult.passed) {
184
- console.log('✅ PASSED');
185
- } else {
186
- console.log('❌ FAILED');
187
- }
188
- }
189
-
190
- // 3. Run linters (if configured)
191
- if (checkpointConfig.hooks.runLinters) {
192
- console.log('\n🎨 Running linters...');
193
- process.stdout.write(' • code style... ');
194
- const lintResult = await runLinters(featureName);
195
- results.push(lintResult);
196
-
197
- if (lintResult.passed) {
198
- console.log('✅ PASSED');
199
- } else {
200
- console.log('⚠️ WARNINGS');
201
- }
202
- }
203
-
204
- // Calculate overall pass/fail
205
- const errorCount = results.reduce((sum, r) => sum + r.errors, 0);
206
- const warningCount = results.reduce((sum, r) => sum + r.warnings, 0);
207
- const passed = errorCount === 0;
208
-
209
- console.log('\n' + '━'.repeat(60));
210
- console.log(`\n📊 Checkpoint ${checkpointNum} Summary:`);
211
- console.log(` Errors: ${errorCount}`);
212
- console.log(` Warnings: ${warningCount}`);
213
- console.log(` Status: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
214
-
215
- if (!passed) {
216
- console.log('\n⚠️ Checkpoint failed! Fix violations before proceeding.');
217
-
218
- // Show details of failures
219
- results.filter(r => r.errors > 0).forEach(r => {
220
- console.log(`\n❌ ${r.validator} errors:`);
221
- r.details.forEach(d => {
222
- if (d.severity === 'error') {
223
- console.log(` - ${d.message}`);
224
- }
225
- });
226
- });
227
- }
228
-
229
- console.log('\n' + '━'.repeat(60) + '\n');
230
-
231
- // Run stop hooks for L-Thread support
232
- let stopHookResult = null;
233
- if (featureName) {
234
- try {
235
- // Use feature name as pseudo thread ID for stop hook feedback
236
- const pseudoThreadId = `checkpoint-${featureName}-${checkpointNum}`;
237
- stopHookResult = await executeStopHooks(pseudoThreadId, 'on-stop', { feature: featureName });
238
-
239
- if (!stopHookResult.passed) {
240
- // Check retry count
241
- let retryCount = null;
242
- if (isMaxRetriesExceeded(pseudoThreadId)) {
243
- console.log(chalk.red('\n❌ Stop hooks failed 5+ times. Escalating to user.'));
244
- } else {
245
- retryCount = incrementRetryCount(pseudoThreadId);
246
- console.log(chalk.yellow(`\n⚠ Stop hooks failed (attempt ${retryCount}/5). Feedback saved.`));
247
- if (stopHookResult.feedback?.suggestions?.length > 0) {
248
- stopHookResult.feedback.suggestions.forEach(s => console.log(` → ${s}`));
249
- }
250
- }
251
-
252
- // Record to analytics
253
- recordEvent({
254
- type: 'stop_hook_failed_at_checkpoint',
255
- feature: featureName,
256
- data: { checkpointNum, retryCount }
257
- });
258
- }
259
- } catch {
260
- // Stop hooks are non-critical — don't block checkpoint if they error
261
- }
262
- }
263
-
264
- const phase = featureName ? (getFeature(featureName)?.phase || null) : null;
265
-
266
- return {
267
- passed,
268
- checkpointNum,
269
- timestamp: new Date().toISOString(),
270
- phase,
271
- results,
272
- summary: {
273
- errors: errorCount,
274
- warnings: warningCount,
275
- validatorsRun: results.length
276
- }
277
- };
278
- }
279
-
280
- /**
281
- * Check if checkpoint should run based on task count
282
- * @param {number} tasksCompleted - Number of tasks completed
283
- * @param {number} frequency - Checkpoint frequency (default: 3)
284
- * @returns {boolean} True if checkpoint should run
285
- */
286
- export function shouldRunCheckpoint(tasksCompleted, frequency = 3) {
287
- return tasksCompleted > 0 && tasksCompleted % frequency === 0;
288
- }
289
-
290
- /**
291
- * Get checkpoint number from task count
292
- * @param {number} tasksCompleted - Number of tasks completed
293
- * @param {number} frequency - Checkpoint frequency (default: 3)
294
- * @returns {number} Checkpoint number
295
- */
296
- export function getCheckpointNumber(tasksCompleted, frequency = 3) {
297
- return Math.floor(tasksCompleted / frequency);
298
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * Checkpoint System
3
- *
4
- * Automatic validation hooks triggered at checkpoints during feature implementation.
5
- */
6
-
7
- export * from './checkpoint-hooks.js';