cc4pm 1.8.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 (108) hide show
  1. package/.claude-plugin/README.md +17 -0
  2. package/.claude-plugin/plugin.json +25 -0
  3. package/LICENSE +21 -0
  4. package/README.md +157 -0
  5. package/README.zh-CN.md +134 -0
  6. package/contexts/dev.md +20 -0
  7. package/contexts/research.md +26 -0
  8. package/contexts/review.md +22 -0
  9. package/examples/CLAUDE.md +100 -0
  10. package/examples/statusline.json +19 -0
  11. package/examples/user-CLAUDE.md +109 -0
  12. package/install.sh +17 -0
  13. package/manifests/install-components.json +173 -0
  14. package/manifests/install-modules.json +335 -0
  15. package/manifests/install-profiles.json +75 -0
  16. package/package.json +117 -0
  17. package/schemas/ecc-install-config.schema.json +58 -0
  18. package/schemas/hooks.schema.json +197 -0
  19. package/schemas/install-components.schema.json +56 -0
  20. package/schemas/install-modules.schema.json +105 -0
  21. package/schemas/install-profiles.schema.json +45 -0
  22. package/schemas/install-state.schema.json +210 -0
  23. package/schemas/package-manager.schema.json +23 -0
  24. package/schemas/plugin.schema.json +58 -0
  25. package/scripts/ci/catalog.js +83 -0
  26. package/scripts/ci/validate-agents.js +81 -0
  27. package/scripts/ci/validate-commands.js +135 -0
  28. package/scripts/ci/validate-hooks.js +239 -0
  29. package/scripts/ci/validate-install-manifests.js +211 -0
  30. package/scripts/ci/validate-no-personal-paths.js +63 -0
  31. package/scripts/ci/validate-rules.js +81 -0
  32. package/scripts/ci/validate-skills.js +54 -0
  33. package/scripts/claw.js +468 -0
  34. package/scripts/doctor.js +110 -0
  35. package/scripts/ecc.js +194 -0
  36. package/scripts/hooks/auto-tmux-dev.js +88 -0
  37. package/scripts/hooks/check-console-log.js +71 -0
  38. package/scripts/hooks/check-hook-enabled.js +12 -0
  39. package/scripts/hooks/cost-tracker.js +78 -0
  40. package/scripts/hooks/doc-file-warning.js +63 -0
  41. package/scripts/hooks/evaluate-session.js +100 -0
  42. package/scripts/hooks/insaits-security-monitor.py +269 -0
  43. package/scripts/hooks/insaits-security-wrapper.js +88 -0
  44. package/scripts/hooks/post-bash-build-complete.js +27 -0
  45. package/scripts/hooks/post-bash-pr-created.js +36 -0
  46. package/scripts/hooks/post-edit-console-warn.js +54 -0
  47. package/scripts/hooks/post-edit-format.js +109 -0
  48. package/scripts/hooks/post-edit-typecheck.js +96 -0
  49. package/scripts/hooks/pre-bash-dev-server-block.js +187 -0
  50. package/scripts/hooks/pre-bash-git-push-reminder.js +28 -0
  51. package/scripts/hooks/pre-bash-tmux-reminder.js +33 -0
  52. package/scripts/hooks/pre-compact.js +48 -0
  53. package/scripts/hooks/pre-write-doc-warn.js +9 -0
  54. package/scripts/hooks/quality-gate.js +168 -0
  55. package/scripts/hooks/run-with-flags-shell.sh +32 -0
  56. package/scripts/hooks/run-with-flags.js +120 -0
  57. package/scripts/hooks/session-end-marker.js +15 -0
  58. package/scripts/hooks/session-end.js +299 -0
  59. package/scripts/hooks/session-start.js +97 -0
  60. package/scripts/hooks/suggest-compact.js +80 -0
  61. package/scripts/install-apply.js +137 -0
  62. package/scripts/install-plan.js +254 -0
  63. package/scripts/lib/hook-flags.js +74 -0
  64. package/scripts/lib/install/apply.js +23 -0
  65. package/scripts/lib/install/config.js +82 -0
  66. package/scripts/lib/install/request.js +113 -0
  67. package/scripts/lib/install/runtime.js +42 -0
  68. package/scripts/lib/install-executor.js +605 -0
  69. package/scripts/lib/install-lifecycle.js +763 -0
  70. package/scripts/lib/install-manifests.js +305 -0
  71. package/scripts/lib/install-state.js +120 -0
  72. package/scripts/lib/install-targets/antigravity-project.js +9 -0
  73. package/scripts/lib/install-targets/claude-home.js +10 -0
  74. package/scripts/lib/install-targets/codex-home.js +10 -0
  75. package/scripts/lib/install-targets/cursor-project.js +10 -0
  76. package/scripts/lib/install-targets/helpers.js +89 -0
  77. package/scripts/lib/install-targets/opencode-home.js +10 -0
  78. package/scripts/lib/install-targets/registry.js +64 -0
  79. package/scripts/lib/orchestration-session.js +299 -0
  80. package/scripts/lib/package-manager.d.ts +119 -0
  81. package/scripts/lib/package-manager.js +431 -0
  82. package/scripts/lib/project-detect.js +428 -0
  83. package/scripts/lib/resolve-formatter.js +185 -0
  84. package/scripts/lib/session-adapters/canonical-session.js +138 -0
  85. package/scripts/lib/session-adapters/claude-history.js +149 -0
  86. package/scripts/lib/session-adapters/dmux-tmux.js +80 -0
  87. package/scripts/lib/session-adapters/registry.js +111 -0
  88. package/scripts/lib/session-aliases.d.ts +136 -0
  89. package/scripts/lib/session-aliases.js +481 -0
  90. package/scripts/lib/session-manager.d.ts +131 -0
  91. package/scripts/lib/session-manager.js +464 -0
  92. package/scripts/lib/shell-split.js +86 -0
  93. package/scripts/lib/skill-improvement/amendify.js +89 -0
  94. package/scripts/lib/skill-improvement/evaluate.js +59 -0
  95. package/scripts/lib/skill-improvement/health.js +118 -0
  96. package/scripts/lib/skill-improvement/observations.js +108 -0
  97. package/scripts/lib/tmux-worktree-orchestrator.js +491 -0
  98. package/scripts/lib/utils.d.ts +183 -0
  99. package/scripts/lib/utils.js +543 -0
  100. package/scripts/list-installed.js +90 -0
  101. package/scripts/orchestrate-codex-worker.sh +92 -0
  102. package/scripts/orchestrate-worktrees.js +108 -0
  103. package/scripts/orchestration-status.js +62 -0
  104. package/scripts/repair.js +97 -0
  105. package/scripts/session-inspect.js +150 -0
  106. package/scripts/setup-package-manager.js +204 -0
  107. package/scripts/skill-create-output.js +244 -0
  108. package/scripts/uninstall.js +96 -0
