@pleri/olam-cli 0.1.109 → 0.1.111
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.
- package/dist/commands/completion.d.ts +30 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +53 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +10 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/setup.d.ts +71 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +344 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/image-digests.json +1 -1
- package/dist/index.js +549 -121
- package/dist/index.js.map +1 -1
- package/dist/lib/completion-generator.d.ts +107 -0
- package/dist/lib/completion-generator.d.ts.map +1 -0
- package/dist/lib/completion-generator.js +226 -0
- package/dist/lib/completion-generator.js.map +1 -0
- package/dist/lib/shell-rc.d.ts +90 -0
- package/dist/lib/shell-rc.d.ts.map +1 -0
- package/dist/lib/shell-rc.js +91 -0
- package/dist/lib/shell-rc.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* completion-generator.ts — Commander command-tree → POSIX shell completion script.
|
|
3
|
+
*
|
|
4
|
+
* Phase B1 of olam-operator-onboarding-parity (plan
|
|
5
|
+
* ~/.claude/plans/olam-operator-onboarding-parity.md).
|
|
6
|
+
*
|
|
7
|
+
* Decision 4 (plan): runtime-emit from the commander tree, NOT static
|
|
8
|
+
* fixtures in the tarball. One source of truth: new CLI commands auto-
|
|
9
|
+
* included in the next `olam completion <shell>` invocation.
|
|
10
|
+
*
|
|
11
|
+
* Supported shells: zsh + bash. Other shells throw `UnsupportedShellError`.
|
|
12
|
+
*
|
|
13
|
+
* Nesting depth: TWO levels of subcommand dispatch (top-level + one
|
|
14
|
+
* nested). Covers ~95% of olam's tree (`olam kg build`, `olam auth login`,
|
|
15
|
+
* `olam workspace list`, etc.). Deeper nesting (`olam world snapshot
|
|
16
|
+
* restore`) falls through gracefully — top + one level still complete.
|
|
17
|
+
*
|
|
18
|
+
* Escaping:
|
|
19
|
+
* - zsh: descriptions inside `name:description` syntax need `:` escaped
|
|
20
|
+
* (since `:` is the field separator) and `'` doubled (since strings
|
|
21
|
+
* are single-quoted).
|
|
22
|
+
* - bash: descriptions inside double-quoted strings need `"`, `\`, `$`,
|
|
23
|
+
* and backtick escaped.
|
|
24
|
+
*
|
|
25
|
+
* Hidden commands (commander's private `_hidden` flag) are excluded:
|
|
26
|
+
* - `olam crystallize` is registered with `{ hidden: !isPleriConfigured() }`
|
|
27
|
+
* for fresh-install operators.
|
|
28
|
+
*
|
|
29
|
+
* Determinism: commander preserves command-registration order in
|
|
30
|
+
* `Command.commands`. The walk is a pure-functional map → the output is
|
|
31
|
+
* a deterministic function of the program tree. Snapshot tests rely on
|
|
32
|
+
* this.
|
|
33
|
+
*/
|
|
34
|
+
import type { Command } from 'commander';
|
|
35
|
+
export interface CommandNode {
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly description: string;
|
|
38
|
+
readonly subcommands: ReadonlyArray<CommandNode>;
|
|
39
|
+
}
|
|
40
|
+
export declare class UnsupportedShellError extends Error {
|
|
41
|
+
constructor(shell: string);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Walk a commander `Command` recursively, extracting (name, description,
|
|
45
|
+
* subcommands). Hidden commands (commander's private `_hidden` flag,
|
|
46
|
+
* set via `.command(..., { hidden: true })`) are excluded.
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractCommandTree(program: Command): CommandNode;
|
|
49
|
+
/**
|
|
50
|
+
* Emit a zsh completion script.
|
|
51
|
+
*
|
|
52
|
+
* Format:
|
|
53
|
+
* #compdef <program>
|
|
54
|
+
*
|
|
55
|
+
* _<program>() {
|
|
56
|
+
* local -a l1_subs
|
|
57
|
+
* l1_subs=(
|
|
58
|
+
* '<sub>:<description>'
|
|
59
|
+
* ...
|
|
60
|
+
* )
|
|
61
|
+
* if (( CURRENT == 2 )); then
|
|
62
|
+
* _describe '<program> command' l1_subs
|
|
63
|
+
* return
|
|
64
|
+
* fi
|
|
65
|
+
* case $words[2] in
|
|
66
|
+
* <sub-with-children>)
|
|
67
|
+
* if (( CURRENT == 3 )); then
|
|
68
|
+
* local -a l2_subs
|
|
69
|
+
* l2_subs=(...)
|
|
70
|
+
* _describe '<sub> subcommand' l2_subs
|
|
71
|
+
* fi
|
|
72
|
+
* ;;
|
|
73
|
+
* ...
|
|
74
|
+
* esac
|
|
75
|
+
* }
|
|
76
|
+
*
|
|
77
|
+
* _<program> "$@"
|
|
78
|
+
*/
|
|
79
|
+
export declare function generateZshCompletion(tree: CommandNode): string;
|
|
80
|
+
/**
|
|
81
|
+
* Emit a bash completion script.
|
|
82
|
+
*
|
|
83
|
+
* Format:
|
|
84
|
+
* _<program>_completions() {
|
|
85
|
+
* local cur="${COMP_WORDS[COMP_CWORD]}"
|
|
86
|
+
* local cword=$COMP_CWORD
|
|
87
|
+
* if [[ $cword -eq 1 ]]; then
|
|
88
|
+
* COMPREPLY=( $(compgen -W "<sub1> <sub2> ..." -- "$cur") )
|
|
89
|
+
* return
|
|
90
|
+
* fi
|
|
91
|
+
* case "${COMP_WORDS[1]}" in
|
|
92
|
+
* <sub-with-children>)
|
|
93
|
+
* if [[ $cword -eq 2 ]]; then
|
|
94
|
+
* COMPREPLY=( $(compgen -W "<l2-1> <l2-2> ..." -- "$cur") )
|
|
95
|
+
* fi
|
|
96
|
+
* ;;
|
|
97
|
+
* esac
|
|
98
|
+
* }
|
|
99
|
+
* complete -F _<program>_completions <program>
|
|
100
|
+
*/
|
|
101
|
+
export declare function generateBashCompletion(tree: CommandNode): string;
|
|
102
|
+
/**
|
|
103
|
+
* Top-level API: walk the commander program + emit a completion script
|
|
104
|
+
* for the requested shell.
|
|
105
|
+
*/
|
|
106
|
+
export declare function generateCompletion(program: Command, shell: 'zsh' | 'bash'): string;
|
|
107
|
+
//# sourceMappingURL=completion-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion-generator.d.ts","sourceRoot":"","sources":["../../src/lib/completion-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;CAClD;AAED,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,KAAK,EAAE,MAAM;CAI1B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,WAAW,CAQhE;AA0CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CA0C/D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAgChE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAKlF"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* completion-generator.ts — Commander command-tree → POSIX shell completion script.
|
|
3
|
+
*
|
|
4
|
+
* Phase B1 of olam-operator-onboarding-parity (plan
|
|
5
|
+
* ~/.claude/plans/olam-operator-onboarding-parity.md).
|
|
6
|
+
*
|
|
7
|
+
* Decision 4 (plan): runtime-emit from the commander tree, NOT static
|
|
8
|
+
* fixtures in the tarball. One source of truth: new CLI commands auto-
|
|
9
|
+
* included in the next `olam completion <shell>` invocation.
|
|
10
|
+
*
|
|
11
|
+
* Supported shells: zsh + bash. Other shells throw `UnsupportedShellError`.
|
|
12
|
+
*
|
|
13
|
+
* Nesting depth: TWO levels of subcommand dispatch (top-level + one
|
|
14
|
+
* nested). Covers ~95% of olam's tree (`olam kg build`, `olam auth login`,
|
|
15
|
+
* `olam workspace list`, etc.). Deeper nesting (`olam world snapshot
|
|
16
|
+
* restore`) falls through gracefully — top + one level still complete.
|
|
17
|
+
*
|
|
18
|
+
* Escaping:
|
|
19
|
+
* - zsh: descriptions inside `name:description` syntax need `:` escaped
|
|
20
|
+
* (since `:` is the field separator) and `'` doubled (since strings
|
|
21
|
+
* are single-quoted).
|
|
22
|
+
* - bash: descriptions inside double-quoted strings need `"`, `\`, `$`,
|
|
23
|
+
* and backtick escaped.
|
|
24
|
+
*
|
|
25
|
+
* Hidden commands (commander's private `_hidden` flag) are excluded:
|
|
26
|
+
* - `olam crystallize` is registered with `{ hidden: !isPleriConfigured() }`
|
|
27
|
+
* for fresh-install operators.
|
|
28
|
+
*
|
|
29
|
+
* Determinism: commander preserves command-registration order in
|
|
30
|
+
* `Command.commands`. The walk is a pure-functional map → the output is
|
|
31
|
+
* a deterministic function of the program tree. Snapshot tests rely on
|
|
32
|
+
* this.
|
|
33
|
+
*/
|
|
34
|
+
export class UnsupportedShellError extends Error {
|
|
35
|
+
constructor(shell) {
|
|
36
|
+
super(`Unsupported shell: ${shell}. Supported shells: zsh, bash.`);
|
|
37
|
+
this.name = 'UnsupportedShellError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Walk a commander `Command` recursively, extracting (name, description,
|
|
42
|
+
* subcommands). Hidden commands (commander's private `_hidden` flag,
|
|
43
|
+
* set via `.command(..., { hidden: true })`) are excluded.
|
|
44
|
+
*/
|
|
45
|
+
export function extractCommandTree(program) {
|
|
46
|
+
return {
|
|
47
|
+
name: program.name(),
|
|
48
|
+
description: program.description() ?? '',
|
|
49
|
+
subcommands: program.commands
|
|
50
|
+
.filter((c) => !isHidden(c))
|
|
51
|
+
.map((c) => extractCommandTree(c)),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function isHidden(cmd) {
|
|
55
|
+
// Commander stores hidden flag privately. Access defensively in case
|
|
56
|
+
// the field name changes across versions.
|
|
57
|
+
const c = cmd;
|
|
58
|
+
return c._hidden === true;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Escape a description string for zsh's `name:description` completion
|
|
62
|
+
* syntax. Three transformations:
|
|
63
|
+
* 1. Collapse newlines + carriage returns to single spaces (multi-line
|
|
64
|
+
* descriptions break single-quoted `'name:description'` syntax —
|
|
65
|
+
* a literal `\n` in the source string forces the next line to start
|
|
66
|
+
* a new entry without a leading quote → zsh parse error → operator's
|
|
67
|
+
* shell rc fails to load; audit finding B-ADV-3).
|
|
68
|
+
* 2. Double single quotes (single-quoted-string termination).
|
|
69
|
+
* 3. Backslash-escape colons (field separator in `name:description`).
|
|
70
|
+
*/
|
|
71
|
+
function escapeZsh(s) {
|
|
72
|
+
return s
|
|
73
|
+
.replace(/[\r\n]+/g, ' ')
|
|
74
|
+
.replace(/'/g, "''")
|
|
75
|
+
.replace(/:/g, '\\:');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Escape a string for bash double-quoted contexts. Three transformations:
|
|
79
|
+
* 1. Collapse newlines + carriage returns to single spaces (bash
|
|
80
|
+
* double-quoted strings tolerate `\n` but `compgen -W` interprets
|
|
81
|
+
* whitespace as word boundaries — a newline silently splits the
|
|
82
|
+
* word list; audit finding B-ADV-3).
|
|
83
|
+
* 2. Backslash-escape `"`, `\`, `$`, backtick (the four chars bash
|
|
84
|
+
* interprets inside double quotes).
|
|
85
|
+
*/
|
|
86
|
+
function escapeBash(s) {
|
|
87
|
+
return s
|
|
88
|
+
.replace(/[\r\n]+/g, ' ')
|
|
89
|
+
.replace(/(["\\$`])/g, '\\$1');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Emit a zsh completion script.
|
|
93
|
+
*
|
|
94
|
+
* Format:
|
|
95
|
+
* #compdef <program>
|
|
96
|
+
*
|
|
97
|
+
* _<program>() {
|
|
98
|
+
* local -a l1_subs
|
|
99
|
+
* l1_subs=(
|
|
100
|
+
* '<sub>:<description>'
|
|
101
|
+
* ...
|
|
102
|
+
* )
|
|
103
|
+
* if (( CURRENT == 2 )); then
|
|
104
|
+
* _describe '<program> command' l1_subs
|
|
105
|
+
* return
|
|
106
|
+
* fi
|
|
107
|
+
* case $words[2] in
|
|
108
|
+
* <sub-with-children>)
|
|
109
|
+
* if (( CURRENT == 3 )); then
|
|
110
|
+
* local -a l2_subs
|
|
111
|
+
* l2_subs=(...)
|
|
112
|
+
* _describe '<sub> subcommand' l2_subs
|
|
113
|
+
* fi
|
|
114
|
+
* ;;
|
|
115
|
+
* ...
|
|
116
|
+
* esac
|
|
117
|
+
* }
|
|
118
|
+
*
|
|
119
|
+
* _<program> "$@"
|
|
120
|
+
*/
|
|
121
|
+
export function generateZshCompletion(tree) {
|
|
122
|
+
const lines = [];
|
|
123
|
+
lines.push(`#compdef ${tree.name}`);
|
|
124
|
+
lines.push('');
|
|
125
|
+
lines.push(`_${tree.name}() {`);
|
|
126
|
+
lines.push(' local -a l1_subs');
|
|
127
|
+
lines.push(' l1_subs=(');
|
|
128
|
+
for (const sub of tree.subcommands) {
|
|
129
|
+
lines.push(` '${sub.name}:${escapeZsh(sub.description)}'`);
|
|
130
|
+
}
|
|
131
|
+
lines.push(' )');
|
|
132
|
+
lines.push('');
|
|
133
|
+
lines.push(' if (( CURRENT == 2 )); then');
|
|
134
|
+
lines.push(` _describe '${tree.name} command' l1_subs`);
|
|
135
|
+
lines.push(' return');
|
|
136
|
+
lines.push(' fi');
|
|
137
|
+
lines.push('');
|
|
138
|
+
const subsWithChildren = tree.subcommands.filter((s) => s.subcommands.length > 0);
|
|
139
|
+
if (subsWithChildren.length > 0) {
|
|
140
|
+
lines.push(' case $words[2] in');
|
|
141
|
+
for (const sub of subsWithChildren) {
|
|
142
|
+
lines.push(` ${sub.name})`);
|
|
143
|
+
lines.push(' if (( CURRENT == 3 )); then');
|
|
144
|
+
lines.push(' local -a l2_subs');
|
|
145
|
+
lines.push(' l2_subs=(');
|
|
146
|
+
for (const s2 of sub.subcommands) {
|
|
147
|
+
lines.push(` '${s2.name}:${escapeZsh(s2.description)}'`);
|
|
148
|
+
}
|
|
149
|
+
lines.push(' )');
|
|
150
|
+
lines.push(` _describe '${sub.name} subcommand' l2_subs`);
|
|
151
|
+
lines.push(' fi');
|
|
152
|
+
lines.push(' ;;');
|
|
153
|
+
}
|
|
154
|
+
lines.push(' esac');
|
|
155
|
+
}
|
|
156
|
+
lines.push('}');
|
|
157
|
+
lines.push('');
|
|
158
|
+
lines.push(`_${tree.name} "$@"`);
|
|
159
|
+
lines.push('');
|
|
160
|
+
return lines.join('\n');
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Emit a bash completion script.
|
|
164
|
+
*
|
|
165
|
+
* Format:
|
|
166
|
+
* _<program>_completions() {
|
|
167
|
+
* local cur="${COMP_WORDS[COMP_CWORD]}"
|
|
168
|
+
* local cword=$COMP_CWORD
|
|
169
|
+
* if [[ $cword -eq 1 ]]; then
|
|
170
|
+
* COMPREPLY=( $(compgen -W "<sub1> <sub2> ..." -- "$cur") )
|
|
171
|
+
* return
|
|
172
|
+
* fi
|
|
173
|
+
* case "${COMP_WORDS[1]}" in
|
|
174
|
+
* <sub-with-children>)
|
|
175
|
+
* if [[ $cword -eq 2 ]]; then
|
|
176
|
+
* COMPREPLY=( $(compgen -W "<l2-1> <l2-2> ..." -- "$cur") )
|
|
177
|
+
* fi
|
|
178
|
+
* ;;
|
|
179
|
+
* esac
|
|
180
|
+
* }
|
|
181
|
+
* complete -F _<program>_completions <program>
|
|
182
|
+
*/
|
|
183
|
+
export function generateBashCompletion(tree) {
|
|
184
|
+
const lines = [];
|
|
185
|
+
lines.push(`_${tree.name}_completions() {`);
|
|
186
|
+
lines.push(' local cur="${COMP_WORDS[COMP_CWORD]}"');
|
|
187
|
+
lines.push(' local cword=$COMP_CWORD');
|
|
188
|
+
lines.push('');
|
|
189
|
+
lines.push(' if [[ $cword -eq 1 ]]; then');
|
|
190
|
+
const l1Names = tree.subcommands.map((s) => s.name).join(' ');
|
|
191
|
+
lines.push(` COMPREPLY=( $(compgen -W "${escapeBash(l1Names)}" -- "$cur") )`);
|
|
192
|
+
lines.push(' return');
|
|
193
|
+
lines.push(' fi');
|
|
194
|
+
lines.push('');
|
|
195
|
+
const subsWithChildren = tree.subcommands.filter((s) => s.subcommands.length > 0);
|
|
196
|
+
if (subsWithChildren.length > 0) {
|
|
197
|
+
lines.push(' case "${COMP_WORDS[1]}" in');
|
|
198
|
+
for (const sub of subsWithChildren) {
|
|
199
|
+
lines.push(` ${sub.name})`);
|
|
200
|
+
lines.push(' if [[ $cword -eq 2 ]]; then');
|
|
201
|
+
const l2Names = sub.subcommands.map((s) => s.name).join(' ');
|
|
202
|
+
lines.push(` COMPREPLY=( $(compgen -W "${escapeBash(l2Names)}" -- "$cur") )`);
|
|
203
|
+
lines.push(' fi');
|
|
204
|
+
lines.push(' ;;');
|
|
205
|
+
}
|
|
206
|
+
lines.push(' esac');
|
|
207
|
+
}
|
|
208
|
+
lines.push('}');
|
|
209
|
+
lines.push('');
|
|
210
|
+
lines.push(`complete -F _${tree.name}_completions ${tree.name}`);
|
|
211
|
+
lines.push('');
|
|
212
|
+
return lines.join('\n');
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Top-level API: walk the commander program + emit a completion script
|
|
216
|
+
* for the requested shell.
|
|
217
|
+
*/
|
|
218
|
+
export function generateCompletion(program, shell) {
|
|
219
|
+
const tree = extractCommandTree(program);
|
|
220
|
+
if (shell === 'zsh')
|
|
221
|
+
return generateZshCompletion(tree);
|
|
222
|
+
if (shell === 'bash')
|
|
223
|
+
return generateBashCompletion(tree);
|
|
224
|
+
throw new UnsupportedShellError(shell);
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=completion-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion-generator.js","sourceRoot":"","sources":["../../src/lib/completion-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shell-rc.ts — Idempotent shell-rc file append + timestamped backup.
|
|
3
|
+
*
|
|
4
|
+
* Phase C1 of olam-operator-onboarding-parity (plan
|
|
5
|
+
* ~/.claude/plans/olam-operator-onboarding-parity.md).
|
|
6
|
+
*
|
|
7
|
+
* Phase C's `olam setup` wizard offers to add
|
|
8
|
+
* eval "$(olam completion zsh)"
|
|
9
|
+
* to the operator's `~/.zshrc` / `~/.bashrc`. This module is the safety
|
|
10
|
+
* layer that:
|
|
11
|
+
*
|
|
12
|
+
* 1. Grep-checks a marker substring before writing — re-running the
|
|
13
|
+
* wizard is a no-op (no duplicate eval lines, no n-times appended).
|
|
14
|
+
* 2. Backs the file up to `<rcPath>.olam-bak.<ISO-timestamp>` before
|
|
15
|
+
* ANY mutation — recoverable if the operator decides they didn't
|
|
16
|
+
* want the line.
|
|
17
|
+
* 3. Writes atomically via tmp-file + rename — `kill -9` mid-write
|
|
18
|
+
* can't leave a half-written rc file.
|
|
19
|
+
*
|
|
20
|
+
* Decision 6 (plan): mirrors adb's `phase_mise_activation` pattern; the
|
|
21
|
+
* idempotency + backup pair is the operator-UX floor.
|
|
22
|
+
*
|
|
23
|
+
* Risk T4 mitigation (per plan + design doc): the grep-for-marker check
|
|
24
|
+
* uses `String.includes` not a per-line scan — a marker can match across
|
|
25
|
+
* whitespace if needed. The backup runs unconditionally before the
|
|
26
|
+
* append; corrupted rc states are recoverable via `mv <backup> <rcPath>`.
|
|
27
|
+
*/
|
|
28
|
+
export type AppendStatus = 'appended' | 'already-present' | 'no-rc-file';
|
|
29
|
+
export interface AppendResult {
|
|
30
|
+
readonly status: AppendStatus;
|
|
31
|
+
/** Path to the timestamped backup file. `null` when `status !== 'appended'`. */
|
|
32
|
+
readonly backupPath: string | null;
|
|
33
|
+
}
|
|
34
|
+
export interface AppendOptions {
|
|
35
|
+
/** Absolute path to the operator's shell rc (e.g. `/Users/me/.zshrc`). */
|
|
36
|
+
readonly rcPath: string;
|
|
37
|
+
/**
|
|
38
|
+
* Substring searched for in the rc to detect "already present."
|
|
39
|
+
* Convention: a short stable token (e.g. `olam completion`) — distinct
|
|
40
|
+
* enough to avoid collisions with unrelated mentions, short enough that
|
|
41
|
+
* the operator can edit the exact line shape without breaking
|
|
42
|
+
* idempotency.
|
|
43
|
+
*/
|
|
44
|
+
readonly marker: string;
|
|
45
|
+
/**
|
|
46
|
+
* The exact line to append. The function adds a trailing newline if
|
|
47
|
+
* not already present + ensures the previous line of the rc file ends
|
|
48
|
+
* with a newline (so the appended line isn't concatenated onto the
|
|
49
|
+
* previous line on rcs that don't end with `\n`).
|
|
50
|
+
*/
|
|
51
|
+
readonly contentLine: string;
|
|
52
|
+
/**
|
|
53
|
+
* Optional clock override for deterministic test timestamps. Defaults
|
|
54
|
+
* to `() => new Date()`.
|
|
55
|
+
*/
|
|
56
|
+
readonly clock?: () => Date;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Append `contentLine` to `rcPath` exactly once.
|
|
60
|
+
*
|
|
61
|
+
* - rcPath doesn't exist → returns `{status: 'no-rc-file', backupPath: null}`
|
|
62
|
+
* - rcPath content already includes the marker substring
|
|
63
|
+
* → returns `{status: 'already-present', backupPath: null}`
|
|
64
|
+
* - otherwise: backup + append
|
|
65
|
+
* → returns `{status: 'appended', backupPath: <ts-path>}`
|
|
66
|
+
*
|
|
67
|
+
* The write is atomic: content is staged to `<rcPath>.olam-tmp.<pid>`
|
|
68
|
+
* and then `renameSync`'d to `rcPath`. POSIX rename is atomic within a
|
|
69
|
+
* filesystem; a `kill -9` mid-call leaves either the backup-only state
|
|
70
|
+
* (no rc change) or the renamed-final state (atomic apply), never a
|
|
71
|
+
* half-written rc.
|
|
72
|
+
*
|
|
73
|
+
* The backup path uses a filesystem-safe ISO timestamp with `:` + `.`
|
|
74
|
+
* replaced by `-` (avoids colons in filenames on Windows for cross-
|
|
75
|
+
* platform consumers; not strictly required on macOS / Linux but
|
|
76
|
+
* cosmetically nicer).
|
|
77
|
+
*/
|
|
78
|
+
export declare function appendIdempotent(opts: AppendOptions): AppendResult;
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the operator's shell rc path based on `$SHELL`.
|
|
81
|
+
*
|
|
82
|
+
* /bin/zsh, /usr/bin/zsh, /opt/homebrew/bin/zsh → ~/.zshrc
|
|
83
|
+
* /bin/bash, /usr/bin/bash → ~/.bashrc
|
|
84
|
+
* anything else → null (caller must surface)
|
|
85
|
+
*
|
|
86
|
+
* The caller decides what to do on null — typically Phase C surfaces a
|
|
87
|
+
* remedy message naming the supported shells.
|
|
88
|
+
*/
|
|
89
|
+
export declare function resolveShellRc(home: string, shellEnv: string | undefined): string | null;
|
|
90
|
+
//# sourceMappingURL=shell-rc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-rc.d.ts","sourceRoot":"","sources":["../../src/lib/shell-rc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,iBAAiB,GAAG,YAAY,CAAC;AAEzE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CA0BlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAMxF"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shell-rc.ts — Idempotent shell-rc file append + timestamped backup.
|
|
3
|
+
*
|
|
4
|
+
* Phase C1 of olam-operator-onboarding-parity (plan
|
|
5
|
+
* ~/.claude/plans/olam-operator-onboarding-parity.md).
|
|
6
|
+
*
|
|
7
|
+
* Phase C's `olam setup` wizard offers to add
|
|
8
|
+
* eval "$(olam completion zsh)"
|
|
9
|
+
* to the operator's `~/.zshrc` / `~/.bashrc`. This module is the safety
|
|
10
|
+
* layer that:
|
|
11
|
+
*
|
|
12
|
+
* 1. Grep-checks a marker substring before writing — re-running the
|
|
13
|
+
* wizard is a no-op (no duplicate eval lines, no n-times appended).
|
|
14
|
+
* 2. Backs the file up to `<rcPath>.olam-bak.<ISO-timestamp>` before
|
|
15
|
+
* ANY mutation — recoverable if the operator decides they didn't
|
|
16
|
+
* want the line.
|
|
17
|
+
* 3. Writes atomically via tmp-file + rename — `kill -9` mid-write
|
|
18
|
+
* can't leave a half-written rc file.
|
|
19
|
+
*
|
|
20
|
+
* Decision 6 (plan): mirrors adb's `phase_mise_activation` pattern; the
|
|
21
|
+
* idempotency + backup pair is the operator-UX floor.
|
|
22
|
+
*
|
|
23
|
+
* Risk T4 mitigation (per plan + design doc): the grep-for-marker check
|
|
24
|
+
* uses `String.includes` not a per-line scan — a marker can match across
|
|
25
|
+
* whitespace if needed. The backup runs unconditionally before the
|
|
26
|
+
* append; corrupted rc states are recoverable via `mv <backup> <rcPath>`.
|
|
27
|
+
*/
|
|
28
|
+
import { copyFileSync, existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
29
|
+
import path from 'node:path';
|
|
30
|
+
/**
|
|
31
|
+
* Append `contentLine` to `rcPath` exactly once.
|
|
32
|
+
*
|
|
33
|
+
* - rcPath doesn't exist → returns `{status: 'no-rc-file', backupPath: null}`
|
|
34
|
+
* - rcPath content already includes the marker substring
|
|
35
|
+
* → returns `{status: 'already-present', backupPath: null}`
|
|
36
|
+
* - otherwise: backup + append
|
|
37
|
+
* → returns `{status: 'appended', backupPath: <ts-path>}`
|
|
38
|
+
*
|
|
39
|
+
* The write is atomic: content is staged to `<rcPath>.olam-tmp.<pid>`
|
|
40
|
+
* and then `renameSync`'d to `rcPath`. POSIX rename is atomic within a
|
|
41
|
+
* filesystem; a `kill -9` mid-call leaves either the backup-only state
|
|
42
|
+
* (no rc change) or the renamed-final state (atomic apply), never a
|
|
43
|
+
* half-written rc.
|
|
44
|
+
*
|
|
45
|
+
* The backup path uses a filesystem-safe ISO timestamp with `:` + `.`
|
|
46
|
+
* replaced by `-` (avoids colons in filenames on Windows for cross-
|
|
47
|
+
* platform consumers; not strictly required on macOS / Linux but
|
|
48
|
+
* cosmetically nicer).
|
|
49
|
+
*/
|
|
50
|
+
export function appendIdempotent(opts) {
|
|
51
|
+
const { rcPath, marker, contentLine, clock = () => new Date() } = opts;
|
|
52
|
+
if (!existsSync(rcPath)) {
|
|
53
|
+
return { status: 'no-rc-file', backupPath: null };
|
|
54
|
+
}
|
|
55
|
+
const content = readFileSync(rcPath, 'utf-8');
|
|
56
|
+
if (content.includes(marker)) {
|
|
57
|
+
return { status: 'already-present', backupPath: null };
|
|
58
|
+
}
|
|
59
|
+
const timestamp = clock().toISOString().replace(/[:.]/g, '-');
|
|
60
|
+
const backupPath = `${rcPath}.olam-bak.${timestamp}`;
|
|
61
|
+
copyFileSync(rcPath, backupPath);
|
|
62
|
+
const separator = content.length === 0 || content.endsWith('\n') ? '' : '\n';
|
|
63
|
+
const trailing = contentLine.endsWith('\n') ? '' : '\n';
|
|
64
|
+
const nextContent = `${content}${separator}${contentLine}${trailing}`;
|
|
65
|
+
// Atomic write: tmp + rename. POSIX rename is atomic within filesystem.
|
|
66
|
+
const tmpPath = `${rcPath}.olam-tmp.${process.pid}`;
|
|
67
|
+
writeFileSync(tmpPath, nextContent, { encoding: 'utf-8' });
|
|
68
|
+
renameSync(tmpPath, rcPath);
|
|
69
|
+
return { status: 'appended', backupPath };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Resolve the operator's shell rc path based on `$SHELL`.
|
|
73
|
+
*
|
|
74
|
+
* /bin/zsh, /usr/bin/zsh, /opt/homebrew/bin/zsh → ~/.zshrc
|
|
75
|
+
* /bin/bash, /usr/bin/bash → ~/.bashrc
|
|
76
|
+
* anything else → null (caller must surface)
|
|
77
|
+
*
|
|
78
|
+
* The caller decides what to do on null — typically Phase C surfaces a
|
|
79
|
+
* remedy message naming the supported shells.
|
|
80
|
+
*/
|
|
81
|
+
export function resolveShellRc(home, shellEnv) {
|
|
82
|
+
if (!shellEnv)
|
|
83
|
+
return null;
|
|
84
|
+
const basename = path.basename(shellEnv);
|
|
85
|
+
if (basename === 'zsh')
|
|
86
|
+
return path.join(home, '.zshrc');
|
|
87
|
+
if (basename === 'bash')
|
|
88
|
+
return path.join(home, '.bashrc');
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=shell-rc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-rc.js","sourceRoot":"","sources":["../../src/lib/shell-rc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC5F,OAAO,IAAI,MAAM,WAAW,CAAC;AAmC7B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAmB;IAClD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC;IAEvE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IACzD,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC9D,MAAM,UAAU,GAAG,GAAG,MAAM,aAAa,SAAS,EAAE,CAAC;IACrD,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAEjC,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACxD,MAAM,WAAW,GAAG,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,EAAE,CAAC;IAEtE,wEAAwE;IACxE,MAAM,OAAO,GAAG,GAAG,MAAM,aAAa,OAAO,CAAC,GAAG,EAAE,CAAC;IACpD,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3D,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE5B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AAC5C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,QAA4B;IACvE,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACzD,IAAI,QAAQ,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC;AACd,CAAC"}
|