Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
36 changes: 33 additions & 3 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
{
"name": "awesome-slash",
"description": "8 specialized plugins for AI workflow automation - task orchestration, PR workflow, slop detection, code review, drift detection, enhancement analysis, documentation sync, and repo mapping",
"description": "9 specialized plugins for AI workflow automation - task orchestration, PR workflow, slop detection, code review, drift detection, enhancement analysis, documentation sync, repo mapping, and perf investigations",
"version": "3.3.3",
"owner": {
"name": "Avi Fenesh",
"url": "https://github.com/avifenesh"
},
"repository": "https://github.com/avifenesh/awesome-slash",
"keywords": ["ai", "llm", "agents", "agentic", "claude-code", "opencode", "codex", "mcp", "automation", "workflow", "code-review", "multi-agent"],
"keywords": [
"ai",
"llm",
"agents",
"agentic",
"claude-code",
"opencode",
"codex",
"mcp",
"automation",
"workflow",
"code-review",
"multi-agent"
],
"plugins": [
{
"name": "next-task",
Expand Down Expand Up @@ -64,12 +77,29 @@
"description": "AST-based repository map generation using ast-grep with incremental updates for faster drift analysis",
"version": "3.3.3",
"category": "development"
},
{
"name": "perf",
"source": "./plugins/perf",
"description": "Rigorous performance investigation workflow with baselines, profiling, hypotheses, and evidence-backed decisions",
"version": "3.3.3",
"category": "development"
}
],
"mcpServer": {
"name": "awesome-slash",
"source": "./mcp-server",
"description": "Cross-platform MCP server with 3-phase slop detection pipeline and enhance analyzers",
"tools": ["workflow_status", "workflow_start", "workflow_resume", "workflow_abort", "task_discover", "review_code", "slop_detect", "enhance_analyze", "repo_map"]
"tools": [
"workflow_status",
"workflow_start",
"workflow_resume",
"workflow_abort",
"task_discover",
"review_code",
"slop_detect",
"enhance_analyze",
"repo_map"
]
}
}
15 changes: 6 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [3.3.3] - 2026-01-29
### Added
- **/enhance Hooks Analyzer** - New hook checks for frontmatter completeness and basic safety cues
- **/enhance Skills Analyzer** - New SKILL.md checks for frontmatter and trigger phrase clarity
- **Enhance MCP Tool** - `enhance_analyze` now supports `hooks` and `skills` focus targets

### Fixed
- **Windows Path Template Substitution** - Fixed plugin path template substitution across 18 command/agent files
- Replaced `'${CLAUDE_PLUGIN_ROOT}'.replace()` with runtime `process.env` access
- Pattern: `(process.env.CLAUDE_PLUGIN_ROOT || process.env.PLUGIN_ROOT || '').replace(/\/g, '/')`
- Supports both CLAUDE_PLUGIN_ROOT and PLUGIN_ROOT for cross-platform compatibility
- Added validation for missing environment variables in bash sections
- Bash pattern: `${CLAUDE_PLUGIN_ROOT:-$PLUGIN_ROOT}` with explicit error handling
- Fixes: Template strings in require() paths were not being substituted at runtime on Windows
### Changed
- **Enhance Orchestrator** - Expanded to run hooks/skills analyzers alongside existing enhancers

## [3.3.2] - 2026-01-29

Expand Down
37 changes: 33 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ AI models can write code. That's not the hard part anymore. The hard part is eve

