@planu/cli 1.2.0 → 1.3.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 (146) hide show
  1. package/dist/config/dev-lifecycle-catalog.json +22 -0
  2. package/dist/config/license-plans.json +17 -1
  3. package/dist/engine/aider-prompt-generator.d.ts +26 -0
  4. package/dist/engine/aider-prompt-generator.d.ts.map +1 -0
  5. package/dist/engine/aider-prompt-generator.js +144 -0
  6. package/dist/engine/aider-prompt-generator.js.map +1 -0
  7. package/dist/engine/claude-config-auditor/settings-auditor.d.ts.map +1 -1
  8. package/dist/engine/claude-config-auditor/settings-auditor.js +20 -1
  9. package/dist/engine/claude-config-auditor/settings-auditor.js.map +1 -1
  10. package/dist/engine/code-graph-configurator.d.ts +23 -0
  11. package/dist/engine/code-graph-configurator.d.ts.map +1 -0
  12. package/dist/engine/code-graph-configurator.js +80 -0
  13. package/dist/engine/code-graph-configurator.js.map +1 -0
  14. package/dist/engine/continue-config-generator.d.ts +15 -0
  15. package/dist/engine/continue-config-generator.d.ts.map +1 -0
  16. package/dist/engine/continue-config-generator.js +53 -0
  17. package/dist/engine/continue-config-generator.js.map +1 -0
  18. package/dist/engine/e2e-test-generator.d.ts +19 -0
  19. package/dist/engine/e2e-test-generator.d.ts.map +1 -0
  20. package/dist/engine/e2e-test-generator.js +210 -0
  21. package/dist/engine/e2e-test-generator.js.map +1 -0
  22. package/dist/engine/ecosystem-detector.d.ts +13 -0
  23. package/dist/engine/ecosystem-detector.d.ts.map +1 -0
  24. package/dist/engine/ecosystem-detector.js +128 -0
  25. package/dist/engine/ecosystem-detector.js.map +1 -0
  26. package/dist/engine/memory-config-generator.d.ts +8 -0
  27. package/dist/engine/memory-config-generator.d.ts.map +1 -0
  28. package/dist/engine/memory-config-generator.js +101 -0
  29. package/dist/engine/memory-config-generator.js.map +1 -0
  30. package/dist/engine/pr-spec-validator.d.ts +25 -0
  31. package/dist/engine/pr-spec-validator.d.ts.map +1 -0
  32. package/dist/engine/pr-spec-validator.js +172 -0
  33. package/dist/engine/pr-spec-validator.js.map +1 -0
  34. package/dist/engine/sweep-issue-generator.d.ts +14 -0
  35. package/dist/engine/sweep-issue-generator.d.ts.map +1 -0
  36. package/dist/engine/sweep-issue-generator.js +140 -0
  37. package/dist/engine/sweep-issue-generator.js.map +1 -0
  38. package/dist/index.js +16 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/tools/aider-launcher-handler.d.ts +4 -0
  41. package/dist/tools/aider-launcher-handler.d.ts.map +1 -0
  42. package/dist/tools/aider-launcher-handler.js +69 -0
  43. package/dist/tools/aider-launcher-handler.js.map +1 -0
  44. package/dist/tools/code-graph-handler.d.ts +6 -0
  45. package/dist/tools/code-graph-handler.d.ts.map +1 -0
  46. package/dist/tools/code-graph-handler.js +68 -0
  47. package/dist/tools/code-graph-handler.js.map +1 -0
  48. package/dist/tools/continue-config-handler.d.ts +4 -0
  49. package/dist/tools/continue-config-handler.d.ts.map +1 -0
  50. package/dist/tools/continue-config-handler.js +88 -0
  51. package/dist/tools/continue-config-handler.js.map +1 -0
  52. package/dist/tools/e2e-test-generator-handler.d.ts +12 -0
  53. package/dist/tools/e2e-test-generator-handler.d.ts.map +1 -0
  54. package/dist/tools/e2e-test-generator-handler.js +54 -0
  55. package/dist/tools/e2e-test-generator-handler.js.map +1 -0
  56. package/dist/tools/ecosystem-tools-handler.d.ts +4 -0
  57. package/dist/tools/ecosystem-tools-handler.d.ts.map +1 -0
  58. package/dist/tools/ecosystem-tools-handler.js +57 -0
  59. package/dist/tools/ecosystem-tools-handler.js.map +1 -0
  60. package/dist/tools/memory-config-handler.d.ts +4 -0
  61. package/dist/tools/memory-config-handler.d.ts.map +1 -0
  62. package/dist/tools/memory-config-handler.js +110 -0
  63. package/dist/tools/memory-config-handler.js.map +1 -0
  64. package/dist/tools/pr-agent-handler.d.ts +12 -0
  65. package/dist/tools/pr-agent-handler.d.ts.map +1 -0
  66. package/dist/tools/pr-agent-handler.js +47 -0
  67. package/dist/tools/pr-agent-handler.js.map +1 -0
  68. package/dist/tools/register-aider-tools.d.ts +3 -0
  69. package/dist/tools/register-aider-tools.d.ts.map +1 -0
  70. package/dist/tools/register-aider-tools.js +28 -0
  71. package/dist/tools/register-aider-tools.js.map +1 -0
  72. package/dist/tools/register-code-graph-tools.d.ts +3 -0
  73. package/dist/tools/register-code-graph-tools.d.ts.map +1 -0
  74. package/dist/tools/register-code-graph-tools.js +36 -0
  75. package/dist/tools/register-code-graph-tools.js.map +1 -0
  76. package/dist/tools/register-continue-tools.d.ts +3 -0
  77. package/dist/tools/register-continue-tools.d.ts.map +1 -0
  78. package/dist/tools/register-continue-tools.js +38 -0
  79. package/dist/tools/register-continue-tools.js.map +1 -0
  80. package/dist/tools/register-e2e-test-generator-tools.d.ts +3 -0
  81. package/dist/tools/register-e2e-test-generator-tools.d.ts.map +1 -0
  82. package/dist/tools/register-e2e-test-generator-tools.js +35 -0
  83. package/dist/tools/register-e2e-test-generator-tools.js.map +1 -0
  84. package/dist/tools/register-memory-config-tools.d.ts +3 -0
  85. package/dist/tools/register-memory-config-tools.d.ts.map +1 -0
  86. package/dist/tools/register-memory-config-tools.js +34 -0
  87. package/dist/tools/register-memory-config-tools.js.map +1 -0
  88. package/dist/tools/register-pr-agent-tools.d.ts +3 -0
  89. package/dist/tools/register-pr-agent-tools.d.ts.map +1 -0
  90. package/dist/tools/register-pr-agent-tools.js +34 -0
  91. package/dist/tools/register-pr-agent-tools.js.map +1 -0
  92. package/dist/tools/register-sweep-tools.d.ts +3 -0
  93. package/dist/tools/register-sweep-tools.d.ts.map +1 -0
  94. package/dist/tools/register-sweep-tools.js +42 -0
  95. package/dist/tools/register-sweep-tools.js.map +1 -0
  96. package/dist/tools/register-token-optimizer-tools.d.ts +3 -0
  97. package/dist/tools/register-token-optimizer-tools.d.ts.map +1 -0
  98. package/dist/tools/register-token-optimizer-tools.js +22 -0
  99. package/dist/tools/register-token-optimizer-tools.js.map +1 -0
  100. package/dist/tools/sweep-integration-handler.d.ts +4 -0
  101. package/dist/tools/sweep-integration-handler.d.ts.map +1 -0
  102. package/dist/tools/sweep-integration-handler.js +60 -0
  103. package/dist/tools/sweep-integration-handler.js.map +1 -0
  104. package/dist/types/aider.d.ts +21 -0
  105. package/dist/types/aider.d.ts.map +1 -0
  106. package/dist/types/aider.js +3 -0
  107. package/dist/types/aider.js.map +1 -0
  108. package/dist/types/code-graph-integration.d.ts +22 -0
  109. package/dist/types/code-graph-integration.d.ts.map +1 -0
  110. package/dist/types/code-graph-integration.js +3 -0
  111. package/dist/types/code-graph-integration.js.map +1 -0
  112. package/dist/types/continue-integration.d.ts +32 -0
  113. package/dist/types/continue-integration.d.ts.map +1 -0
  114. package/dist/types/continue-integration.js +3 -0
  115. package/dist/types/continue-integration.js.map +1 -0
  116. package/dist/types/e2e-test-generator.d.ts +27 -0
  117. package/dist/types/e2e-test-generator.d.ts.map +1 -0
  118. package/dist/types/e2e-test-generator.js +3 -0
  119. package/dist/types/e2e-test-generator.js.map +1 -0
  120. package/dist/types/index.d.ts +6 -0
  121. package/dist/types/index.d.ts.map +1 -1
  122. package/dist/types/index.js +6 -0
  123. package/dist/types/index.js.map +1 -1
  124. package/dist/types/pr-agent.d.ts +28 -0
  125. package/dist/types/pr-agent.d.ts.map +1 -0
  126. package/dist/types/pr-agent.js +3 -0
  127. package/dist/types/pr-agent.js.map +1 -0
  128. package/dist/types/sweep.d.ts +22 -0
  129. package/dist/types/sweep.d.ts.map +1 -0
  130. package/dist/types/sweep.js +3 -0
  131. package/dist/types/sweep.js.map +1 -0
  132. package/dist/types/tooling/index.d.ts +2 -0
  133. package/dist/types/tooling/index.d.ts.map +1 -1
  134. package/dist/types/tooling/memory-config.d.ts +25 -0
  135. package/dist/types/tooling/memory-config.d.ts.map +1 -0
  136. package/dist/types/tooling/memory-config.js +3 -0
  137. package/dist/types/tooling/memory-config.js.map +1 -0
  138. package/dist/types/tooling/token-optimizer.d.ts +42 -0
  139. package/dist/types/tooling/token-optimizer.d.ts.map +1 -0
  140. package/dist/types/tooling/token-optimizer.js +3 -0
  141. package/dist/types/tooling/token-optimizer.js.map +1 -0
  142. package/dist/types/tooling.d.ts +1 -1
  143. package/dist/types/tooling.d.ts.map +1 -1
  144. package/package.json +1 -1
  145. package/src/config/dev-lifecycle-catalog.json +22 -0
  146. package/src/config/license-plans.json +17 -1
