fauxnix-cli 0.9.3 → 0.12.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.
package/dist/registry.js CHANGED
@@ -154,6 +154,92 @@ export function parseWords(args, shortValues = [], longValues = []) {
154
154
  return { flags, longs, values, missingValue, operandWords };
155
155
  }
156
156
  const specs = new Map();
157
+ /**
158
+ * C-5's curated agent-daily command set. Keep this explicit: a raw count of
159
+ * CommandSpecs is not enough to prove that the commands agents use every day
160
+ * are the ones protected by fail-loud option validation.
161
+ */
162
+ export const AGENT_DAILY_60 = [
163
+ 'basename',
164
+ 'cat',
165
+ 'cd',
166
+ 'chmod',
167
+ 'chown',
168
+ 'clear',
169
+ 'command',
170
+ 'cp',
171
+ 'cut',
172
+ 'date',
173
+ 'df',
174
+ 'diff',
175
+ 'dirname',
176
+ 'du',
177
+ 'echo',
178
+ 'env',
179
+ 'export',
180
+ 'file',
181
+ 'free',
182
+ 'grep',
183
+ 'groups',
184
+ 'gunzip',
185
+ 'gzip',
186
+ 'head',
187
+ 'hostname',
188
+ 'id',
189
+ 'll',
190
+ 'ln',
191
+ 'ls',
192
+ 'mkdir',
193
+ 'mktemp',
194
+ 'mv',
195
+ 'nproc',
196
+ 'printenv',
197
+ 'printf',
198
+ 'ps',
199
+ 'pwd',
200
+ 'readlink',
201
+ 'realpath',
202
+ 'rm',
203
+ 'rmdir',
204
+ 'sleep',
205
+ 'sort',
206
+ 'stat',
207
+ 'tail',
208
+ 'tee',
209
+ 'timeout',
210
+ 'touch',
211
+ 'tr',
212
+ 'type',
213
+ 'uname',
214
+ 'uniq',
215
+ 'unset',
216
+ 'unzip',
217
+ 'uptime',
218
+ 'wc',
219
+ 'which',
220
+ 'whoami',
221
+ 'zcat',
222
+ 'zip',
223
+ ];
224
+ /** Commands deliberately kept outside generic CommandSpec option walking. */
225
+ export const COMMAND_SPEC_EXCLUSIONS = [
226
+ {
227
+ names: ['find'],
228
+ reason: 'option-looking predicates are parsed by the find expression compiler; generic short-option bundling would misread -name',
229
+ },
230
+ {
231
+ names: ['sed', 'awk'],
232
+ reason: 'program text and option grammar require command-specific parsing; any remaining unchecked options must be fixed there rather than treated as generic flags',
233
+ },
234
+ {
235
+ names: ['egrep'],
236
+ reason: 'semantic alias injects grep -E through its own handler; it must not be wrapped as an independent generic option parser',
237
+ },
238
+ {
239
+ names: ['tar'],
240
+ reason: 'argv is passed to Windows bsdtar; rejecting unlisted GNU/bsdtar options before native dispatch would reduce compatibility',
241
+ },
242
+ ];
157
243
  /** Register a spec'd command. Unknown/unsupported options become GNU-style usage errors. */
