vralphy 0.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 (158) hide show
  1. package/README.md +512 -0
  2. package/bin/vralphy.js +3 -0
  3. package/dist/commands/build.d.ts +9 -0
  4. package/dist/commands/build.d.ts.map +1 -0
  5. package/dist/commands/build.js +176 -0
  6. package/dist/commands/build.js.map +1 -0
  7. package/dist/commands/cleanup.d.ts +19 -0
  8. package/dist/commands/cleanup.d.ts.map +1 -0
  9. package/dist/commands/cleanup.js +159 -0
  10. package/dist/commands/cleanup.js.map +1 -0
  11. package/dist/commands/cleanup.test.d.ts +2 -0
  12. package/dist/commands/cleanup.test.d.ts.map +1 -0
  13. package/dist/commands/cleanup.test.js +389 -0
  14. package/dist/commands/cleanup.test.js.map +1 -0
  15. package/dist/commands/init.d.ts +13 -0
  16. package/dist/commands/init.d.ts.map +1 -0
  17. package/dist/commands/init.js +120 -0
  18. package/dist/commands/init.js.map +1 -0
  19. package/dist/commands/plan.d.ts +9 -0
  20. package/dist/commands/plan.d.ts.map +1 -0
  21. package/dist/commands/plan.js +147 -0
  22. package/dist/commands/plan.js.map +1 -0
  23. package/dist/commands/spec.d.ts +9 -0
  24. package/dist/commands/spec.d.ts.map +1 -0
  25. package/dist/commands/spec.js +111 -0
  26. package/dist/commands/spec.js.map +1 -0
  27. package/dist/index.d.ts +3 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +251 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/lib/agents.d.ts +32 -0
  32. package/dist/lib/agents.d.ts.map +1 -0
  33. package/dist/lib/agents.js +96 -0
  34. package/dist/lib/agents.js.map +1 -0
  35. package/dist/lib/config.d.ts +54 -0
  36. package/dist/lib/config.d.ts.map +1 -0
  37. package/dist/lib/config.js +199 -0
  38. package/dist/lib/config.js.map +1 -0
  39. package/dist/lib/config.test.d.ts +2 -0
  40. package/dist/lib/config.test.d.ts.map +1 -0
  41. package/dist/lib/config.test.js +57 -0
  42. package/dist/lib/config.test.js.map +1 -0
  43. package/dist/lib/context.d.ts +29 -0
  44. package/dist/lib/context.d.ts.map +1 -0
  45. package/dist/lib/context.js +175 -0
  46. package/dist/lib/context.js.map +1 -0
  47. package/dist/lib/engines/base.d.ts +75 -0
  48. package/dist/lib/engines/base.d.ts.map +1 -0
  49. package/dist/lib/engines/base.js +62 -0
  50. package/dist/lib/engines/base.js.map +1 -0
  51. package/dist/lib/engines/base.test.d.ts +2 -0
  52. package/dist/lib/engines/base.test.d.ts.map +1 -0
  53. package/dist/lib/engines/base.test.js +28 -0
  54. package/dist/lib/engines/base.test.js.map +1 -0
  55. package/dist/lib/engines/claude.d.ts +12 -0
  56. package/dist/lib/engines/claude.d.ts.map +1 -0
  57. package/dist/lib/engines/claude.js +156 -0
  58. package/dist/lib/engines/claude.js.map +1 -0
  59. package/dist/lib/engines/codex.d.ts +28 -0
  60. package/dist/lib/engines/codex.d.ts.map +1 -0
  61. package/dist/lib/engines/codex.js +177 -0
  62. package/dist/lib/engines/codex.js.map +1 -0
  63. package/dist/lib/engines/index.d.ts +19 -0
  64. package/dist/lib/engines/index.d.ts.map +1 -0
  65. package/dist/lib/engines/index.js +40 -0
  66. package/dist/lib/engines/index.js.map +1 -0
  67. package/dist/lib/engines/opencode.d.ts +14 -0
  68. package/dist/lib/engines/opencode.d.ts.map +1 -0
  69. package/dist/lib/engines/opencode.js +127 -0
  70. package/dist/lib/engines/opencode.js.map +1 -0
  71. package/dist/lib/events/index.d.ts +6 -0
  72. package/dist/lib/events/index.d.ts.map +1 -0
  73. package/dist/lib/events/index.js +5 -0
  74. package/dist/lib/events/index.js.map +1 -0
  75. package/dist/lib/events/types.d.ts +93 -0
  76. package/dist/lib/events/types.d.ts.map +1 -0
  77. package/dist/lib/events/types.js +7 -0
  78. package/dist/lib/events/types.js.map +1 -0
  79. package/dist/lib/events/writer.d.ts +68 -0
  80. package/dist/lib/events/writer.d.ts.map +1 -0
  81. package/dist/lib/events/writer.js +178 -0
  82. package/dist/lib/events/writer.js.map +1 -0
  83. package/dist/lib/events.d.ts +33 -0
  84. package/dist/lib/events.d.ts.map +1 -0
  85. package/dist/lib/events.js +81 -0
  86. package/dist/lib/events.js.map +1 -0
  87. package/dist/lib/events.test.d.ts +2 -0
  88. package/dist/lib/events.test.d.ts.map +1 -0
  89. package/dist/lib/events.test.js +123 -0
  90. package/dist/lib/events.test.js.map +1 -0
  91. package/dist/lib/file-injection.d.ts +32 -0
  92. package/dist/lib/file-injection.d.ts.map +1 -0
  93. package/dist/lib/file-injection.js +138 -0
  94. package/dist/lib/file-injection.js.map +1 -0
  95. package/dist/lib/file-injection.test.d.ts +2 -0
  96. package/dist/lib/file-injection.test.d.ts.map +1 -0
  97. package/dist/lib/file-injection.test.js +508 -0
  98. package/dist/lib/file-injection.test.js.map +1 -0
  99. package/dist/lib/init.d.ts +27 -0
  100. package/dist/lib/init.d.ts.map +1 -0
  101. package/dist/lib/init.js +363 -0
  102. package/dist/lib/init.js.map +1 -0
  103. package/dist/lib/init.test.d.ts +2 -0
  104. package/dist/lib/init.test.d.ts.map +1 -0
  105. package/dist/lib/init.test.js +315 -0
  106. package/dist/lib/init.test.js.map +1 -0
  107. package/dist/lib/plan.d.ts +11 -0
  108. package/dist/lib/plan.d.ts.map +1 -0
  109. package/dist/lib/plan.js +22 -0
  110. package/dist/lib/plan.js.map +1 -0
  111. package/dist/lib/plan.test.d.ts +2 -0
  112. package/dist/lib/plan.test.d.ts.map +1 -0
  113. package/dist/lib/plan.test.js +70 -0
  114. package/dist/lib/plan.test.js.map +1 -0
  115. package/dist/lib/prompts/codex.d.ts +18 -0
  116. package/dist/lib/prompts/codex.d.ts.map +1 -0
  117. package/dist/lib/prompts/codex.js +82 -0
  118. package/dist/lib/prompts/codex.js.map +1 -0
  119. package/dist/lib/prompts/opencode.d.ts +16 -0
  120. package/dist/lib/prompts/opencode.d.ts.map +1 -0
  121. package/dist/lib/prompts/opencode.js +71 -0
  122. package/dist/lib/prompts/opencode.js.map +1 -0
  123. package/dist/lib/prompts.d.ts +57 -0
  124. package/dist/lib/prompts.d.ts.map +1 -0
  125. package/dist/lib/prompts.js +255 -0
  126. package/dist/lib/prompts.js.map +1 -0
  127. package/dist/lib/prompts.test.d.ts +2 -0
  128. package/dist/lib/prompts.test.d.ts.map +1 -0
  129. package/dist/lib/prompts.test.js +128 -0
  130. package/dist/lib/prompts.test.js.map +1 -0
  131. package/dist/lib/skills.d.ts +36 -0
  132. package/dist/lib/skills.d.ts.map +1 -0
  133. package/dist/lib/skills.js +132 -0
  134. package/dist/lib/skills.js.map +1 -0
  135. package/dist/lib/slack.d.ts +43 -0
  136. package/dist/lib/slack.d.ts.map +1 -0
  137. package/dist/lib/slack.js +130 -0
  138. package/dist/lib/slack.js.map +1 -0
  139. package/dist/lib/slack.test.d.ts +2 -0
  140. package/dist/lib/slack.test.d.ts.map +1 -0
  141. package/dist/lib/slack.test.js +74 -0
  142. package/dist/lib/slack.test.js.map +1 -0
  143. package/dist/lib/templates/injections.d.ts +18 -0
  144. package/dist/lib/templates/injections.d.ts.map +1 -0
  145. package/dist/lib/templates/injections.js +32 -0
  146. package/dist/lib/templates/injections.js.map +1 -0
  147. package/dist/lib/templates/readme.d.ts +6 -0
  148. package/dist/lib/templates/readme.d.ts.map +1 -0
  149. package/dist/lib/templates/readme.js +168 -0
  150. package/dist/lib/templates/readme.js.map +1 -0
  151. package/docs/COMMANDS.md +664 -0
  152. package/docs/DESIGN.md +537 -0
  153. package/docs/EXAMPLES.md +812 -0
  154. package/docs/METHODOLOGY.md +390 -0
  155. package/docs/README.md +110 -0
  156. package/docs/WORKFLOWS.md +808 -0
  157. package/llms.txt +104 -0
  158. package/package.json +58 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-injection.d.ts","sourceRoot":"","sources":["../../src/lib/file-injection.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAE9E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAIxF;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CAsD1B;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CAkE1B"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Utilities for marker-based content injection and removal in files
