openplanr 1.2.7 → 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 (164) hide show
  1. package/README.md +41 -4
  2. package/dist/agents/task-parser.d.ts.map +1 -1
  3. package/dist/agents/task-parser.js +8 -34
  4. package/dist/agents/task-parser.js.map +1 -1
  5. package/dist/ai/prompts/prompt-builder.d.ts +48 -0
  6. package/dist/ai/prompts/prompt-builder.d.ts.map +1 -1
  7. package/dist/ai/prompts/prompt-builder.js +57 -1
  8. package/dist/ai/prompts/prompt-builder.js.map +1 -1
  9. package/dist/ai/prompts/system-prompts.d.ts +24 -1
  10. package/dist/ai/prompts/system-prompts.d.ts.map +1 -1
  11. package/dist/ai/prompts/system-prompts.js +104 -6
  12. package/dist/ai/prompts/system-prompts.js.map +1 -1
  13. package/dist/ai/schemas/ai-response-schemas.d.ts +68 -0
  14. package/dist/ai/schemas/ai-response-schemas.d.ts.map +1 -1
  15. package/dist/ai/schemas/ai-response-schemas.js +81 -0
  16. package/dist/ai/schemas/ai-response-schemas.js.map +1 -1
  17. package/dist/ai/types.d.ts +2 -0
  18. package/dist/ai/types.d.ts.map +1 -1
  19. package/dist/ai/types.js +4 -0
  20. package/dist/ai/types.js.map +1 -1
  21. package/dist/cli/commands/backlog.d.ts +12 -0
  22. package/dist/cli/commands/backlog.d.ts.map +1 -1
  23. package/dist/cli/commands/backlog.js +88 -2
  24. package/dist/cli/commands/backlog.js.map +1 -1
  25. package/dist/cli/commands/config.d.ts.map +1 -1
  26. package/dist/cli/commands/config.js +8 -2
  27. package/dist/cli/commands/config.js.map +1 -1
  28. package/dist/cli/commands/linear.d.ts +8 -0
  29. package/dist/cli/commands/linear.d.ts.map +1 -0
  30. package/dist/cli/commands/linear.js +550 -0
  31. package/dist/cli/commands/linear.js.map +1 -0
  32. package/dist/cli/commands/quick.d.ts +17 -0
  33. package/dist/cli/commands/quick.d.ts.map +1 -1
  34. package/dist/cli/commands/quick.js +31 -15
  35. package/dist/cli/commands/quick.js.map +1 -1
  36. package/dist/cli/commands/revise.d.ts +24 -0
  37. package/dist/cli/commands/revise.d.ts.map +1 -0
  38. package/dist/cli/commands/revise.js +570 -0
  39. package/dist/cli/commands/revise.js.map +1 -0
  40. package/dist/cli/index.js +4 -0
  41. package/dist/cli/index.js.map +1 -1
  42. package/dist/models/schema.d.ts +43 -0
  43. package/dist/models/schema.d.ts.map +1 -1
  44. package/dist/models/schema.js +49 -0
  45. package/dist/models/schema.js.map +1 -1
  46. package/dist/models/types.d.ts +296 -0
  47. package/dist/models/types.d.ts.map +1 -1
  48. package/dist/services/artifact-gathering.d.ts +4 -0
  49. package/dist/services/artifact-gathering.d.ts.map +1 -1
  50. package/dist/services/artifact-gathering.js +1 -1
  51. package/dist/services/artifact-gathering.js.map +1 -1
  52. package/dist/services/artifact-service.d.ts +12 -1
  53. package/dist/services/artifact-service.d.ts.map +1 -1
  54. package/dist/services/artifact-service.js +49 -6
  55. package/dist/services/artifact-service.js.map +1 -1
  56. package/dist/services/atomic-write-service.d.ts +41 -0
  57. package/dist/services/atomic-write-service.d.ts.map +1 -0
  58. package/dist/services/atomic-write-service.js +87 -0
  59. package/dist/services/atomic-write-service.js.map +1 -0
  60. package/dist/services/audit-log-service.d.ts +47 -0
  61. package/dist/services/audit-log-service.d.ts.map +1 -0
  62. package/dist/services/audit-log-service.js +210 -0
  63. package/dist/services/audit-log-service.js.map +1 -0
  64. package/dist/services/cascade-service.d.ts +62 -0
  65. package/dist/services/cascade-service.d.ts.map +1 -0
  66. package/dist/services/cascade-service.js +189 -0
  67. package/dist/services/cascade-service.js.map +1 -0
  68. package/dist/services/credentials-service.js +2 -2
  69. package/dist/services/credentials-service.js.map +1 -1
  70. package/dist/services/diff-service.d.ts +18 -0
  71. package/dist/services/diff-service.d.ts.map +1 -0
  72. package/dist/services/diff-service.js +35 -0
  73. package/dist/services/diff-service.js.map +1 -0
  74. package/dist/services/evidence-verifier.d.ts +71 -0
  75. package/dist/services/evidence-verifier.d.ts.map +1 -0
  76. package/dist/services/evidence-verifier.js +174 -0
  77. package/dist/services/evidence-verifier.js.map +1 -0
  78. package/dist/services/git-service.d.ts +60 -0
  79. package/dist/services/git-service.d.ts.map +1 -0
  80. package/dist/services/git-service.js +137 -0
  81. package/dist/services/git-service.js.map +1 -0
  82. package/dist/services/graph-integrity.d.ts +35 -0
  83. package/dist/services/graph-integrity.d.ts.map +1 -0
  84. package/dist/services/graph-integrity.js +53 -0
  85. package/dist/services/graph-integrity.js.map +1 -0
  86. package/dist/services/linear/body-formatters.d.ts +69 -0
  87. package/dist/services/linear/body-formatters.d.ts.map +1 -0
  88. package/dist/services/linear/body-formatters.js +183 -0
  89. package/dist/services/linear/body-formatters.js.map +1 -0
  90. package/dist/services/linear/constants.d.ts +61 -0
  91. package/dist/services/linear/constants.d.ts.map +1 -0
  92. package/dist/services/linear/constants.js +84 -0
  93. package/dist/services/linear/constants.js.map +1 -0
  94. package/dist/services/linear/errors.d.ts +14 -0
  95. package/dist/services/linear/errors.d.ts.map +1 -0
  96. package/dist/services/linear/errors.js +106 -0
  97. package/dist/services/linear/errors.js.map +1 -0
  98. package/dist/services/linear/estimate-resolver.d.ts +50 -0
  99. package/dist/services/linear/estimate-resolver.d.ts.map +1 -0
  100. package/dist/services/linear/estimate-resolver.js +82 -0
  101. package/dist/services/linear/estimate-resolver.js.map +1 -0
  102. package/dist/services/linear/plan-builders.d.ts +64 -0
  103. package/dist/services/linear/plan-builders.d.ts.map +1 -0
  104. package/dist/services/linear/plan-builders.js +237 -0
  105. package/dist/services/linear/plan-builders.js.map +1 -0
  106. package/dist/services/linear/scope-loaders.d.ts +79 -0
  107. package/dist/services/linear/scope-loaders.d.ts.map +1 -0
  108. package/dist/services/linear/scope-loaders.js +227 -0
  109. package/dist/services/linear/scope-loaders.js.map +1 -0
  110. package/dist/services/linear/strategy-context.d.ts +66 -0
  111. package/dist/services/linear/strategy-context.d.ts.map +1 -0
  112. package/dist/services/linear/strategy-context.js +121 -0
  113. package/dist/services/linear/strategy-context.js.map +1 -0
  114. package/dist/services/linear-mapping-service.d.ts +11 -0
  115. package/dist/services/linear-mapping-service.d.ts.map +1 -0
  116. package/dist/services/linear-mapping-service.js +220 -0
  117. package/dist/services/linear-mapping-service.js.map +1 -0
  118. package/dist/services/linear-pull-service.d.ts +137 -0
  119. package/dist/services/linear-pull-service.d.ts.map +1 -0
  120. package/dist/services/linear-pull-service.js +720 -0
  121. package/dist/services/linear-pull-service.js.map +1 -0
  122. package/dist/services/linear-push-service.d.ts +86 -0
  123. package/dist/services/linear-push-service.d.ts.map +1 -0
  124. package/dist/services/linear-push-service.js +956 -0
  125. package/dist/services/linear-push-service.js.map +1 -0
  126. package/dist/services/linear-service.d.ts +122 -0
  127. package/dist/services/linear-service.d.ts.map +1 -0
  128. package/dist/services/linear-service.js +361 -0
  129. package/dist/services/linear-service.js.map +1 -0
  130. package/dist/services/prompt-service.d.ts +37 -0
  131. package/dist/services/prompt-service.d.ts.map +1 -1
  132. package/dist/services/prompt-service.js +111 -0
  133. package/dist/services/prompt-service.js.map +1 -1
  134. package/dist/services/revise-apply-service.d.ts +55 -0
  135. package/dist/services/revise-apply-service.d.ts.map +1 -0
  136. package/dist/services/revise-apply-service.js +255 -0
  137. package/dist/services/revise-apply-service.js.map +1 -0
  138. package/dist/services/revise-cache-service.d.ts +46 -0
  139. package/dist/services/revise-cache-service.d.ts.map +1 -0
  140. package/dist/services/revise-cache-service.js +88 -0
  141. package/dist/services/revise-cache-service.js.map +1 -0
  142. package/dist/services/revise-plan-service.d.ts +38 -0
  143. package/dist/services/revise-plan-service.d.ts.map +1 -0
  144. package/dist/services/revise-plan-service.js +151 -0
  145. package/dist/services/revise-plan-service.js.map +1 -0
  146. package/dist/services/revise-service.d.ts +115 -0
  147. package/dist/services/revise-service.d.ts.map +1 -0
  148. package/dist/services/revise-service.js +294 -0
  149. package/dist/services/revise-service.js.map +1 -0
  150. package/dist/services/template-sections.d.ts +28 -0
  151. package/dist/services/template-sections.d.ts.map +1 -0
  152. package/dist/services/template-sections.js +55 -0
  153. package/dist/services/template-sections.js.map +1 -0
  154. package/dist/templates/backlog/backlog-item.md.hbs +3 -0
  155. package/dist/templates/quick/quick-task.md.hbs +6 -0
  156. package/dist/utils/diff.d.ts +47 -0
  157. package/dist/utils/diff.d.ts.map +1 -0
  158. package/dist/utils/diff.js +278 -0
  159. package/dist/utils/diff.js.map +1 -0
  160. package/dist/utils/markdown.d.ts +23 -0
  161. package/dist/utils/markdown.d.ts.map +1 -1
  162. package/dist/utils/markdown.js +79 -0
  163. package/dist/utils/markdown.js.map +1 -1
  164. package/package.json +3 -2