158
244
  export function registerSpec(spec) {
159
245
  for (const name of spec.names) {
@@ -188,11 +274,30 @@ export function registeredSpecs() {
188
274
  }
189
275
  /** Markdown dump of every CommandSpec — source for docs/command-specs.md. */
190
276
  export function specsMarkdown() {
277
+ const dailySpecd = AGENT_DAILY_60.filter((name) => lookupSpec(name));
278
+ const dailyMissing = AGENT_DAILY_60.filter((name) => !lookupSpec(name));
191
279
  const lines = [
192
280
  '# Command specs',
193
281
  '',
194
282
  'Generated from `CommandSpec`. Unlisted commands still use unchecked `parseWords` (unknown flags ignored). Spec\'d commands fail loud on unknown or unsupported options.',
195
283
  '',
284
+ '## Agent-daily 60 (C-5)',
285
+ '',
286
+ 'Curated command names: ' + AGENT_DAILY_60.map((name) => '`' + name + '`').join(', '),
287
+ '',
288
+ 'Coverage: **' + dailySpecd.length + ' / ' + AGENT_DAILY_60.length + ' spec\'d**.',
289
+ '',
290
+ ...(dailyMissing.length
291
+ ? ['Missing specs: ' + dailyMissing.map((name) => '`' + name + '`').join(', '), '']
292
+ : []),
293
+ '## Intentional CommandSpec exclusions',
294
+ '',
295
+ 'These commands stay outside the generic option walker by design. This is a structural rationale, not a claim that every command-specific option path is already strict.',
296
+ '',
297
+ '| Command | Rationale |',
298
+ '| --- | --- |',
299
+ ...COMMAND_SPEC_EXCLUSIONS.map((entry) => '| ' + entry.names.map((name) => '`' + name + '`').join(', ') + ' | ' + entry.reason + ' |'),
300
+ '',
196
301
  ];
197
302
  for (const spec of registeredSpecs()) {
198
303
  lines.push('## `' + spec.names.join('` / `') + '`');
@@ -296,7 +401,10 @@ export function specOptionError(spec, args, cmdName) {
296
401
  i++;
297
402
  continue;
298
403
  }
299
- if (t.startsWith('-') && t.length > 1 && !/^-?\d/.test(t.slice(1, 2))) {
404
+ const firstShort = t.slice(1, 2);
405
+ if (t.startsWith('-') &&
406
+ t.length > 1 &&
407
+ (!/^\d$/.test(firstShort) || shorts.has(firstShort))) {
300
408
  const body = t.slice(1);
301
409
  for (let c = 0; c < body.length; c++) {
302
410
  const ch = body[c];
@@ -1,12 +1,42 @@
1
- import { Assignment, CommandList, Redirect, SimpleCommand, IfCommand, ForCommand, Word, WordPart } from './ast.js';
1
+ import { Assignment, CommandList, Redirect, SimpleCommand, IfCommand, ForCommand, WhileCommand, CaseCommand, Word, WordPart } from './ast.js';
2
2
  import { PipelineCtx } from './registry.js';
3
+ export interface TranslationContext {
4
+ /** `pure` renders a script without consulting command operands on disk. */
5
+ mode: 'execute' | 'pure';
6
+ }
7
+ export declare const EXECUTE_TRANSLATION: TranslationContext;
8
+ export declare const PURE_TRANSLATION: TranslationContext;
9
+ export declare const PURE_SED_FILE_MESSAGE = "fauxnix: translate does not read sed script files; use -e with the script text, or run the command to use -f";
3
10
  /** `${name:-word}` and friends using case-exact fx-scalar0. */
4
11
  export declare function paramExpr(name: string, op: ':-' | ':=' | ':+' | ':?' | '-' | '+' | '?', word: string): string;
12
+ export declare function varExtraOf(p: WordPart): {
13
+ replace?: {
14
+ global: boolean;
15
+ pat: string;
16
+ repl: string;
17
+ };
18
+ slice?: {
19
+ offset: string;
20
+ length?: string;
21
+ };
22
+ } | undefined;
5
23
  /** Map a bash $VAR name to a PowerShell expression (usable inside $(...)). */
6
24
  export declare function varExpr(name: string, index?: string, param?: {
7
25
  op: ':-' | ':=' | ':+' | ':?' | '-' | '+' | '?';
8
26
  word: string;
9
- }, length?: boolean): string;
27
+ }, length?: boolean, extra?: {
28
+ replace?: {
29
+ global: boolean;
30
+ pat: string;
31
+ repl: string;
32
+ };
33
+ slice?: {
34
+ offset: string;
35
+ length?: string;
36
+ };
37
+ }): string;
38
+ /** `$?` `$$` `$0`–`$n` `$#` `$@` `$*` — not ordinary `$env:` names. */
39
+ export declare function isSpecialShellVar(name: string): boolean;
10
40
  /** Escape text destined for the inside of a PS double-quoted string. */
11
41
  export declare function escapeDq(s: string): string;
12
42
  /** Normalize a literal POSIX-ish path to its Windows equivalent. */
@@ -39,6 +69,7 @@ export declare function operandExpr(w: Word): string;
39
69
  /**
40
70
  * `${name[@]}` / `"pre${name[@]}post"` — one argv per element.
41
71
  * Unquoted `${name[*]}` also splats (bash); quoted `"${name[*]}"` stays one join.
72
+ * `$@` / unquoted `$*` splat like `${arr[@]}`; quoted `"$@"` still splats.
42
73
  */
43
74
  export declare function splatSpec(w: Word): {
44
75
  name: string;
@@ -53,9 +84,11 @@ export declare function argListExpr(words: Word[], fn?: (w: Word) => string): st
53
84
  * Unquoted command words join non-empty lines with a space (IFS
54
85
  * word-split approximation). Handlers often emit one string object, so
55
86
  * a bare `$(…)` interpolation would keep those newlines.
87
+ * Lists (`;` `&&` `||`) reuse translateListInline inside the fx-csub
88
+ * scriptblock so the newline contract is unchanged.
56
89
  */
57
- export declare function translateCmdSub(cmdText: string, keepNl?: boolean): string;
58
- export declare function translateSimple(cmd: SimpleCommand, position: PipelineCtx['position'], hasStdin: boolean): string;
90
+ export declare function translateCmdSub(cmdText: string, keepNl?: boolean, translation?: TranslationContext): string;
91
+ export declare function translateSimple(cmd: SimpleCommand, position: PipelineCtx['position'], hasStdin: boolean, translation?: TranslationContext): string;
59
92
  /** PS expr: encode a string so SETVALS records can stay newline-delimited. */
60
93
  export declare function encodeSetValExpr(srcExpr: string): string;
61
94
  /**
@@ -76,11 +109,11 @@ export interface PipelineParts {
76
109
  call: string;
77
110
  }
78
111
  export declare function translatePipelineBody(p: {
79
- commands: Array<SimpleCommand | IfCommand | ForCommand>;
80
- }): PipelineParts;
112
+ commands: Array<SimpleCommand | IfCommand | ForCommand | WhileCommand | CaseCommand>;
113
+ }, translation?: TranslationContext): PipelineParts;
81
114
  export interface SegmentPlan {
82
115
  op: ';' | '&&' | '||';
83
- /** Spawn-mode wrapScript (CLI/MCP `translate`, one-shot powershell.exe). */
116
+ /** Spawn-mode wrapScript (`translate` output for a one-shot PowerShell process). */
84
117
  script: string;
85
118
  /** Pipeline body before wrapScript — executor host mode re-wraps this. */
86
119
  body: string;
@@ -91,7 +124,7 @@ export interface SegmentPlan {
91
124
  /** First-stage `<` only — FAUXNIX_STDIN_FILE feed. */
92
125
  stdinRedirects: Redirect[];
93
126
  }
94
- export declare function translateCommandList(list: CommandList): SegmentPlan[];
127
+ export declare function translateCommandList(list: CommandList, translation?: TranslationContext): SegmentPlan[];
95
128
  export type WrapMode = 'spawn' | 'host';
96
129
  export interface WrapScriptOptions {
97
130
  /** spawn (default): one-shot process, `exit` at the end. host: no `exit`, no helper re-emit. */
@@ -106,6 +139,6 @@ export interface WrapScriptOptions {
106
139
  export declare function wrapScript(body: string, opts?: WrapScriptOptions): string;
107
140
  /**
108
141
  * Resident-host bootstrap: encoding + full fx-* catalog + JSON-line RPC loop.
109
- * Loaded once via `powershell.exe -File`. Must never `exit` a successful frame.
142
+ * Loaded once via the selected PowerShell's `-File`. Must never `exit` a successful frame.
110
143
  */
111
144
  export declare function hostBootstrapScript(): string;