dsh-hashline-edit 0.2.1 → 0.2.2

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 (109) hide show
  1. package/README.md +60 -56
  2. package/cordis.patch.yml +3 -3
  3. package/lib/index.js +7224 -6454
  4. package/lib/types/dsh/contracts.d.ts +3 -12
  5. package/lib/types/dsh/fs.d.ts +0 -3
  6. package/lib/types/dsh/guidance.d.ts +5 -5
  7. package/lib/types/dsh/host-compat.d.ts +78 -7
  8. package/lib/types/dsh/index.d.ts +9 -21
  9. package/lib/types/dsh/metrics/events.d.ts +1 -1
  10. package/lib/types/dsh/metrics/observer.d.ts +1 -1
  11. package/lib/types/dsh/sandbox.d.ts +2 -2
  12. package/lib/types/dsh/shell-hook.d.ts +1 -1
  13. package/lib/types/dsh/tools/edit.d.ts +10 -0
  14. package/lib/types/dsh/tools/read.d.ts +3 -3
  15. package/lib/types/src/domain-errors.d.ts +2 -16
  16. package/lib/types/src/edit-tool.d.ts +41 -0
  17. package/lib/types/src/edit-undo.d.ts +2 -11
  18. package/lib/types/src/file-content/index.d.ts +1 -1
  19. package/lib/types/src/file-content/preview.d.ts +1 -1
  20. package/lib/types/src/hash-store.d.ts +2 -2
  21. package/lib/types/src/hashline/alphabet.d.ts +16 -1
  22. package/lib/types/src/hashline/hash-identity.d.ts +10 -4
  23. package/lib/types/src/mutation-engine/engine.d.ts +31 -0
  24. package/lib/types/src/mutation-engine/pipeline.d.ts +1 -8
  25. package/lib/types/src/mutation-engine/types.d.ts +50 -40
  26. package/lib/types/src/payload-contract.d.ts +135 -0
  27. package/lib/types/src/served-session/serve-render.d.ts +0 -6
  28. package/lib/types/src/undo-store.d.ts +1 -17
  29. package/package.json +21 -18
  30. package/prompts/edit-guidelines.md +10 -0
  31. package/prompts/edit-snippet.md +1 -0
  32. package/prompts/edit.md +1 -0
  33. package/prompts/read-guidelines.md +4 -4
  34. package/prompts/read.md +1 -1
  35. package/prompts/undo-last-edit-guidelines.md +2 -2
  36. package/prompts/undo-last-edit-snippet.md +1 -1
  37. package/prompts/undo-last-edit.md +1 -1
  38. package/src/bash-classifier.ts +1626 -0
  39. package/src/bash-conversion/decide.ts +812 -0
  40. package/src/bash-display-transform.ts +50 -0
  41. package/src/bash-multifile-config.ts +239 -0
  42. package/src/bash-view-config.ts +158 -0
  43. package/src/cut-repair.ts +11 -77
  44. package/src/domain-errors.ts +3 -35
  45. package/src/edit-normalize.ts +8 -0
  46. package/src/edit-render.ts +161 -0
  47. package/src/edit-tool.ts +118 -0
  48. package/src/edit-undo.ts +25 -379
  49. package/src/edit.ts +136 -0
  50. package/src/file-content/index.ts +1 -1
  51. package/src/file-content/preview.ts +1 -1
  52. package/src/file-content/served-header.ts +1 -1
  53. package/src/hash-store.ts +1 -18
  54. package/src/hashline/alphabet.ts +42 -1
  55. package/src/hashline/hash-identity.ts +12 -3
  56. package/src/integrations/pi-lens/command.ts +712 -0
  57. package/src/integrations/pi-lens/config.ts +148 -0
  58. package/src/integrations/pi-lens/io-bridge-adapter.ts +234 -0
  59. package/src/integrations/pi-lens/io-bridge.ts +112 -0
  60. package/src/integrations/pi-lens/settings-overlay.ts +272 -0
  61. package/src/lifecycle-hooks/index.ts +1992 -0
  62. package/src/lifecycle-hooks/types.ts +64 -0
  63. package/src/mutated-files.ts +75 -0
  64. package/src/mutation-engine/engine.ts +239 -0
  65. package/src/mutation-engine/index.ts +17 -0
  66. package/src/mutation-engine/pipeline.ts +27 -198
  67. package/src/mutation-engine/types.ts +57 -39
  68. package/src/mutation-queue.ts +1 -1
  69. package/src/payload-contract.ts +685 -0
  70. package/src/preview-controller.ts +84 -0
  71. package/src/prompts.ts +43 -10
  72. package/src/read.ts +865 -0
  73. package/src/search-hook.ts +41 -0
  74. package/src/served-session/serve-render.ts +2 -2
  75. package/src/store-config.ts +73 -0
  76. package/src/store-janitor-warning.ts +169 -0
  77. package/src/store-janitor.ts +539 -0
  78. package/src/store-kernel/settings.ts +233 -0
  79. package/src/store.ts +154 -0
  80. package/src/tui-presenter.ts +134 -0
  81. package/src/undo-store.ts +5 -74
  82. package/src/write-hook.ts +130 -0
  83. package/lib/types/dsh/hide.d.ts +0 -79
  84. package/lib/types/dsh/tools/apply-patch.d.ts +0 -38
  85. package/lib/types/src/apply-patch/commit.d.ts +0 -49
  86. package/lib/types/src/apply-patch/grammar.d.ts +0 -29
  87. package/lib/types/src/apply-patch/lifecycle.d.ts +0 -32
  88. package/lib/types/src/apply-patch/plan.d.ts +0 -50
  89. package/lib/types/src/apply-patch/render.d.ts +0 -51
  90. package/lib/types/src/apply-patch/resolve.d.ts +0 -129
  91. package/lib/types/src/apply-patch/tool.d.ts +0 -54
  92. package/lib/types/src/apply-patch/types.d.ts +0 -362
  93. package/lib/types/src/fs-lifecycle.d.ts +0 -44
  94. package/lib/types/src/host-utils.d.ts +0 -63
  95. package/lib/types/src/mutation-engine/edit-request.d.ts +0 -34
  96. package/prompts/apply-patch-guidelines.md +0 -10
  97. package/prompts/apply-patch.md +0 -1
  98. package/src/apply-patch/commit.ts +0 -190
  99. package/src/apply-patch/grammar.ts +0 -620
  100. package/src/apply-patch/index.ts +0 -17
  101. package/src/apply-patch/lifecycle.ts +0 -195
  102. package/src/apply-patch/plan.ts +0 -800
  103. package/src/apply-patch/render.ts +0 -137
  104. package/src/apply-patch/resolve.ts +0 -470
  105. package/src/apply-patch/tool.ts +0 -257
  106. package/src/apply-patch/types.ts +0 -387
  107. package/src/fs-lifecycle.ts +0 -201
  108. package/src/host-utils.ts +0 -224
  109. package/src/mutation-engine/edit-request.ts +0 -39
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Interactive settings overlay — the headed form of `/pi-better-edit settings`.
3
+ *
4
+ * WHY this module keeps no I/O of its own: every diagnostic line derives from the
5
+ * existing resolvers, readers and env consts, so the overlay cannot drift from the
6
+ * status text it mirrors. Writes go through the matching saver for the active scope.
7
+ */
8
+ import {
9
+ Container,
10
+ SettingsList,
11
+ Text,
12
+ type SettingItem,
13
+ type SettingsListTheme,
14
+ } from "@earendil-works/pi-tui";
15
+
16
+ import {
17
+ BASH_VIEW_ENV_VAR,
18
+ readBashViewConfig,
19
+ resolveBashViewModeWithSource,
20
+ saveBashViewConfig,
21
+ type BashViewMode,
22
+ } from "../../bash-view-config.js";
23
+ import {
24
+ READ_EXPANSION_ENV_VAR,
25
+ readReadExpansionConfig,
26
+ resolveReadExpansionModeWithSource,
27
+ saveReadExpansionConfig,
28
+ type ReadExpansionMode,
29
+ } from "../../read-expansion-config.js";
30
+ import { resolveBashMultiFileSettingsWithSources } from "../../bash-multifile-config.js";
31
+ import type { ConfigScope } from "../../json-config.js";
32
+ import {
33
+ readLensBridgeConfig,
34
+ resolveEffectiveLensModeWithSource,
35
+ saveLensBridgeConfig,
36
+ LENS_BRIDGE_ENV_VAR,
37
+ type LensBridgeMode,
38
+ } from "./config.js";
39
+ import { saveStoreJanitorConfig } from "../../store-config.js";
40
+ import {
41
+ readStoreJanitorConfig,
42
+ resolveStoreJanitorSettingsWithSources,
43
+ STORE_JANITOR_ENV_VAR,
44
+ } from "../../store-kernel/settings.js";
45
+
46
+ /** WHY a local theme: pi-tui 0.85.1 ships the interface but no factory, so identity styling keeps the rows legible without inventing border art. */
47
+ function getSettingsListTheme(): SettingsListTheme {
48
+ return {
49
+ label: (text: string): string => text,
50
+ value: (text: string): string => text,
51
+ description: (text: string): string => text,
52
+ cursor: "> ",
53
+ hint: (text: string): string => text,
54
+ };
55
+ }
56
+
57
+ function envDisplay(raw: string | undefined): string {
58
+ if (raw === undefined) return "unset";
59
+ const trimmed = raw.trim();
60
+ return trimmed.length > 0 ? trimmed : "unset";
61
+ }
62
+
63
+ function lensLine2(cwd: string): string {
64
+ const effective = resolveEffectiveLensModeWithSource(cwd);
65
+ const config = readLensBridgeConfig(cwd);
66
+ const line =
67
+ `Effective: ${effective.mode} (source: ${effective.source}) ` +
68
+ `\u2022 Global: ${config.global ?? "unset"} \u2022 Env: ${envDisplay(process.env[LENS_BRIDGE_ENV_VAR])}`;
69
+ return effective.source === "env"
70
+ ? `\u26a0\ufe0f Overridden by ${LENS_BRIDGE_ENV_VAR} \u2022 ${line}`
71
+ : line;
72
+ }
73
+
74
+ function bashViewLine2(cwd: string): string {
75
+ const effective = resolveBashViewModeWithSource(cwd);
76
+ const config = readBashViewConfig(cwd);
77
+ const line =
78
+ `Effective: ${effective.value} (source: ${effective.source}) ` +
79
+ `\u2022 Global: ${config.global ?? "unset"} \u2022 Env: ${envDisplay(process.env[BASH_VIEW_ENV_VAR])}`;
80
+ return effective.source === "env"
81
+ ? `\u26a0\ufe0f Overridden by ${BASH_VIEW_ENV_VAR} \u2022 ${line}`
82
+ : line;
83
+ }
84
+
85
+ function readExpansionLine2(cwd: string): string {
86
+ const effective = resolveReadExpansionModeWithSource(cwd);
87
+ const config = readReadExpansionConfig(cwd);
88
+ const line =
89
+ `Effective: ${effective.value} (source: ${effective.source}) ` +
90
+ `\u2022 Global: ${config.global ?? "unset"} \u2022 Env: ${envDisplay(process.env[READ_EXPANSION_ENV_VAR])}`;
91
+ return effective.source === "env"
92
+ ? `\u26a0\ufe0f Overridden by ${READ_EXPANSION_ENV_VAR} \u2022 ${line}`
93
+ : line;
94
+ }
95
+
96
+ /**
97
+ * The multi-file caps, REPORTED ONLY (ADR-0049 M6): the overlay's `SettingsList` is enum-only
98
+ * (ADR-0038), so numeric editing belongs to `/pi-better-edit bash-view multi-file …`. This line still
99
+ * names each cap's rung, because a display that showed the effective numbers without their source
100
+ * would hide which file to edit.
101
+ */
102
+ function multiFileLine(cwd: string): string {
103
+ const caps = resolveBashMultiFileSettingsWithSources(cwd);
104
+ return (
105
+ `Multi-file caps: maxFiles ${caps.maxFiles.value} (source: ${caps.maxFiles.source}) ` +
106
+ `\u2022 maxRows ${caps.maxRows.value} (source: ${caps.maxRows.source})`
107
+ );
108
+ }
109
+
110
+ function storeLine2(cwd: string): string {
111
+ const effective = resolveStoreJanitorSettingsWithSources(cwd);
112
+ const config = readStoreJanitorConfig(cwd);
113
+ const line =
114
+ `Effective: ${effective.mode.value} (source: ${effective.mode.source}) ` +
115
+ `\u2022 Global: ${config.global?.mode ?? "unset"} \u2022 Env: ${envDisplay(process.env[STORE_JANITOR_ENV_VAR])}`;
116
+ return effective.mode.source === "env"
117
+ ? `\u26a0\ufe0f Overridden by ${STORE_JANITOR_ENV_VAR} \u2022 ${line}`
118
+ : line;
119
+ }
120
+
121
+ function targetScopeLine2(scope: ConfigScope): string {
122
+ return `Effective: ${scope} (source: switch) \u2022 Global: n/a \u2022 Env: n/a`;
123
+ }
124
+
125
+ /**
126
+ * The overlay: a title plus the four-row settings list. The list keeps the item
127
+ * array by reference, so refreshes mutate descriptions in place and invalidate.
128
+ */
129
+ export class SettingsOverlay extends Container {
130
+ private readonly cwd: string;
131
+ private readonly done: (result: undefined) => void;
132
+ private scope: ConfigScope = "project";
133
+ private readonly items: SettingItem[];
134
+ private readonly list: SettingsList;
135
+
136
+ constructor(cwd: string, done: (result: undefined) => void) {
137
+ super();
138
+ this.cwd = cwd;
139
+ this.done = done;
140
+ this.items = [
141
+ {
142
+ id: "target-scope",
143
+ label: "Target scope",
144
+ description: `Write scope for the rows below\n${targetScopeLine2(this.scope)}`,
145
+ currentValue: this.scope,
146
+ values: ["project", "global"],
147
+ },
148
+ {
149
+ id: "lens",
150
+ label: "Lens bridge",
151
+ description: `Bridge to pi-lens diagnostics\n${lensLine2(this.cwd)}`,
152
+ currentValue: resolveEffectiveLensModeWithSource(this.cwd).mode,
153
+ values: ["auto", "on", "off"],
154
+ },
155
+ {
156
+ id: "bash-view",
157
+ label: "Bash view",
158
+ description: `Bash view interception switch\n${bashViewLine2(this.cwd)}\n${multiFileLine(this.cwd)}`,
159
+ currentValue: resolveBashViewModeWithSource(this.cwd).value,
160
+ values: ["on", "off"],
161
+ },
162
+ {
163
+ id: "read-expansion",
164
+ label: "Read expansion",
165
+ description: `Read window expansion switch\n${readExpansionLine2(this.cwd)}`,
166
+ currentValue: resolveReadExpansionModeWithSource(this.cwd).value,
167
+ values: ["on", "off"],
168
+ },
169
+ {
170
+ id: "store",
171
+ label: "Store janitor",
172
+ description: `Store janitor sweep switch\n${storeLine2(this.cwd)}`,
173
+ currentValue: resolveStoreJanitorSettingsWithSources(this.cwd).mode.value,
174
+ values: ["on", "off"],
175
+ },
176
+ ];
177
+ this.list = new SettingsList(
178
+ this.items,
179
+ 8,
180
+ getSettingsListTheme(),
181
+ (id, newValue) => {
182
+ this.handleChange(id, newValue);
183
+ },
184
+ () => {
185
+ this.done(undefined);
186
+ },
187
+ );
188
+ this.addChild(new Text("pi-better-edit settings", 1, 0));
189
+ this.addChild(this.list);
190
+ }
191
+
192
+ /** WHY a delegating input: Container owns layout only, so focus keys reach the list. */
193
+ handleInput(data: string): void {
194
+ this.list.handleInput(data);
195
+ }
196
+
197
+ /** Test and routing seam to surface a row's two-line description. */
198
+ selectItem(id: string): void {
199
+ this.list.selectItem(id);
200
+ }
201
+
202
+ /** Active write scope; the Target Scope row cycles it without persisting. */
203
+ currentScope(): ConfigScope {
204
+ return this.scope;
205
+ }
206
+
207
+ private handleChange(id: string, newValue: string): void {
208
+ if (id === "target-scope") {
209
+ this.scope = newValue === "global" ? "global" : "project";
210
+ this.refreshAll();
211
+ return;
212
+ }
213
+ if (id === "lens" && (newValue === "auto" || newValue === "on" || newValue === "off")) {
214
+ const mode: LensBridgeMode = newValue;
215
+ saveLensBridgeConfig(mode, this.scope, this.cwd);
216
+ this.refreshRow("lens");
217
+ return;
218
+ }
219
+ if (id === "bash-view" && (newValue === "on" || newValue === "off")) {
220
+ const mode: BashViewMode = newValue;
221
+ saveBashViewConfig(mode, this.scope, this.cwd);
222
+ this.refreshRow("bash-view");
223
+ return;
224
+ }
225
+ if (id === "read-expansion" && (newValue === "on" || newValue === "off")) {
226
+ const mode: ReadExpansionMode = newValue;
227
+ saveReadExpansionConfig(mode, this.scope, this.cwd);
228
+ this.refreshRow("read-expansion");
229
+ return;
230
+ }
231
+ if (id === "store" && (newValue === "on" || newValue === "off")) {
232
+ saveStoreJanitorConfig({ mode: newValue }, this.scope, this.cwd);
233
+ this.refreshRow("store");
234
+ }
235
+ }
236
+
237
+ private refreshRow(id: string): void {
238
+ const item = this.items.find((candidate) => candidate.id === id);
239
+ if (item === undefined) return;
240
+ if (id === "lens") {
241
+ const effective = resolveEffectiveLensModeWithSource(this.cwd);
242
+ this.list.updateValue(id, effective.mode);
243
+ item.description = `Bridge to pi-lens diagnostics\n${lensLine2(this.cwd)}`;
244
+ } else if (id === "bash-view") {
245
+ const effective = resolveBashViewModeWithSource(this.cwd);
246
+ this.list.updateValue(id, effective.value);
247
+ item.description = `Bash view interception switch\n${bashViewLine2(this.cwd)}\n${multiFileLine(this.cwd)}`;
248
+ } else if (id === "read-expansion") {
249
+ const effective = resolveReadExpansionModeWithSource(this.cwd);
250
+ this.list.updateValue(id, effective.value);
251
+ item.description = `Read window expansion switch\n${readExpansionLine2(this.cwd)}`;
252
+ } else if (id === "store") {
253
+ const effective = resolveStoreJanitorSettingsWithSources(this.cwd);
254
+ this.list.updateValue(id, effective.mode.value);
255
+ item.description = `Store janitor sweep switch\n${storeLine2(this.cwd)}`;
256
+ }
257
+ this.list.invalidate();
258
+ this.invalidate();
259
+ }
260
+
261
+ private refreshAll(): void {
262
+ const scopeItem = this.items.find((candidate) => candidate.id === "target-scope");
263
+ if (scopeItem !== undefined) {
264
+ this.list.updateValue("target-scope", this.scope);
265
+ scopeItem.description = `Write scope for the rows below\n${targetScopeLine2(this.scope)}`;
266
+ }
267
+ this.refreshRow("lens");
268
+ this.refreshRow("bash-view");
269
+ this.refreshRow("read-expansion");
270
+ this.refreshRow("store");
271
+ }
272
+ }