| Section | What's there |
|---------|--------------|
| [Commands](#commands) | All 8 commands with jump links |
| [Commands](#commands) | All 9 commands with jump links |
| [What This Does](#what-this-project-does) | The problem and how this solves it |
| [What's Different](#what-makes-this-different) | Why this isn't just another AI tool |
| [Design Philosophy](#design-philosophy) | The thinking behind the architecture |
Expand All @@ -34,8 +34,9 @@ AI models can write code. That's not the hard part anymore. The hard part is eve
| [`/audit-project`](#audit-project) | Multi-agent code review until issues resolved | [→](#audit-project) |
| [`/drift-detect`](#drift-detect) | Compares your docs to actual code state | [→](#drift-detect) |
| [`/repo-map`](#repo-map) | Builds a cached AST repo map for fast analysis | [→](#repo-map) |
| [`/enhance`](#enhance) | Analyzes prompts, plugins, docs for improvements | [→](#enhance) |
| [`/enhance`](#enhance) | Analyzes prompts, plugins, agents, docs, hooks, skills | [→](#enhance) |
| [`/sync-docs`](#sync-docs) | Syncs documentation with code changes | [→](#sync-docs) |
| [`/perf`](#perf) | Runs structured performance investigations | [→](#perf) |

---

Expand Down Expand Up @@ -455,9 +456,9 @@ Tools like `/drift-detect` and planners can use the map instead of re-scanning t

### /enhance

**Purpose:** Analyzes your prompts, plugins, agents, and docs for improvement opportunities.
**Purpose:** Analyzes your prompts, plugins, agents, docs, hooks, and skills for improvement opportunities.

**Five analyzers run in parallel:**
**Seven analyzers run in parallel:**

| Analyzer | What it checks |
|----------|----------------|
Expand All @@ -466,6 +467,8 @@ Tools like `/drift-detect` and planners can use the map instead of re-scanning t
| claudemd-enhancer | CLAUDE.md/AGENTS.md structure, token efficiency |
| docs-enhancer | Documentation readability, RAG optimization |
| prompt-enhancer | Prompt engineering patterns, clarity, examples |
| hooks-enhancer | Hook frontmatter, structure, safety |
| skills-enhancer | SKILL.md structure, trigger phrases |

**Each finding includes:**
- Certainty level (HIGH/MEDIUM/LOW)
Expand All @@ -484,6 +487,32 @@ Tools like `/drift-detect` and planners can use the map instead of re-scanning t

---

### /perf

**Purpose:** Run structured performance investigations with baselines, profiling, and evidence‑backed decisions.

**Usage:**

```bash
/perf # Start new investigation
/perf --resume # Resume previous investigation
```

**Phase flags (advanced):**

```bash
/perf --phase baseline --command "npm run bench" --version v1.2.0
/perf --phase breaking-point --command "npm run bench" --param-min 1 --param-max 500
/perf --phase constraints --command "npm run bench" --cpu 1 --memory 1GB
/perf --phase hypotheses --hypotheses-file perf-hypotheses.json
/perf --phase code-paths
/perf --phase optimization --change "reduce allocations"
/perf --phase decision --verdict stop --rationale "no measurable improvement"
/perf --phase consolidation --version v1.2.0
```

---

### /sync-docs

**Purpose:** Sync documentation with actual code changes—find outdated refs, update CHANGELOG, flag stale examples.
Expand Down
64 changes: 64 additions & 0 deletions __tests__/enhance-hooks-skills-analyzer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const fs = require('fs');
const os = require('os');
const path = require('path');

const { analyzeHook, analyzeAllHooks } = require('../lib/enhance/hook-analyzer');
const { analyzeSkill, analyzeAllSkills } = require('../lib/enhance/skill-analyzer');

describe('enhance hook/skill analyzers', () => {
let tempDir;

beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'enhance-test-'));
});

afterEach(() => {
fs.rmSync(tempDir, { recursive: true, force: true });
});

test('analyzeHook detects missing frontmatter', () => {
const hookPath = path.join(tempDir, 'hooks');
fs.mkdirSync(hookPath, { recursive: true });
const filePath = path.join(hookPath, 'pre-commit.md');
fs.writeFileSync(filePath, '# Hook\n\nNo frontmatter.');

const result = analyzeHook(filePath);
expect(result.structureIssues.length).toBeGreaterThanOrEqual(1);
expect(result.structureIssues.some(issue => /Missing YAML frontmatter/i.test(issue.issue))).toBe(true);
});

test('analyzeAllHooks scans nested hooks directories', () => {
const hookA = path.join(tempDir, 'plugins', 'alpha', 'hooks');
const hookB = path.join(tempDir, 'tools', 'hooks');
fs.mkdirSync(hookA, { recursive: true });
fs.mkdirSync(hookB, { recursive: true });
fs.writeFileSync(path.join(hookA, 'a.md'), '---\nname: a\ndescription: test\n---\n');
fs.writeFileSync(path.join(hookB, 'b.md'), '---\nname: b\ndescription: test\n---\n');

const results = analyzeAllHooks(tempDir);
expect(results.length).toBe(2);
});

test('analyzeSkill detects missing trigger phrase', () => {
const skillDir = path.join(tempDir, 'skills', 'example');
fs.mkdirSync(skillDir, { recursive: true });
const filePath = path.join(skillDir, 'SKILL.md');
fs.writeFileSync(filePath, '---\nname: example\ndescription: Helpful skill.\n---\n');

const result = analyzeSkill(filePath);
expect(result.triggerIssues.length).toBe(1);
expect(result.triggerIssues[0].issue).toMatch(/trigger phrase/i);
});

test('analyzeAllSkills finds nested SKILL.md files', () => {
const skillA = path.join(tempDir, 'skills', 'alpha');
const skillB = path.join(tempDir, 'plugins', 'beta', 'skills', 'beta-skill');
fs.mkdirSync(skillA, { recursive: true });
fs.mkdirSync(skillB, { recursive: true });
fs.writeFileSync(path.join(skillA, 'SKILL.md'), '---\nname: alpha\ndescription: Use when user asks about alpha.\n---\n');
fs.writeFileSync(path.join(skillB, 'SKILL.md'), '---\nname: beta\ndescription: Use when user asks about beta.\n---\n');

const results = analyzeAllSkills(tempDir);
expect(results.length).toBe(2);
});
});
38 changes: 38 additions & 0 deletions __tests__/perf-argument-parser.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const { parseArguments } = require('../lib/perf/argument-parser');

describe('perf argument parser', () => {
it('handles empty input', () => {
expect(parseArguments('')).toEqual([]);
expect(parseArguments(' ')).toEqual([]);
expect(parseArguments(null)).toEqual([]);
});

it('splits basic arguments', () => {
expect(parseArguments('--phase baseline --version v1')).toEqual([
'--phase',
'baseline',
'--version',
'v1'
]);
});

it('preserves quoted values', () => {
const raw = '--command \"npm run bench -- --scenario small\" --quote \"latency spikes\"';
expect(parseArguments(raw)).toEqual([
'--command',
'npm run bench -- --scenario small',
'--quote',
'latency spikes'
]);
});

it('supports single quotes', () => {
const raw = "--quote 'cache miss surge' --phase profiling";
expect(parseArguments(raw)).toEqual([
'--quote',
'cache miss surge',
'--phase',
'profiling'
]);
});
});
42 changes: 42 additions & 0 deletions __tests__/perf-baseline.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const fs = require('fs');
const os = require('os');
const path = require('path');

const stateDir = require('../lib/platform/state-dir');
const baselineStore = require('../lib/perf/baseline-store');
const baselineComparator = require('../lib/perf/baseline-comparator');

describe('perf baseline store', () => {
let tempDir;

beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'perf-baseline-'));
process.env.AI_STATE_DIR = '.ai-state';
stateDir.clearCache();
});

afterEach(() => {
stateDir.clearCache();
delete process.env.AI_STATE_DIR;
fs.rmSync(tempDir, { recursive: true, force: true });
});

it('writes and reads baselines', () => {
baselineStore.writeBaseline('v1.0.0', { metrics: { latency: 120 }, command: 'npm run bench' }, tempDir);
const baseline = baselineStore.readBaseline('v1.0.0', tempDir);
expect(baseline).not.toBeNull();
expect(baseline.metrics.latency).toBe(120);
});

it('rejects invalid baseline versions', () => {
expect(() => baselineStore.getBaselinePath('../v1.0.0', tempDir)).toThrow();
});

it('compares baseline metrics', () => {
const result = baselineComparator.compareBaselines(
{ metrics: { latency: 100 } },
{ metrics: { latency: 125 } }
);
expect(result.metrics.latency.delta).toBe(25);
});
});
34 changes: 34 additions & 0 deletions __tests__/perf-benchmark-runner.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { parseMetrics } = require('../lib/perf/benchmark-runner');

describe('perf benchmark parser', () => {
it('parses single scenario metrics', () => {
const output = [
'noise',
'PERF_METRICS_START',
'{"latency_ms":120,"throughput_rps":450}',
'PERF_METRICS_END',
'tail'
].join('\n');

const result = parseMetrics(output);
expect(result.ok).toBe(true);
expect(result.metrics.latency_ms).toBe(120);
});

it('parses multi-scenario metrics', () => {
const output = [
'PERF_METRICS_START',
'{"scenarios":{"low":{"latency_ms":120},"high":{"latency_ms":450}}}',
'PERF_METRICS_END'
].join('\n');

const result = parseMetrics(output);
expect(result.ok).toBe(true);
expect(result.metrics.scenarios.low.latency_ms).toBe(120);
});

it('fails when markers are missing', () => {
const result = parseMetrics('no metrics here');
expect(result.ok).toBe(false);
});
});
19 changes: 19 additions & 0 deletions __tests__/perf-breaking-point-runner.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const { runBreakingPointSearch } = require('../lib/perf/breaking-point-runner');

describe('perf breaking point runner', () => {
it('finds breaking point in synthetic range', async () => {
const originalEnv = process.env.PERF_PARAM_VALUE;

const result = await runBreakingPointSearch({
command: 'node -e "const v=parseInt(process.env.PERF_PARAM_VALUE||\'0\',10); if(v>=5){process.exit(1);} console.log(\'PERF_METRICS_START\\n{}\\nPERF_METRICS_END\');"',
paramEnv: 'PERF_PARAM_VALUE',
min: 1,
max: 8
});

process.env.PERF_PARAM_VALUE = originalEnv;

expect(result.attempts).toBeGreaterThan(0);
expect(result.breakingPoint).toBe(5);
});
});
Loading
Loading