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.
- package/.claude-plugin/marketplace.json +21 -14
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +18 -0
- package/README.md +7 -6
- package/adapters/codex/skills/agnix/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project-agents/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project-github/SKILL.md +0 -1
- package/adapters/codex/skills/consult/SKILL.md +132 -57
- package/adapters/codex/skills/debate/SKILL.md +214 -0
- package/adapters/codex/skills/delivery-approval/SKILL.md +0 -1
- package/adapters/codex/skills/deslop/SKILL.md +0 -1
- package/adapters/codex/skills/drift-detect/SKILL.md +0 -1
- package/adapters/codex/skills/enhance/SKILL.md +0 -1
- package/adapters/codex/skills/learn/SKILL.md +0 -1
- package/adapters/codex/skills/next-task/SKILL.md +0 -1
- package/adapters/codex/skills/perf/SKILL.md +0 -1
- package/adapters/codex/skills/repo-map/SKILL.md +0 -1
- package/adapters/codex/skills/ship/SKILL.md +0 -1
- package/adapters/codex/skills/ship-ci-review-loop/SKILL.md +0 -1
- package/adapters/codex/skills/ship-deployment/SKILL.md +0 -1
- package/adapters/codex/skills/ship-error-handling/SKILL.md +0 -1
- package/adapters/codex/skills/sync-docs/SKILL.md +0 -1
- package/adapters/opencode/agents/agent-enhancer.md +0 -1
- package/adapters/opencode/agents/agnix-agent.md +0 -1
- package/adapters/opencode/agents/ci-fixer.md +0 -1
- package/adapters/opencode/agents/ci-monitor.md +0 -1
- package/adapters/opencode/agents/claudemd-enhancer.md +0 -1
- package/adapters/opencode/agents/consult-agent.md +122 -30
- package/adapters/opencode/agents/cross-file-enhancer.md +0 -1
- package/adapters/opencode/agents/debate-orchestrator.md +169 -0
- package/adapters/opencode/agents/delivery-validator.md +0 -1
- package/adapters/opencode/agents/deslop-agent.md +0 -1
- package/adapters/opencode/agents/docs-enhancer.md +0 -1
- package/adapters/opencode/agents/exploration-agent.md +0 -1
- package/adapters/opencode/agents/hooks-enhancer.md +0 -1
- package/adapters/opencode/agents/implementation-agent.md +0 -1
- package/adapters/opencode/agents/learn-agent.md +0 -1
- package/adapters/opencode/agents/map-validator.md +0 -1
- package/adapters/opencode/agents/perf-analyzer.md +0 -1
- package/adapters/opencode/agents/perf-code-paths.md +0 -1
- package/adapters/opencode/agents/perf-investigation-logger.md +0 -1
- package/adapters/opencode/agents/perf-orchestrator.md +0 -1
- package/adapters/opencode/agents/perf-theory-gatherer.md +0 -1
- package/adapters/opencode/agents/perf-theory-tester.md +0 -1
- package/adapters/opencode/agents/plan-synthesizer.md +0 -1
- package/adapters/opencode/agents/planning-agent.md +0 -1
- package/adapters/opencode/agents/plugin-enhancer.md +0 -1
- package/adapters/opencode/agents/prompt-enhancer.md +0 -1
- package/adapters/opencode/agents/simple-fixer.md +0 -1
- package/adapters/opencode/agents/skills-enhancer.md +0 -1
- package/adapters/opencode/agents/sync-docs-agent.md +0 -1
- package/adapters/opencode/agents/task-discoverer.md +0 -1
- package/adapters/opencode/agents/test-coverage-checker.md +0 -1
- package/adapters/opencode/agents/worktree-manager.md +0 -1
- package/adapters/opencode/commands/agnix.md +0 -1
- package/adapters/opencode/commands/audit-project-agents.md +0 -1
- package/adapters/opencode/commands/audit-project-github.md +0 -1
- package/adapters/opencode/commands/audit-project.md +0 -1
- package/adapters/opencode/commands/consult.md +133 -57
- package/adapters/opencode/commands/debate.md +224 -0
- package/adapters/opencode/commands/delivery-approval.md +0 -1
- package/adapters/opencode/commands/deslop.md +0 -1
- package/adapters/opencode/commands/drift-detect.md +0 -1
- package/adapters/opencode/commands/enhance.md +0 -1
- package/adapters/opencode/commands/learn.md +0 -1
- package/adapters/opencode/commands/next-task.md +0 -1
- package/adapters/opencode/commands/perf.md +0 -1
- package/adapters/opencode/commands/repo-map.md +0 -1
- package/adapters/opencode/commands/ship-ci-review-loop.md +0 -1
- package/adapters/opencode/commands/ship-deployment.md +0 -1
- package/adapters/opencode/commands/ship-error-handling.md +0 -1
- package/adapters/opencode/commands/ship.md +0 -1
- package/adapters/opencode/commands/sync-docs.md +0 -1
- package/adapters/opencode/skills/agnix/SKILL.md +1 -2
- package/adapters/opencode/skills/consult/SKILL.md +33 -23
- package/adapters/opencode/skills/debate/SKILL.md +245 -0
- package/adapters/opencode/skills/deslop/SKILL.md +1 -2
- package/adapters/opencode/skills/discover-tasks/SKILL.md +1 -2
- package/adapters/opencode/skills/drift-analysis/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-agent-prompts/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-claude-memory/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-cross-file/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-docs/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-hooks/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-orchestrator/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-plugins/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-prompts/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-skills/SKILL.md +1 -2
- package/adapters/opencode/skills/learn/SKILL.md +1 -2
- package/adapters/opencode/skills/orchestrate-review/SKILL.md +0 -1
- package/adapters/opencode/skills/perf-analyzer/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-baseline-manager/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-benchmarker/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-code-paths/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-investigation-logger/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-profiler/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-theory-gatherer/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-theory-tester/SKILL.md +1 -2
- package/adapters/opencode/skills/repo-mapping/SKILL.md +1 -2
- package/adapters/opencode/skills/sync-docs/SKILL.md +1 -2
- package/adapters/opencode/skills/validate-delivery/SKILL.md +1 -2
- package/lib/adapter-transforms.js +24 -4
- package/package.json +1 -1
- package/plugins/agnix/.claude-plugin/plugin.json +1 -1
- package/plugins/agnix/skills/agnix/SKILL.md +1 -1
- package/plugins/audit-project/.claude-plugin/plugin.json +1 -1
- package/plugins/audit-project/lib/adapter-transforms.js +24 -4
- package/plugins/consult/.claude-plugin/plugin.json +1 -1
- package/plugins/consult/agents/consult-agent.md +122 -29
- package/plugins/consult/commands/consult.md +135 -58
- package/plugins/consult/skills/consult/SKILL.md +31 -20
- package/plugins/debate/.claude-plugin/plugin.json +21 -0
- package/plugins/debate/agents/debate-orchestrator.md +175 -0
- package/plugins/debate/commands/debate.md +221 -0
- package/plugins/debate/lib/adapter-transforms.js +298 -0
- package/plugins/debate/lib/collectors/codebase.js +392 -0
- package/plugins/debate/lib/collectors/docs-patterns.js +713 -0
- package/plugins/debate/lib/collectors/documentation.js +219 -0
- package/plugins/debate/lib/collectors/github.js +330 -0
- package/plugins/debate/lib/collectors/index.js +126 -0
- package/plugins/debate/lib/config/index.js +14 -0
- package/plugins/debate/lib/cross-platform/index.js +539 -0
- package/plugins/debate/lib/discovery/index.js +352 -0
- package/plugins/debate/lib/drift-detect/collectors.js +37 -0
- package/plugins/debate/lib/enhance/agent-analyzer.js +421 -0
- package/plugins/debate/lib/enhance/agent-patterns.js +571 -0
- package/plugins/debate/lib/enhance/auto-suppression.js +622 -0
- package/plugins/debate/lib/enhance/benchmark.js +417 -0
- package/plugins/debate/lib/enhance/cross-file-analyzer.js +930 -0
- package/plugins/debate/lib/enhance/cross-file-patterns.js +370 -0
- package/plugins/debate/lib/enhance/docs-analyzer.js +325 -0
- package/plugins/debate/lib/enhance/docs-patterns.js +671 -0
- package/plugins/debate/lib/enhance/fixer.js +721 -0
- package/plugins/debate/lib/enhance/hook-analyzer.js +135 -0
- package/plugins/debate/lib/enhance/hook-patterns.js +40 -0
- package/plugins/debate/lib/enhance/index.js +127 -0
- package/plugins/debate/lib/enhance/plugin-analyzer.js +402 -0
- package/plugins/debate/lib/enhance/plugin-patterns.js +326 -0
- package/plugins/debate/lib/enhance/projectmemory-analyzer.js +551 -0
- package/plugins/debate/lib/enhance/projectmemory-patterns.js +617 -0
- package/plugins/debate/lib/enhance/prompt-analyzer.js +457 -0
- package/plugins/debate/lib/enhance/prompt-patterns.js +1484 -0
- package/plugins/debate/lib/enhance/reporter.js +1348 -0
- package/plugins/debate/lib/enhance/security-patterns.js +284 -0
- package/plugins/debate/lib/enhance/skill-analyzer.js +182 -0
- package/plugins/debate/lib/enhance/skill-patterns.js +147 -0
- package/plugins/debate/lib/enhance/suppression.js +352 -0
- package/plugins/debate/lib/enhance/tool-patterns.js +373 -0
- package/plugins/debate/lib/index.js +270 -0
- package/plugins/debate/lib/patterns/cli-enhancers.js +611 -0
- package/plugins/debate/lib/patterns/pipeline.js +948 -0
- package/plugins/debate/lib/patterns/review-patterns.js +558 -0
- package/plugins/debate/lib/patterns/slop-analyzers.js +2305 -0
- package/plugins/debate/lib/patterns/slop-patterns.js +1187 -0
- package/plugins/debate/lib/perf/analyzer/index.js +22 -0
- package/plugins/debate/lib/perf/argument-parser.js +105 -0
- package/plugins/debate/lib/perf/baseline-comparator.js +50 -0
- package/plugins/debate/lib/perf/baseline-store.js +127 -0
- package/plugins/debate/lib/perf/benchmark-runner.js +404 -0
- package/plugins/debate/lib/perf/breaking-point-finder.js +52 -0
- package/plugins/debate/lib/perf/breaking-point-runner.js +60 -0
- package/plugins/debate/lib/perf/checkpoint.js +123 -0
- package/plugins/debate/lib/perf/code-paths.js +86 -0
- package/plugins/debate/lib/perf/consolidation.js +37 -0
- package/plugins/debate/lib/perf/constraint-runner.js +71 -0
- package/plugins/debate/lib/perf/experiment-runner.js +32 -0
- package/plugins/debate/lib/perf/index.js +41 -0
- package/plugins/debate/lib/perf/investigation-state.js +874 -0
- package/plugins/debate/lib/perf/optimization-runner.js +79 -0
- package/plugins/debate/lib/perf/profilers/go.js +22 -0
- package/plugins/debate/lib/perf/profilers/index.js +46 -0
- package/plugins/debate/lib/perf/profilers/java.js +23 -0
- package/plugins/debate/lib/perf/profilers/node.js +27 -0
- package/plugins/debate/lib/perf/profilers/python.js +23 -0
- package/plugins/debate/lib/perf/profilers/rust.js +23 -0
- package/plugins/debate/lib/perf/profiling-runner.js +75 -0
- package/plugins/debate/lib/perf/schemas.js +140 -0
- package/plugins/debate/lib/platform/detect-platform.js +413 -0
- package/plugins/debate/lib/platform/detection-configs.js +93 -0
- package/plugins/debate/lib/platform/state-dir.js +132 -0
- package/plugins/debate/lib/platform/verify-tools.js +182 -0
- package/plugins/debate/lib/repo-map/cache.js +152 -0
- package/plugins/debate/lib/repo-map/concurrency.js +29 -0
- package/plugins/debate/lib/repo-map/index.js +222 -0
- package/plugins/debate/lib/repo-map/installer.js +212 -0
- package/plugins/debate/lib/repo-map/queries/go.js +27 -0
- package/plugins/debate/lib/repo-map/queries/index.js +100 -0
- package/plugins/debate/lib/repo-map/queries/java.js +38 -0
- package/plugins/debate/lib/repo-map/queries/javascript.js +55 -0
- package/plugins/debate/lib/repo-map/queries/python.js +24 -0
- package/plugins/debate/lib/repo-map/queries/rust.js +73 -0
- package/plugins/debate/lib/repo-map/queries/typescript.js +38 -0
- package/plugins/debate/lib/repo-map/runner.js +1364 -0
- package/plugins/debate/lib/repo-map/updater.js +562 -0
- package/plugins/debate/lib/repo-map/usage-analyzer.js +407 -0
- package/plugins/debate/lib/schemas/plugin-manifest.schema.json +57 -0
- package/plugins/debate/lib/schemas/validator.js +247 -0
- package/plugins/debate/lib/sources/custom-handler.js +199 -0
- package/plugins/debate/lib/sources/policy-questions.js +246 -0
- package/plugins/debate/lib/sources/source-cache.js +165 -0
- package/plugins/debate/lib/state/workflow-state.js +576 -0
- package/plugins/debate/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/debate/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/debate/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/debate/lib/types/index.d.ts +84 -0
- package/plugins/debate/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/debate/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/debate/lib/utils/atomic-write.js +94 -0
- package/plugins/debate/lib/utils/cache-manager.js +159 -0
- package/plugins/debate/lib/utils/command-parser.js +0 -0
- package/plugins/debate/lib/utils/context-optimizer.js +300 -0
- package/plugins/debate/lib/utils/deprecation.js +37 -0
- package/plugins/debate/lib/utils/shell-escape.js +88 -0
- package/plugins/debate/lib/utils/state-helpers.js +61 -0
- package/plugins/debate/skills/debate/SKILL.md +264 -0
- package/plugins/deslop/.claude-plugin/plugin.json +1 -1
- package/plugins/deslop/lib/adapter-transforms.js +24 -4
- package/plugins/deslop/skills/deslop/SKILL.md +1 -1
- package/plugins/drift-detect/.claude-plugin/plugin.json +1 -1
- package/plugins/drift-detect/lib/adapter-transforms.js +24 -4
- package/plugins/drift-detect/skills/drift-analysis/SKILL.md +1 -1
- package/plugins/enhance/.claude-plugin/plugin.json +1 -1
- package/plugins/enhance/lib/adapter-transforms.js +24 -4
- package/plugins/enhance/skills/enhance-agent-prompts/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-claude-memory/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-cross-file/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-docs/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-hooks/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-orchestrator/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-plugins/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-prompts/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-skills/SKILL.md +1 -1
- package/plugins/learn/.claude-plugin/plugin.json +1 -1
- package/plugins/learn/agents/learn-agent.md +1 -1
- package/plugins/learn/lib/adapter-transforms.js +24 -4
- package/plugins/learn/skills/learn/SKILL.md +1 -1
- package/plugins/next-task/.claude-plugin/plugin.json +1 -1
- package/plugins/next-task/agents/exploration-agent.md +1 -1
- package/plugins/next-task/lib/adapter-transforms.js +24 -4
- package/plugins/next-task/skills/discover-tasks/SKILL.md +1 -1
- package/plugins/next-task/skills/validate-delivery/SKILL.md +1 -1
- package/plugins/perf/.claude-plugin/plugin.json +1 -1
- package/plugins/perf/lib/adapter-transforms.js +24 -4
- package/plugins/perf/skills/perf-analyzer/SKILL.md +1 -1
- package/plugins/perf/skills/perf-baseline-manager/SKILL.md +1 -1
- package/plugins/perf/skills/perf-benchmarker/SKILL.md +1 -1
- package/plugins/perf/skills/perf-code-paths/SKILL.md +1 -1
- package/plugins/perf/skills/perf-investigation-logger/SKILL.md +1 -1
- package/plugins/perf/skills/perf-profiler/SKILL.md +1 -1
- package/plugins/perf/skills/perf-theory-gatherer/SKILL.md +1 -1
- package/plugins/perf/skills/perf-theory-tester/SKILL.md +1 -1
- package/plugins/repo-map/.claude-plugin/plugin.json +1 -1
- package/plugins/repo-map/lib/adapter-transforms.js +24 -4
- package/plugins/ship/.claude-plugin/plugin.json +1 -1
- package/plugins/ship/lib/adapter-transforms.js +24 -4
- package/plugins/sync-docs/.claude-plugin/plugin.json +1 -1
- package/plugins/sync-docs/lib/adapter-transforms.js +24 -4
- package/plugins/sync-docs/skills/sync-docs/SKILL.md +1 -1
- package/scripts/gen-adapters.js +6 -7
- package/scripts/generate-docs.js +4 -2
- package/scripts/plugins.txt +1 -0
- 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
|
+
};
|