@cloudstreamsoftware/claude-tools 1.0.0 → 1.1.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 (189) hide show
  1. package/README.md +152 -37
  2. package/agents/INDEX.md +183 -0
  3. package/agents/architect.md +247 -0
  4. package/agents/build-error-resolver.md +555 -0
  5. package/agents/catalyst-deployer.md +132 -0
  6. package/agents/code-reviewer.md +121 -0
  7. package/agents/compliance-auditor.md +148 -0
  8. package/agents/creator-architect.md +395 -0
  9. package/agents/deluge-reviewer.md +98 -0
  10. package/agents/doc-updater.md +471 -0
  11. package/agents/e2e-runner.md +711 -0
  12. package/agents/planner.md +122 -0
  13. package/agents/refactor-cleaner.md +309 -0
  14. package/agents/security-reviewer.md +582 -0
  15. package/agents/tdd-guide.md +302 -0
  16. package/config/versions.json +63 -0
  17. package/dist/hooks/hooks.json +209 -0
  18. package/dist/index.js +47 -0
  19. package/dist/lib/asset-value.js +609 -0
  20. package/dist/lib/client-manager.js +300 -0
  21. package/dist/lib/command-matcher.js +242 -0
  22. package/dist/lib/cross-session-patterns.js +754 -0
  23. package/dist/lib/intent-classifier.js +1075 -0
  24. package/dist/lib/package-manager.js +374 -0
  25. package/dist/lib/recommendation-engine.js +597 -0
  26. package/dist/lib/session-memory.js +489 -0
  27. package/dist/lib/skill-effectiveness.js +486 -0
  28. package/dist/lib/skill-matcher.js +595 -0
  29. package/dist/lib/tutorial-metrics.js +242 -0
  30. package/dist/lib/tutorial-progress.js +209 -0
  31. package/dist/lib/tutorial-renderer.js +431 -0
  32. package/dist/lib/utils.js +380 -0
  33. package/dist/lib/verify-formatter.js +143 -0
  34. package/dist/lib/workflow-state.js +249 -0
  35. package/hooks/hooks.json +209 -0
  36. package/package.json +5 -1
  37. package/scripts/aggregate-sessions.js +290 -0
  38. package/scripts/branch-name-validator.js +291 -0
  39. package/scripts/build.js +101 -0
  40. package/scripts/commands/client-switch.js +231 -0
  41. package/scripts/deprecate-skill.js +610 -0
  42. package/scripts/diagnose.js +324 -0
  43. package/scripts/doc-freshness.js +168 -0
  44. package/scripts/generate-weekly-digest.js +393 -0
  45. package/scripts/health-check.js +270 -0
  46. package/scripts/hooks/credential-check.js +101 -0
  47. package/scripts/hooks/evaluate-session.js +81 -0
  48. package/scripts/hooks/pre-compact.js +66 -0
  49. package/scripts/hooks/prompt-analyzer.js +276 -0
  50. package/scripts/hooks/prompt-router.js +422 -0
  51. package/scripts/hooks/quality-gate-enforcer.js +371 -0
  52. package/scripts/hooks/session-end.js +156 -0
  53. package/scripts/hooks/session-start.js +195 -0
  54. package/scripts/hooks/skill-injector.js +333 -0
  55. package/scripts/hooks/suggest-compact.js +58 -0
  56. package/scripts/lib/asset-value.js +609 -0
  57. package/scripts/lib/client-manager.js +300 -0
  58. package/scripts/lib/command-matcher.js +242 -0
  59. package/scripts/lib/cross-session-patterns.js +754 -0
  60. package/scripts/lib/intent-classifier.js +1075 -0
  61. package/scripts/lib/package-manager.js +374 -0
  62. package/scripts/lib/recommendation-engine.js +597 -0
  63. package/scripts/lib/session-memory.js +489 -0
  64. package/scripts/lib/skill-effectiveness.js +486 -0
  65. package/scripts/lib/skill-matcher.js +595 -0
  66. package/scripts/lib/tutorial-metrics.js +242 -0
  67. package/scripts/lib/tutorial-progress.js +209 -0
  68. package/scripts/lib/tutorial-renderer.js +431 -0
  69. package/scripts/lib/utils.js +380 -0
  70. package/scripts/lib/verify-formatter.js +143 -0
  71. package/scripts/lib/workflow-state.js +249 -0
  72. package/scripts/onboard.js +363 -0
  73. package/scripts/quarterly-report.js +692 -0
  74. package/scripts/setup-package-manager.js +204 -0
  75. package/scripts/sync-upstream.js +391 -0
  76. package/scripts/test.js +108 -0
  77. package/scripts/tutorial-runner.js +351 -0
  78. package/scripts/validate-all.js +201 -0
  79. package/scripts/verifiers/agents.js +245 -0
  80. package/scripts/verifiers/config.js +186 -0
  81. package/scripts/verifiers/environment.js +123 -0
  82. package/scripts/verifiers/hooks.js +188 -0
  83. package/scripts/verifiers/index.js +38 -0
  84. package/scripts/verifiers/persistence.js +140 -0
  85. package/scripts/verifiers/plugin.js +215 -0
  86. package/scripts/verifiers/skills.js +209 -0
  87. package/scripts/verify-setup.js +164 -0
  88. package/skills/INDEX.md +157 -0
  89. package/skills/backend-patterns/SKILL.md +586 -0
  90. package/skills/backend-patterns/catalyst-patterns.md +128 -0
  91. package/skills/bigquery-patterns/SKILL.md +27 -0
  92. package/skills/bigquery-patterns/performance-optimization.md +518 -0
  93. package/skills/bigquery-patterns/query-patterns.md +372 -0
  94. package/skills/bigquery-patterns/schema-design.md +78 -0
  95. package/skills/cloudstream-project-template/SKILL.md +20 -0
  96. package/skills/cloudstream-project-template/structure.md +65 -0
  97. package/skills/coding-standards/SKILL.md +524 -0
  98. package/skills/coding-standards/deluge-standards.md +83 -0
  99. package/skills/compliance-patterns/SKILL.md +28 -0
  100. package/skills/compliance-patterns/hipaa/audit-requirements.md +251 -0
  101. package/skills/compliance-patterns/hipaa/baa-process.md +298 -0
  102. package/skills/compliance-patterns/hipaa/data-archival-strategy.md +387 -0
  103. package/skills/compliance-patterns/hipaa/phi-handling.md +52 -0
  104. package/skills/compliance-patterns/pci-dss/saq-a-requirements.md +307 -0
  105. package/skills/compliance-patterns/pci-dss/tokenization-patterns.md +382 -0
  106. package/skills/compliance-patterns/pci-dss/zoho-checkout-patterns.md +56 -0
  107. package/skills/compliance-patterns/soc2/access-controls.md +344 -0
  108. package/skills/compliance-patterns/soc2/audit-logging.md +458 -0
  109. package/skills/compliance-patterns/soc2/change-management.md +403 -0
  110. package/skills/compliance-patterns/soc2/deluge-execution-logging.md +407 -0
  111. package/skills/consultancy-workflows/SKILL.md +19 -0
  112. package/skills/consultancy-workflows/client-isolation.md +21 -0
  113. package/skills/consultancy-workflows/documentation-automation.md +454 -0
  114. package/skills/consultancy-workflows/handoff-procedures.md +257 -0
  115. package/skills/consultancy-workflows/knowledge-capture.md +513 -0
  116. package/skills/consultancy-workflows/time-tracking.md +26 -0
  117. package/skills/continuous-learning/SKILL.md +84 -0
  118. package/skills/continuous-learning/config.json +18 -0
  119. package/skills/continuous-learning/evaluate-session.sh +60 -0
  120. package/skills/continuous-learning-v2/SKILL.md +126 -0
  121. package/skills/continuous-learning-v2/config.json +61 -0
  122. package/skills/frontend-patterns/SKILL.md +635 -0
  123. package/skills/frontend-patterns/zoho-widget-patterns.md +103 -0
  124. package/skills/gcp-data-engineering/SKILL.md +36 -0
  125. package/skills/gcp-data-engineering/bigquery/performance-optimization.md +337 -0
  126. package/skills/gcp-data-engineering/dataflow/error-handling.md +496 -0
  127. package/skills/gcp-data-engineering/dataflow/pipeline-patterns.md +444 -0
  128. package/skills/gcp-data-engineering/dbt/model-organization.md +63 -0
  129. package/skills/gcp-data-engineering/dbt/testing-patterns.md +503 -0
  130. package/skills/gcp-data-engineering/medallion-architecture/bronze-layer.md +60 -0
  131. package/skills/gcp-data-engineering/medallion-architecture/gold-layer.md +311 -0
  132. package/skills/gcp-data-engineering/medallion-architecture/layer-transitions.md +517 -0
  133. package/skills/gcp-data-engineering/medallion-architecture/silver-layer.md +305 -0
  134. package/skills/gcp-data-engineering/zoho-to-gcp/data-extraction.md +543 -0
  135. package/skills/gcp-data-engineering/zoho-to-gcp/real-time-vs-batch.md +337 -0
  136. package/skills/security-review/SKILL.md +498 -0
  137. package/skills/security-review/compliance-checklist.md +53 -0
  138. package/skills/strategic-compact/SKILL.md +67 -0
  139. package/skills/tdd-workflow/SKILL.md +413 -0
  140. package/skills/tdd-workflow/zoho-testing.md +124 -0
  141. package/skills/tutorial/SKILL.md +249 -0
  142. package/skills/tutorial/docs/ACCESSIBILITY.md +169 -0
  143. package/skills/tutorial/lessons/00-philosophy-and-workflow.md +198 -0
  144. package/skills/tutorial/lessons/01-basics.md +81 -0
  145. package/skills/tutorial/lessons/02-training.md +86 -0
  146. package/skills/tutorial/lessons/03-commands.md +109 -0
  147. package/skills/tutorial/lessons/04-workflows.md +115 -0
  148. package/skills/tutorial/lessons/05-compliance.md +116 -0
  149. package/skills/tutorial/lessons/06-zoho.md +121 -0
  150. package/skills/tutorial/lessons/07-hooks-system.md +277 -0
  151. package/skills/tutorial/lessons/08-mcp-servers.md +316 -0
  152. package/skills/tutorial/lessons/09-client-management.md +215 -0
  153. package/skills/tutorial/lessons/10-testing-e2e.md +260 -0
  154. package/skills/tutorial/lessons/11-skills-deep-dive.md +272 -0
  155. package/skills/tutorial/lessons/12-rules-system.md +326 -0
  156. package/skills/tutorial/lessons/13-golden-standard-graduation.md +213 -0
  157. package/skills/tutorial/lessons/14-fork-setup-and-sync.md +312 -0
  158. package/skills/tutorial/lessons/15-living-examples-system.md +221 -0
  159. package/skills/tutorial/tracks/accelerated/README.md +134 -0
  160. package/skills/tutorial/tracks/accelerated/assessment/checkpoint-1.md +161 -0
  161. package/skills/tutorial/tracks/accelerated/assessment/checkpoint-2.md +175 -0
  162. package/skills/tutorial/tracks/accelerated/day-1-core-concepts.md +234 -0
  163. package/skills/tutorial/tracks/accelerated/day-2-essential-commands.md +270 -0
  164. package/skills/tutorial/tracks/accelerated/day-3-workflow-mastery.md +305 -0
  165. package/skills/tutorial/tracks/accelerated/day-4-compliance-zoho.md +304 -0
  166. package/skills/tutorial/tracks/accelerated/day-5-hooks-skills.md +344 -0
  167. package/skills/tutorial/tracks/accelerated/day-6-client-testing.md +386 -0
  168. package/skills/tutorial/tracks/accelerated/day-7-graduation.md +369 -0
  169. package/skills/zoho-patterns/CHANGELOG.md +108 -0
  170. package/skills/zoho-patterns/SKILL.md +446 -0
  171. package/skills/zoho-patterns/analytics/dashboard-patterns.md +352 -0
  172. package/skills/zoho-patterns/analytics/zoho-to-bigquery-pipeline.md +427 -0
  173. package/skills/zoho-patterns/catalyst/appsail-deployment.md +349 -0
  174. package/skills/zoho-patterns/catalyst/context-close-patterns.md +354 -0
  175. package/skills/zoho-patterns/catalyst/cron-batch-processing.md +374 -0
  176. package/skills/zoho-patterns/catalyst/function-patterns.md +439 -0
  177. package/skills/zoho-patterns/creator/form-design.md +304 -0
  178. package/skills/zoho-patterns/creator/publish-api-patterns.md +313 -0
  179. package/skills/zoho-patterns/creator/widget-integration.md +306 -0
  180. package/skills/zoho-patterns/creator/workflow-automation.md +253 -0
  181. package/skills/zoho-patterns/deluge/api-patterns.md +468 -0
  182. package/skills/zoho-patterns/deluge/batch-processing.md +403 -0
  183. package/skills/zoho-patterns/deluge/cross-app-integration.md +356 -0
  184. package/skills/zoho-patterns/deluge/error-handling.md +423 -0
  185. package/skills/zoho-patterns/deluge/syntax-reference.md +65 -0
  186. package/skills/zoho-patterns/integration/cors-proxy-architecture.md +426 -0
  187. package/skills/zoho-patterns/integration/crm-books-native-sync.md +277 -0
  188. package/skills/zoho-patterns/integration/oauth-token-management.md +461 -0
  189. package/skills/zoho-patterns/integration/zoho-flow-patterns.md +334 -0
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Tutorial metrics collection module.
4
+ * Tracks local, privacy-respecting analytics for tutorial usage.
5
+ *
6
+ * Metrics are stored in ~/.claude/tutorial-metrics.json
7
+ * No external telemetry - data stays on your machine.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const os = require('os');
13
+
14
+ // Metrics file location
15
+ const METRICS_DIR = path.join(os.homedir(), '.claude');
16
+ const METRICS_FILE = path.join(METRICS_DIR, 'tutorial-metrics.json');
17
+
18
+ /**
19
+ * Ensure the metrics directory exists.
20
+ */
21
+ function ensureMetricsDir() {
22
+ if (!fs.existsSync(METRICS_DIR)) {
23
+ fs.mkdirSync(METRICS_DIR, { recursive: true });
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Get default metrics state.
29
+ * @returns {object} Default metrics object
30
+ */
31
+ function getDefaultMetrics() {
32
+ return {
33
+ lessonMetrics: {},
34
+ overallMetrics: {
35
+ startedAt: new Date().toISOString(),
36
+ completedAt: null,
37
+ totalSessions: 0,
38
+ totalTimeMs: 0,
39
+ },
40
+ activeLesson: null,
41
+ activeLessonStartTime: null,
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Load metrics from file.
47
+ * @returns {object} Metrics state or default if not exists
48
+ */
49
+ function loadMetrics() {
50
+ try {
51
+ if (fs.existsSync(METRICS_FILE)) {
52
+ const data = fs.readFileSync(METRICS_FILE, 'utf8');
53
+ return JSON.parse(data);
54
+ }
55
+ } catch {
56
+ // If file is corrupted, start fresh
57
+ }
58
+ return getDefaultMetrics();
59
+ }
60
+
61
+ /**
62
+ * Save metrics to file.
63
+ * @param {object} metrics - Metrics state to save
64
+ */
65
+ function saveMetrics(metrics) {
66
+ ensureMetricsDir();
67
+ fs.writeFileSync(METRICS_FILE, JSON.stringify(metrics, null, 2));
68
+ }
69
+
70
+ /**
71
+ * Record that a lesson was started/viewed.
72
+ * @param {number} lessonNum - Lesson number (1-9)
73
+ * @returns {object} Updated metrics
74
+ */
75
+ function recordLessonStart(lessonNum) {
76
+ const metrics = loadMetrics();
77
+
78
+ // Initialize lesson metrics if needed
79
+ if (!metrics.lessonMetrics[lessonNum]) {
80
+ metrics.lessonMetrics[lessonNum] = {
81
+ visits: 0,
82
+ totalTimeMs: 0,
83
+ lastVisit: null,
84
+ };
85
+ }
86
+
87
+ // Increment visit count
88
+ metrics.lessonMetrics[lessonNum].visits++;
89
+ metrics.lessonMetrics[lessonNum].lastVisit = new Date().toISOString();
90
+
91
+ // Track active lesson for time calculation
92
+ metrics.activeLesson = lessonNum;
93
+ metrics.activeLessonStartTime = Date.now();
94
+
95
+ saveMetrics(metrics);
96
+ return metrics;
97
+ }
98
+
99
+ /**
100
+ * Record that a lesson was completed/ended.
101
+ * @param {number} lessonNum - Lesson number (1-9)
102
+ * @returns {object} Updated metrics
103
+ */
104
+ function recordLessonEnd(lessonNum) {
105
+ const metrics = loadMetrics();
106
+
107
+ // Calculate time spent if this was the active lesson
108
+ if (metrics.activeLesson === lessonNum && metrics.activeLessonStartTime) {
109
+ const timeSpent = Date.now() - metrics.activeLessonStartTime;
110
+
111
+ if (!metrics.lessonMetrics[lessonNum]) {
112
+ metrics.lessonMetrics[lessonNum] = {
113
+ visits: 1,
114
+ totalTimeMs: 0,
115
+ lastVisit: new Date().toISOString(),
116
+ };
117
+ }
118
+
119
+ metrics.lessonMetrics[lessonNum].totalTimeMs += timeSpent;
120
+ metrics.overallMetrics.totalTimeMs += timeSpent;
121
+ }
122
+
123
+ // Clear active lesson
124
+ metrics.activeLesson = null;
125
+ metrics.activeLessonStartTime = null;
126
+
127
+ saveMetrics(metrics);
128
+ return metrics;
129
+ }
130
+
131
+ /**
132
+ * Record a new session.
133
+ * @returns {object} Updated metrics
134
+ */
135
+ function recordSession() {
136
+ const metrics = loadMetrics();
137
+ metrics.overallMetrics.totalSessions++;
138
+ saveMetrics(metrics);
139
+ return metrics;
140
+ }
141
+
142
+ /**
143
+ * Mark tutorial as completed.
144
+ * @returns {object} Updated metrics
145
+ */
146
+ function markComplete() {
147
+ const metrics = loadMetrics();
148
+ metrics.overallMetrics.completedAt = new Date().toISOString();
149
+ saveMetrics(metrics);
150
+ return metrics;
151
+ }
152
+
153
+ /**
154
+ * Format milliseconds as human-readable time.
155
+ * @param {number} ms - Milliseconds
156
+ * @returns {string} Formatted time string
157
+ */
158
+ function formatTime(ms) {
159
+ if (ms < 1000) return '< 1s';
160
+
161
+ const seconds = Math.floor(ms / 1000);
162
+ const minutes = Math.floor(seconds / 60);
163
+ const hours = Math.floor(minutes / 60);
164
+
165
+ if (hours > 0) {
166
+ const remainingMinutes = minutes % 60;
167
+ return `${hours}h ${remainingMinutes}m`;
168
+ }
169
+ if (minutes > 0) {
170
+ const remainingSeconds = seconds % 60;
171
+ return `${minutes}m ${remainingSeconds}s`;
172
+ }
173
+ return `${seconds}s`;
174
+ }
175
+
176
+ /**
177
+ * Get summary statistics.
178
+ * @returns {object} Stats object
179
+ */
180
+ function getStats() {
181
+ const metrics = loadMetrics();
182
+
183
+ // Calculate most visited lesson
184
+ let mostVisited = null;
185
+ let maxVisits = 0;
186
+
187
+ for (const [lessonNum, data] of Object.entries(metrics.lessonMetrics)) {
188
+ if (data.visits > maxVisits) {
189
+ maxVisits = data.visits;
190
+ mostVisited = parseInt(lessonNum, 10);
191
+ }
192
+ }
193
+
194
+ // Calculate average time per lesson
195
+ const lessonCount = Object.keys(metrics.lessonMetrics).length;
196
+ const avgTimeMs = lessonCount > 0 ? metrics.overallMetrics.totalTimeMs / lessonCount : 0;
197
+
198
+ return {
199
+ totalTime: formatTime(metrics.overallMetrics.totalTimeMs),
200
+ totalTimeMs: metrics.overallMetrics.totalTimeMs,
201
+ totalSessions: metrics.overallMetrics.totalSessions,
202
+ lessonsVisited: lessonCount,
203
+ mostVisited: mostVisited,
204
+ mostVisitedCount: maxVisits,
205
+ averageTimePerLesson: formatTime(avgTimeMs),
206
+ startedAt: metrics.overallMetrics.startedAt,
207
+ completedAt: metrics.overallMetrics.completedAt,
208
+ lessonMetrics: metrics.lessonMetrics,
209
+ };
210
+ }
211
+
212
+ /**
213
+ * Reset all metrics.
214
+ * @returns {object} Fresh metrics state
215
+ */
216
+ function resetMetrics() {
217
+ const metrics = getDefaultMetrics();
218
+ saveMetrics(metrics);
219
+ return metrics;
220
+ }
221
+
222
+ /**
223
+ * Check if metrics file exists.
224
+ * @returns {boolean} True if metrics file exists
225
+ */
226
+ function hasMetrics() {
227
+ return fs.existsSync(METRICS_FILE);
228
+ }
229
+
230
+ module.exports = {
231
+ METRICS_FILE,
232
+ loadMetrics,
233
+ saveMetrics,
234
+ recordLessonStart,
235
+ recordLessonEnd,
236
+ recordSession,
237
+ markComplete,
238
+ getStats,
239
+ resetMetrics,
240
+ hasMetrics,
241
+ formatTime,
242
+ };
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Tutorial progress persistence module.
4
+ * Manages progress state for the interactive tutorial system.
5
+ *
6
+ * Progress is stored in ~/.claude/tutorial-progress.json
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const os = require('os');
12
+
13
+ // Progress file location
14
+ const PROGRESS_DIR = path.join(os.homedir(), '.claude');
15
+ const PROGRESS_FILE = path.join(PROGRESS_DIR, 'tutorial-progress.json');
16
+
17
+ // Total lessons in the tutorial (lessons 0-14 = 15 lessons total)
18
+ const TOTAL_LESSONS = 15;
19
+
20
+ // Lesson metadata (lesson numbers 1-14 for display, but lesson 0 is Philosophy)
21
+ const LESSONS = [
22
+ { num: 0, file: '00-philosophy-and-workflow.md', title: 'Philosophy & Workflow' },
23
+ { num: 1, file: '01-basics.md', title: 'Core Concepts' },
24
+ { num: 2, file: '02-training.md', title: 'Training Mode' },
25
+ { num: 3, file: '03-commands.md', title: 'Essential Commands' },
26
+ { num: 4, file: '04-workflows.md', title: 'Workflow Patterns' },
27
+ { num: 5, file: '05-compliance.md', title: 'Compliance' },
28
+ { num: 6, file: '06-zoho.md', title: 'Zoho Development' },
29
+ { num: 7, file: '07-hooks-system.md', title: 'Hooks System' },
30
+ { num: 8, file: '08-mcp-servers.md', title: 'MCP Servers' },
31
+ { num: 9, file: '09-client-management.md', title: 'Client Management' },
32
+ { num: 10, file: '10-testing-e2e.md', title: 'Testing & E2E' },
33
+ { num: 11, file: '11-skills-deep-dive.md', title: 'Skills Deep Dive' },
34
+ { num: 12, file: '12-rules-system.md', title: 'Rules System' },
35
+ { num: 13, file: '13-golden-standard-graduation.md', title: 'Golden Standard Graduation' },
36
+ { num: 14, file: '14-fork-setup-and-sync.md', title: 'Fork Setup & Sync' },
37
+ ];
38
+
39
+ /**
40
+ * Ensure the progress directory exists.
41
+ */
42
+ function ensureProgressDir() {
43
+ if (!fs.existsSync(PROGRESS_DIR)) {
44
+ fs.mkdirSync(PROGRESS_DIR, { recursive: true });
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Get default progress state.
50
+ * @returns {object} Default progress object
51
+ */
52
+ function getDefaultProgress() {
53
+ return {
54
+ currentLesson: 0, // Start with lesson 0 (Philosophy)
55
+ completedLessons: [],
56
+ startedAt: new Date().toISOString(),
57
+ lastAccessed: new Date().toISOString(),
58
+ totalSessions: 1,
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Load progress from file.
64
+ * @returns {object} Progress state or default if not exists
65
+ */
66
+ function loadProgress() {
67
+ try {
68
+ if (fs.existsSync(PROGRESS_FILE)) {
69
+ const data = fs.readFileSync(PROGRESS_FILE, 'utf8');
70
+ const progress = JSON.parse(data);
71
+ // Update last accessed time
72
+ progress.lastAccessed = new Date().toISOString();
73
+ progress.totalSessions = (progress.totalSessions || 0) + 1;
74
+ return progress;
75
+ }
76
+ } catch {
77
+ // If file is corrupted, start fresh
78
+ }
79
+ return getDefaultProgress();
80
+ }
81
+
82
+ /**
83
+ * Save progress to file.
84
+ * @param {object} progress - Progress state to save
85
+ */
86
+ function saveProgress(progress) {
87
+ ensureProgressDir();
88
+ progress.lastAccessed = new Date().toISOString();
89
+ fs.writeFileSync(PROGRESS_FILE, JSON.stringify(progress, null, 2));
90
+ }
91
+
92
+ /**
93
+ * Mark a lesson as completed.
94
+ * @param {number} lessonNum - Lesson number (0-14)
95
+ * @returns {object} Updated progress
96
+ */
97
+ function completeLesson(lessonNum) {
98
+ const progress = loadProgress();
99
+
100
+ if (!progress.completedLessons.includes(lessonNum)) {
101
+ progress.completedLessons.push(lessonNum);
102
+ progress.completedLessons.sort((a, b) => a - b);
103
+ }
104
+
105
+ // Move to next lesson if current (max lesson is TOTAL_LESSONS - 1)
106
+ if (lessonNum === progress.currentLesson && lessonNum < TOTAL_LESSONS - 1) {
107
+ progress.currentLesson = lessonNum + 1;
108
+ }
109
+
110
+ // Check for completion
111
+ if (progress.completedLessons.length === TOTAL_LESSONS) {
112
+ progress.completedAt = new Date().toISOString();
113
+ }
114
+
115
+ saveProgress(progress);
116
+ return progress;
117
+ }
118
+
119
+ /**
120
+ * Set the current lesson.
121
+ * @param {number} lessonNum - Lesson number (0-14)
122
+ * @returns {object} Updated progress
123
+ */
124
+ function setCurrentLesson(lessonNum) {
125
+ if (lessonNum < 0 || lessonNum >= TOTAL_LESSONS) {
126
+ throw new Error(`Invalid lesson number: ${lessonNum}. Must be 0-${TOTAL_LESSONS - 1}`);
127
+ }
128
+ const progress = loadProgress();
129
+ progress.currentLesson = lessonNum;
130
+ saveProgress(progress);
131
+ return progress;
132
+ }
133
+
134
+ /**
135
+ * Reset all progress.
136
+ * @returns {object} Fresh progress state
137
+ */
138
+ function resetProgress() {
139
+ const progress = getDefaultProgress();
140
+ saveProgress(progress);
141
+ return progress;
142
+ }
143
+
144
+ /**
145
+ * Get completion percentage.
146
+ * @param {object} progress - Progress state
147
+ * @returns {number} Completion percentage (0-100)
148
+ */
149
+ function getCompletionPercent(progress) {
150
+ return Math.round((progress.completedLessons.length / TOTAL_LESSONS) * 100);
151
+ }
152
+
153
+ /**
154
+ * Check if tutorial is complete.
155
+ * @param {object} progress - Progress state
156
+ * @returns {boolean} True if all lessons completed
157
+ */
158
+ function isComplete(progress) {
159
+ return progress.completedLessons.length === TOTAL_LESSONS;
160
+ }
161
+
162
+ /**
163
+ * Get lesson info by number.
164
+ * @param {number} lessonNum - Lesson number (0-14)
165
+ * @returns {object|null} Lesson info or null
166
+ */
167
+ function getLessonInfo(lessonNum) {
168
+ return LESSONS.find((l) => l.num === lessonNum) || null;
169
+ }
170
+
171
+ /**
172
+ * Get lesson status for display.
173
+ * @param {object} progress - Progress state
174
+ * @param {number} lessonNum - Lesson number
175
+ * @returns {string} Status: 'completed', 'current', or 'pending'
176
+ */
177
+ function getLessonStatus(progress, lessonNum) {
178
+ if (progress.completedLessons.includes(lessonNum)) {
179
+ return 'completed';
180
+ }
181
+ if (lessonNum === progress.currentLesson) {
182
+ return 'current';
183
+ }
184
+ return 'pending';
185
+ }
186
+
187
+ /**
188
+ * Check if progress file exists.
189
+ * @returns {boolean} True if progress file exists
190
+ */
191
+ function hasProgress() {
192
+ return fs.existsSync(PROGRESS_FILE);
193
+ }
194
+
195
+ module.exports = {
196
+ TOTAL_LESSONS,
197
+ LESSONS,
198
+ PROGRESS_FILE,
199
+ loadProgress,
200
+ saveProgress,
201
+ completeLesson,
202
+ setCurrentLesson,
203
+ resetProgress,
204
+ getCompletionPercent,
205
+ isComplete,
206
+ getLessonInfo,
207
+ getLessonStatus,
208
+ hasProgress,
209
+ };