@@ -0,0 +1,299 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { spawnSync } = require('child_process');
6
+
7
+ function stripCodeTicks(value) {
8
+ if (typeof value !== 'string') {
9
+ return value;
10
+ }
11
+
12
+ const trimmed = value.trim();
13
+ if (trimmed.startsWith('`') && trimmed.endsWith('`') && trimmed.length >= 2) {
14
+ return trimmed.slice(1, -1);
15
+ }
16
+
17
+ return trimmed;
18
+ }
19
+
20
+ function parseSection(content, heading) {
21
+ if (typeof content !== 'string' || content.length === 0) {
22
+ return '';
23
+ }
24
+
25
+ const lines = content.split('\n');
26
+ const headingLines = new Set([`## ${heading}`, `**${heading}**`]);
27
+ const startIndex = lines.findIndex(line => headingLines.has(line.trim()));
28
+
29
+ if (startIndex === -1) {
30
+ return '';
31
+ }
32
+
33
+ const collected = [];
34
+ for (let index = startIndex + 1; index < lines.length; index += 1) {
35
+ const line = lines[index];
36
+ const trimmed = line.trim();
37
+ if (trimmed.startsWith('## ') || (/^\*\*.+\*\*$/.test(trimmed) && !headingLines.has(trimmed))) {
38
+ break;
39
+ }
40
+ collected.push(line);
41
+ }
42
+
43
+ return collected.join('\n').trim();
44
+ }
45
+
46
+ function parseBullets(section) {
47
+ if (!section) {
48
+ return [];
49
+ }
50
+
51
+ return section
52
+ .split('\n')
53
+ .map(line => line.trim())
54
+ .filter(line => line.startsWith('- '))
55
+ .map(line => stripCodeTicks(line.replace(/^- /, '').trim()));
56
+ }
57
+
58
+ function parseWorkerStatus(content) {
59
+ const status = {
60
+ state: null,
61
+ updated: null,
62
+ branch: null,
63
+ worktree: null,
64
+ taskFile: null,
65
+ handoffFile: null
66
+ };
67
+
68
+ if (typeof content !== 'string' || content.length === 0) {
69
+ return status;
70
+ }
71
+
72
+ for (const line of content.split('\n')) {
73
+ const match = line.match(/^- ([A-Za-z ]+):\s*(.+)$/);
74
+ if (!match) {
75
+ continue;
76
+ }
77
+
78
+ const key = match[1].trim().toLowerCase().replace(/\s+/g, '');
79
+ const value = stripCodeTicks(match[2]);
80
+
81
+ if (key === 'state') status.state = value;
82
+ if (key === 'updated') status.updated = value;
83
+ if (key === 'branch') status.branch = value;
84
+ if (key === 'worktree') status.worktree = value;
85
+ if (key === 'taskfile') status.taskFile = value;
86
+ if (key === 'handofffile') status.handoffFile = value;
87
+ }
88
+
89
+ return status;
90
+ }
91
+
92
+ function parseWorkerTask(content) {
93
+ return {
94
+ objective: parseSection(content, 'Objective'),
95
+ seedPaths: parseBullets(parseSection(content, 'Seeded Local Overlays'))
96
+ };
97
+ }
98
+
99
+ function parseWorkerHandoff(content) {
100
+ return {
101
+ summary: parseBullets(parseSection(content, 'Summary')),
102
+ validation: parseBullets(parseSection(content, 'Validation')),
103
+ remainingRisks: parseBullets(parseSection(content, 'Remaining Risks'))
104
+ };
105
+ }
106
+
107
+ function readTextIfExists(filePath) {
108
+ if (!filePath || !fs.existsSync(filePath)) {
109
+ return '';
110
+ }
111
+
112
+ return fs.readFileSync(filePath, 'utf8');
113
+ }
114
+
115
+ function listWorkerDirectories(coordinationDir) {
116
+ if (!coordinationDir || !fs.existsSync(coordinationDir)) {
117
+ return [];
118
+ }
119
+
120
+ return fs.readdirSync(coordinationDir, { withFileTypes: true })
121
+ .filter(entry => entry.isDirectory())
122
+ .filter(entry => {
123
+ const workerDir = path.join(coordinationDir, entry.name);
124
+ return ['status.md', 'task.md', 'handoff.md']
125
+ .some(filename => fs.existsSync(path.join(workerDir, filename)));
126
+ })
127
+ .map(entry => entry.name)
128
+ .sort();
129
+ }
130
+
131
+ function loadWorkerSnapshots(coordinationDir) {
132
+ return listWorkerDirectories(coordinationDir).map(workerSlug => {
133
+ const workerDir = path.join(coordinationDir, workerSlug);
134
+ const statusPath = path.join(workerDir, 'status.md');
135
+ const taskPath = path.join(workerDir, 'task.md');
136
+ const handoffPath = path.join(workerDir, 'handoff.md');
137
+
138
+ const status = parseWorkerStatus(readTextIfExists(statusPath));
139
+ const task = parseWorkerTask(readTextIfExists(taskPath));
140
+ const handoff = parseWorkerHandoff(readTextIfExists(handoffPath));
141
+
142
+ return {
143
+ workerSlug,
144
+ workerDir,
145
+ status,
146
+ task,
147
+ handoff,
148
+ files: {
149
+ status: statusPath,
150
+ task: taskPath,
151
+ handoff: handoffPath
152
+ }
153
+ };
154
+ });
155
+ }
156
+
157
+ function listTmuxPanes(sessionName, options = {}) {
158
+ const { spawnSyncImpl = spawnSync } = options;
159
+ const format = [
160
+ '#{pane_id}',
161
+ '#{window_index}',
162
+ '#{pane_index}',
163
+ '#{pane_title}',
164
+ '#{pane_current_command}',
165
+ '#{pane_current_path}',
166
+ '#{pane_active}',
167
+ '#{pane_dead}',
168
+ '#{pane_pid}'
169
+ ].join('\t');
170
+
171
+ const result = spawnSyncImpl('tmux', ['list-panes', '-t', sessionName, '-F', format], {
172
+ encoding: 'utf8',
173
+ stdio: ['ignore', 'pipe', 'pipe']
174
+ });
175
+
176
+ if (result.error) {
177
+ if (result.error.code === 'ENOENT') {
178
+ return [];
179
+ }
180
+ throw result.error;
181
+ }
182
+
183
+ if (result.status !== 0) {
184
+ return [];
185
+ }
186
+
187
+ return (result.stdout || '')
188
+ .split('\n')
189
+ .map(line => line.trim())
190
+ .filter(Boolean)
191
+ .map(line => {
192
+ const [
193
+ paneId,
194
+ windowIndex,
195
+ paneIndex,
196
+ title,
197
+ currentCommand,
198
+ currentPath,
199
+ active,
200
+ dead,
201
+ pid
202
+ ] = line.split('\t');
203
+
204
+ return {
205
+ paneId,
206
+ windowIndex: Number(windowIndex),
207
+ paneIndex: Number(paneIndex),
208
+ title,
209
+ currentCommand,
210
+ currentPath,
211
+ active: active === '1',
212
+ dead: dead === '1',
213
+ pid: pid ? Number(pid) : null
214
+ };
215
+ });
216
+ }
217
+
218
+ function summarizeWorkerStates(workers) {
219
+ return workers.reduce((counts, worker) => {
220
+ const state = worker.status.state || 'unknown';
221
+ counts[state] = (counts[state] || 0) + 1;
222
+ return counts;
223
+ }, {});
224
+ }
225
+
226
+ function buildSessionSnapshot({ sessionName, coordinationDir, panes }) {
227
+ const workerSnapshots = loadWorkerSnapshots(coordinationDir);
228
+ const paneMap = new Map(panes.map(pane => [pane.title, pane]));
229
+
230
+ const workers = workerSnapshots.map(worker => ({
231
+ ...worker,
232
+ pane: paneMap.get(worker.workerSlug) || null
233
+ }));
234
+
235
+ return {
236
+ sessionName,
237
+ coordinationDir,
238
+ sessionActive: panes.length > 0,
239
+ paneCount: panes.length,
240
+ workerCount: workers.length,
241
+ workerStates: summarizeWorkerStates(workers),
242
+ panes,
243
+ workers
244
+ };
245
+ }
246
+
247
+ function resolveSnapshotTarget(targetPath, cwd = process.cwd()) {
248
+ const absoluteTarget = path.resolve(cwd, targetPath);
249
+
250
+ if (fs.existsSync(absoluteTarget) && fs.statSync(absoluteTarget).isFile()) {
251
+ const config = JSON.parse(fs.readFileSync(absoluteTarget, 'utf8'));
252
+ const repoRoot = path.resolve(config.repoRoot || cwd);
253
+ const coordinationRoot = path.resolve(
254
+ config.coordinationRoot || path.join(repoRoot, '.orchestration')
255
+ );
256
+
257
+ return {
258
+ sessionName: config.sessionName,
259
+ coordinationDir: path.join(coordinationRoot, config.sessionName),
260
+ repoRoot,
261
+ targetType: 'plan'
262
+ };
263
+ }
264
+
265
+ return {
266
+ sessionName: targetPath,
267
+ coordinationDir: path.join(cwd, '.claude', 'orchestration', targetPath),
268
+ repoRoot: cwd,
269
+ targetType: 'session'
270
+ };
271
+ }
272
+
273
+ function collectSessionSnapshot(targetPath, cwd = process.cwd()) {
274
+ const target = resolveSnapshotTarget(targetPath, cwd);
275
+ const panes = listTmuxPanes(target.sessionName);
276
+ const snapshot = buildSessionSnapshot({
277
+ sessionName: target.sessionName,
278
+ coordinationDir: target.coordinationDir,
279
+ panes
280
+ });
281
+
282
+ return {
283
+ ...snapshot,
284
+ repoRoot: target.repoRoot,
285
+ targetType: target.targetType
286
+ };
287
+ }
288
+
289
+ module.exports = {
290
+ buildSessionSnapshot,
291
+ collectSessionSnapshot,
292
+ listTmuxPanes,
293
+ loadWorkerSnapshots,
294
+ normalizeText: stripCodeTicks,
295
+ parseWorkerHandoff,
296
+ parseWorkerStatus,
297
+ parseWorkerTask,
298
+ resolveSnapshotTarget
299
+ };
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Package Manager Detection and Selection.
3
+ * Supports: npm, pnpm, yarn, bun.
4
+ */
5
+
6
+ /** Supported package manager names */
7
+ export type PackageManagerName = 'npm' | 'pnpm' | 'yarn' | 'bun';
8
+
9
+ /** Configuration for a single package manager */
10
+ export interface PackageManagerConfig {
11
+ name: PackageManagerName;
12
+ /** Lock file name (e.g., "package-lock.json", "pnpm-lock.yaml") */
13
+ lockFile: string;
14
+ /** Install command (e.g., "npm install") */
15
+ installCmd: string;
16
+ /** Run script command prefix (e.g., "npm run", "pnpm") */
17
+ runCmd: string;
18
+ /** Execute binary command (e.g., "npx", "pnpm dlx") */
19
+ execCmd: string;
20
+ /** Test command (e.g., "npm test") */
21
+ testCmd: string;
22
+ /** Build command (e.g., "npm run build") */
23
+ buildCmd: string;
24
+ /** Dev server command (e.g., "npm run dev") */
25
+ devCmd: string;
26
+ }
27
+
28
+ /** How the package manager was detected */
29
+ export type DetectionSource =
30
+ | 'environment'
31
+ | 'project-config'
32
+ | 'package.json'
33
+ | 'lock-file'
34
+ | 'global-config'
35
+ | 'default';
36
+
37
+ /** Result from getPackageManager() */
38
+ export interface PackageManagerResult {
39
+ name: PackageManagerName;
40
+ config: PackageManagerConfig;
41
+ source: DetectionSource;
42
+ }
43
+
44
+ /** Map of all supported package managers keyed by name */
45
+ export const PACKAGE_MANAGERS: Record<PackageManagerName, PackageManagerConfig>;
46
+
47
+ /** Priority order for lock file detection */
48
+ export const DETECTION_PRIORITY: PackageManagerName[];
49
+
50
+ export interface GetPackageManagerOptions {
51
+ /** Project directory to detect from (default: process.cwd()) */
52
+ projectDir?: string;
53
+ }
54
+
55
+ /**
56
+ * Get the package manager to use for the current project.
57
+ *
58
+ * Detection priority:
59
+ * 1. CLAUDE_PACKAGE_MANAGER environment variable
60
+ * 2. Project-specific config (.claude/package-manager.json)
61
+ * 3. package.json `packageManager` field
62
+ * 4. Lock file detection
63
+ * 5. Global user preference (~/.claude/package-manager.json)
64
+ * 6. Default to npm (no child processes spawned)
65
+ */
66
+ export function getPackageManager(options?: GetPackageManagerOptions): PackageManagerResult;
67
+
68
+ /**
69
+ * Set the user's globally preferred package manager.
70
+ * Saves to ~/.claude/package-manager.json.
71
+ * @throws If pmName is not a known package manager or if save fails
72
+ */
73
+ export function setPreferredPackageManager(pmName: PackageManagerName): { packageManager: string; setAt: string };
74
+
75
+ /**
76
+ * Set a project-specific preferred package manager.
77
+ * Saves to <projectDir>/.claude/package-manager.json.
78
+ * @throws If pmName is not a known package manager
79
+ */
80
+ export function setProjectPackageManager(pmName: PackageManagerName, projectDir?: string): { packageManager: string; setAt: string };
81
+
82
+ /**
83
+ * Get package managers installed on the system.
84
+ * WARNING: Spawns child processes for each PM check.
85
+ * Do NOT call during session startup hooks.
86
+ */
87
+ export function getAvailablePackageManagers(): PackageManagerName[];
88
+
89
+ /** Detect package manager from lock file in the given directory */
90
+ export function detectFromLockFile(projectDir?: string): PackageManagerName | null;
91
+
92
+ /** Detect package manager from package.json `packageManager` field */
93
+ export function detectFromPackageJson(projectDir?: string): PackageManagerName | null;
94
+
95
+ /**
96
+ * Get the full command string to run a script.
97
+ * @param script - Script name: "install", "test", "build", "dev", or custom
98
+ */
99
+ export function getRunCommand(script: string, options?: GetPackageManagerOptions): string;
100
+
101
+ /**
102
+ * Get the full command string to execute a package binary.
103
+ * @param binary - Binary name (e.g., "prettier", "eslint")
104
+ * @param args - Arguments to pass to the binary
105
+ */
106
+ export function getExecCommand(binary: string, args?: string, options?: GetPackageManagerOptions): string;
107
+
108
+ /**
109
+ * Get a message prompting the user to configure their package manager.
110
+ * Does NOT spawn child processes.
111
+ */
112
+ export function getSelectionPrompt(): string;
113
+
114
+ /**
115
+ * Generate a regex pattern string that matches commands for all package managers.
116
+ * @param action - Action like "dev", "install", "test", "build", or custom
117
+ * @returns Parenthesized alternation regex string, e.g., "(npm run dev|pnpm( run)? dev|...)"
118
+ */
119
+ export function getCommandPattern(action: string): string;