@@ -0,0 +1,210 @@
1
+ // Planu — E2E Test Generator engine (SPEC-381)
2
+ // Pure functions for generating E2E tests from spec acceptance criteria.
3
+ import { readFile, readdir, mkdir, writeFile } from 'node:fs/promises';
4
+ import { join, extname } from 'node:path';
5
+ /** Extract acceptance criteria from spec.md content (checkbox lines). */
6
+ function extractAcceptanceCriteria(specContent) {
7
+ const lines = specContent.split('\n');
8
+ const criteria = [];
9
+ for (const line of lines) {
10
+ const match = /^\s*-\s*\[[ xX]?\]\s+(.+)$/.exec(line);
11
+ if (match?.[1]) {
12
+ criteria.push(match[1].trim());
13
+ }
14
+ }
15
+ return criteria;
16
+ }
17
+ /** Extract spec title from frontmatter or first H1. */
18
+ function extractSpecTitle(specContent) {
19
+ const titleMatch = /^#\s+(.+)$/m.exec(specContent);
20
+ if (titleMatch?.[1]) {
21
+ return titleMatch[1].replace(/^SPEC-\d+[:\s-]+/i, '').trim();
22
+ }
23
+ const fmMatch = /^title:\s*(.+)$/m.exec(specContent);
24
+ return fmMatch?.[1]?.trim() ?? 'Spec';
25
+ }
26
+ /** Extract spec description from frontmatter or first paragraph. */
27
+ function extractSpecDescription(specContent) {
28
+ const descMatch = /^description:\s*(.+)$/m.exec(specContent);
29
+ if (descMatch?.[1]) {
30
+ return descMatch[1].trim();
31
+ }
32
+ const lines = specContent.split('\n');
33
+ for (const line of lines) {
34
+ const trimmed = line.trim();
35
+ if (trimmed &&
36
+ !trimmed.startsWith('#') &&
37
+ !trimmed.startsWith('-') &&
38
+ !trimmed.startsWith('`')) {
39
+ return trimmed;
40
+ }
41
+ }
42
+ return '';
43
+ }
44
+ /** Generate Gherkin feature file content. */
45
+ export function generateGherkin(title, description, criteria) {
46
+ const scenarios = criteria
47
+ .map((criterion) => ` Scenario: ${criterion}\n` +
48
+ ` Given the system is ready\n` +
49
+ ` When the user performs the action\n` +
50
+ ` Then ${criterion}`)
51
+ .join('\n\n');
52
+ const descLine = description ? ` ${description}\n\n` : '';
53
+ return `Feature: ${title}\n${descLine}${scenarios}\n`;
54
+ }
55
+ /** Generate Playwright TypeScript test content. */
56
+ export function generatePlaywright(title, criteria) {
57
+ const tests = criteria
58
+ .map((criterion) => ` test('${criterion.replace(/'/g, "\\'")}', async ({ page }) => {\n` +
59
+ ` // TODO: implement test for: ${criterion}\n` +
60
+ ` });`)
61
+ .join('\n\n');
62
+ return (`import { test, expect } from '@playwright/test';\n\n` +
63
+ `test.describe('${title.replace(/'/g, "\\'")}', () => {\n` +
64
+ `${tests}\n` +
65
+ `});\n`);
66
+ }
67
+ /** Generate testRigor plain English steps. */
68
+ export function generateTestRigor(title, criteria) {
69
+ const steps = criteria.map((criterion, i) => `${i + 1}. ${criterion}`).join('\n');
70
+ return `# ${title}\n\n${steps}\n`;
71
+ }
72
+ /** Generate plain numbered list. */
73
+ export function generatePlain(criteria) {
74
+ return criteria.map((criterion, i) => `${i + 1}. Verify that ${criterion}`).join('\n') + '\n';
75
+ }
76
+ /** Get file extension for each framework. */
77
+ export function getFrameworkExtension(framework) {
78
+ switch (framework) {
79
+ case 'gherkin':
80
+ return 'feature';
81
+ case 'playwright':
82
+ return 'spec.ts';
83
+ case 'testRigor':
84
+ return 'txt';
85
+ case 'plain':
86
+ return 'txt';
87
+ }
88
+ }
89
+ /** Derive default output path for tests. */
90
+ function deriveDefaultOutputPath(specId, slug, framework) {
91
+ const ext = getFrameworkExtension(framework);
92
+ const fileName = `${specId}-${slug}.${ext}`;
93
+ return join('tests', 'e2e', fileName);
94
+ }
95
+ /** Extract slug from spec directory name. */
96
+ function extractSlug(specDirName) {
97
+ return specDirName.replace(/^SPEC-\d+-?/i, '').toLowerCase() || 'spec';
98
+ }
99
+ /** Resolve spec directory from specId and projectPath. */
100
+ async function resolveSpecDir(specId, projectPath) {
101
+ const specsBase = join(projectPath, 'planu', 'specs');
102
+ let entries;
103
+ try {
104
+ entries = await readdir(specsBase);
105
+ }
106
+ catch {
107
+ return null;
108
+ }
109
+ const upper = specId.toUpperCase();
110
+ const match = entries.find((e) => e.toUpperCase().startsWith(upper));
111
+ return match ? join(specsBase, match) : null;
112
+ }
113
+ /** Generate E2E tests from a spec. */
114
+ export async function generateE2eTests(specId, projectPath, framework = 'gherkin', outputPath) {
115
+ const specDir = await resolveSpecDir(specId, projectPath);
116
+ if (!specDir) {
117
+ return { isError: true, message: `Spec ${specId} not found in ${projectPath}/planu/specs/` };
118
+ }
119
+ let specContent;
120
+ try {
121
+ specContent = await readFile(join(specDir, 'spec.md'), 'utf8');
122
+ }
123
+ catch {
124
+ return { isError: true, message: `Could not read spec.md for ${specId}` };
125
+ }
126
+ const criteria = extractAcceptanceCriteria(specContent);
127
+ const title = extractSpecTitle(specContent);
128
+ const description = extractSpecDescription(specContent);
129
+ let content;
130
+ switch (framework) {
131
+ case 'gherkin':
132
+ content = generateGherkin(title, description, criteria);
133
+ break;
134
+ case 'playwright':
135
+ content = generatePlaywright(title, criteria);
136
+ break;
137
+ case 'testRigor':
138
+ content = generateTestRigor(title, criteria);
139
+ break;
140
+ case 'plain':
141
+ content = generatePlain(criteria);
142
+ break;
143
+ }
144
+ const specDirName = specDir.split('/').pop() ?? specId;
145
+ const slug = extractSlug(specDirName);
146
+ const resolvedOutputPath = outputPath ?? deriveDefaultOutputPath(specId, slug, framework);
147
+ const absoluteOutputPath = resolvedOutputPath.startsWith('/')
148
+ ? resolvedOutputPath
149
+ : join(projectPath, resolvedOutputPath);
150
+ try {
151
+ const dir = absoluteOutputPath.substring(0, absoluteOutputPath.lastIndexOf('/'));
152
+ await mkdir(dir, { recursive: true });
153
+ await writeFile(absoluteOutputPath, content, 'utf8');
154
+ }
155
+ catch {
156
+ // If write fails, still return content without outputPath
157
+ return {
158
+ framework,
159
+ specId,
160
+ testCount: criteria.length,
161
+ content,
162
+ outputPath: null,
163
+ };
164
+ }
165
+ return {
166
+ framework,
167
+ specId,
168
+ testCount: criteria.length,
169
+ content,
170
+ outputPath: absoluteOutputPath,
171
+ };
172
+ }
173
+ /** List existing E2E test files matching SPEC-XXX pattern. */
174
+ export async function getE2eTestStatus(projectPath) {
175
+ const e2eDir = join(projectPath, 'tests', 'e2e');
176
+ let files;
177
+ try {
178
+ files = await readdir(e2eDir);
179
+ }
180
+ catch {
181
+ return { testFiles: [], total: 0 };
182
+ }
183
+ const specPattern = /^(SPEC-\d+)/i;
184
+ const testFiles = [];
185
+ for (const file of files) {
186
+ const ext = extname(file);
187
+ const isTestFile = file.endsWith('.feature') || file.endsWith('.spec.ts') || file.endsWith('.test.ts');
188
+ if (!isTestFile) {
189
+ continue;
190
+ }
191
+ const match = specPattern.exec(file);
192
+ if (!match?.[1]) {
193
+ continue;
194
+ }
195
+ const specId = match[1].toUpperCase();
196
+ let framework;
197
+ if (file.endsWith('.feature')) {
198
+ framework = 'gherkin';
199
+ }
200
+ else if (ext === '.ts') {
201
+ framework = 'playwright';
202
+ }
203
+ else {
204
+ framework = 'unknown';
205
+ }
206
+ testFiles.push({ file, specId, framework });
207
+ }
208
+ return { testFiles, total: testFiles.length };
209
+ }
210
+ //# sourceMappingURL=e2e-test-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e-test-generator.js","sourceRoot":"","sources":["../../src/engine/e2e-test-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { TokenOptimizerStatus, TokenOptimizerRecommendation } from '../types/index.js';
2
+ export declare function detectRtkHooks(projectPath: string): Promise<number>;
3
+ export declare function detectHeadroomPackage(projectPath: string): Promise<{
4
+ installed: boolean;
5
+ version?: string;
6
+ }>;
7
+ export declare function detectRtkPackage(projectPath: string): Promise<{
8
+ installed: boolean;
9
+ version?: string;
10
+ }>;
11
+ export declare function getTokenOptimizerStatus(projectPath: string): Promise<TokenOptimizerStatus>;
12
+ export declare function buildTokenOptimizerRecommendations(projectPath: string): Promise<TokenOptimizerRecommendation[]>;
13
+ //# sourceMappingURL=ecosystem-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ecosystem-detector.d.ts","sourceRoot":"","sources":["../../src/engine/ecosystem-detector.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,oBAAoB,EACpB,4BAA4B,EAG7B,MAAM,mBAAmB,CAAC;AAW3B,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUzE;AAED,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBnD;AAED,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBnD;AAED,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAkBhG;AAED,wBAAsB,kCAAkC,CACtD,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,4BAA4B,EAAE,CAAC,CAyDzC"}
@@ -0,0 +1,128 @@
1
+ // Planu — Ecosystem detector engine (SPEC-374)
2
+ // Pure functions to detect token optimization tools (RTK, Headroom)
3
+ import { readFile } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ async function readJsonFile(filePath) {
6
+ try {
7
+ const content = await readFile(filePath, 'utf-8');
8
+ return JSON.parse(content);
9
+ }
10
+ catch {
11
+ return null;
12
+ }
13
+ }
14
+ export async function detectRtkHooks(projectPath) {
15
+ const settingsPath = join(projectPath, '.claude', 'settings.json');
16
+ const settings = await readJsonFile(settingsPath);
17
+ if (!settings?.hooks?.PreToolUse) {
18
+ return 0;
19
+ }
20
+ return settings.hooks.PreToolUse.filter((hook) => {
21
+ const hooksArr = hook.hooks ?? [];
22
+ return hooksArr.some((h) => typeof h === 'string' && h.toLowerCase().includes('rtk'));
23
+ }).length;
24
+ }
25
+ export async function detectHeadroomPackage(projectPath) {
26
+ const pkgPath = join(projectPath, 'package.json');
27
+ const pkg = await readJsonFile(pkgPath);
28
+ if (!pkg) {
29
+ return { installed: false };
30
+ }
31
+ const allDeps = {
32
+ ...(pkg.devDependencies ?? {}),
33
+ ...(pkg.dependencies ?? {}),
34
+ };
35
+ const headroom = allDeps.headroom;
36
+ const headroomCli = allDeps['@headroom/cli'];
37
+ const version = headroom ?? headroomCli;
38
+ if (version !== undefined) {
39
+ return { installed: true, version };
40
+ }
41
+ return { installed: false };
42
+ }
43
+ export async function detectRtkPackage(projectPath) {
44
+ const pkgPath = join(projectPath, 'package.json');
45
+ const pkg = await readJsonFile(pkgPath);
46
+ if (!pkg) {
47
+ return { installed: false };
48
+ }
49
+ const allDeps = {
50
+ ...(pkg.devDependencies ?? {}),
51
+ ...(pkg.dependencies ?? {}),
52
+ };
53
+ const rtk = allDeps.rtk;
54
+ const rtkCli = allDeps['@rtk/cli'];
55
+ const version = rtk ?? rtkCli;
56
+ if (version !== undefined) {
57
+ return { installed: true, version };
58
+ }
59
+ return { installed: false };
60
+ }
61
+ export async function getTokenOptimizerStatus(projectPath) {
62
+ const [rtkPkg, headroomPkg, rtkHooks] = await Promise.all([
63
+ detectRtkPackage(projectPath),
64
+ detectHeadroomPackage(projectPath),
65
+ detectRtkHooks(projectPath),
66
+ ]);
67
+ return {
68
+ rtk: {
69
+ installed: rtkPkg.installed,
70
+ version: rtkPkg.version,
71
+ hooksConfigured: rtkHooks,
72
+ },
73
+ headroom: {
74
+ installed: headroomPkg.installed,
75
+ version: headroomPkg.version,
76
+ },
77
+ };
78
+ }
79
+ export async function buildTokenOptimizerRecommendations(projectPath) {
80
+ const status = await getTokenOptimizerStatus(projectPath);
81
+ const recommendations = [];
82
+ if (!status.rtk.installed) {
83
+ recommendations.push({
84
+ tool: 'rtk',
85
+ installed: false,
86
+ reason: 'RTK (Response Token Killer) filters verbose Claude output via PreToolUse hooks, reducing token usage by 30-60% on large codebases.',
87
+ installCommand: 'npm install --save-dev rtk',
88
+ configExample: JSON.stringify({
89
+ hooks: {
90
+ PreToolUse: [
91
+ {
92
+ matcher: 'Bash',
93
+ hooks: ['npx rtk filter'],
94
+ },
95
+ ],
96
+ },
97
+ }, null, 2),
98
+ });
99
+ }
100
+ else {
101
+ recommendations.push({
102
+ tool: 'rtk',
103
+ installed: true,
104
+ reason: `RTK is installed (${status.rtk.version ?? 'unknown version'}). ${status.rtk.hooksConfigured === 0
105
+ ? 'No PreToolUse hooks configured yet — add hooks to .claude/settings.json to activate filtering.'
106
+ : `${String(status.rtk.hooksConfigured)} PreToolUse hook(s) configured.`}`,
107
+ installCommand: 'npm install --save-dev rtk',
108
+ });
109
+ }
110
+ if (!status.headroom.installed) {
111
+ recommendations.push({
112
+ tool: 'headroom',
113
+ installed: false,
114
+ reason: 'Headroom monitors your context window usage in real-time and warns before you hit the limit, preventing lost work.',
115
+ installCommand: 'npm install --save-dev headroom',
116
+ });
117
+ }
118
+ else {
119
+ recommendations.push({
120
+ tool: 'headroom',
121
+ installed: true,
122
+ reason: `Headroom is installed (${status.headroom.version ?? 'unknown version'}).`,
123
+ installCommand: 'npm install --save-dev headroom',
124
+ });
125
+ }
126
+ return recommendations;
127
+ }
128
+ //# sourceMappingURL=ecosystem-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ecosystem-detector.js","sourceRoot":"","sources":["../../src/engine/ecosystem-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { MemoryProvider, MemoryStatusResult, ClaudeJsonConfig } from '../types/index.js';
2
+ export declare function resolveClaudeJsonPath(projectPath: string): string;
3
+ export declare function detectMemoryProvider(config: ClaudeJsonConfig): MemoryProvider;
4
+ export declare function getMemoryStatus(projectPath: string): Promise<MemoryStatusResult>;
5
+ export declare function setupOpenMemory(projectPath: string): Promise<string>;
6
+ export declare function setupMem0Cloud(projectPath: string, apiKey: string): Promise<string>;
7
+ export declare function resetMemoryConfig(projectPath: string): Promise<string>;
8
+ //# sourceMappingURL=memory-config-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-config-generator.d.ts","sourceRoot":"","sources":["../../src/engine/memory-config-generator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAEjB,MAAM,mBAAmB,CAAC;AAe3B,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,GAAG,cAAc,CAkB7E;AAED,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAWtF;AAED,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmB1E;AAED,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBzF;AAED,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkB5E"}
@@ -0,0 +1,101 @@
1
+ // Planu — Memory config generator engine (SPEC-375)
2
+ // Generates .claude.json MCP entries for mem0/OpenMemory
3
+ import { readFile, writeFile } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ async function readClaudeJson(configPath) {
6
+ try {
7
+ const content = await readFile(configPath, 'utf-8');
8
+ return JSON.parse(content);
9
+ }
10
+ catch {
11
+ return {};
12
+ }
13
+ }
14
+ async function writeClaudeJson(configPath, config) {
15
+ await writeFile(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
16
+ }
17
+ export function resolveClaudeJsonPath(projectPath) {
18
+ return join(projectPath, '.claude.json');
19
+ }
20
+ export function detectMemoryProvider(config) {
21
+ const servers = config.mcpServers;
22
+ if (servers === undefined) {
23
+ return 'none';
24
+ }
25
+ const memoryServer = servers.memory;
26
+ if (memoryServer === undefined) {
27
+ return 'none';
28
+ }
29
+ const args = memoryServer.args;
30
+ if (!args.includes('@mem0/mcp')) {
31
+ return 'openmemory';
32
+ }
33
+ const apiKey = memoryServer.env?.MEM0_API_KEY;
34
+ if (apiKey !== undefined && apiKey !== '') {
35
+ return 'mem0-cloud';
36
+ }
37
+ return 'openmemory';
38
+ }
39
+ export async function getMemoryStatus(projectPath) {
40
+ const configPath = resolveClaudeJsonPath(projectPath);
41
+ const config = await readClaudeJson(configPath);
42
+ const provider = detectMemoryProvider(config);
43
+ return {
44
+ provider,
45
+ connected: false,
46
+ memoryCount: 0,
47
+ configPath,
48
+ };
49
+ }
50
+ export async function setupOpenMemory(projectPath) {
51
+ const configPath = resolveClaudeJsonPath(projectPath);
52
+ const config = await readClaudeJson(configPath);
53
+ const memoryServer = {
54
+ command: 'npx',
55
+ args: ['-y', '@mem0/mcp'],
56
+ };
57
+ const updated = {
58
+ ...config,
59
+ mcpServers: {
60
+ ...(config.mcpServers ?? {}),
61
+ memory: memoryServer,
62
+ },
63
+ };
64
+ await writeClaudeJson(configPath, updated);
65
+ return configPath;
66
+ }
67
+ export async function setupMem0Cloud(projectPath, apiKey) {
68
+ const configPath = resolveClaudeJsonPath(projectPath);
69
+ const config = await readClaudeJson(configPath);
70
+ const memoryServer = {
71
+ command: 'npx',
72
+ args: ['-y', '@mem0/mcp'],
73
+ env: { MEM0_API_KEY: apiKey },
74
+ };
75
+ const updated = {
76
+ ...config,
77
+ mcpServers: {
78
+ ...(config.mcpServers ?? {}),
79
+ memory: memoryServer,
80
+ },
81
+ };
82
+ await writeClaudeJson(configPath, updated);
83
+ return configPath;
84
+ }
85
+ export async function resetMemoryConfig(projectPath) {
86
+ const configPath = resolveClaudeJsonPath(projectPath);
87
+ const config = await readClaudeJson(configPath);
88
+ if (config.mcpServers?.memory === undefined) {
89
+ return configPath;
90
+ }
91
+ const { memory: _removed, ...remainingServers } = config.mcpServers;
92
+ /* v8 ignore next */
93
+ void _removed;
94
+ const updated = {
95
+ ...config,
96
+ mcpServers: remainingServers,
97
+ };
98
+ await writeClaudeJson(configPath, updated);
99
+ return configPath;
100
+ }
101
+ //# sourceMappingURL=memory-config-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-config-generator.js","sourceRoot":"","sources":["../../src/engine/memory-config-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { CriterionComplianceResult, CriterionStatus, ReviewPrAgainstSpecResult, PrAgentStatusResult } from '../types/index.js';
2
+ /** Extract acceptance criteria from spec.md content (checkbox lines). */
3
+ export declare function extractAcceptanceCriteria(specContent: string): string[];
4
+ /** Resolve spec directory from specId and projectPath. */
5
+ export declare function resolveSpecDir(specId: string, projectPath: string): Promise<string | null>;
6
+ /** Read spec.md content for a given specId. */
7
+ export declare function readSpecContent(specId: string, projectPath: string): Promise<string | null>;
8
+ /** Run `gh pr diff` and return the diff text, or null on failure. */
9
+ export declare function fetchPrDiff(prNumber: number, githubRepo: string): string | null;
10
+ /** Check if a criterion is matched in the diff. */
11
+ export declare function checkCriterion(criterion: string, diff: string): CriterionStatus;
12
+ /** Build compliance results from criteria and diff. */
13
+ export declare function buildComplianceResults(criteria: string[], diff: string): CriterionComplianceResult[];
14
+ /** Calculate compliance score (0-100, rounded integer). */
15
+ export declare function calcComplianceScore(results: CriterionComplianceResult[]): number;
16
+ /** Build the human-readable summary string. */
17
+ export declare function buildSummary(score: number, results: CriterionComplianceResult[], specId: string, prNumber: number): string;
18
+ /** Run the full PR compliance check. */
19
+ export declare function reviewPrAgainstSpec(specId: string, projectPath: string, prNumber: number, githubRepo: string): Promise<ReviewPrAgainstSpecResult | {
20
+ isError: true;
21
+ message: string;
22
+ }>;
23
+ /** Check gh CLI installation and PR agent configuration. */
24
+ export declare function checkPrAgentStatus(projectPath: string): PrAgentStatusResult;
25
+ //# sourceMappingURL=pr-spec-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-spec-validator.d.ts","sourceRoot":"","sources":["../../src/engine/pr-spec-validator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,yBAAyB,EACzB,eAAe,EACf,yBAAyB,EACzB,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAE3B,yEAAyE;AACzE,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAUvE;AAED,0DAA0D;AAC1D,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYhG;AAED,+CAA+C;AAC/C,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUjG;AAED,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS/E;AAED,mDAAmD;AACnD,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAgB/E;AAED,uDAAuD;AACvD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAAE,EAClB,IAAI,EAAE,MAAM,GACX,yBAAyB,EAAE,CAK7B;AAED,2DAA2D;AAC3D,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,yBAAyB,EAAE,GAAG,MAAM,CAMhF;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,EAAE,EACpC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,MAAM,CAgBR;AAED,wCAAwC;AACxC,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,yBAAyB,GAAG;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBzE;AAED,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,mBAAmB,CAwC3E"}