Context
ClaudeAutoPM v3.25.5 is a Node.js framework (~655 .js, ~727 .md, ~134 .sh) distributed via npm; autopm install copies .claude/ and supporting dirs into target projects. The codebase has a triplication problem: the same logic exists in /.claude/ (primary source), /autopm/.claude/ (install template), and /packages/plugin-*/ (plugin variants). Additionally there are parallel .sh/.js implementations of the same commands and three competing epic-sync versions.
Concrete duplication findings
1. Frontmatter parsing — 3-4 implementations
lib/filter-engine.js::parseFrontmatter() (lines 111-145)
lib/services/ContextService.js::_parseFrontmatter() (lines 190+)
.claude/lib/frontmatter.js
autopm/.claude/lib/frontmatter.js (copy)
Each with slightly different regex and error handling.
2. Epic-sync command triplication → 9 files
.claude/commands/pm:epic-sync.md (485 lines), pm:epic-sync-original.md (473), pm:epic-sync-modular.md (337)
- × 3 locations (main,
autopm/.claude/, packages/plugin-pm-github/)
Three competing implementations; "original"/"modular" suggest incomplete migration.
3. Parallel .sh + .js implementations — 14 command pairs
In .claude/scripts/pm/: blocked, epic-list, epic-show, epic-status, in-progress, init, next, prd-list, prd-status, search, standup, status, validate, help — each exists as both .js and .sh with observed divergence (e.g. blocked.js vs blocked.sh).
4. Shell utility triplication in scripts/lib/
datetime-utils.sh (253 lines), frontmatter-utils.sh (308), github-utils.sh (236), logging-utils.sh (198), validation-utils.sh (338) — each ×3 copies (main, autopm, plugin-core). A bug fix requires patching 3 locations; function signatures already inconsistent.
5. Provider architecture duplication
lib/providers/GitHubProvider.js (475 lines, Octokit)
lib/providers/AzureDevOpsProvider.js (575) + AzureDevOpsCliWrapper.js (370) + AzureDevOpsRestClient.js (424) + AzureDevOpsResourcesProvider.js (322)
No shared BaseProvider; Azure has 4 variants with unclear responsibilities; auth patterns repeated.
6. Install logic hardcoded
install/install.js lines 37-53: hardcoded installItems array (.claude/agents, .claude/commands, …, lib). No manifest; adding a directory means editing install.js. Plus install/install.sh wrapper and install/merge-claude.js.
7. Sync command duplication
.claude/scripts/pm/sync.js, sync-batch.js, epic-sync.sh (orchestrator), and epic-sync/{create-epic-issue,create-task-issues,update-epic-file,update-references}.sh — bash is modularized, JS isn't; pattern unclear.
8. Rule files in both .md and .xml
tdd.enforcement, command-pipelines, agent-mandatory, context7, naming-conventions, github-operations — each has a .md AND .xml version with overlapping content (.xml referenced via @include in CLAUDE.md). Canonical unclear.
9. Core rules copied into 10+ plugin directories
/.claude/rules/ (24 files) ≈ /packages/plugin-core/rules/ (18 near-identical files) + plugin-ai, plugin-cloud, etc.
10. Six service classes without shared base
lib/services/{PRD,Epic,Task,Issue,Context,Agent}Service.js — each independently implements read/write/filter/search on markdown files with repeated error handling.
11. Three different CLI/SDK access styles
GitHubProvider uses Octokit SDK; AzureDevOpsCliWrapper shells out to az; pm scripts shell out to gh directly. No unified CLI abstraction.
12. Two batch processors
lib/batch-processor.js + lib/batch-processor-integration.js + separate utils/RateLimiter.js + utils/CircuitBreaker.js — unclear which to use when.
13. Mixed test frameworks
Jest tests (test/jest-tests/epic-sync-scripts-jest.test.js, azure-epic-sync-jest.test.js) coexist with Node native --test files.
Proposed target structure
lib/
├── core/{frontmatter.js, file-service.js, sync-engine.js}
├── providers/{base-provider.js (NEW), github-provider.js, azure-provider.js (4→1), cli-wrapper.js (NEW)}
├── services/{base-service.js (NEW) + 6 services extending it}
└── utils/{batch-processor.js (2→1), rate-limiter.js, circuit-breaker.js}
.claude/
├── scripts/lib/common.sh # 5 util files → 1, with README of functions
├── scripts/pm/*.js # .js canonical; .sh deprecated
└── commands/pm:epic-sync.md # single version; -original/-modular deleted
.claude/MANIFEST.json # NEW: install manifest replaces hardcoded array
packages/plugin-core/rules/ # reference core rules, don't copy
Distribution & release impact
This repo has no Docker — distribution = npm publish + autopm install/update copying files into user projects.
Current: installItems hardcoded in install.js (lines 37-53); users update via npm update claude-autopm + autopm update --merge (bin/autopm.js lines 58-78 → install/update.sh).
Post-refactor release procedure:
npm run test:all # full suite before publishing
npm run build:manifest # NEW: generate MANIFEST.json from structure
npm run validate:duplicates # NEW: CI gate — fails if dup functions/files reappear
npm version minor # 3.25.5 → 3.26.0 (breaking: removed -original/-modular commands)
npm publish --access public
# Users then:
npm update claude-autopm
autopm update --merge # merges CLAUDE.md, resolves conflicts
Breaking-change note for CHANGELOG: pm:epic-sync-original and pm:epic-sync-modular removed (alias → pm:epic-sync via MANIFEST aliases map); deprecated .sh command scripts print pointer to .js versions.
Execution plan
Phase 1 — Consolidation
Phase 2 — Service layer
Phase 3 — Providers
Phase 4 — Distribution
Phase 5 — Release
Verification
npm run test:all — zero regressions
npm run validate:duplicates — 0 remaining (identical function names across modules, identical .sh files, duplicate command .md files)
npm run test:performance — sync speed not regressed
- Fresh install smoke:
npm i claude-autopm@3.26.0 && autopm install && autopm status
- Expected outcome: single frontmatter parser, single epic-sync, unified service+provider layers,
15-20% LOC reduction, shell utils 1333→300 lines
Context
ClaudeAutoPM v3.25.5 is a Node.js framework (~655 .js, ~727 .md, ~134 .sh) distributed via npm;
autopm installcopies.claude/and supporting dirs into target projects. The codebase has a triplication problem: the same logic exists in/.claude/(primary source),/autopm/.claude/(install template), and/packages/plugin-*/(plugin variants). Additionally there are parallel .sh/.js implementations of the same commands and three competing epic-sync versions.Concrete duplication findings
1. Frontmatter parsing — 3-4 implementations
lib/filter-engine.js::parseFrontmatter()(lines 111-145)lib/services/ContextService.js::_parseFrontmatter()(lines 190+).claude/lib/frontmatter.jsautopm/.claude/lib/frontmatter.js(copy)Each with slightly different regex and error handling.
2. Epic-sync command triplication → 9 files
.claude/commands/pm:epic-sync.md(485 lines),pm:epic-sync-original.md(473),pm:epic-sync-modular.md(337)autopm/.claude/,packages/plugin-pm-github/)Three competing implementations; "original"/"modular" suggest incomplete migration.
3. Parallel .sh + .js implementations — 14 command pairs
In
.claude/scripts/pm/: blocked, epic-list, epic-show, epic-status, in-progress, init, next, prd-list, prd-status, search, standup, status, validate, help — each exists as both.jsand.shwith observed divergence (e.g. blocked.js vs blocked.sh).4. Shell utility triplication in scripts/lib/
datetime-utils.sh(253 lines),frontmatter-utils.sh(308),github-utils.sh(236),logging-utils.sh(198),validation-utils.sh(338) — each ×3 copies (main, autopm, plugin-core). A bug fix requires patching 3 locations; function signatures already inconsistent.5. Provider architecture duplication
lib/providers/GitHubProvider.js(475 lines, Octokit)lib/providers/AzureDevOpsProvider.js(575) +AzureDevOpsCliWrapper.js(370) +AzureDevOpsRestClient.js(424) +AzureDevOpsResourcesProvider.js(322)No shared BaseProvider; Azure has 4 variants with unclear responsibilities; auth patterns repeated.
6. Install logic hardcoded
install/install.jslines 37-53: hardcodedinstallItemsarray (.claude/agents,.claude/commands, …,lib). No manifest; adding a directory means editing install.js. Plusinstall/install.shwrapper andinstall/merge-claude.js.7. Sync command duplication
.claude/scripts/pm/sync.js,sync-batch.js,epic-sync.sh(orchestrator), andepic-sync/{create-epic-issue,create-task-issues,update-epic-file,update-references}.sh— bash is modularized, JS isn't; pattern unclear.8. Rule files in both .md and .xml
tdd.enforcement, command-pipelines, agent-mandatory, context7, naming-conventions, github-operations — each has a .md AND .xml version with overlapping content (.xml referenced via
@includein CLAUDE.md). Canonical unclear.9. Core rules copied into 10+ plugin directories
/.claude/rules/(24 files) ≈/packages/plugin-core/rules/(18 near-identical files) + plugin-ai, plugin-cloud, etc.10. Six service classes without shared base
lib/services/{PRD,Epic,Task,Issue,Context,Agent}Service.js— each independently implements read/write/filter/search on markdown files with repeated error handling.11. Three different CLI/SDK access styles
GitHubProvider uses Octokit SDK; AzureDevOpsCliWrapper shells out to
az; pm scripts shell out toghdirectly. No unified CLI abstraction.12. Two batch processors
lib/batch-processor.js+lib/batch-processor-integration.js+ separateutils/RateLimiter.js+utils/CircuitBreaker.js— unclear which to use when.13. Mixed test frameworks
Jest tests (
test/jest-tests/epic-sync-scripts-jest.test.js,azure-epic-sync-jest.test.js) coexist with Node native--testfiles.Proposed target structure
Distribution & release impact
This repo has no Docker — distribution = npm publish +
autopm install/updatecopying files into user projects.Current:
installItemshardcoded in install.js (lines 37-53); users update vianpm update claude-autopm+autopm update --merge(bin/autopm.js lines 58-78 → install/update.sh).Post-refactor release procedure:
Breaking-change note for CHANGELOG:
pm:epic-sync-originalandpm:epic-sync-modularremoved (alias →pm:epic-syncvia MANIFESTaliasesmap); deprecated.shcommand scripts print pointer to.jsversions.Execution plan
Phase 1 — Consolidation
lib/core/frontmatter.jssingle source; migrate filter-engine.js, ContextService.js, .claude/lib/frontmatter.js consumers;npm test.claude/scripts/lib/common.sh+ README; update all .sh sources; run bash test suite-originaland-modularfrom all 3 locations (9 files); consolidate epic-sync .js/.sh into one .js implementationnpm run test:installnpm run test:all && npm run test:install:scenariosPhase 2 — Service layer
lib/services/base-service.js(loadFiles/parseFile/writeFile/filter/search + uniform errors)npm run test:unitafter eachlib/core/file-service.jsshared markdown opsPhase 3 — Providers
lib/providers/base-provider.jsabstract (authenticate/create/read/update/delete/search + retry + rate limiting)lib/providers/cli-wrapper.jsunified gh/az abstractionazure-provider.js; GitHubProvider extends basenpm run test:github:integration && npm run test:integration:azurePhase 4 — Distribution
/autopm/.claude/duplication (template generated at build, not hand-maintained).claude/MANIFEST.json; install.js reads manifest instead of hardcoded arrayscripts/build-manifest.js+scripts/validate-duplicates.jswired into CInpm run test:install:scenarios(lite, github, azure, docker, full, performance)Phase 5 — Release
npm run test:full && npm run test:security && npm run test:regression && npm run test:clilib/ARCHITECTURE.mdnpm version minor && npm publish --access publicnpm update claude-autopm && autopm update --merge→ verify/pm:help,/pm:statuswork and CLAUDE.md merged cleanlyVerification
npm run test:all— zero regressionsnpm run validate:duplicates— 0 remaining (identical function names across modules, identical .sh files, duplicate command .md files)npm run test:performance— sync speed not regressednpm i claude-autopm@3.26.0 && autopm install && autopm status15-20% LOC reduction, shell utils 1333→300 lines