agentsys 5.0.3 → 5.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 (264) hide show
  1. package/.claude-plugin/marketplace.json +21 -14
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/AGENTS.md +2 -1
  4. package/CHANGELOG.md +18 -0
  5. package/README.md +7 -6
  6. package/adapters/codex/skills/agnix/SKILL.md +0 -1
  7. package/adapters/codex/skills/audit-project/SKILL.md +0 -1
  8. package/adapters/codex/skills/audit-project-agents/SKILL.md +0 -1
  9. package/adapters/codex/skills/audit-project-github/SKILL.md +0 -1
  10. package/adapters/codex/skills/consult/SKILL.md +132 -57
  11. package/adapters/codex/skills/debate/SKILL.md +214 -0
  12. package/adapters/codex/skills/delivery-approval/SKILL.md +0 -1
  13. package/adapters/codex/skills/deslop/SKILL.md +0 -1
  14. package/adapters/codex/skills/drift-detect/SKILL.md +0 -1
  15. package/adapters/codex/skills/enhance/SKILL.md +0 -1
  16. package/adapters/codex/skills/learn/SKILL.md +0 -1
  17. package/adapters/codex/skills/next-task/SKILL.md +0 -1
  18. package/adapters/codex/skills/perf/SKILL.md +0 -1
  19. package/adapters/codex/skills/repo-map/SKILL.md +0 -1
  20. package/adapters/codex/skills/ship/SKILL.md +0 -1
  21. package/adapters/codex/skills/ship-ci-review-loop/SKILL.md +0 -1
  22. package/adapters/codex/skills/ship-deployment/SKILL.md +0 -1
  23. package/adapters/codex/skills/ship-error-handling/SKILL.md +0 -1
  24. package/adapters/codex/skills/sync-docs/SKILL.md +0 -1
  25. package/adapters/opencode/agents/agent-enhancer.md +0 -1
  26. package/adapters/opencode/agents/agnix-agent.md +0 -1
  27. package/adapters/opencode/agents/ci-fixer.md +0 -1
  28. package/adapters/opencode/agents/ci-monitor.md +0 -1
  29. package/adapters/opencode/agents/claudemd-enhancer.md +0 -1
  30. package/adapters/opencode/agents/consult-agent.md +122 -30
  31. package/adapters/opencode/agents/cross-file-enhancer.md +0 -1
  32. package/adapters/opencode/agents/debate-orchestrator.md +169 -0
  33. package/adapters/opencode/agents/delivery-validator.md +0 -1
  34. package/adapters/opencode/agents/deslop-agent.md +0 -1
  35. package/adapters/opencode/agents/docs-enhancer.md +0 -1
  36. package/adapters/opencode/agents/exploration-agent.md +0 -1
  37. package/adapters/opencode/agents/hooks-enhancer.md +0 -1
  38. package/adapters/opencode/agents/implementation-agent.md +0 -1
  39. package/adapters/opencode/agents/learn-agent.md +0 -1
  40. package/adapters/opencode/agents/map-validator.md +0 -1
  41. package/adapters/opencode/agents/perf-analyzer.md +0 -1
  42. package/adapters/opencode/agents/perf-code-paths.md +0 -1
  43. package/adapters/opencode/agents/perf-investigation-logger.md +0 -1
  44. package/adapters/opencode/agents/perf-orchestrator.md +0 -1
  45. package/adapters/opencode/agents/perf-theory-gatherer.md +0 -1
  46. package/adapters/opencode/agents/perf-theory-tester.md +0 -1
  47. package/adapters/opencode/agents/plan-synthesizer.md +0 -1
  48. package/adapters/opencode/agents/planning-agent.md +0 -1
  49. package/adapters/opencode/agents/plugin-enhancer.md +0 -1
  50. package/adapters/opencode/agents/prompt-enhancer.md +0 -1
  51. package/adapters/opencode/agents/simple-fixer.md +0 -1
  52. package/adapters/opencode/agents/skills-enhancer.md +0 -1
  53. package/adapters/opencode/agents/sync-docs-agent.md +0 -1
  54. package/adapters/opencode/agents/task-discoverer.md +0 -1
  55. package/adapters/opencode/agents/test-coverage-checker.md +0 -1
  56. package/adapters/opencode/agents/worktree-manager.md +0 -1
  57. package/adapters/opencode/commands/agnix.md +0 -1
  58. package/adapters/opencode/commands/audit-project-agents.md +0 -1
  59. package/adapters/opencode/commands/audit-project-github.md +0 -1
  60. package/adapters/opencode/commands/audit-project.md +0 -1
  61. package/adapters/opencode/commands/consult.md +133 -57
  62. package/adapters/opencode/commands/debate.md +224 -0
  63. package/adapters/opencode/commands/delivery-approval.md +0 -1
  64. package/adapters/opencode/commands/deslop.md +0 -1
  65. package/adapters/opencode/commands/drift-detect.md +0 -1
  66. package/adapters/opencode/commands/enhance.md +0 -1
  67. package/adapters/opencode/commands/learn.md +0 -1
  68. package/adapters/opencode/commands/next-task.md +0 -1
  69. package/adapters/opencode/commands/perf.md +0 -1
  70. package/adapters/opencode/commands/repo-map.md +0 -1
  71. package/adapters/opencode/commands/ship-ci-review-loop.md +0 -1
  72. package/adapters/opencode/commands/ship-deployment.md +0 -1
  73. package/adapters/opencode/commands/ship-error-handling.md +0 -1
  74. package/adapters/opencode/commands/ship.md +0 -1
  75. package/adapters/opencode/commands/sync-docs.md +0 -1
  76. package/adapters/opencode/skills/agnix/SKILL.md +1 -2
  77. package/adapters/opencode/skills/consult/SKILL.md +33 -23
  78. package/adapters/opencode/skills/debate/SKILL.md +245 -0
  79. package/adapters/opencode/skills/deslop/SKILL.md +1 -2
  80. package/adapters/opencode/skills/discover-tasks/SKILL.md +1 -2
  81. package/adapters/opencode/skills/drift-analysis/SKILL.md +1 -2
  82. package/adapters/opencode/skills/enhance-agent-prompts/SKILL.md +1 -2
  83. package/adapters/opencode/skills/enhance-claude-memory/SKILL.md +1 -2
  84. package/adapters/opencode/skills/enhance-cross-file/SKILL.md +1 -2
  85. package/adapters/opencode/skills/enhance-docs/SKILL.md +1 -2
  86. package/adapters/opencode/skills/enhance-hooks/SKILL.md +1 -2
  87. package/adapters/opencode/skills/enhance-orchestrator/SKILL.md +1 -2
  88. package/adapters/opencode/skills/enhance-plugins/SKILL.md +1 -2
  89. package/adapters/opencode/skills/enhance-prompts/SKILL.md +1 -2
  90. package/adapters/opencode/skills/enhance-skills/SKILL.md +1 -2
  91. package/adapters/opencode/skills/learn/SKILL.md +1 -2
  92. package/adapters/opencode/skills/orchestrate-review/SKILL.md +0 -1
  93. package/adapters/opencode/skills/perf-analyzer/SKILL.md +1 -2
  94. package/adapters/opencode/skills/perf-baseline-manager/SKILL.md +1 -2
  95. package/adapters/opencode/skills/perf-benchmarker/SKILL.md +1 -2
  96. package/adapters/opencode/skills/perf-code-paths/SKILL.md +1 -2
  97. package/adapters/opencode/skills/perf-investigation-logger/SKILL.md +1 -2
  98. package/adapters/opencode/skills/perf-profiler/SKILL.md +1 -2
  99. package/adapters/opencode/skills/perf-theory-gatherer/SKILL.md +1 -2
  100. package/adapters/opencode/skills/perf-theory-tester/SKILL.md +1 -2
  101. package/adapters/opencode/skills/repo-mapping/SKILL.md +1 -2
  102. package/adapters/opencode/skills/sync-docs/SKILL.md +1 -2
  103. package/adapters/opencode/skills/validate-delivery/SKILL.md +1 -2
  104. package/lib/adapter-transforms.js +24 -4
  105. package/package.json +1 -1
  106. package/plugins/agnix/.claude-plugin/plugin.json +1 -1
  107. package/plugins/agnix/skills/agnix/SKILL.md +1 -1
  108. package/plugins/audit-project/.claude-plugin/plugin.json +1 -1
  109. package/plugins/audit-project/lib/adapter-transforms.js +24 -4
  110. package/plugins/consult/.claude-plugin/plugin.json +1 -1
  111. package/plugins/consult/agents/consult-agent.md +122 -29
  112. package/plugins/consult/commands/consult.md +135 -58
  113. package/plugins/consult/skills/consult/SKILL.md +31 -20
  114. package/plugins/debate/.claude-plugin/plugin.json +21 -0
  115. package/plugins/debate/agents/debate-orchestrator.md +175 -0
  116. package/plugins/debate/commands/debate.md +221 -0
  117. package/plugins/debate/lib/adapter-transforms.js +298 -0
  118. package/plugins/debate/lib/collectors/codebase.js +392 -0
  119. package/plugins/debate/lib/collectors/docs-patterns.js +713 -0
  120. package/plugins/debate/lib/collectors/documentation.js +219 -0
  121. package/plugins/debate/lib/collectors/github.js +330 -0
  122. package/plugins/debate/lib/collectors/index.js +126 -0
  123. package/plugins/debate/lib/config/index.js +14 -0
  124. package/plugins/debate/lib/cross-platform/index.js +539 -0
  125. package/plugins/debate/lib/discovery/index.js +352 -0
  126. package/plugins/debate/lib/drift-detect/collectors.js +37 -0
  127. package/plugins/debate/lib/enhance/agent-analyzer.js +421 -0
  128. package/plugins/debate/lib/enhance/agent-patterns.js +571 -0
  129. package/plugins/debate/lib/enhance/auto-suppression.js +622 -0
  130. package/plugins/debate/lib/enhance/benchmark.js +417 -0
  131. package/plugins/debate/lib/enhance/cross-file-analyzer.js +930 -0
  132. package/plugins/debate/lib/enhance/cross-file-patterns.js +370 -0
  133. package/plugins/debate/lib/enhance/docs-analyzer.js +325 -0
  134. package/plugins/debate/lib/enhance/docs-patterns.js +671 -0
  135. package/plugins/debate/lib/enhance/fixer.js +721 -0
  136. package/plugins/debate/lib/enhance/hook-analyzer.js +135 -0
  137. package/plugins/debate/lib/enhance/hook-patterns.js +40 -0
  138. package/plugins/debate/lib/enhance/index.js +127 -0
  139. package/plugins/debate/lib/enhance/plugin-analyzer.js +402 -0
  140. package/plugins/debate/lib/enhance/plugin-patterns.js +326 -0
  141. package/plugins/debate/lib/enhance/projectmemory-analyzer.js +551 -0
  142. package/plugins/debate/lib/enhance/projectmemory-patterns.js +617 -0
  143. package/plugins/debate/lib/enhance/prompt-analyzer.js +457 -0
  144. package/plugins/debate/lib/enhance/prompt-patterns.js +1484 -0
  145. package/plugins/debate/lib/enhance/reporter.js +1348 -0
  146. package/plugins/debate/lib/enhance/security-patterns.js +284 -0
  147. package/plugins/debate/lib/enhance/skill-analyzer.js +182 -0
  148. package/plugins/debate/lib/enhance/skill-patterns.js +147 -0
  149. package/plugins/debate/lib/enhance/suppression.js +352 -0
  150. package/plugins/debate/lib/enhance/tool-patterns.js +373 -0
  151. package/plugins/debate/lib/index.js +270 -0
  152. package/plugins/debate/lib/patterns/cli-enhancers.js +611 -0
  153. package/plugins/debate/lib/patterns/pipeline.js +948 -0
  154. package/plugins/debate/lib/patterns/review-patterns.js +558 -0
  155. package/plugins/debate/lib/patterns/slop-analyzers.js +2305 -0
  156. package/plugins/debate/lib/patterns/slop-patterns.js +1187 -0
  157. package/plugins/debate/lib/perf/analyzer/index.js +22 -0
  158. package/plugins/debate/lib/perf/argument-parser.js +105 -0
  159. package/plugins/debate/lib/perf/baseline-comparator.js +50 -0
  160. package/plugins/debate/lib/perf/baseline-store.js +127 -0
  161. package/plugins/debate/lib/perf/benchmark-runner.js +404 -0
  162. package/plugins/debate/lib/perf/breaking-point-finder.js +52 -0
  163. package/plugins/debate/lib/perf/breaking-point-runner.js +60 -0
  164. package/plugins/debate/lib/perf/checkpoint.js +123 -0
  165. package/plugins/debate/lib/perf/code-paths.js +86 -0
  166. package/plugins/debate/lib/perf/consolidation.js +37 -0
  167. package/plugins/debate/lib/perf/constraint-runner.js +71 -0
  168. package/plugins/debate/lib/perf/experiment-runner.js +32 -0
  169. package/plugins/debate/lib/perf/index.js +41 -0
  170. package/plugins/debate/lib/perf/investigation-state.js +874 -0
  171. package/plugins/debate/lib/perf/optimization-runner.js +79 -0
  172. package/plugins/debate/lib/perf/profilers/go.js +22 -0
  173. package/plugins/debate/lib/perf/profilers/index.js +46 -0
  174. package/plugins/debate/lib/perf/profilers/java.js +23 -0
  175. package/plugins/debate/lib/perf/profilers/node.js +27 -0
  176. package/plugins/debate/lib/perf/profilers/python.js +23 -0
  177. package/plugins/debate/lib/perf/profilers/rust.js +23 -0
  178. package/plugins/debate/lib/perf/profiling-runner.js +75 -0
  179. package/plugins/debate/lib/perf/schemas.js +140 -0
  180. package/plugins/debate/lib/platform/detect-platform.js +413 -0
  181. package/plugins/debate/lib/platform/detection-configs.js +93 -0
  182. package/plugins/debate/lib/platform/state-dir.js +132 -0
  183. package/plugins/debate/lib/platform/verify-tools.js +182 -0
  184. package/plugins/debate/lib/repo-map/cache.js +152 -0
  185. package/plugins/debate/lib/repo-map/concurrency.js +29 -0
  186. package/plugins/debate/lib/repo-map/index.js +222 -0
  187. package/plugins/debate/lib/repo-map/installer.js +212 -0
  188. package/plugins/debate/lib/repo-map/queries/go.js +27 -0
  189. package/plugins/debate/lib/repo-map/queries/index.js +100 -0
  190. package/plugins/debate/lib/repo-map/queries/java.js +38 -0
  191. package/plugins/debate/lib/repo-map/queries/javascript.js +55 -0
  192. package/plugins/debate/lib/repo-map/queries/python.js +24 -0
  193. package/plugins/debate/lib/repo-map/queries/rust.js +73 -0
  194. package/plugins/debate/lib/repo-map/queries/typescript.js +38 -0
  195. package/plugins/debate/lib/repo-map/runner.js +1364 -0
  196. package/plugins/debate/lib/repo-map/updater.js +562 -0
  197. package/plugins/debate/lib/repo-map/usage-analyzer.js +407 -0
  198. package/plugins/debate/lib/schemas/plugin-manifest.schema.json +57 -0
  199. package/plugins/debate/lib/schemas/validator.js +247 -0
  200. package/plugins/debate/lib/sources/custom-handler.js +199 -0
  201. package/plugins/debate/lib/sources/policy-questions.js +246 -0
  202. package/plugins/debate/lib/sources/source-cache.js +165 -0
  203. package/plugins/debate/lib/state/workflow-state.js +576 -0
  204. package/plugins/debate/lib/types/agent-frontmatter.d.ts +134 -0
  205. package/plugins/debate/lib/types/command-frontmatter.d.ts +107 -0
  206. package/plugins/debate/lib/types/hook-frontmatter.d.ts +115 -0
  207. package/plugins/debate/lib/types/index.d.ts +84 -0
  208. package/plugins/debate/lib/types/plugin-manifest.d.ts +102 -0
  209. package/plugins/debate/lib/types/skill-frontmatter.d.ts +89 -0
  210. package/plugins/debate/lib/utils/atomic-write.js +94 -0
  211. package/plugins/debate/lib/utils/cache-manager.js +159 -0
  212. package/plugins/debate/lib/utils/command-parser.js +0 -0
  213. package/plugins/debate/lib/utils/context-optimizer.js +300 -0
  214. package/plugins/debate/lib/utils/deprecation.js +37 -0
  215. package/plugins/debate/lib/utils/shell-escape.js +88 -0
  216. package/plugins/debate/lib/utils/state-helpers.js +61 -0
  217. package/plugins/debate/skills/debate/SKILL.md +264 -0
  218. package/plugins/deslop/.claude-plugin/plugin.json +1 -1
  219. package/plugins/deslop/lib/adapter-transforms.js +24 -4
  220. package/plugins/deslop/skills/deslop/SKILL.md +1 -1
  221. package/plugins/drift-detect/.claude-plugin/plugin.json +1 -1
  222. package/plugins/drift-detect/lib/adapter-transforms.js +24 -4
  223. package/plugins/drift-detect/skills/drift-analysis/SKILL.md +1 -1
  224. package/plugins/enhance/.claude-plugin/plugin.json +1 -1
  225. package/plugins/enhance/lib/adapter-transforms.js +24 -4
  226. package/plugins/enhance/skills/enhance-agent-prompts/SKILL.md +1 -1
  227. package/plugins/enhance/skills/enhance-claude-memory/SKILL.md +1 -1
  228. package/plugins/enhance/skills/enhance-cross-file/SKILL.md +1 -1
  229. package/plugins/enhance/skills/enhance-docs/SKILL.md +1 -1
  230. package/plugins/enhance/skills/enhance-hooks/SKILL.md +1 -1
  231. package/plugins/enhance/skills/enhance-orchestrator/SKILL.md +1 -1
  232. package/plugins/enhance/skills/enhance-plugins/SKILL.md +1 -1
  233. package/plugins/enhance/skills/enhance-prompts/SKILL.md +1 -1
  234. package/plugins/enhance/skills/enhance-skills/SKILL.md +1 -1
  235. package/plugins/learn/.claude-plugin/plugin.json +1 -1
  236. package/plugins/learn/agents/learn-agent.md +1 -1
  237. package/plugins/learn/lib/adapter-transforms.js +24 -4
  238. package/plugins/learn/skills/learn/SKILL.md +1 -1
  239. package/plugins/next-task/.claude-plugin/plugin.json +1 -1
  240. package/plugins/next-task/agents/exploration-agent.md +1 -1
  241. package/plugins/next-task/lib/adapter-transforms.js +24 -4
  242. package/plugins/next-task/skills/discover-tasks/SKILL.md +1 -1
  243. package/plugins/next-task/skills/validate-delivery/SKILL.md +1 -1
  244. package/plugins/perf/.claude-plugin/plugin.json +1 -1
  245. package/plugins/perf/lib/adapter-transforms.js +24 -4
  246. package/plugins/perf/skills/perf-analyzer/SKILL.md +1 -1
  247. package/plugins/perf/skills/perf-baseline-manager/SKILL.md +1 -1
  248. package/plugins/perf/skills/perf-benchmarker/SKILL.md +1 -1
  249. package/plugins/perf/skills/perf-code-paths/SKILL.md +1 -1
  250. package/plugins/perf/skills/perf-investigation-logger/SKILL.md +1 -1
  251. package/plugins/perf/skills/perf-profiler/SKILL.md +1 -1
  252. package/plugins/perf/skills/perf-theory-gatherer/SKILL.md +1 -1
  253. package/plugins/perf/skills/perf-theory-tester/SKILL.md +1 -1
  254. package/plugins/repo-map/.claude-plugin/plugin.json +1 -1
  255. package/plugins/repo-map/lib/adapter-transforms.js +24 -4
  256. package/plugins/ship/.claude-plugin/plugin.json +1 -1
  257. package/plugins/ship/lib/adapter-transforms.js +24 -4
  258. package/plugins/sync-docs/.claude-plugin/plugin.json +1 -1
  259. package/plugins/sync-docs/lib/adapter-transforms.js +24 -4
  260. package/plugins/sync-docs/skills/sync-docs/SKILL.md +1 -1
  261. package/scripts/gen-adapters.js +6 -7
  262. package/scripts/generate-docs.js +4 -2
  263. package/scripts/plugins.txt +1 -0
  264. package/site/content.json +6 -6
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Documentation Collector
3
+ *
4
+ * Analyzes documentation files: README, PLAN, CHANGELOG, etc.
5
+ * Extracted from drift-detect/collectors.js for shared use.
6
+ *
7
+ * @module lib/collectors/documentation
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ const DEFAULT_OPTIONS = {
16
+ depth: 'thorough',
17
+ cwd: process.cwd()
18
+ };
19
+
20
+ /**
21
+ * Validate file path to prevent path traversal
22
+ * @param {string} filePath - Path to validate
23
+ * @param {string} basePath - Base directory
24
+ * @returns {boolean} True if path is safe
25
+ */
26
+ function isPathSafe(filePath, basePath) {
27
+ const resolved = path.resolve(basePath, filePath);
28
+ return resolved.startsWith(path.resolve(basePath));
29
+ }
30
+
31
+ /**
32
+ * Safe file read with path validation
33
+ * @param {string} filePath - Path to read
34
+ * @param {string} basePath - Base directory for validation
35
+ * @returns {string|null} File contents or null
36
+ */
37
+ function safeReadFile(filePath, basePath) {
38
+ const fullPath = path.resolve(basePath, filePath);
39
+ if (!isPathSafe(filePath, basePath)) {
40
+ return null;
41
+ }
42
+ try {
43
+ return fs.readFileSync(fullPath, 'utf8');
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Analyze a single markdown file
51
+ */
52
+ function analyzeMarkdownFile(content, filePath) {
53
+ const sectionMatches = content.match(/^##\s+(.+)$/gm) || [];
54
+ const sections = sectionMatches.slice(0, 10).map(s => s.replace(/^##\s+/, ''));
55
+ const sectionLower = sections.map(s => s.toLowerCase()).join(' ');
56
+
57
+ return {
58
+ path: filePath,
59
+ sectionCount: sectionMatches.length,
60
+ sections,
61
+ hasInstallation: /install|setup|getting.started/i.test(sectionLower),
62
+ hasUsage: /usage|how.to|example/i.test(sectionLower),
63
+ hasApi: /api|reference|methods/i.test(sectionLower),
64
+ hasTesting: /test|spec|coverage/i.test(sectionLower),
65
+ codeBlocks: Math.floor((content.match(/```/g) || []).length / 2),
66
+ wordCount: content.split(/\s+/).length
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Extract checkboxes from content
72
+ */
73
+ function extractCheckboxes(result, content) {
74
+ const checked = (content.match(/^[-*]\s+\[x\]/gim) || []).length;
75
+ const unchecked = (content.match(/^[-*]\s+\[\s\]/gim) || []).length;
76
+
77
+ result.checkboxes.checked += checked;
78
+ result.checkboxes.unchecked += unchecked;
79
+ result.checkboxes.total += checked + unchecked;
80
+ }
81
+
82
+ /**
83
+ * Extract documented features
84
+ */
85
+ function extractFeatures(result, content) {
86
+ const featurePattern = /^[-*]\s+\*{0,2}(.+?)\*{0,2}(?:\s*[-–]\s*(.+))?$/gm;
87
+ let match;
88
+
89
+ while ((match = featurePattern.exec(content)) !== null && result.features.length < 20) {
90
+ const feature = match[1].trim();
91
+ if (feature.length > 5 && feature.length < 80) {
92
+ result.features.push(feature);
93
+ }
94
+ }
95
+
96
+ result.features = [...new Set(result.features)].slice(0, 20);
97
+ }
98
+
99
+ /**
100
+ * Extract planned items from content
101
+ */
102
+ function extractPlans(result, content) {
103
+ const planPatterns = [
104
+ /(?:TODO|FIXME|PLAN):\s*(.+)/gi,
105
+ /^##\s+(?:Roadmap|Future|Planned|Coming Soon)/gim
106
+ ];
107
+
108
+ for (const pattern of planPatterns) {
109
+ let match;
110
+ while ((match = pattern.exec(content)) !== null && result.plans.length < 15) {
111
+ const plan = (match[1] || match[0]).slice(0, 100);
112
+ result.plans.push(plan);
113
+ }
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Identify documentation gaps
119
+ */
120
+ function identifyDocGaps(result) {
121
+ const readme = result.files['README.md'];
122
+
123
+ if (!readme) {
124
+ result.gaps.push({ type: 'missing', file: 'README.md', severity: 'high' });
125
+ } else {
126
+ if (!readme.hasInstallation) {
127
+ result.gaps.push({ type: 'missing-section', file: 'README.md', section: 'Installation', severity: 'medium' });
128
+ }
129
+ if (!readme.hasUsage) {
130
+ result.gaps.push({ type: 'missing-section', file: 'README.md', section: 'Usage', severity: 'medium' });
131
+ }
132
+ }
133
+
134
+ if (!result.files['CHANGELOG.md']) {
135
+ result.gaps.push({ type: 'missing', file: 'CHANGELOG.md', severity: 'low' });
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Analyze documentation files
141
+ * @param {Object} options - Collection options
142
+ * @returns {Object} Documentation analysis
143
+ */
144
+ function analyzeDocumentation(options = {}) {
145
+ const opts = { ...DEFAULT_OPTIONS, ...options };
146
+ const basePath = opts.cwd;
147
+
148
+ const result = {
149
+ summary: { fileCount: 0, totalWords: 0 },
150
+ files: {},
151
+ features: [],
152
+ plans: [],
153
+ checkboxes: { total: 0, checked: 0, unchecked: 0 },
154
+ gaps: []
155
+ };
156
+
157
+ const docFiles = [
158
+ 'README.md',
159
+ 'PLAN.md',
160
+ 'CLAUDE.md',
161
+ 'AGENTS.md',
162
+ 'CONTRIBUTING.md',
163
+ 'CHANGELOG.md',
164
+ 'docs/README.md',
165
+ 'docs/PLAN.md'
166
+ ];
167
+
168
+ for (const file of docFiles) {
169
+ const content = safeReadFile(file, basePath);
170
+ if (content) {
171
+ const analysis = analyzeMarkdownFile(content, file);
172
+ result.files[file] = analysis;
173
+ result.summary.totalWords += analysis.wordCount;
174
+ extractCheckboxes(result, content);
175
+ extractFeatures(result, content);
176
+ extractPlans(result, content);
177
+ }
178
+ }
179
+
180
+ // Find additional markdown files if depth is thorough
181
+ if (opts.depth === 'thorough') {
182
+ const docsDir = path.join(basePath, 'docs');
183
+ if (fs.existsSync(docsDir)) {
184
+ try {
185
+ const additionalFiles = fs.readdirSync(docsDir)
186
+ .filter(f => f.endsWith('.md') && !docFiles.includes(`docs/${f}`));
187
+
188
+ for (const file of additionalFiles.slice(0, 5)) {
189
+ const filePath = `docs/${file}`;
190
+ const content = safeReadFile(filePath, basePath);
191
+ if (content) {
192
+ const analysis = analyzeMarkdownFile(content, filePath);
193
+ result.files[filePath] = analysis;
194
+ result.summary.totalWords += analysis.wordCount;
195
+ }
196
+ }
197
+ } catch {
198
+ // Ignore directory read errors
199
+ }
200
+ }
201
+ }
202
+
203
+ result.summary.fileCount = Object.keys(result.files).length;
204
+ identifyDocGaps(result);
205
+
206
+ return result;
207
+ }
208
+
209
+ module.exports = {
210
+ DEFAULT_OPTIONS,
211
+ analyzeDocumentation,
212
+ analyzeMarkdownFile,
213
+ safeReadFile,
214
+ isPathSafe,
215
+ extractCheckboxes,
216
+ extractFeatures,
217
+ extractPlans,
218
+ identifyDocGaps
219
+ };
@@ -0,0 +1,330 @@
1
+ /**
2
+ * GitHub Data Collector
3
+ *
4
+ * Collects GitHub state: issues, PRs, milestones.
5
+ * Extracted from drift-detect/collectors.js for shared use.
6
+ *
7
+ * @module lib/collectors/github
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const { execFileSync } = require('child_process');
13
+
14
+ const DEFAULT_OPTIONS = {
15
+ issueLimit: 100,
16
+ prLimit: 50,
17
+ milestoneLimit: 100,
18
+ timeout: 10000,
19
+ cwd: process.cwd()
20
+ };
21
+
22
+ /**
23
+ * Execute gh CLI command safely
24
+ * @param {string[]} args - Command arguments
25
+ * @param {Object} options - Execution options
26
+ * @returns {Object|null} Parsed JSON result or null
27
+ */
28
+ function execGh(args, options = {}) {
29
+ const result = execGhWithResult(args, options);
30
+ return result.ok ? result.data : null;
31
+ }
32
+
33
+ function execGhWithResult(args, options = {}) {
34
+ try {
35
+ const output = execFileSync('gh', args, {
36
+ encoding: 'utf8',
37
+ stdio: 'pipe',
38
+ timeout: options.timeout || DEFAULT_OPTIONS.timeout,
39
+ cwd: options.cwd || DEFAULT_OPTIONS.cwd
40
+ });
41
+
42
+ try {
43
+ return { ok: true, data: JSON.parse(output) };
44
+ } catch (error) {
45
+ return {
46
+ ok: false,
47
+ error: {
48
+ type: 'parse',
49
+ message: `Failed to parse gh output as JSON: ${error.message}`,
50
+ raw: output.slice(0, 500)
51
+ }
52
+ };
53
+ }
54
+ } catch (error) {
55
+ return {
56
+ ok: false,
57
+ error: {
58
+ type: error.killed ? 'timeout' : 'process',
59
+ message: error.message,
60
+ exitCode: error.status ?? null,
61
+ stderr: error.stderr ? String(error.stderr).trim() : ''
62
+ }
63
+ };
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Check if gh CLI is available and authenticated
69
+ * @returns {boolean} True if gh is ready
70
+ */
71
+ function isGhAvailable() {
72
+ try {
73
+ execFileSync('gh', ['auth', 'status'], {
74
+ encoding: 'utf8',
75
+ stdio: 'pipe',
76
+ timeout: 5000
77
+ });
78
+ return true;
79
+ } catch {
80
+ return false;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Summarize an issue for analysis
86
+ * @param {Object} item - Issue object
87
+ * @returns {Object} Summarized item
88
+ */
89
+ function summarizeIssue(item) {
90
+ return {
91
+ number: item.number,
92
+ title: item.title,
93
+ labels: (item.labels || []).map(l => l.name || l),
94
+ milestone: item.milestone?.title || item.milestone || null,
95
+ createdAt: item.createdAt,
96
+ updatedAt: item.updatedAt,
97
+ snippet: item.body ? item.body.slice(0, 200).replace(/\n/g, ' ').trim() + (item.body.length > 200 ? '...' : '') : ''
98
+ };
99
+ }
100
+
101
+ /**
102
+ * Summarize a PR for analysis
103
+ * @param {Object} item - PR object
104
+ * @returns {Object} Summarized item
105
+ */
106
+ function summarizePR(item) {
107
+ return {
108
+ number: item.number,
109
+ title: item.title,
110
+ labels: (item.labels || []).map(l => l.name || l),
111
+ isDraft: item.isDraft,
112
+ createdAt: item.createdAt,
113
+ updatedAt: item.updatedAt,
114
+ files: item.files || [],
115
+ snippet: item.body ? item.body.slice(0, 150).replace(/\n/g, ' ').trim() + (item.body.length > 150 ? '...' : '') : ''
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Categorize issues by labels
121
+ */
122
+ function categorizeIssues(result, issues) {
123
+ const labelMap = {
124
+ bug: 'bugs',
125
+ 'type: bug': 'bugs',
126
+ feature: 'features',
127
+ 'type: feature': 'features',
128
+ enhancement: 'enhancements',
129
+ security: 'security',
130
+ 'type: security': 'security'
131
+ };
132
+
133
+ const labelPatterns = Object.entries(labelMap).map(([pattern, category]) => ({
134
+ regex: new RegExp(`(^|[^a-z])${pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([^a-z]|$)`, 'i'),
135
+ category
136
+ }));
137
+
138
+ for (const issue of issues) {
139
+ const labels = (issue.labels || []).map(l => (l.name || l).toLowerCase());
140
+ let categorized = false;
141
+ const ref = { number: issue.number, title: issue.title };
142
+
143
+ for (const { regex, category } of labelPatterns) {
144
+ if (labels.some(l => regex.test(l))) {
145
+ result.categorized[category].push(ref);
146
+ categorized = true;
147
+ break;
148
+ }
149
+ }
150
+
151
+ if (!categorized) {
152
+ result.categorized.other.push(ref);
153
+ }
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Find stale items
159
+ */
160
+ function findStaleItems(result, items, staleDays) {
161
+ const staleDate = new Date();
162
+ staleDate.setDate(staleDate.getDate() - staleDays);
163
+
164
+ for (const item of items) {
165
+ const updated = new Date(item.updatedAt);
166
+ if (updated < staleDate) {
167
+ result.stale.push({
168
+ number: item.number,
169
+ title: item.title,
170
+ lastUpdated: item.updatedAt,
171
+ daysStale: Math.floor((Date.now() - updated) / (1000 * 60 * 60 * 24))
172
+ });
173
+ }
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Extract common themes from issue titles
179
+ */
180
+ function extractThemes(result, issues) {
181
+ const words = {};
182
+ const stopWords = new Set(['the', 'a', 'an', 'is', 'are', 'to', 'for', 'in', 'on', 'at', 'with', 'and', 'or', 'of']);
183
+
184
+ for (const issue of issues) {
185
+ const titleWords = (issue.title || '').toLowerCase().split(/\s+/);
186
+ for (const word of titleWords) {
187
+ if (word.length > 3 && !stopWords.has(word)) {
188
+ words[word] = (words[word] || 0) + 1;
189
+ }
190
+ }
191
+ }
192
+
193
+ result.themes = Object.entries(words)
194
+ .filter(([, count]) => count > 1)
195
+ .sort((a, b) => b[1] - a[1])
196
+ .slice(0, 10)
197
+ .map(([word, count]) => ({ word, count }));
198
+ }
199
+
200
+ /**
201
+ * Find overdue milestones
202
+ */
203
+ function findOverdueMilestones(result) {
204
+ const now = new Date();
205
+ result.overdueMilestones = result.milestones.filter(m => {
206
+ if (!m.due_on || m.state === 'closed') return false;
207
+ return new Date(m.due_on) < now;
208
+ });
209
+ }
210
+
211
+ /**
212
+ * Scan GitHub state: issues, PRs, milestones
213
+ * @param {Object} options - Collection options
214
+ * @returns {Object} GitHub state data
215
+ */
216
+ function scanGitHubState(options = {}) {
217
+ const opts = { ...DEFAULT_OPTIONS, ...options };
218
+
219
+ const result = {
220
+ available: false,
221
+ partial: false,
222
+ errors: [],
223
+ summary: { issueCount: 0, prCount: 0, milestoneCount: 0 },
224
+ issues: [],
225
+ prs: [],
226
+ milestones: [],
227
+ overdueMilestones: [],
228
+ pagination: {
229
+ issues: { requestedLimit: opts.issueLimit, fetchedCount: 0, hasMore: false },
230
+ prs: { requestedLimit: opts.prLimit, fetchedCount: 0, hasMore: false },
231
+ milestones: { requestedLimit: opts.milestoneLimit, fetchedCount: 0, hasMore: false }
232
+ },
233
+ categorized: { bugs: [], features: [], security: [], enhancements: [], other: [] },
234
+ stale: [],
235
+ themes: []
236
+ };
237
+
238
+ if (!isGhAvailable()) {
239
+ result.error = 'gh CLI not available or not authenticated';
240
+ return result;
241
+ }
242
+
243
+ result.available = true;
244
+
245
+ // Fetch open issues
246
+ const issuesResult = execGhWithResult([
247
+ 'issue', 'list',
248
+ '--state', 'open',
249
+ '--json', 'number,title,labels,milestone,createdAt,updatedAt,body',
250
+ '--limit', String(opts.issueLimit)
251
+ ], opts);
252
+
253
+ if (issuesResult.ok && Array.isArray(issuesResult.data)) {
254
+ const issues = issuesResult.data;
255
+ result.issues = issues.map(summarizeIssue);
256
+ result.summary.issueCount = issues.length;
257
+ result.pagination.issues.fetchedCount = issues.length;
258
+ result.pagination.issues.hasMore = opts.issueLimit > 0 && issues.length >= opts.issueLimit;
259
+ categorizeIssues(result, issues);
260
+ findStaleItems(result, issues, 90);
261
+ extractThemes(result, issues);
262
+ } else if (!issuesResult.ok) {
263
+ result.errors.push({ source: 'issues', ...issuesResult.error });
264
+ }
265
+
266
+ // Fetch open PRs with files changed
267
+ const prsResult = execGhWithResult([
268
+ 'pr', 'list',
269
+ '--state', 'open',
270
+ '--json', 'number,title,labels,isDraft,createdAt,updatedAt,body,files',
271
+ '--limit', String(opts.prLimit)
272
+ ], opts);
273
+
274
+ if (prsResult.ok && Array.isArray(prsResult.data)) {
275
+ const prs = prsResult.data;
276
+ result.prs = prs.map(summarizePR);
277
+ result.summary.prCount = prs.length;
278
+ result.pagination.prs.fetchedCount = prs.length;
279
+ result.pagination.prs.hasMore = opts.prLimit > 0 && prs.length >= opts.prLimit;
280
+ } else if (!prsResult.ok) {
281
+ result.errors.push({ source: 'prs', ...prsResult.error });
282
+ }
283
+
284
+ // Fetch milestones
285
+ const milestonesResult = execGhWithResult([
286
+ 'api', 'repos/{owner}/{repo}/milestones',
287
+ '--paginate',
288
+ '--slurp'
289
+ ], opts);
290
+
291
+ if (milestonesResult.ok && Array.isArray(milestonesResult.data)) {
292
+ const pages = milestonesResult.data;
293
+ const allMilestones = pages.flatMap(page => Array.isArray(page) ? page : []);
294
+ const mappedMilestones = allMilestones.map((milestone) => ({
295
+ title: milestone.title,
296
+ state: milestone.state,
297
+ due_on: milestone.due_on,
298
+ open_issues: milestone.open_issues,
299
+ closed_issues: milestone.closed_issues
300
+ }));
301
+
302
+ result.pagination.milestones.fetchedCount = mappedMilestones.length;
303
+ result.pagination.milestones.hasMore = opts.milestoneLimit > 0 && mappedMilestones.length > opts.milestoneLimit;
304
+ result.milestones = mappedMilestones.slice(0, opts.milestoneLimit);
305
+ result.summary.milestoneCount = result.milestones.length;
306
+ findOverdueMilestones(result);
307
+ } else if (!milestonesResult.ok) {
308
+ result.errors.push({ source: 'milestones', ...milestonesResult.error });
309
+ }
310
+
311
+ result.partial = result.errors.length > 0;
312
+ if (result.partial && !result.error) {
313
+ result.error = 'Partial GitHub data collected';
314
+ }
315
+
316
+ return result;
317
+ }
318
+
319
+ module.exports = {
320
+ DEFAULT_OPTIONS,
321
+ scanGitHubState,
322
+ isGhAvailable,
323
+ execGh,
324
+ summarizeIssue,
325
+ summarizePR,
326
+ categorizeIssues,
327
+ findStaleItems,
328
+ extractThemes,
329
+ findOverdueMilestones
330
+ };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Shared Data Collectors
3
+ *
4
+ * Composable data collection infrastructure for drift-detect, deslop, sync-docs.
5
+ * Follows the "JS collectors + single LLM call" pattern for token efficiency.
6
+ *
7
+ * @module lib/collectors
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const github = require('./github');
13
+ const documentation = require('./documentation');
14
+ const codebase = require('./codebase');
15
+ const docsPatterns = require('./docs-patterns');
16
+
17
+ const DEFAULT_OPTIONS = {
18
+ collectors: ['github', 'docs', 'code'],
19
+ depth: 'thorough',
20
+ cwd: process.cwd()
21
+ };
22
+
23
+ /**
24
+ * Collect data from specified collectors
25
+ *
26
+ * @param {Object} options - Collection options
27
+ * @param {string[]} options.collectors - Which collectors to run: 'github', 'docs', 'code', 'docs-patterns'
28
+ * @param {string} options.depth - 'quick' or 'thorough'
29
+ * @param {string} options.cwd - Working directory
30
+ * @param {string[]} options.changedFiles - For docs-patterns collector
31
+ * @returns {Object} Collected data
32
+ *
33
+ * @example
34
+ * // Drift-detect uses all three
35
+ * const data = collect({ collectors: ['github', 'docs', 'code'] });
36
+ *
37
+ * // Deslop uses codebase only
38
+ * const data = collect({ collectors: ['code'] });
39
+ *
40
+ * // Sync-docs uses docs + docs-patterns
41
+ * const data = collect({ collectors: ['docs', 'docs-patterns'], changedFiles });
42
+ */
43
+ function collect(options = {}) {
44
+ const opts = { ...DEFAULT_OPTIONS, ...options };
45
+ const collectors = Array.isArray(opts.collectors) ? opts.collectors : DEFAULT_OPTIONS.collectors;
46
+
47
+ const data = {
48
+ timestamp: new Date().toISOString(),
49
+ options: opts,
50
+ github: null,
51
+ docs: null,
52
+ code: null,
53
+ docsPatterns: null
54
+ };
55
+
56
+ // Collect from each enabled collector
57
+ if (collectors.includes('github')) {
58
+ data.github = github.scanGitHubState(opts);
59
+ }
60
+
61
+ if (collectors.includes('docs')) {
62
+ data.docs = documentation.analyzeDocumentation(opts);
63
+ }
64
+
65
+ if (collectors.includes('code')) {
66
+ data.code = codebase.scanCodebase(opts);
67
+ }
68
+
69
+ if (collectors.includes('docs-patterns')) {
70
+ data.docsPatterns = docsPatterns.collect(opts);
71
+ }
72
+
73
+ return data;
74
+ }
75
+
76
+ /**
77
+ * Collect all data (backward compat with drift-detect)
78
+ *
79
+ * Supports both new 'collectors' option and legacy 'sources' option
80
+ */
81
+ function collectAllData(options = {}) {
82
+ // Map legacy 'sources' option to 'collectors'
83
+ let collectors = ['github', 'docs', 'code'];
84
+ if (options.sources) {
85
+ collectors = options.sources;
86
+ } else if (options.collectors) {
87
+ collectors = options.collectors;
88
+ }
89
+
90
+ return collect({
91
+ ...options,
92
+ collectors
93
+ });
94
+ }
95
+
96
+ module.exports = {
97
+ // Main entry point
98
+ collect,
99
+ collectAllData,
100
+
101
+ // Individual collectors
102
+ github,
103
+ documentation,
104
+ codebase,
105
+ docsPatterns,
106
+
107
+ // Re-export commonly used functions for convenience
108
+ scanGitHubState: github.scanGitHubState,
109
+ isGhAvailable: github.isGhAvailable,
110
+ analyzeDocumentation: documentation.analyzeDocumentation,
111
+ scanCodebase: codebase.scanCodebase,
112
+ findRelatedDocs: docsPatterns.findRelatedDocs,
113
+ analyzeDocIssues: docsPatterns.analyzeDocIssues,
114
+ checkChangelog: docsPatterns.checkChangelog,
115
+
116
+ // New: repo-map integration exports
117
+ ensureRepoMap: docsPatterns.ensureRepoMap,
118
+ ensureRepoMapSync: docsPatterns.ensureRepoMapSync,
119
+ getExportsFromRepoMap: docsPatterns.getExportsFromRepoMap,
120
+ findUndocumentedExports: docsPatterns.findUndocumentedExports,
121
+ isInternalExport: docsPatterns.isInternalExport,
122
+ isEntryPoint: docsPatterns.isEntryPoint,
123
+
124
+ // Constants
125
+ DEFAULT_OPTIONS
126
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Configuration Module
3
+ *
4
+ * Placeholder for future configuration management.
5
+ * This module exists to satisfy lib/index.js imports.
6
+ *
7
+ * @module config
8
+ * @author Avi Fenesh
9
+ * @license MIT
10
+ */
11
+
12
+ module.exports = {
13
+ // Configuration will be added as needed
14
+ };