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,551 @@
1
+ /**
2
+ * Project Memory Analyzer
3
+ * Analyzes CLAUDE.md/AGENTS.md project memory files for optimization opportunities
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const { projectMemoryPatterns } = require('./projectmemory-patterns');
9
+
10
+ const PROJECT_MEMORY_FILES = [
11
+ 'CLAUDE.md',
12
+ 'AGENTS.md',
13
+ '.github/CLAUDE.md',
14
+ '.github/AGENTS.md'
15
+ ];
16
+
17
+ /**
18
+ * Find the project memory file in a directory
19
+ * @param {string} projectPath - Project root directory
20
+ * @returns {Object|null} { path, name, type } or null if not found
21
+ */
22
+ function findProjectMemoryFile(projectPath) {
23
+ for (const fileName of PROJECT_MEMORY_FILES) {
24
+ const filePath = path.join(projectPath, fileName);
25
+ if (fs.existsSync(filePath)) {
26
+ return {
27
+ path: filePath,
28
+ name: fileName,
29
+ type: fileName.includes('AGENTS') ? 'agents' : 'claude'
30
+ };
31
+ }
32
+ }
33
+ return null;
34
+ }
35
+
36
+ /**
37
+ * Extract file references from markdown content
38
+ * @param {string} content - Markdown content
39
+ * @returns {Array} Array of file paths referenced
40
+ */
41
+ function extractFileReferences(content) {
42
+ if (!content || typeof content !== 'string') return [];
43
+
44
+ const references = [];
45
+
46
+ // Extract markdown links [text](path) using indexOf scanning (ReDoS-safe)
47
+ let pos = 0;
48
+ while (pos < content.length) {
49
+ const openBracket = content.indexOf('[', pos);
50
+ if (openBracket === -1) break;
51
+ const closeBracket = content.indexOf(']', openBracket + 1);
52
+ if (closeBracket === -1) break;
53
+ if (content[closeBracket + 1] === '(') {
54
+ const closeParen = content.indexOf(')', closeBracket + 2);
55
+ if (closeParen !== -1 && closeParen - closeBracket - 2 <= 500) {
56
+ const href = content.substring(closeBracket + 2, closeParen);
57
+ if (!href.startsWith('http') && !href.startsWith('#') && !href.startsWith('mailto:')) {
58
+ references.push(href.split('#')[0]); // Remove anchor
59
+ }
60
+ pos = closeParen + 1;
61
+ continue;
62
+ }
63
+ }
64
+ pos = openBracket + 1;
65
+ }
66
+
67
+ // Match backtick paths: `path/to/file.ext` or `file.ext` (root files)
68
+ const backtickMatches = content.match(/`([^`]+)`/g) || [];
69
+ for (const match of backtickMatches) {
70
+ const filePath = match.replace(/`/g, '');
71
+ // Include paths with / or extension, exclude spaces and variables
72
+ if ((filePath.includes('.') || filePath.includes('/')) && !filePath.includes(' ') && !filePath.startsWith('$')) {
73
+ references.push(filePath);
74
+ }
75
+ }
76
+
77
+ return [...new Set(references)];
78
+ }
79
+
80
+ /**
81
+ * Validate file references exist
82
+ * @param {string} content - Markdown content
83
+ * @param {string} projectPath - Project root directory
84
+ * @returns {Object} { valid: [], broken: [] }
85
+ */
86
+ function validateFileReferences(content, projectPath) {
87
+ const references = extractFileReferences(content);
88
+ const valid = [];
89
+ const broken = [];
90
+
91
+ const resolvedProjectPath = path.resolve(projectPath);
92
+
93
+ for (const ref of references) {
94
+ // Skip glob patterns and variable references
95
+ if (ref.includes('*') || ref.includes('${')) {
96
+ valid.push(ref);
97
+ continue;
98
+ }
99
+
100
+ // Resolve path and validate it stays within project root (prevent path traversal)
101
+ // Use path.relative() for Windows case-insensitive path comparison
102
+ const fullPath = path.resolve(projectPath, ref);
103
+ const relativePath = path.relative(resolvedProjectPath, fullPath);
104
+ if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath) && fs.existsSync(fullPath)) {
105
+ valid.push(ref);
106
+ } else {
107
+ broken.push(ref);
108
+ }
109
+ }
110
+
111
+ return { valid, broken };
112
+ }
113
+
114
+ /**
115
+ * Extract npm script references from content
116
+ * @param {string} content - Markdown content
117
+ * @returns {Array} Array of npm commands referenced
118
+ */
119
+ function extractCommandReferences(content) {
120
+ if (!content || typeof content !== 'string') return [];
121
+
122
+ const commands = [];
123
+
124
+ // Match npm run commands: npm run <script>
125
+ const npmRunMatches = content.match(/npm\s+run\s+([a-z][\w:-]*)/gi) || [];
126
+ for (const match of npmRunMatches) {
127
+ const scriptMatch = match.match(/npm\s+run\s+([a-z][\w:-]*)/i);
128
+ if (scriptMatch && scriptMatch[1]) {
129
+ commands.push(scriptMatch[1]);
130
+ }
131
+ }
132
+
133
+ // Match npm <script> shorthand (test, start, etc)
134
+ const npmShortMatches = content.match(/npm\s+(test|start|build|lint|install)/gi) || [];
135
+ for (const match of npmShortMatches) {
136
+ const cmdMatch = match.match(/npm\s+(\w+)/i);
137
+ if (cmdMatch && cmdMatch[1]) {
138
+ commands.push(cmdMatch[1]);
139
+ }
140
+ }
141
+
142
+ return [...new Set(commands)];
143
+ }
144
+
145
+ /**
146
+ * Validate command references exist in package.json
147
+ * @param {string} content - Markdown content
148
+ * @param {string} projectPath - Project root directory
149
+ * @returns {Object} { valid: [], broken: [] }
150
+ */
151
+ function validateCommandReferences(content, projectPath) {
152
+ const commands = extractCommandReferences(content);
153
+ const valid = [];
154
+ const broken = [];
155
+
156
+ const packagePath = path.join(projectPath, 'package.json');
157
+ let scripts = {};
158
+
159
+ let packageParseError = null;
160
+ if (fs.existsSync(packagePath)) {
161
+ try {
162
+ const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
163
+ scripts = pkg.scripts || {};
164
+ } catch (err) {
165
+ packageParseError = err.message;
166
+ // Continue with empty scripts - commands will be marked as broken
167
+ }
168
+ }
169
+
170
+ const builtInCommands = ['install', 'i', 'ci', 'test', 't', 'start', 'build', 'publish', 'pack'];
171
+
172
+ for (const cmd of commands) {
173
+ if (scripts[cmd] || builtInCommands.includes(cmd)) {
174
+ valid.push(cmd);
175
+ } else {
176
+ broken.push(cmd);
177
+ }
178
+ }
179
+
180
+ const result = { valid, broken };
181
+ if (packageParseError) {
182
+ result.parseError = packageParseError;
183
+ }
184
+ return result;
185
+ }
186
+
187
+ /**
188
+ * Calculate token metrics for content
189
+ * @param {string} content - Content to analyze
190
+ * @param {string} readmeContent - Optional README content for comparison
191
+ * @returns {Object} Token metrics
192
+ */
193
+ function calculateTokenMetrics(content, readmeContent = null) {
194
+ if (!content || typeof content !== 'string') {
195
+ return { estimatedTokens: 0, characterCount: 0, lineCount: 0, wordCount: 0 };
196
+ }
197
+
198
+ const characterCount = content.length;
199
+ const lineCount = content.split('\n').length;
200
+ const estimatedTokens = Math.ceil(characterCount / 4);
201
+
202
+ const result = {
203
+ estimatedTokens,
204
+ characterCount,
205
+ lineCount,
206
+ wordCount: content.split(/\s+/).filter(Boolean).length
207
+ };
208
+
209
+ // Calculate README overlap if provided
210
+ if (readmeContent && typeof readmeContent === 'string') {
211
+ result.readmeOverlap = calculateTextOverlap(content, readmeContent);
212
+ }
213
+
214
+ return result;
215
+ }
216
+
217
+ /**
218
+ * Calculate text overlap between two documents
219
+ * @param {string} text1 - First text
220
+ * @param {string} text2 - Second text
221
+ * @returns {number} Overlap ratio (0-1)
222
+ */
223
+ function calculateTextOverlap(text1, text2) {
224
+ if (!text1 || !text2) return 0;
225
+
226
+ const normalize = (text) => text.toLowerCase().replace(/[^\w\s]/g, ' ').replace(/\s+/g, ' ').trim();
227
+ const sentences1 = normalize(text1).split(/[.!?]+/).filter(s => s.length > 20);
228
+ const sentences2 = new Set(normalize(text2).split(/[.!?]+/).filter(s => s.length > 20));
229
+
230
+ if (sentences1.length === 0) return 0;
231
+
232
+ let matchCount = 0;
233
+ for (const sentence of sentences1) {
234
+ for (const s2 of sentences2) {
235
+ const words1 = sentence.split(' ');
236
+ const words2 = new Set(s2.split(' '));
237
+ const matchingWords = words1.filter(w => words2.has(w)).length;
238
+ if (matchingWords / words1.length > 0.8) {
239
+ matchCount++;
240
+ break;
241
+ }
242
+ }
243
+ }
244
+
245
+ return matchCount / sentences1.length;
246
+ }
247
+
248
+ /**
249
+ * Detect README duplication
250
+ * @param {string} memoryFilePath - Path to project memory file
251
+ * @param {string} readmePath - Path to README.md (optional, auto-detected)
252
+ * @returns {Object} Duplication analysis
253
+ */
254
+ function detectReadmeDuplication(memoryFilePath, readmePath = null) {
255
+ if (!readmePath) {
256
+ const possibleReadmes = ['README.md', 'readme.md', 'Readme.md'];
257
+ let currentDir = path.dirname(memoryFilePath);
258
+
259
+ // Walk up directory tree to find README (handles .github/ case)
260
+ while (currentDir && currentDir.length > 0) {
261
+ for (const name of possibleReadmes) {
262
+ const tryPath = path.join(currentDir, name);
263
+ if (fs.existsSync(tryPath)) {
264
+ readmePath = tryPath;
265
+ break;
266
+ }
267
+ }
268
+ if (readmePath) break;
269
+
270
+ const parentDir = path.dirname(currentDir);
271
+ if (!parentDir || parentDir === currentDir) break;
272
+ currentDir = parentDir;
273
+ }
274
+ }
275
+
276
+ if (!readmePath || !fs.existsSync(readmePath)) {
277
+ return { hasReadme: false, duplicationRatio: 0 };
278
+ }
279
+
280
+ try {
281
+ const memoryContent = fs.readFileSync(memoryFilePath, 'utf8');
282
+ const readmeContent = fs.readFileSync(readmePath, 'utf8');
283
+
284
+ return {
285
+ hasReadme: true,
286
+ readmePath,
287
+ duplicationRatio: calculateTextOverlap(memoryContent, readmeContent)
288
+ };
289
+ } catch (err) {
290
+ return { hasReadme: true, readmePath, error: err.message };
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Analyze a single project memory file
296
+ * @param {string} filePath - Path to project memory file
297
+ * @param {Object} options - Analysis options
298
+ * @param {boolean} options.verbose - Include LOW certainty issues
299
+ * @param {boolean} options.checkReferences - Validate file/command references
300
+ * @returns {Object} Analysis results
301
+ */
302
+ function analyzeFile(filePath, options = {}) {
303
+ const { verbose = false, checkReferences = true } = options;
304
+
305
+ const results = {
306
+ fileName: path.basename(filePath),
307
+ filePath,
308
+ fileType: filePath.includes('AGENTS') ? 'agents' : 'claude',
309
+ structureIssues: [],
310
+ referenceIssues: [],
311
+ efficiencyIssues: [],
312
+ qualityIssues: [],
313
+ crossPlatformIssues: [],
314
+ metrics: null
315
+ };
316
+
317
+ // Read file
318
+ if (!fs.existsSync(filePath)) {
319
+ results.structureIssues.push({
320
+ issue: 'File not found',
321
+ file: filePath,
322
+ certainty: 'HIGH',
323
+ patternId: 'file_not_found'
324
+ });
325
+ return results;
326
+ }
327
+
328
+ let content;
329
+ try {
330
+ content = fs.readFileSync(filePath, 'utf8');
331
+ } catch (err) {
332
+ results.structureIssues.push({
333
+ issue: `Failed to read file: ${err.message}`,
334
+ file: filePath,
335
+ certainty: 'HIGH',
336
+ patternId: 'read_error'
337
+ });
338
+ return results;
339
+ }
340
+
341
+ // Determine project root - if file is in .github/, use parent directory
342
+ let projectPath = path.dirname(filePath);
343
+ if (projectPath.endsWith('.github') || projectPath.endsWith('.github/') || projectPath.endsWith('.github\\')) {
344
+ projectPath = path.dirname(projectPath);
345
+ }
346
+
347
+ const readmeDuplication = detectReadmeDuplication(filePath);
348
+ let readmeContent = null;
349
+ if (readmeDuplication.hasReadme && !readmeDuplication.error) {
350
+ try {
351
+ readmeContent = fs.readFileSync(readmeDuplication.readmePath, 'utf8');
352
+ } catch (err) {
353
+ // README exists but couldn't be read - continue without it
354
+ }
355
+ }
356
+ results.metrics = calculateTokenMetrics(content, readmeContent);
357
+
358
+ const context = {
359
+ fileName: results.fileName,
360
+ duplicationRatio: readmeDuplication.duplicationRatio
361
+ };
362
+
363
+ if (checkReferences) {
364
+ const fileRefs = validateFileReferences(content, projectPath);
365
+ context.brokenFiles = fileRefs.broken;
366
+
367
+ const cmdRefs = validateCommandReferences(content, projectPath);
368
+ context.brokenCommands = cmdRefs.broken;
369
+ }
370
+
371
+ const structurePatterns = ['missing_critical_rules', 'missing_architecture', 'missing_key_commands'];
372
+ for (const patternName of structurePatterns) {
373
+ const pattern = projectMemoryPatterns[patternName];
374
+ if (!pattern) continue;
375
+
376
+ const result = pattern.check(content, context);
377
+ if (result && (verbose || pattern.certainty !== 'LOW')) {
378
+ results.structureIssues.push({
379
+ ...result,
380
+ file: filePath,
381
+ certainty: pattern.certainty,
382
+ patternId: pattern.id
383
+ });
384
+ }
385
+ }
386
+
387
+ const referencePatterns = ['broken_file_reference', 'broken_command_reference'];
388
+ for (const patternName of referencePatterns) {
389
+ const pattern = projectMemoryPatterns[patternName];
390
+ if (!pattern) continue;
391
+
392
+ const result = pattern.check(content, context);
393
+ if (result && (verbose || pattern.certainty !== 'LOW')) {
394
+ results.referenceIssues.push({
395
+ ...result,
396
+ file: filePath,
397
+ certainty: pattern.certainty,
398
+ patternId: pattern.id
399
+ });
400
+ }
401
+ }
402
+
403
+ const efficiencyPatterns = ['readme_duplication', 'excessive_token_count', 'verbose_instructions', 'example_overload'];
404
+ for (const patternName of efficiencyPatterns) {
405
+ const pattern = projectMemoryPatterns[patternName];
406
+ if (!pattern) continue;
407
+
408
+ const result = pattern.check(content, context);
409
+ if (result && (verbose || pattern.certainty !== 'LOW')) {
410
+ results.efficiencyIssues.push({
411
+ ...result,
412
+ file: filePath,
413
+ certainty: pattern.certainty,
414
+ patternId: pattern.id
415
+ });
416
+ }
417
+ }
418
+
419
+ const qualityPatterns = ['missing_why', 'deep_nesting'];
420
+ for (const patternName of qualityPatterns) {
421
+ const pattern = projectMemoryPatterns[patternName];
422
+ if (!pattern) continue;
423
+
424
+ const result = pattern.check(content, context);
425
+ if (result && (verbose || pattern.certainty !== 'LOW')) {
426
+ results.qualityIssues.push({
427
+ ...result,
428
+ file: filePath,
429
+ certainty: pattern.certainty,
430
+ patternId: pattern.id
431
+ });
432
+ }
433
+ }
434
+
435
+ const crossPlatformPatterns = ['hardcoded_state_dir', 'claude_only_terminology', 'missing_agents_md_mention'];
436
+ for (const patternName of crossPlatformPatterns) {
437
+ const pattern = projectMemoryPatterns[patternName];
438
+ if (!pattern) continue;
439
+
440
+ const result = pattern.check(content, context);
441
+ if (result && (verbose || pattern.certainty !== 'LOW')) {
442
+ results.crossPlatformIssues.push({
443
+ ...result,
444
+ file: filePath,
445
+ certainty: pattern.certainty,
446
+ patternId: pattern.id
447
+ });
448
+ }
449
+ }
450
+
451
+ return results;
452
+ }
453
+
454
+ /**
455
+ * Main analyze function
456
+ * @param {string} targetPath - Path to project directory or specific file
457
+ * @param {Object} options - Analysis options
458
+ * @param {boolean} options.verbose - Include LOW certainty issues
459
+ * @param {boolean} options.checkReferences - Validate file/command references
460
+ * @returns {Object} Analysis results
461
+ */
462
+ function analyze(targetPath, options = {}) {
463
+ let filePath;
464
+
465
+ if (fs.existsSync(targetPath)) {
466
+ const stat = fs.statSync(targetPath);
467
+ if (stat.isFile()) {
468
+ filePath = targetPath;
469
+ } else {
470
+ const found = findProjectMemoryFile(targetPath);
471
+ if (!found) {
472
+ return {
473
+ error: 'No project memory file found',
474
+ searchedPaths: PROJECT_MEMORY_FILES.map(f => path.join(targetPath, f)),
475
+ structureIssues: [{
476
+ issue: 'No CLAUDE.md or AGENTS.md found in project',
477
+ fix: 'Create CLAUDE.md with project memory content',
478
+ certainty: 'HIGH',
479
+ patternId: 'missing_project_memory'
480
+ }]
481
+ };
482
+ }
483
+ filePath = found.path;
484
+ }
485
+ } else {
486
+ return {
487
+ error: `Path does not exist: ${targetPath}`,
488
+ structureIssues: [{
489
+ issue: 'Target path does not exist',
490
+ file: targetPath,
491
+ certainty: 'HIGH',
492
+ patternId: 'path_not_found'
493
+ }]
494
+ };
495
+ }
496
+
497
+ return analyzeFile(filePath, options);
498
+ }
499
+
500
+ /**
501
+ * Apply fixes to analysis results
502
+ * @param {Object} results - Analysis results
503
+ * @param {Object} options - Fix options
504
+ * @returns {Object} Fix results
505
+ */
506
+ function applyFixes(results, options = {}) {
507
+ const allIssues = [
508
+ ...(results.structureIssues || []),
509
+ ...(results.referenceIssues || []),
510
+ ...(results.efficiencyIssues || []),
511
+ ...(results.qualityIssues || []),
512
+ ...(results.crossPlatformIssues || [])
513
+ ];
514
+
515
+ return {
516
+ applied: [],
517
+ skipped: allIssues.map(i => ({
518
+ ...i,
519
+ reason: 'No auto-fix available - requires human judgment'
520
+ })),
521
+ errors: []
522
+ };
523
+ }
524
+
525
+ /**
526
+ * Generate markdown report from analysis results
527
+ * @param {Object} results - Analysis results
528
+ * @param {Object} options - Report options
529
+ * @returns {string} Markdown report
530
+ */
531
+ function generateReport(results, options = {}) {
532
+ const reporter = require('./reporter');
533
+
534
+ return reporter.generateProjectMemoryReport(results, options);
535
+ }
536
+
537
+ module.exports = {
538
+ PROJECT_MEMORY_FILES,
539
+ findProjectMemoryFile,
540
+ extractFileReferences,
541
+ validateFileReferences,
542
+ extractCommandReferences,
543
+ validateCommandReferences,
544
+ calculateTokenMetrics,
545
+ calculateTextOverlap,
546
+ detectReadmeDuplication,
547
+ analyzeFile,
548
+ analyze,
549
+ applyFixes,
550
+ generateReport
551
+ };