open-claude-p 1.0.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.
@@ -0,0 +1,4 @@
1
+ // Barrel re-export for the options module.
2
+ export { OPTION_SPEC, getOption } from './spec.js';
3
+ export { parseArgv } from './parse-argv.js';
4
+ export { CROSS_RULES, validate } from './validate.js';
@@ -0,0 +1,214 @@
1
+ // argv -> normalized options object.
2
+ //
3
+ // A small hand-rolled parser that consumes the OPTION_SPEC contract. No
4
+ // third-party dependency, to keep this package's runtime footprint to just
5
+ // `node-pty`.
6
+ //
7
+ // Supported forms:
8
+ // --flag boolean true (or array-marker for kind=array)
9
+ // --flag value string / number / enum / json / first array elem
10
+ // --flag=value inline value
11
+ // --flag a b c array kind: collect variadic until next flag
12
+ // --flag a --flag b array kind with repeatable: accumulates
13
+ // -x short flag
14
+ // -xyz bundled boolean shorts: -x -y -z
15
+ // -- end of options; remaining tokens are positional
16
+ // anything else positional
17
+ //
18
+ // Returns `{ options, positional, unknown, errors }`. Callers decide whether
19
+ // to fail on errors or treat unknown flags as forwarded pass-through.
20
+
21
+ import { OPTION_SPEC, getOption } from './spec.js';
22
+
23
+ /**
24
+ * @typedef {object} ParseResult
25
+ * @property {Record<string, unknown>} options
26
+ * @property {string[]} positional
27
+ * @property {string[]} unknown Tokens that look like flags but were not in the spec.
28
+ * @property {string[]} errors Human-readable error strings.
29
+ */
30
+
31
+ /**
32
+ * @param {string[]} argv
33
+ * @returns {ParseResult}
34
+ */
35
+ export function parseArgv(argv) {
36
+ /** @type {ParseResult} */
37
+ const out = { options: {}, positional: [], unknown: [], errors: [] };
38
+
39
+ // Pre-seed defaults so the validator sees them.
40
+ for (const spec of OPTION_SPEC) {
41
+ if (Object.prototype.hasOwnProperty.call(spec, 'default')) {
42
+ out.options[spec.name] = spec.default;
43
+ }
44
+ }
45
+
46
+ let i = 0;
47
+ while (i < argv.length) {
48
+ const tok = argv[i];
49
+
50
+ if (tok === '--') {
51
+ for (const rest of argv.slice(i + 1)) out.positional.push(rest);
52
+ break;
53
+ }
54
+
55
+ if (tok.startsWith('--')) {
56
+ // Long flag (possibly with inline =value).
57
+ const eq = tok.indexOf('=');
58
+ const name = eq === -1 ? tok.slice(2) : tok.slice(2, eq);
59
+ const inline = eq === -1 ? undefined : tok.slice(eq + 1);
60
+ const spec = getOption(name);
61
+ if (!spec) {
62
+ out.unknown.push(tok);
63
+ i++;
64
+ continue;
65
+ }
66
+ i = consumeOption(spec, argv, i, inline, out);
67
+ continue;
68
+ }
69
+
70
+ if (tok.length > 1 && tok.startsWith('-') && tok !== '-') {
71
+ // Short flag or bundle (e.g. -p or -pv).
72
+ const chars = tok.slice(1).split('');
73
+ // `consumedByValue` is true only if a non-boolean short flag was
74
+ // matched and consumeOption() already advanced `i` for us. In every
75
+ // other case (all booleans, all unknown, mixed unknown+boolean) we
76
+ // must advance i by one ourselves at the end.
77
+ let consumedByValue = false;
78
+ for (let k = 0; k < chars.length; k++) {
79
+ const ch = chars[k];
80
+ const spec = OPTION_SPEC.find((o) => o.short === ch);
81
+ if (!spec) {
82
+ out.unknown.push(`-${ch}`);
83
+ continue;
84
+ }
85
+ if (spec.kind !== 'boolean') {
86
+ // Non-boolean short. The value comes from the next argv token;
87
+ // bundling a non-boolean with trailing chars is rejected.
88
+ if (k !== chars.length - 1) {
89
+ out.errors.push(
90
+ `Short flag -${ch} requires a value and cannot be bundled with -${chars
91
+ .slice(k + 1)
92
+ .join('')}.`,
93
+ );
94
+ break;
95
+ }
96
+ i = consumeOption(spec, argv, i, undefined, out);
97
+ consumedByValue = true;
98
+ break;
99
+ }
100
+ // Boolean short: just set.
101
+ out.options[spec.name] = true;
102
+ }
103
+ if (!consumedByValue) i++;
104
+ continue;
105
+ }
106
+
107
+ // Positional argument.
108
+ out.positional.push(tok);
109
+ i++;
110
+ }
111
+
112
+ return out;
113
+ }
114
+
115
+ /**
116
+ * Consume the value(s) for `spec` starting at argv index `i` (where argv[i]
117
+ * is the flag token itself). `inline` is the post-`=` value when present.
118
+ * Returns the new index to resume parsing at.
119
+ *
120
+ * @returns {number}
121
+ */
122
+ function consumeOption(spec, argv, i, inline, out) {
123
+ const name = spec.name;
124
+ const flagToken = argv[i];
125
+
126
+ if (spec.kind === 'boolean') {
127
+ if (inline !== undefined) {
128
+ // --flag=true / --flag=false / --flag=1 / --flag=0
129
+ const v = inline.toLowerCase();
130
+ out.options[name] = v === 'true' || v === '1' || v === 'yes';
131
+ } else {
132
+ out.options[name] = true;
133
+ }
134
+ return i + 1;
135
+ }
136
+
137
+ if (spec.kind === 'array') {
138
+ /** @type {string[]} */
139
+ const arr = Array.isArray(out.options[name]) ? out.options[name] : [];
140
+ let j = i + 1;
141
+ if (inline !== undefined) {
142
+ arr.push(inline);
143
+ } else {
144
+ // Collect variadic values until the next flag-looking token.
145
+ while (j < argv.length && !looksLikeFlag(argv[j])) {
146
+ arr.push(argv[j]);
147
+ j++;
148
+ }
149
+ if (arr.length === 0) {
150
+ out.errors.push(`${flagToken} requires at least one value.`);
151
+ }
152
+ }
153
+ out.options[name] = arr;
154
+ return j;
155
+ }
156
+
157
+ // string / number / enum / json — consume exactly one value.
158
+ let raw;
159
+ if (inline !== undefined) {
160
+ raw = inline;
161
+ } else if (i + 1 < argv.length && !looksLikeFlag(argv[i + 1])) {
162
+ raw = argv[i + 1];
163
+ i += 1;
164
+ } else {
165
+ out.errors.push(`${flagToken} requires a value.`);
166
+ return i + 1;
167
+ }
168
+
169
+ const { value, error } = coerce(spec, raw);
170
+ if (error) out.errors.push(`${flagToken}: ${error}`);
171
+ else out.options[name] = value;
172
+ return i + 1;
173
+ }
174
+
175
+ /**
176
+ * @param {string} tok
177
+ */
178
+ function looksLikeFlag(tok) {
179
+ if (tok === undefined) return false;
180
+ if (tok === '-' || tok === '--') return true;
181
+ return tok.startsWith('--') || /^-[A-Za-z]/.test(tok);
182
+ }
183
+
184
+ /**
185
+ * @param {object} spec
186
+ * @param {string} raw
187
+ * @returns {{ value?: unknown, error?: string }}
188
+ */
189
+ function coerce(spec, raw) {
190
+ switch (spec.kind) {
191
+ case 'string':
192
+ return { value: raw };
193
+ case 'number': {
194
+ const n = Number(raw);
195
+ if (Number.isNaN(n)) return { error: `expected a number, got ${JSON.stringify(raw)}` };
196
+ return { value: n };
197
+ }
198
+ case 'enum':
199
+ if (!spec.choices.includes(raw)) {
200
+ return {
201
+ error: `expected one of ${spec.choices.join(', ')}, got ${JSON.stringify(raw)}`,
202
+ };
203
+ }
204
+ return { value: raw };
205
+ case 'json':
206
+ try {
207
+ return { value: JSON.parse(raw) };
208
+ } catch (e) {
209
+ return { error: `invalid JSON: ${e.message}` };
210
+ }
211
+ default:
212
+ return { error: `unsupported kind ${JSON.stringify(spec.kind)}` };
213
+ }
214
+ }