@@ -1,6 +1,7 @@
1
1
  import { checkbox, confirm, editor, input, password, select } from '@inquirer/prompts';
2
2
  import { logger } from '../utils/logger.js';
3
3
  import { isNonInteractive } from './interactive-state.js';
4
+ import { getTeamProjects } from './linear-service.js';
4
5
  /** Prompt the user for a single line of text input. Falls back to defaultValue in non-interactive mode. */
5
6
  export async function promptText(message, defaultValue) {
6
7
  if (isNonInteractive()) {
@@ -70,4 +71,114 @@ export async function promptMultiText(message, hint) {
70
71
  .map((s) => s.trim())
71
72
  .filter(Boolean);
72
73
  }
74
+ const REVISE_CONFIRM_CHOICES = [
75
+ { name: '[a] Apply — write this revision to disk', value: 'apply' },
76
+ { name: '[s] Skip — do not write; cascade continues', value: 'skip' },
77
+ { name: '[e] Edit rationale — record a human reason for this decision', value: 'edit-rationale' },
78
+ { name: '[d] Diff again — re-print the diff', value: 'diff-again' },
79
+ { name: '[q] Quit — stop cascade; already-applied artifacts remain applied', value: 'quit' },
80
+ ];
81
+ /**
82
+ * Prompt the user for the per-artifact revise confirmation menu. In
83
+ * non-interactive mode, returns `apply` by default (caller should only
84
+ * enter this path after the typed-YES gate in `confirmBulkRevise`).
85
+ */
86
+ export async function promptReviseConfirm(artifactId) {
87
+ if (isNonInteractive()) {
88
+ logger.dim(` [auto] Revise ${artifactId} → apply`);
89
+ return 'apply';
90
+ }
91
+ return select({
92
+ message: `Revise ${artifactId}:`,
93
+ choices: REVISE_CONFIRM_CHOICES,
94
+ default: 'apply',
95
+ });
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // Linear integration prompts
99
+ // ---------------------------------------------------------------------------
100
+ /**
101
+ * First-time epic-push: offer the three mapping strategies and (for
102
+ * `milestone-of` / `label-on`) let the user pick an existing Linear project
103
+ * to attach into. Pure UI + one read-only SDK call (`getTeamProjects`).
104
+ */
105
+ export async function promptMappingStrategy(client, teamId, epicId) {
106
+ if (isNonInteractive())
107
+ return null;
108
+ const strategy = await select({
109
+ message: `How should ${epicId} map to Linear?`,
110
+ choices: [
111
+ {
112
+ name: '[a] Create a new Linear project (recommended — Epic = Project, v1 behavior)',
113
+ value: 'project',
114
+ },
115
+ { name: '[b] Attach as a milestone of an existing Linear project', value: 'milestone-of' },
116
+ { name: '[c] Attach as a label on an existing Linear project', value: 'label-on' },
117
+ ],
118
+ default: 'project',
119
+ });
120
+ if (strategy === 'project') {
121
+ return { strategy };
122
+ }
123
+ const projects = await getTeamProjects(client, teamId);
124
+ if (projects.length === 0) {
125
+ logger.warn('This team has no Linear projects yet — falling back to creating a new one (strategy: project).');
126
+ return { strategy: 'project' };
127
+ }
128
+ const targetProjectId = await select({
129
+ message: `Pick the target Linear project for ${epicId}:`,
130
+ choices: projects.map((p) => ({ name: `${p.name} (${p.url})`, value: p.id })),
131
+ });
132
+ return { strategy, targetProjectId };
133
+ }
134
+ /**
135
+ * First-time QT / BL push: let the user pick the Linear project that will
136
+ * host `QT-*` and `BL-*` issues (stored in `linear.standaloneProjectId`).
137
+ */
138
+ export async function promptStandaloneProject(client, teamId) {
139
+ if (isNonInteractive())
140
+ return null;
141
+ const projects = await getTeamProjects(client, teamId);
142
+ if (projects.length === 0) {
143
+ logger.warn('This team has no Linear projects yet — create one in Linear first, then re-run the push.');
144
+ return null;
145
+ }
146
+ const choice = await select({
147
+ message: 'Pick the Linear project that will host Planr quick tasks & backlog items:',
148
+ choices: [
149
+ ...projects.map((p) => ({ name: `${p.name} (${p.url})`, value: p.id })),
150
+ { name: '[cancel]', value: '__cancel__' },
151
+ ],
152
+ });
153
+ if (choice === '__cancel__')
154
+ return null;
155
+ const picked = projects.find((p) => p.id === choice);
156
+ if (!picked)
157
+ return null;
158
+ return { projectId: picked.id, projectName: picked.name };
159
+ }
160
+ /**
161
+ * Typed-YES confirmation gate for `--yes` bulk-apply runs.
162
+ *
163
+ * In an interactive TTY, prints the provided summary and blocks on the user
164
+ * typing "YES" (case-sensitive) to proceed. In non-TTY environments
165
+ * (piped stdout, CI), returns `true` unconditionally — the `--yes` flag
166
+ * alone is the contract with the pipeline, and PR review is the upstream
167
+ * human gate. Returns `false` if the user types anything other than "YES".
168
+ */
169
+ export async function confirmBulkRevise(summary) {
170
+ // Non-TTY: the flag is sufficient. Humans can't type at pipelines.
171
+ if (!process.stdout.isTTY) {
172
+ logger.dim('Non-interactive environment detected — --yes flag accepted without typed-YES.');
173
+ return true;
174
+ }
175
+ if (isNonInteractive()) {
176
+ // Explicit --no-interactive / -y also skips the typed-YES.
177
+ logger.dim('Non-interactive mode — --yes flag accepted without typed-YES.');
178
+ return true;
179
+ }
180
+ logger.info(summary);
181
+ const typed = await input({ message: 'Type YES to continue:' });
182
+ return typed === 'YES';
183
+ }
73
184
  //# sourceMappingURL=prompt-service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-service.js","sourceRoot":"","sources":["../../src/services/prompt-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACvF,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,2GAA2G;AAC3G,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,YAAqB;IACrE,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,OAAO,OAAO,YAAY,GAAG,CAAC,CAAC;YACtD,OAAO,YAAY,CAAC;QACtB,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,sDAAsD,OAAO,GAAG,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,KAAK,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,OAA0C,EAC1C,YAAgB;IAEhB,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,YAAY,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC/C,MAAM,CAAC,GAAG,CAAC,YAAY,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC;QAC/C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,iDAAiD;AACjD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,YAAY,GAAG,IAAI;IACtE,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,YAAY,OAAO,MAAM,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACnE,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,gEAAgE;AAChE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe,EAAE,YAAqB;IACvE,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,OAAO,cAAc,CAAC,CAAC;YAC9C,OAAO,YAAY,CAAC;QACtB,CAAC;QACD,MAAM,IAAI,KAAK,CACb,qEAAqE,OAAO,GAAG,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe;IAChD,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAC;QAC7E,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,OAA6D;IAE7D,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACrE,MAAM,CAAC,GAAG,CAAC,YAAY,OAAO,MAAM,OAAO,CAAC,MAAM,uBAAuB,CAAC,CAAC;QAC3E,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,QAAQ,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe,EAAE,IAAa;IAClE,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,4CAA4C,OAAO,8BAA8B,CAClF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;QACzB,OAAO,EAAE,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;KACjD,CAAC,CAAC;IACH,OAAO,MAAM;SACV,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC"}
1
+ {"version":3,"file":"prompt-service.js","sourceRoot":"","sources":["../../src/services/prompt-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGvF,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,2GAA2G;AAC3G,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,YAAqB;IACrE,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,OAAO,OAAO,YAAY,GAAG,CAAC,CAAC;YACtD,OAAO,YAAY,CAAC;QACtB,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,sDAAsD,OAAO,GAAG,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,KAAK,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,OAA0C,EAC1C,YAAgB;IAEhB,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,YAAY,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC/C,MAAM,CAAC,GAAG,CAAC,YAAY,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC;QAC/C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,iDAAiD;AACjD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,YAAY,GAAG,IAAI;IACtE,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,YAAY,OAAO,MAAM,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACnE,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,gEAAgE;AAChE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe,EAAE,YAAqB;IACvE,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,OAAO,cAAc,CAAC,CAAC;YAC9C,OAAO,YAAY,CAAC;QACtB,CAAC;QACD,MAAM,IAAI,KAAK,CACb,qEAAqE,OAAO,GAAG,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe;IAChD,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAC;QAC7E,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,OAA6D;IAE7D,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACrE,MAAM,CAAC,GAAG,CAAC,YAAY,OAAO,MAAM,OAAO,CAAC,MAAM,uBAAuB,CAAC,CAAC;QAC3E,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,QAAQ,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe,EAAE,IAAa;IAClE,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,4CAA4C,OAAO,8BAA8B,CAClF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;QACzB,OAAO,EAAE,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;KACjD,CAAC,CAAC;IACH,OAAO,MAAM;SACV,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AASD,MAAM,sBAAsB,GAAwD;IAClF,EAAE,IAAI,EAAE,yCAAyC,EAAE,KAAK,EAAE,OAAO,EAAE;IACnE,EAAE,IAAI,EAAE,4CAA4C,EAAE,KAAK,EAAE,MAAM,EAAE;IACrE,EAAE,IAAI,EAAE,8DAA8D,EAAE,KAAK,EAAE,gBAAgB,EAAE;IACjG,EAAE,IAAI,EAAE,oCAAoC,EAAE,KAAK,EAAE,YAAY,EAAE;IACnE,EAAE,IAAI,EAAE,mEAAmE,EAAE,KAAK,EAAE,MAAM,EAAE;CAC7F,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,UAAkB;IAC1D,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,mBAAmB,UAAU,UAAU,CAAC,CAAC;QACpD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,MAAM,CAAC;QACZ,OAAO,EAAE,UAAU,UAAU,GAAG;QAChC,OAAO,EAAE,sBAAsB;QAC/B,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAoB,EACpB,MAAc,EACd,MAAc;IAEd,IAAI,gBAAgB,EAAE;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAwB;QACnD,OAAO,EAAE,cAAc,MAAM,iBAAiB;QAC9C,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,6EAA6E;gBACnF,KAAK,EAAE,SAAS;aACjB;YACD,EAAE,IAAI,EAAE,yDAAyD,EAAE,KAAK,EAAE,cAAc,EAAE;YAC1F,EAAE,IAAI,EAAE,qDAAqD,EAAE,KAAK,EAAE,UAAU,EAAE;SACnF;QACD,OAAO,EAAE,SAAS;KACnB,CAAC,CAAC;IACH,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACtB,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CACT,gGAAgG,CACjG,CAAC;QACF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;IACD,MAAM,eAAe,GAAG,MAAM,MAAM,CAAS;QAC3C,OAAO,EAAE,sCAAsC,MAAM,GAAG;QACxD,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;KAC/E,CAAC,CAAC;IACH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,MAAoB,EACpB,MAAc;IAEd,IAAI,gBAAgB,EAAE;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CACT,0FAA0F,CAC3F,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAS;QAClC,OAAO,EAAE,2EAA2E;QACpF,OAAO,EAAE;YACP,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACxE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE;SAC1C;KACF,CAAC,CAAC;IACH,IAAI,MAAM,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAe;IACrD,mEAAmE;IACnE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,+EAA+E,CAAC,CAAC;QAC5F,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,2DAA2D;QAC3D,MAAM,CAAC,GAAG,CAAC,+DAA+D,CAAC,CAAC;QAC5E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrB,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAChE,OAAO,KAAK,KAAK,KAAK,CAAC;AACzB,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Replay a previously-written revise audit to disk without any model calls.
3
+ *
4
+ * Implements after `planr revise --dry-run` produces an audit log
5
+ * with the proposed diffs, `planr revise --apply-from <audit>` reads those
6
+ * diffs and writes them to the corresponding artifacts. Zero tokens spent
7
+ * in this mode.
8
+ *
9
+ * Pipeline:
10
+ * 1. Clean-tree gate (same as normal revise)
11
+ * 2. Parse the audit (via `revise-plan-service`) → list of replayable entries
12
+ * 3. Filter to `would-apply` entries that have a diff + artifact path
13
+ * 4. Per entry:
14
+ * a. Read current artifact content
15
+ * b. Apply the stored diff; if the diff doesn't land cleanly (the
16
+ * source has drifted since the dry-run), skip the entry and
17
+ * record the skip reason in the new audit
18
+ * c. Atomic write with sidecar backup
19
+ * d. Append an `applied-from-plan` entry to the new audit log
20
+ * 5. Post-flight graph-integrity check + git rollback on break
21
+ *
22
+ * Safety gates preserved vs. normal apply:
23
+ * - Clean-tree gate ✓
24
+ * - Atomic writes (temp file + rename) ✓
25
+ * - Post-flight graph integrity + git rollback ✓
26
+ * - Per-artifact confirmation (prompt unless `--yes`) ✓
27
+ *
28
+ * Note: sidecar `.bak` files are deliberately NOT written on this path.
29
+ * Rollback already flows through git (clean-tree gate guarantees HEAD is a
30
+ * valid restore point), so per-file backups would be redundant noise in
31
+ * `.planr/reports/`. The atomic-write guarantee covers partial-write
32
+ * crashes; git covers "I wish I hadn't applied that."
33
+ *
34
+ * Deliberately NOT run on replay:
35
+ * - AI model calls (the point of this feature)
36
+ * - Evidence verification (the dry-run already verified; nothing changes
37
+ * between dry-run and apply that evidence verification would catch that
38
+ * the diff-apply staleness check doesn't already catch)
39
+ * - `--cascade` / `--all` orchestration (the audit already encodes the
40
+ * cascade order as entry order)
41
+ */
42
+ import type { OpenPlanrConfig } from '../models/types.js';
43
+ export interface ApplyFromAuditOptions {
44
+ projectDir: string;
45
+ config: OpenPlanrConfig;
46
+ auditPath: string;
47
+ allowDirty: boolean;
48
+ /** Print the plan without writing — useful to confirm what would replay. */
49
+ dryRun: boolean;
50
+ /** Skip per-artifact confirmation; still requires typed-YES in an interactive TTY. */
51
+ yes: boolean;
52
+ }
53
+ /** Returns the process exit code. 0 on success, 1 on fatal error, non-zero on partial/rollback. */
54
+ export declare function runApplyFromAudit(opts: ApplyFromAuditOptions): Promise<number>;
55
+ //# sourceMappingURL=revise-apply-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revise-apply-service.d.ts","sourceRoot":"","sources":["../../src/services/revise-apply-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,oBAAoB,CAAC;AAW5E,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,4EAA4E;IAC5E,MAAM,EAAE,OAAO,CAAC;IAChB,sFAAsF;IACtF,GAAG,EAAE,OAAO,CAAC;CACd;AAED,mGAAmG;AACnG,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyMpF"}
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Replay a previously-written revise audit to disk without any model calls.
3
+ *
4
+ * Implements after `planr revise --dry-run` produces an audit log
5
+ * with the proposed diffs, `planr revise --apply-from <audit>` reads those
6
+ * diffs and writes them to the corresponding artifacts. Zero tokens spent
7
+ * in this mode.
8
+ *
9
+ * Pipeline:
10
+ * 1. Clean-tree gate (same as normal revise)
11
+ * 2. Parse the audit (via `revise-plan-service`) → list of replayable entries
12
+ * 3. Filter to `would-apply` entries that have a diff + artifact path
13
+ * 4. Per entry:
14
+ * a. Read current artifact content
15
+ * b. Apply the stored diff; if the diff doesn't land cleanly (the
16
+ * source has drifted since the dry-run), skip the entry and
17
+ * record the skip reason in the new audit
18
+ * c. Atomic write with sidecar backup
19
+ * d. Append an `applied-from-plan` entry to the new audit log
20
+ * 5. Post-flight graph-integrity check + git rollback on break
21
+ *
22
+ * Safety gates preserved vs. normal apply:
23
+ * - Clean-tree gate ✓
24
+ * - Atomic writes (temp file + rename) ✓
25
+ * - Post-flight graph integrity + git rollback ✓
26
+ * - Per-artifact confirmation (prompt unless `--yes`) ✓
27
+ *
28
+ * Note: sidecar `.bak` files are deliberately NOT written on this path.
29
+ * Rollback already flows through git (clean-tree gate guarantees HEAD is a
30
+ * valid restore point), so per-file backups would be redundant noise in
31
+ * `.planr/reports/`. The atomic-write guarantee covers partial-write
32
+ * crashes; git covers "I wish I hadn't applied that."
33
+ *
34
+ * Deliberately NOT run on replay:
35
+ * - AI model calls (the point of this feature)
36
+ * - Evidence verification (the dry-run already verified; nothing changes
37
+ * between dry-run and apply that evidence verification would catch that
38
+ * the diff-apply staleness check doesn't already catch)
39
+ * - `--cascade` / `--all` orchestration (the audit already encodes the
40
+ * cascade order as entry order)
41
+ */
42
+ import path from 'node:path';
43
+ import chalk from 'chalk';
44
+ import { applyUnifiedDiff } from '../utils/diff.js';
45
+ import { readFile } from '../utils/fs.js';
46
+ import { display, logger } from '../utils/logger.js';
47
+ import { atomicWriteFile } from './atomic-write-service.js';
48
+ import { createAuditLogWriter } from './audit-log-service.js';
49
+ import { checkCleanTree, checkoutPaths } from './git-service.js';
50
+ import { checkGraphIntegrity } from './graph-integrity.js';
51
+ import { confirmBulkRevise, promptReviseConfirm } from './prompt-service.js';
52
+ import { filterReplayable, readPlanFromAudit } from './revise-plan-service.js';
53
+ /** Returns the process exit code. 0 on success, 1 on fatal error, non-zero on partial/rollback. */
54
+ export async function runApplyFromAudit(opts) {
55
+ const { projectDir, config, auditPath, allowDirty, dryRun, yes } = opts;
56
+ logger.heading(`Apply revise plan from ${path.basename(auditPath)}${dryRun ? ' (dry-run)' : ''}`);
57
+ logger.dim('Mode: replay — zero AI tokens will be spent.');
58
+ // --- Layer 1: clean-tree gate -------------------------------------------
59
+ const treeCheck = await checkCleanTree(projectDir, { allowDirty });
60
+ if (!treeCheck.ok) {
61
+ logger.error(treeCheck.message);
62
+ return 1;
63
+ }
64
+ if (treeCheck.status.kind !== 'clean') {
65
+ logger.warn(treeCheck.message);
66
+ }
67
+ // --- Layer 2: parse the audit -------------------------------------------
68
+ let plan;
69
+ try {
70
+ plan = readPlanFromAudit(auditPath);
71
+ }
72
+ catch (err) {
73
+ logger.error(err instanceof Error ? err.message : String(err));
74
+ return 1;
75
+ }
76
+ const replayable = filterReplayable(plan);
77
+ logger.info(`Parsed ${plan.entries.length} audit entries (${replayable.length} replayable, ${plan.entries.length - replayable.length} skipped/flagged without diff).`);
78
+ if (replayable.length === 0) {
79
+ logger.warn('No replayable entries (would-apply + diff + artifact path). Nothing to do.');
80
+ return 0;
81
+ }
82
+ // --- Bulk confirmation: typed-YES for TTY, flag-only for CI -------------
83
+ if (yes && !dryRun) {
84
+ const summary = buildReplaySummary(auditPath, replayable);
85
+ const confirmed = await confirmBulkRevise(summary);
86
+ if (!confirmed) {
87
+ logger.dim('Confirmation declined — exiting without changes.');
88
+ return 0;
89
+ }
90
+ }
91
+ // --- New audit log for the apply-from-plan run --------------------------
92
+ const writer = createAuditLogWriter({
93
+ projectDir,
94
+ scope: `${plan.scope}-applied`,
95
+ cascade: false,
96
+ dryRun,
97
+ format: 'md',
98
+ });
99
+ logger.dim(`Apply audit log: ${writer.path}`);
100
+ // --- Per-entry replay ---------------------------------------------------
101
+ const affectedPaths = [];
102
+ let applied = 0;
103
+ let stale = 0;
104
+ let conflicts = 0;
105
+ let skipped = 0;
106
+ let userQuit = false;
107
+ for (let i = 0; i < replayable.length; i++) {
108
+ if (userQuit)
109
+ break;
110
+ const entry = replayable[i];
111
+ const progress = `[${i + 1}/${replayable.length}] ${entry.artifactId}`;
112
+ if (!entry.artifactPath) {
113
+ skipped++;
114
+ recordSkip(writer, entry, 'stale-skipped', 'no artifact path recorded in plan');
115
+ continue;
116
+ }
117
+ let currentContent;
118
+ try {
119
+ currentContent = await readFile(entry.artifactPath);
120
+ }
121
+ catch (err) {
122
+ stale++;
123
+ recordSkip(writer, entry, 'stale-skipped', `cannot read artifact: ${err instanceof Error ? err.message : String(err)}`);
124
+ logger.warn(`${progress}: stale — artifact not readable.`);
125
+ continue;
126
+ }
127
+ const patch = applyUnifiedDiff(currentContent, entry.diff ?? '');
128
+ if (!patch.ok) {
129
+ conflicts++;
130
+ recordSkip(writer, entry, 'conflict-skipped', patch.error ?? 'diff did not apply cleanly');
131
+ logger.warn(`${progress}: conflict — ${patch.error}`);
132
+ continue;
133
+ }
134
+ // Per-artifact confirmation (skipped when --yes or --dry-run).
135
+ if (!dryRun && !yes) {
136
+ display.separator(60);
137
+ display.heading(` ${progress}: ${chalk.yellow('REPLAY REVISE')}`);
138
+ display.line(` Artifact: ${entry.artifactId}`);
139
+ display.line(` Rationale: ${entry.rationale}`);
140
+ display.line('');
141
+ display.line(' Diff:');
142
+ const diffPreview = (entry.diff ?? '')
143
+ .split('\n')
144
+ .map((l) => ` ${colorizeDiffLine(l)}`)
145
+ .join('\n');
146
+ display.line(diffPreview);
147
+ display.separator(60);
148
+ const action = await promptReviseConfirm(entry.artifactId);
149
+ if (action === 'skip') {
150
+ skipped++;
151
+ recordSkip(writer, entry, 'skipped-by-user', 'user declined at replay prompt');
152
+ logger.dim(`${progress}: skipped by user.`);
153
+ continue;
154
+ }
155
+ if (action === 'quit') {
156
+ recordSkip(writer, entry, 'skipped-by-user', 'user quit replay at this entry');
157
+ userQuit = true;
158
+ break;
159
+ }
160
+ // For 'diff-again' we just continue; the diff was already shown. For
161
+ // 'edit-rationale' we accept as-is (replay doesn't let you edit).
162
+ }
163
+ if (dryRun) {
164
+ writer.appendEntry({
165
+ ...entry,
166
+ outcome: 'would-apply',
167
+ timestamp: new Date().toISOString(),
168
+ });
169
+ applied++;
170
+ logger.info(`${progress}: would apply (dry-run).`);
171
+ continue;
172
+ }
173
+ // Atomic write (temp file + rename). No sidecar backup — git covers
174
+ // rollback via the clean-tree gate + post-flight checkout.
175
+ try {
176
+ await atomicWriteFile(entry.artifactPath, patch.result ?? '');
177
+ affectedPaths.push(path.relative(projectDir, entry.artifactPath) || entry.artifactPath);
178
+ applied++;
179
+ writer.appendEntry({
180
+ ...entry,
181
+ outcome: 'applied-from-plan',
182
+ timestamp: new Date().toISOString(),
183
+ });
184
+ logger.info(`${progress}: ${chalk.green('applied')}.`);
185
+ }
186
+ catch (err) {
187
+ conflicts++;
188
+ recordSkip(writer, entry, 'conflict-skipped', `write failed: ${err instanceof Error ? err.message : String(err)}`);
189
+ logger.error(`${progress}: write failed — ${err}`);
190
+ }
191
+ }
192
+ // --- Post-flight graph integrity + rollback -----------------------------
193
+ let exitCode = 0;
194
+ if (!dryRun && affectedPaths.length > 0) {
195
+ const integrity = await checkGraphIntegrity(projectDir, config);
196
+ if (!integrity.ok) {
197
+ logger.error(`Post-flight graph integrity broken: ${integrity.issues.length} issue(s). Rolling back…`);
198
+ if (treeCheck.status.kind === 'clean') {
199
+ await checkoutPaths(projectDir, affectedPaths);
200
+ logger.info(`Rolled back ${affectedPaths.length} artifact path(s) to HEAD.`);
201
+ }
202
+ else {
203
+ logger.warn('Cannot rollback — pre-run tree was dirty. Inspect changes manually.');
204
+ }
205
+ exitCode = 1;
206
+ }
207
+ else {
208
+ logger.dim('Post-flight graph integrity: ok.');
209
+ }
210
+ }
211
+ writer.close();
212
+ // --- Summary ------------------------------------------------------------
213
+ display.separator(60);
214
+ display.heading(' Replay summary');
215
+ display.line(` Applied: ${chalk.green(String(applied))}`);
216
+ display.line(` Stale-skipped: ${stale} (artifact missing or unreadable)`);
217
+ display.line(` Conflict-skipped: ${conflicts} (diff did not apply cleanly)`);
218
+ display.line(` User-skipped: ${skipped}`);
219
+ display.line(` ${chalk.bold('AI tokens spent: 0')} (replay mode — zero model calls)`);
220
+ if (!dryRun && applied > 0 && exitCode === 0) {
221
+ display.line('');
222
+ display.line(` Suggested commit: ${chalk.cyan(`git commit -am "chore(plan): apply revise plan ${plan.scope}"`)}`);
223
+ }
224
+ display.separator(60);
225
+ return exitCode;
226
+ }
227
+ function recordSkip(writer, entry, outcome, reason) {
228
+ writer.appendEntry({
229
+ ...entry,
230
+ outcome,
231
+ error: reason,
232
+ timestamp: new Date().toISOString(),
233
+ });
234
+ }
235
+ function buildReplaySummary(auditPath, entries) {
236
+ const head = `About to replay ${entries.length} revise entries from ${path.basename(auditPath)}.`;
237
+ const sample = entries
238
+ .slice(0, 8)
239
+ .map((e) => ` - ${e.artifactId} (${e.outcome} → applied-from-plan)`)
240
+ .join('\n');
241
+ const tail = entries.length > 8 ? `\n … and ${entries.length - 8} more` : '';
242
+ return `${head}\nZero model calls will be made.\n${sample}${tail}`;
243
+ }
244
+ function colorizeDiffLine(line) {
245
+ if (line.startsWith('+++') || line.startsWith('---'))
246
+ return chalk.bold(line);
247
+ if (line.startsWith('@@'))
248
+ return chalk.cyan(line);
249
+ if (line.startsWith('+'))
250
+ return chalk.green(line);
251
+ if (line.startsWith('-'))
252
+ return chalk.red(line);
253
+ return chalk.dim(line);
254
+ }
255
+ //# sourceMappingURL=revise-apply-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revise-apply-service.js","sourceRoot":"","sources":["../../src/services/revise-apply-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Content-hash run cache for `planr revise`.
3
+ *
4
+ * Between runs, we hash each artifact's raw content (+ the codebase-digest
5
+ * input, when present) and skip artifacts whose hash matches the last
6
+ * successful revise of that artifact with the same codebase state. Keeps
7
+ * `--all` cheap to re-run on an untouched repo (a common check-before-PR
8
+ * workflow) without sacrificing correctness: any edit to the artifact or
9
+ * the codebase invalidates the cache entry automatically.
10
+ *
11
+ * Persisted at `.planr/reports/.revise-cache.json`. JSON was chosen over
12
+ * a line-oriented format because the cache is small (one entry per
13
+ * artifact) and atomic updates via atomicWriteFile are simpler with JSON.
14
+ *
15
+ * Cache semantics are best-effort: if the cache file is missing or
16
+ * malformed, reads return empty (never throw). Writes are fire-and-forget.
17
+ */
18
+ export interface ReviseCacheEntry {
19
+ artifactHash: string;
20
+ /** Digest over codebase-context-relevant state (folder tree, architecture files) — optional. */
21
+ codebaseHash?: string;
22
+ lastOutcome: 'skipped-by-agent' | 'applied' | 'would-apply' | 'flagged';
23
+ lastRunAt: string;
24
+ }
25
+ export interface ReviseCache {
26
+ entries: Record<string, ReviseCacheEntry>;
27
+ }
28
+ export declare function defaultCachePath(projectDir: string): string;
29
+ export declare function loadCache(projectDir: string): ReviseCache;
30
+ export declare function saveCache(projectDir: string, cache: ReviseCache): Promise<void>;
31
+ /** SHA-256 of the artifact raw content (body + frontmatter) used as cache key. */
32
+ export declare function hashArtifactContent(raw: string): string;
33
+ /** Optional SHA-256 over the codebase context string, so code changes invalidate cache. */
34
+ export declare function hashCodebaseContext(formatted?: string): string | undefined;
35
+ /**
36
+ * Returns true when the given artifact + codebase hash matches the cache
37
+ * entry from a prior successful revise — caller may skip the AI call.
38
+ * A mismatch on either dimension invalidates the entry.
39
+ */
40
+ export declare function shouldSkipArtifact(cache: ReviseCache, artifactId: string, artifactHash: string, codebaseHash: string | undefined): boolean;
41
+ /**
42
+ * Record an outcome in the cache. Returns a new cache object (pure) so
43
+ * callers can decide when to flush.
44
+ */
45
+ export declare function recordOutcome(cache: ReviseCache, artifactId: string, artifactHash: string, codebaseHash: string | undefined, outcome: ReviseCacheEntry['lastOutcome']): ReviseCache;
46
+ //# sourceMappingURL=revise-cache-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revise-cache-service.d.ts","sourceRoot":"","sources":["../../src/services/revise-cache-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,kBAAkB,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC;IACxE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC3C;AAID,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAazD;AAED,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAErF;AAED,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,OAAO,CAOT;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,OAAO,EAAE,gBAAgB,CAAC,aAAa,CAAC,GACvC,WAAW,CAYb"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Content-hash run cache for `planr revise`.
3
+ *
4
+ * Between runs, we hash each artifact's raw content (+ the codebase-digest
5
+ * input, when present) and skip artifacts whose hash matches the last
6
+ * successful revise of that artifact with the same codebase state. Keeps
7
+ * `--all` cheap to re-run on an untouched repo (a common check-before-PR
8
+ * workflow) without sacrificing correctness: any edit to the artifact or
9
+ * the codebase invalidates the cache entry automatically.
10
+ *
11
+ * Persisted at `.planr/reports/.revise-cache.json`. JSON was chosen over
12
+ * a line-oriented format because the cache is small (one entry per
13
+ * artifact) and atomic updates via atomicWriteFile are simpler with JSON.
14
+ *
15
+ * Cache semantics are best-effort: if the cache file is missing or
16
+ * malformed, reads return empty (never throw). Writes are fire-and-forget.
17
+ */
18
+ import { createHash } from 'node:crypto';
19
+ import { existsSync, readFileSync } from 'node:fs';
20
+ import path from 'node:path';
21
+ import { atomicWriteFile } from './atomic-write-service.js';
22
+ const EMPTY = { entries: {} };
23
+ export function defaultCachePath(projectDir) {
24
+ return path.join(projectDir, '.planr', 'reports', '.revise-cache.json');
25
+ }
26
+ export function loadCache(projectDir) {
27
+ const p = defaultCachePath(projectDir);
28
+ if (!existsSync(p))
29
+ return { entries: {} };
30
+ try {
31
+ const raw = readFileSync(p, 'utf-8');
32
+ const parsed = JSON.parse(raw);
33
+ if (parsed && typeof parsed === 'object' && parsed.entries) {
34
+ return { entries: parsed.entries };
35
+ }
36
+ return EMPTY;
37
+ }
38
+ catch {
39
+ return EMPTY;
40
+ }
41
+ }
42
+ export async function saveCache(projectDir, cache) {
43
+ await atomicWriteFile(defaultCachePath(projectDir), JSON.stringify(cache, null, 2));
44
+ }
45
+ /** SHA-256 of the artifact raw content (body + frontmatter) used as cache key. */
46
+ export function hashArtifactContent(raw) {
47
+ return createHash('sha256').update(raw).digest('hex');
48
+ }
49
+ /** Optional SHA-256 over the codebase context string, so code changes invalidate cache. */
50
+ export function hashCodebaseContext(formatted) {
51
+ if (!formatted)
52
+ return undefined;
53
+ return createHash('sha256').update(formatted).digest('hex');
54
+ }
55
+ /**
56
+ * Returns true when the given artifact + codebase hash matches the cache
57
+ * entry from a prior successful revise — caller may skip the AI call.
58
+ * A mismatch on either dimension invalidates the entry.
59
+ */
60
+ export function shouldSkipArtifact(cache, artifactId, artifactHash, codebaseHash) {
61
+ const entry = cache.entries[artifactId];
62
+ if (!entry)
63
+ return false;
64
+ if (entry.artifactHash !== artifactHash)
65
+ return false;
66
+ if (entry.codebaseHash !== codebaseHash)
67
+ return false;
68
+ // Only cache-skip when the prior outcome indicated "nothing to do".
69
+ return entry.lastOutcome === 'skipped-by-agent';
70
+ }
71
+ /**
72
+ * Record an outcome in the cache. Returns a new cache object (pure) so
73
+ * callers can decide when to flush.
74
+ */
75
+ export function recordOutcome(cache, artifactId, artifactHash, codebaseHash, outcome) {
76
+ return {
77
+ entries: {
78
+ ...cache.entries,
79
+ [artifactId]: {
80
+ artifactHash,
81
+ codebaseHash,
82
+ lastOutcome: outcome,
83
+ lastRunAt: new Date().toISOString(),
84
+ },
85
+ },
86
+ };
87
+ }
88
+ //# sourceMappingURL=revise-cache-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revise-cache-service.js","sourceRoot":"","sources":["../../src/services/revise-cache-service.ts"],"names":[],"mappings":"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"}