3
+ */
4
+ import { readFile, writeFile } from 'fs/promises';
5
+ import { existsSync } from 'fs';
6
+ /**
7
+ * Check if content already has injection markers
8
+ */
9
+ export function hasMarkers(content, markers) {
10
+ return content.includes(markers.start) && content.includes(markers.end);
11
+ }
12
+ /**
13
+ * Check if content has incomplete markers (only start or end)
14
+ */
15
+ export function hasIncompleteMarkers(content, markers) {
16
+ const hasStart = content.includes(markers.start);
17
+ const hasEnd = content.includes(markers.end);
18
+ return (hasStart && !hasEnd) || (!hasStart && hasEnd);
19
+ }
20
+ /**
21
+ * Inject content into a file between markers
22
+ * If markers exist, replaces content between them
23
+ * If markers don't exist, appends to end of file
24
+ */
25
+ export async function injectContent(filePath, content, markers) {
26
+ try {
27
+ // Check if file exists
28
+ if (!existsSync(filePath)) {
29
+ return {
30
+ success: false,
31
+ action: 'skipped',
32
+ message: 'File does not exist',
33
+ };
34
+ }
35
+ const fileContent = await readFile(filePath, 'utf-8');
36
+ // Check for incomplete markers
37
+ if (hasIncompleteMarkers(fileContent, markers)) {
38
+ return {
39
+ success: false,
40
+ action: 'skipped',
41
+ message: 'File has incomplete markers (only start or end)',
42
+ };
43
+ }
44
+ // Build the injection block
45
+ const injectionBlock = `${markers.start}${content}${markers.end}`;
46
+ let newContent;
47
+ let action;
48
+ if (hasMarkers(fileContent, markers)) {
49
+ // Replace existing content between markers
50
+ const startIdx = fileContent.indexOf(markers.start);
51
+ const endIdx = fileContent.indexOf(markers.end) + markers.end.length;
52
+ newContent = fileContent.slice(0, startIdx) + injectionBlock + fileContent.slice(endIdx);
53
+ action = 'replaced';
54
+ }
55
+ else {
56
+ // Append to end of file
57
+ const separator = fileContent.endsWith('\n') ? '\n' : '\n\n';
58
+ newContent = fileContent + separator + injectionBlock + '\n';
59
+ action = 'injected';
60
+ }
61
+ await writeFile(filePath, newContent);
62
+ return {
63
+ success: true,
64
+ action,
65
+ };
66
+ }
67
+ catch (error) {
68
+ return {
69
+ success: false,
70
+ action: 'error',
71
+ message: error instanceof Error ? error.message : String(error),
72
+ };
73
+ }
74
+ }
75
+ /**
76
+ * Remove injected content from a file
77
+ * Removes everything between and including the markers
78
+ */
79
+ export async function removeInjectedContent(filePath, markers) {
80
+ try {
81
+ // Check if file exists
82
+ if (!existsSync(filePath)) {
83
+ return {
84
+ success: false,
85
+ action: 'skipped',
86
+ message: 'File does not exist',
87
+ };
88
+ }
89
+ const fileContent = await readFile(filePath, 'utf-8');
90
+ // Check for incomplete markers
91
+ if (hasIncompleteMarkers(fileContent, markers)) {
92
+ return {
93
+ success: false,
94
+ action: 'skipped',
95
+ message: 'File has incomplete markers (only start or end)',
96
+ };
97
+ }
98
+ // Check if markers exist
99
+ if (!hasMarkers(fileContent, markers)) {
100
+ return {
101
+ success: false,
102
+ action: 'skipped',
103
+ message: 'No injection markers found',
104
+ };
105
+ }
106
+ // Remove content between markers (including markers)
107
+ const startIdx = fileContent.indexOf(markers.start);
108
+ const endIdx = fileContent.indexOf(markers.end) + markers.end.length;
109
+ // Include any trailing newline in removal
110
+ let removeEndIdx = endIdx;
111
+ if (fileContent[endIdx] === '\n') {
112
+ removeEndIdx = endIdx + 1;
113
+ }
114
+ // Include any leading newlines if preceded by content
115
+ let removeStartIdx = startIdx;
116
+ if (startIdx > 0 && fileContent[startIdx - 1] === '\n') {
117
+ removeStartIdx = startIdx - 1;
118
+ // Remove extra blank line if there's another newline before
119
+ if (removeStartIdx > 0 && fileContent[removeStartIdx - 1] === '\n') {
120
+ removeStartIdx = removeStartIdx - 1;
121
+ }
122
+ }
123
+ const newContent = fileContent.slice(0, removeStartIdx) + fileContent.slice(removeEndIdx);
124
+ await writeFile(filePath, newContent);
125
+ return {
126
+ success: true,
127
+ action: 'removed',
128
+ };
129
+ }
130
+ catch (error) {
131
+ return {
132
+ success: false,
133
+ action: 'error',
134
+ message: error instanceof Error ? error.message : String(error),
135
+ };
136
+ }
137
+ }
138
+ //# sourceMappingURL=file-injection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-injection.js","sourceRoot":"","sources":["../../src/lib/file-injection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=file-injection.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-injection.test.d.ts","sourceRoot":"","sources":["../../src/lib/file-injection.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,508 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdir, writeFile, readFile, rm } from 'fs/promises';
3
+ import { existsSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { injectContent, removeInjectedContent, hasMarkers, hasIncompleteMarkers, } from './file-injection.js';
6
+ import { INJECTION_MARKERS, CLAUDE_MD_INJECTION, AGENTS_MD_INJECTION } from './templates/injections.js';
7
+ const TEST_DIR = '/tmp/vralphy-injection-test';
8
+ describe('file-injection', () => {
9
+ beforeEach(async () => {
10
+ if (existsSync(TEST_DIR)) {
11
+ await rm(TEST_DIR, { recursive: true, force: true });
12
+ }
13
+ await mkdir(TEST_DIR, { recursive: true });
14
+ });
15
+ afterEach(async () => {
16
+ if (existsSync(TEST_DIR)) {
17
+ await rm(TEST_DIR, { recursive: true, force: true });
18
+ }
19
+ });
20
+ describe('hasMarkers', () => {
21
+ it('should return true when both markers present', () => {
22
+ const content = `# Title\n${INJECTION_MARKERS.start}\nInjected\n${INJECTION_MARKERS.end}\n`;
23
+ expect(hasMarkers(content, INJECTION_MARKERS)).toBe(true);
24
+ });
25
+ it('should return false when no markers present', () => {
26
+ const content = '# Title\nSome content\n';
27
+ expect(hasMarkers(content, INJECTION_MARKERS)).toBe(false);
28
+ });
29
+ it('should return false when only start marker present', () => {
30
+ const content = `# Title\n${INJECTION_MARKERS.start}\nContent\n`;
31
+ expect(hasMarkers(content, INJECTION_MARKERS)).toBe(false);
32
+ });
33
+ it('should return false when only end marker present', () => {
34
+ const content = `# Title\n${INJECTION_MARKERS.end}\nContent\n`;
35
+ expect(hasMarkers(content, INJECTION_MARKERS)).toBe(false);
36
+ });
37
+ it('should work with markers in middle of file', () => {
38
+ const content = `# Header\n\nParagraph.\n\n${INJECTION_MARKERS.start}\nMiddle\n${INJECTION_MARKERS.end}\n\n## Footer\n`;
39
+ expect(hasMarkers(content, INJECTION_MARKERS)).toBe(true);
40
+ });
41
+ it('should work with custom markers', () => {
42
+ const customMarkers = { start: '<!-- BEGIN -->', end: '<!-- END -->' };
43
+ const content = '# Title\n<!-- BEGIN -->\nContent\n<!-- END -->\n';
44
+ expect(hasMarkers(content, customMarkers)).toBe(true);
45
+ });
46
+ });
47
+ describe('hasIncompleteMarkers', () => {
48
+ it('should return true when only start marker present', () => {
49
+ const content = `# Title\n${INJECTION_MARKERS.start}\nContent\n`;
50
+ expect(hasIncompleteMarkers(content, INJECTION_MARKERS)).toBe(true);
51
+ });
52
+ it('should return true when only end marker present', () => {
53
+ const content = `# Title\n${INJECTION_MARKERS.end}\nContent\n`;
54
+ expect(hasIncompleteMarkers(content, INJECTION_MARKERS)).toBe(true);
55
+ });
56
+ it('should return false when both markers present', () => {
57
+ const content = `# Title\n${INJECTION_MARKERS.start}\nInjected\n${INJECTION_MARKERS.end}\n`;
58
+ expect(hasIncompleteMarkers(content, INJECTION_MARKERS)).toBe(false);
59
+ });
60
+ it('should return false when no markers present', () => {
61
+ const content = '# Title\nSome content\n';
62
+ expect(hasIncompleteMarkers(content, INJECTION_MARKERS)).toBe(false);
63
+ });
64
+ });
65
+ describe('injectContent', () => {
66
+ it('should inject content at end of file when no markers exist', async () => {
67
+ const filePath = join(TEST_DIR, 'test.md');
68
+ await writeFile(filePath, '# Title\n\nExisting content.\n');
69
+ const result = await injectContent(filePath, '\nNew section\n', INJECTION_MARKERS);
70
+ expect(result.success).toBe(true);
71
+ expect(result.action).toBe('injected');
72
+ const content = await readFile(filePath, 'utf-8');
73
+ expect(content).toContain(INJECTION_MARKERS.start);
74
+ expect(content).toContain('New section');
75
+ expect(content).toContain(INJECTION_MARKERS.end);
76
+ expect(content).toContain('Existing content.');
77
+ });
78
+ it('should replace content when markers already exist', async () => {
79
+ const filePath = join(TEST_DIR, 'test.md');
80
+ await writeFile(filePath, `# Title\n\n${INJECTION_MARKERS.start}\nOld content\n${INJECTION_MARKERS.end}\n\nMore content.\n`);
81
+ const result = await injectContent(filePath, '\nNew content\n', INJECTION_MARKERS);
82
+ expect(result.success).toBe(true);
83
+ expect(result.action).toBe('replaced');
84
+ const content = await readFile(filePath, 'utf-8');
85
+ expect(content).toContain('New content');
86
+ expect(content).not.toContain('Old content');
87
+ expect(content).toContain('More content.');
88
+ });
89
+ it('should skip when file does not exist', async () => {
90
+ const filePath = join(TEST_DIR, 'nonexistent.md');
91
+ const result = await injectContent(filePath, '\nContent\n', INJECTION_MARKERS);
92
+ expect(result.success).toBe(false);
93
+ expect(result.action).toBe('skipped');
94
+ expect(result.message).toBe('File does not exist');
95
+ });
96
+ it('should skip when file has incomplete markers', async () => {
97
+ const filePath = join(TEST_DIR, 'test.md');
98
+ await writeFile(filePath, `# Title\n\n${INJECTION_MARKERS.start}\nBroken content\n`);
99
+ const result = await injectContent(filePath, '\nContent\n', INJECTION_MARKERS);
100
+ expect(result.success).toBe(false);
101
+ expect(result.action).toBe('skipped');
102
+ expect(result.message).toContain('incomplete markers');
103
+ });
104
+ it('should preserve content before and after injection point', async () => {
105
+ const filePath = join(TEST_DIR, 'test.md');
106
+ const originalContent = `# Header
107
+
108
+ First paragraph with important info.
109
+
110
+ ## Section A
111
+
112
+ Content A.
113
+
114
+ ## Section B
115
+
116
+ Content B.
117
+ `;
118
+ await writeFile(filePath, originalContent);
119
+ await injectContent(filePath, '\n## Injected\n\nNew stuff.\n', INJECTION_MARKERS);
120
+ const content = await readFile(filePath, 'utf-8');
121
+ expect(content).toContain('# Header');
122
+ expect(content).toContain('First paragraph with important info.');
123
+ expect(content).toContain('## Section A');
124
+ expect(content).toContain('Content A.');
125
+ expect(content).toContain('## Section B');
126
+ expect(content).toContain('Content B.');
127
+ expect(content).toContain('## Injected');
128
+ expect(content).toContain('New stuff.');
129
+ });
130
+ it('should handle empty file', async () => {
131
+ const filePath = join(TEST_DIR, 'empty.md');
132
+ await writeFile(filePath, '');
133
+ const result = await injectContent(filePath, '\nContent\n', INJECTION_MARKERS);
134
+ expect(result.success).toBe(true);
135
+ const content = await readFile(filePath, 'utf-8');
136
+ expect(content).toContain(INJECTION_MARKERS.start);
137
+ expect(content).toContain('Content');
138
+ expect(content).toContain(INJECTION_MARKERS.end);
139
+ });
140
+ it('should handle file without trailing newline', async () => {
141
+ const filePath = join(TEST_DIR, 'no-newline.md');
142
+ await writeFile(filePath, '# Title\n\nNo trailing newline');
143
+ const result = await injectContent(filePath, '\nContent\n', INJECTION_MARKERS);
144
+ expect(result.success).toBe(true);
145
+ const content = await readFile(filePath, 'utf-8');
146
+ expect(content).toContain('No trailing newline');
147
+ expect(content).toContain(INJECTION_MARKERS.start);
148
+ });
149
+ it('should work with actual CLAUDE_MD_INJECTION content', async () => {
150
+ const filePath = join(TEST_DIR, 'CLAUDE.md');
151
+ await writeFile(filePath, `# vralphy
152
+
153
+ CLI tool implementing the Ralph Playbook.
154
+
155
+ ## Commands
156
+
157
+ \`\`\`bash
158
+ npm run build
159
+ npm test
160
+ \`\`\`
161
+ `);
162
+ const result = await injectContent(filePath, CLAUDE_MD_INJECTION, INJECTION_MARKERS);
163
+ expect(result.success).toBe(true);
164
+ expect(result.action).toBe('injected');
165
+ const content = await readFile(filePath, 'utf-8');
166
+ expect(content).toContain('## vralphy Integration');
167
+ expect(content).toContain('@.vralphy/README.md');
168
+ expect(content).toContain('@.vralphy/AGENTS.md');
169
+ expect(content).toContain('@IMPLEMENTATION_PLAN.md');
170
+ // Original content preserved
171
+ expect(content).toContain('# vralphy');
172
+ expect(content).toContain('npm run build');
173
+ });
174
+ it('should work with actual AGENTS_MD_INJECTION content', async () => {
175
+ const filePath = join(TEST_DIR, 'AGENTS.md');
176
+ await writeFile(filePath, `# AGENTS.md
177
+
178
+ Operational guide for AI agents.
179
+
180
+ ## Build & Validate
181
+
182
+ \`\`\`bash
183
+ npm install
184
+ \`\`\`
185
+ `);
186
+ const result = await injectContent(filePath, AGENTS_MD_INJECTION, INJECTION_MARKERS);
187
+ expect(result.success).toBe(true);
188
+ const content = await readFile(filePath, 'utf-8');
189
+ expect(content).toContain('## vralphy');
190
+ expect(content).toContain('.vralphy/README.md');
191
+ // Original content preserved
192
+ expect(content).toContain('# AGENTS.md');
193
+ expect(content).toContain('Operational guide');
194
+ });
195
+ it('should handle multiple inject/replace cycles', async () => {
196
+ const filePath = join(TEST_DIR, 'test.md');
197
+ await writeFile(filePath, '# Project\n');
198
+ // First injection
199
+ await injectContent(filePath, '\nVersion 1\n', INJECTION_MARKERS);
200
+ let content = await readFile(filePath, 'utf-8');
201
+ expect(content).toContain('Version 1');
202
+ // Second injection (replace)
203
+ await injectContent(filePath, '\nVersion 2\n', INJECTION_MARKERS);
204
+ content = await readFile(filePath, 'utf-8');
205
+ expect(content).toContain('Version 2');
206
+ expect(content).not.toContain('Version 1');
207
+ // Third injection (replace)
208
+ await injectContent(filePath, '\nVersion 3\n', INJECTION_MARKERS);
209
+ content = await readFile(filePath, 'utf-8');
210
+ expect(content).toContain('Version 3');
211
+ expect(content).not.toContain('Version 2');
212
+ // Markers should appear exactly once
213
+ const startCount = (content.match(new RegExp(INJECTION_MARKERS.start, 'g')) || []).length;
214
+ const endCount = (content.match(new RegExp(INJECTION_MARKERS.end, 'g')) || []).length;
215
+ expect(startCount).toBe(1);
216
+ expect(endCount).toBe(1);
217
+ });
218
+ });
219
+ describe('removeInjectedContent', () => {
220
+ it('should remove injected content and markers', async () => {
221
+ const filePath = join(TEST_DIR, 'test.md');
222
+ await writeFile(filePath, `# Title\n\n${INJECTION_MARKERS.start}\nInjected content\n${INJECTION_MARKERS.end}\n\nRemaining content.\n`);
223
+ const result = await removeInjectedContent(filePath, INJECTION_MARKERS);
224
+ expect(result.success).toBe(true);
225
+ expect(result.action).toBe('removed');
226
+ const content = await readFile(filePath, 'utf-8');
227
+ expect(content).not.toContain(INJECTION_MARKERS.start);
228
+ expect(content).not.toContain('Injected content');
229
+ expect(content).not.toContain(INJECTION_MARKERS.end);
230
+ expect(content).toContain('# Title');
231
+ expect(content).toContain('Remaining content.');
232
+ });
233
+ it('should skip when file does not exist', async () => {
234
+ const filePath = join(TEST_DIR, 'nonexistent.md');
235
+ const result = await removeInjectedContent(filePath, INJECTION_MARKERS);
236
+ expect(result.success).toBe(false);
237
+ expect(result.action).toBe('skipped');
238
+ expect(result.message).toBe('File does not exist');
239
+ });
240
+ it('should skip when no markers found', async () => {
241
+ const filePath = join(TEST_DIR, 'test.md');
242
+ await writeFile(filePath, '# Title\n\nNo markers here.\n');
243
+ const result = await removeInjectedContent(filePath, INJECTION_MARKERS);
244
+ expect(result.success).toBe(false);
245
+ expect(result.action).toBe('skipped');
246
+ expect(result.message).toBe('No injection markers found');
247
+ });
248
+ it('should skip when file has incomplete markers', async () => {
249
+ const filePath = join(TEST_DIR, 'test.md');
250
+ await writeFile(filePath, `# Title\n\n${INJECTION_MARKERS.start}\nBroken\n`);
251
+ const result = await removeInjectedContent(filePath, INJECTION_MARKERS);
252
+ expect(result.success).toBe(false);
253
+ expect(result.action).toBe('skipped');
254
+ expect(result.message).toContain('incomplete markers');
255
+ });
256
+ it('should leave file non-empty after removal', async () => {
257
+ const filePath = join(TEST_DIR, 'test.md');
258
+ await writeFile(filePath, `# Project\n\n${INJECTION_MARKERS.start}\nInjected\n${INJECTION_MARKERS.end}\n`);
259
+ await removeInjectedContent(filePath, INJECTION_MARKERS);
260
+ const content = await readFile(filePath, 'utf-8');
261
+ expect(content.length).toBeGreaterThan(0);
262
+ expect(content).toContain('# Project');
263
+ });
264
+ it('should preserve content before injection', async () => {
265
+ const filePath = join(TEST_DIR, 'test.md');
266
+ await writeFile(filePath, `# Header
267
+
268
+ First section.
269
+
270
+ ## Section A
271
+
272
+ Content A.
273
+
274
+ ${INJECTION_MARKERS.start}
275
+ ## Injected Section
276
+
277
+ This will be removed.
278
+ ${INJECTION_MARKERS.end}
279
+ `);
280
+ await removeInjectedContent(filePath, INJECTION_MARKERS);
281
+ const content = await readFile(filePath, 'utf-8');
282
+ expect(content).toContain('# Header');
283
+ expect(content).toContain('First section.');
284
+ expect(content).toContain('## Section A');
285
+ expect(content).toContain('Content A.');
286
+ expect(content).not.toContain('Injected Section');
287
+ expect(content).not.toContain('This will be removed');
288
+ });
289
+ it('should preserve content after injection', async () => {
290
+ const filePath = join(TEST_DIR, 'test.md');
291
+ await writeFile(filePath, `# Header
292
+
293
+ ${INJECTION_MARKERS.start}
294
+ ## Injected Section
295
+
296
+ This will be removed.
297
+ ${INJECTION_MARKERS.end}
298
+
299
+ ## Footer Section
300
+
301
+ Footer content.
302
+ `);
303
+ await removeInjectedContent(filePath, INJECTION_MARKERS);
304
+ const content = await readFile(filePath, 'utf-8');
305
+ expect(content).toContain('# Header');
306
+ expect(content).toContain('## Footer Section');
307
+ expect(content).toContain('Footer content.');
308
+ expect(content).not.toContain('Injected Section');
309
+ });
310
+ it('should handle injection in middle of file', async () => {
311
+ const filePath = join(TEST_DIR, 'test.md');
312
+ await writeFile(filePath, `# Header
313
+
314
+ Before content.
315
+
316
+ ${INJECTION_MARKERS.start}
317
+ Injected middle.
318
+ ${INJECTION_MARKERS.end}
319
+
320
+ After content.
321
+
322
+ ## Footer
323
+ `);
324
+ await removeInjectedContent(filePath, INJECTION_MARKERS);
325
+ const content = await readFile(filePath, 'utf-8');
326
+ expect(content).toContain('Before content.');
327
+ expect(content).toContain('After content.');
328
+ expect(content).toContain('## Footer');
329
+ expect(content).not.toContain('Injected middle');
330
+ });
331
+ it('should work with actual CLAUDE_MD_INJECTION content', async () => {
332
+ const filePath = join(TEST_DIR, 'CLAUDE.md');
333
+ const originalContent = `# vralphy
334
+
335
+ CLI tool implementing the Ralph Playbook.
336
+
337
+ ## Commands
338
+
339
+ \`\`\`bash
340
+ npm run build
341
+ npm test
342
+ \`\`\`
343
+ `;
344
+ await writeFile(filePath, originalContent);
345
+ // Inject first
346
+ await injectContent(filePath, CLAUDE_MD_INJECTION, INJECTION_MARKERS);
347
+ // Then remove
348
+ const result = await removeInjectedContent(filePath, INJECTION_MARKERS);
349
+ expect(result.success).toBe(true);
350
+ const content = await readFile(filePath, 'utf-8');
351
+ expect(content).not.toContain('## vralphy Integration');
352
+ expect(content).not.toContain('@.vralphy/README.md');
353
+ // Original content preserved
354
+ expect(content).toContain('# vralphy');
355
+ expect(content).toContain('npm run build');
356
+ });
357
+ it('should handle inject-remove-inject cycle', async () => {
358
+ const filePath = join(TEST_DIR, 'test.md');
359
+ await writeFile(filePath, '# Project\n\nOriginal content.\n');
360
+ // Inject
361
+ await injectContent(filePath, '\nFirst injection\n', INJECTION_MARKERS);
362
+ let content = await readFile(filePath, 'utf-8');
363
+ expect(content).toContain('First injection');
364
+ // Remove
365
+ await removeInjectedContent(filePath, INJECTION_MARKERS);
366
+ content = await readFile(filePath, 'utf-8');
367
+ expect(content).not.toContain('First injection');
368
+ expect(content).toContain('Original content.');
369
+ // Inject again
370
+ await injectContent(filePath, '\nSecond injection\n', INJECTION_MARKERS);
371
+ content = await readFile(filePath, 'utf-8');
372
+ expect(content).toContain('Second injection');
373
+ expect(content).toContain('Original content.');
374
+ });
375
+ });
376
+ describe('integration with real file structures', () => {
377
+ it('should handle CLAUDE.md structure like the real project', async () => {
378
+ const filePath = join(TEST_DIR, 'CLAUDE.md');
379
+ // Mimic the real CLAUDE.md structure
380
+ await writeFile(filePath, `# vralphy
381
+
382
+ CLI tool implementing the Ralph Playbook - autonomous AI development where agents plan, build, test, and commit code.
383
+
384
+ ## Commands
385
+
386
+ \`\`\`bash
387
+ npm run build # Compile TypeScript
388
+ npm run test # Run tests (vitest)
389
+ npm run typecheck # Type check only
390
+ npm run lint # ESLint
391
+ \`\`\`
392
+
393
+ ## Architecture
394
+
395
+ - \`src/commands/\` - CLI commands (build, plan, spec, init, cleanup)
396
+ - \`src/lib/engines/\` - Engine abstraction (Claude, OpenCode, Codex)
397
+ - \`src/lib/\` - Core: config, prompts, events, slack, plan parsing
398
+
399
+ ## Critical Rules
400
+
401
+ - IMPORTANT: Run \`npm run typecheck && npm test\` before committing
402
+ - IMPORTANT: Tests are colocated (\`*.test.ts\` next to source)
403
+ - IMPORTANT: Import paths use \`.js\` extension (ES modules)
404
+ - Use TypeScript strict mode, avoid \`any\`
405
+
406
+ ## Conventions
407
+
408
+ - Async/await over callbacks
409
+ - Composition over inheritance
410
+ - Named exports preferred
411
+ - Silent failure for non-critical operations (slack, events)
412
+
413
+ ## Detailed References
414
+
415
+ @.claude/docs/architecture.md
416
+ @.claude/docs/commands.md
417
+ @.claude/docs/engines.md
418
+ @.claude/docs/testing.md
419
+ `);
420
+ // Inject
421
+ const injectResult = await injectContent(filePath, CLAUDE_MD_INJECTION, INJECTION_MARKERS);
422
+ expect(injectResult.success).toBe(true);
423
+ let content = await readFile(filePath, 'utf-8');
424
+ expect(content).toContain('## vralphy Integration');
425
+ expect(content).toContain('## Commands'); // Original preserved
426
+ expect(content).toContain('## Critical Rules'); // Original preserved
427
+ // Remove
428
+ const removeResult = await removeInjectedContent(filePath, INJECTION_MARKERS);
429
+ expect(removeResult.success).toBe(true);
430
+ content = await readFile(filePath, 'utf-8');
431
+ expect(content).not.toContain('## vralphy Integration');
432
+ expect(content).toContain('## Commands');
433
+ expect(content).toContain('## Critical Rules');
434
+ expect(content).toContain('@.claude/docs/architecture.md');
435
+ });
436
+ it('should handle AGENTS.md structure like the real project', async () => {
437
+ const filePath = join(TEST_DIR, 'AGENTS.md');
438
+ // Mimic the real AGENTS.md structure
439
+ await writeFile(filePath, `# AGENTS.md
440
+
441
+ Operational guide for AI agents working on vralphy.
442
+
443
+ ## Build & Validate
444
+
445
+ \`\`\`bash
446
+ npm install # Install dependencies
447
+ npm run build # Compile TypeScript to dist/
448
+ npm run test # Run vitest tests
449
+ npm run typecheck # Type check without emit
450
+ npm run lint # ESLint check
451
+ \`\`\`
452
+
453
+ ## Project Structure
454
+
455
+ \`\`\`
456
+ src/
457
+ ├── index.ts # CLI entry (commander.js)
458
+ ├── commands/ # build, plan, spec, init, cleanup
459
+ └── lib/
460
+ ├── engines/ # Claude, OpenCode, Codex implementations
461
+ ├── config.ts # Config resolution (CLI > env > file > defaults)
462
+ ├── prompts.ts # Prompt loading & interpolation
463
+ ├── events.ts # Event logging with rotation
464
+ ├── slack.ts # Slack notifications (fire-and-forget)
465
+ └── plan.ts # IMPLEMENTATION_PLAN.md parsing
466
+ \`\`\`
467
+
468
+ ## Tech Stack
469
+
470
+ - TypeScript (ES2022, strict mode)
471
+ - Node.js >= 18
472
+ - Commander.js for CLI
473
+ - Vitest for testing
474
+
475
+ ## Key Patterns
476
+
477
+ - **Engine interface**: All engines implement \`Engine\` from \`base.ts\`
478
+ - **Config hierarchy**: CLI flags > env vars > config file > defaults
479
+ - **Streaming output**: Engines yield \`Chunk\` objects (text, tool_use, error, done)
480
+ - **Silent failures**: Logging/notifications never break execution
481
+
482
+ ## Code Style
483
+
484
+ - Async/await, no callbacks
485
+ - Named exports, single responsibility
486
+ - \`_\` prefix for intentionally unused vars
487
+ - Import paths include \`.js\` extension
488
+ `);
489
+ // Inject
490
+ const injectResult = await injectContent(filePath, AGENTS_MD_INJECTION, INJECTION_MARKERS);
491
+ expect(injectResult.success).toBe(true);
492
+ let content = await readFile(filePath, 'utf-8');
493
+ expect(content).toContain('## vralphy');
494
+ expect(content).toContain('.vralphy/README.md');
495
+ expect(content).toContain('## Build & Validate'); // Original preserved
496
+ expect(content).toContain('## Tech Stack'); // Original preserved
497
+ // Remove
498
+ const removeResult = await removeInjectedContent(filePath, INJECTION_MARKERS);
499
+ expect(removeResult.success).toBe(true);
500
+ content = await readFile(filePath, 'utf-8');
501
+ expect(content).not.toContain(INJECTION_MARKERS.start);
502
+ expect(content).toContain('## Build & Validate');
503
+ expect(content).toContain('## Tech Stack');
504
+ expect(content).toContain('# AGENTS.md');
505
+ });
506
+ });
507
+ });
508
+ //# sourceMappingURL=file-injection.test.js.map