@mengyuly/dsh-ponytail 0.1.5 → 0.2.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/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { Service } from "@deepseek-ai/cordis";
3
+ import z from "@deepseek-ai/schemastery";
3
4
  import { mkdirSync, readFileSync, renameSync, unlinkSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
4
5
  import { homedir } from "node:os";
5
6
  import { dirname, join } from "node:path";
@@ -93,798 +94,6 @@ function createUserMessage(input) {
93
94
  });
94
95
  }
95
96
  //#endregion
96
- //#region ../../../vendor/cosmokit/src/misc.ts
97
- /** Return true when a value is `null` or `undefined`. */
98
- function isNullable(value) {
99
- return value === null || value === void 0;
100
- }
101
- /** Return true for non-array object values. */
102
- function isPlainObject(data) {
103
- return data && typeof data === "object" && !Array.isArray(data);
104
- }
105
- /** Filter object entries and return a new object. */
106
- function filterKeys(object, filter) {
107
- return Object.fromEntries(Object.entries(object).filter(([key, value]) => filter(key, value)));
108
- }
109
- /** Map object values while preserving the original key set. */
110
- function mapValues(object, transform) {
111
- return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, transform(value, key)]));
112
- }
113
- /** Pick selected keys from an object, optionally including `undefined` values. */
114
- function pick(source, keys, forced) {
115
- if (!keys) return { ...source };
116
- const result = {};
117
- for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
118
- return result;
119
- }
120
- //#endregion
121
- //#region ../../../vendor/cosmokit/src/types.ts
122
- /** Test values using `instanceof` with a `toStringTag` fallback. */
123
- function is(type, value) {
124
- if (arguments.length === 1) return (value) => is(type, value);
125
- return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
126
- }
127
- function isArrayBufferLike(value) {
128
- return is("ArrayBuffer", value) || is("SharedArrayBuffer", value);
129
- }
130
- function isArrayBufferSource(value) {
131
- return isArrayBufferLike(value) || ArrayBuffer.isView(value);
132
- }
133
- let Binary;
134
- (function(_Binary) {
135
- _Binary.is = isArrayBufferLike;
136
- _Binary.isSource = isArrayBufferSource;
137
- function fromSource(source) {
138
- if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
139
- else return source;
140
- }
141
- _Binary.fromSource = fromSource;
142
- function toBase64(source) {
143
- source = fromSource(source);
144
- if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64");
145
- let binary = "";
146
- const bytes = new Uint8Array(source);
147
- for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
148
- return btoa(binary);
149
- }
150
- _Binary.toBase64 = toBase64;
151
- function fromBase64(source) {
152
- if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
153
- return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
154
- }
155
- _Binary.fromBase64 = fromBase64;
156
- function toHex(source) {
157
- source = fromSource(source);
158
- if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
159
- return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
160
- }
161
- _Binary.toHex = toHex;
162
- function fromHex(source) {
163
- if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
164
- const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
165
- const buffer = [];
166
- for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
167
- return Uint8Array.from(buffer).buffer;
168
- }
169
- _Binary.fromHex = fromHex;
170
- })(Binary || (Binary = {}));
171
- Binary.fromBase64;
172
- Binary.toBase64;
173
- Binary.fromHex;
174
- Binary.toHex;
175
- /** Deep-clone common JavaScript values while preserving prototypes and cycles. */
176
- function clone(source, refs = /* @__PURE__ */ new Map()) {
177
- if (!source || typeof source !== "object") return source;
178
- if (is("Date", source)) return new Date(source.valueOf());
179
- if (is("RegExp", source)) return new RegExp(source.source, source.flags);
180
- if (isArrayBufferLike(source)) return source.slice(0);
181
- if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
182
- const cached = refs.get(source);
183
- if (cached) return cached;
184
- if (Array.isArray(source)) {
185
- const result = [];
186
- refs.set(source, result);
187
- source.forEach((value, index) => {
188
- result[index] = Reflect.apply(clone, null, [value, refs]);
189
- });
190
- return result;
191
- }
192
- const result = Object.create(Object.getPrototypeOf(source));
193
- refs.set(source, result);
194
- for (const key of Reflect.ownKeys(source)) {
195
- const descriptor = { ...Reflect.getOwnPropertyDescriptor(source, key) };
196
- if ("value" in descriptor) descriptor.value = Reflect.apply(clone, null, [descriptor.value, refs]);
197
- Reflect.defineProperty(result, key, descriptor);
198
- }
199
- return result;
200
- }
201
- /** Deeply compare arrays, dates, regexps, buffers, and plain object fields. */
202
- function deepEqual(a, b, strict) {
203
- if (a === b) return true;
204
- if (!strict && isNullable(a) && isNullable(b)) return true;
205
- if (typeof a !== typeof b) return false;
206
- if (typeof a !== "object") return false;
207
- if (!a || !b) return false;
208
- function check(test, then) {
209
- return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
210
- }
211
- return check(Array.isArray, (a, b) => a.length === b.length && a.every((item, index) => deepEqual(item, b[index]))) ?? check(is("Date"), (a, b) => a.valueOf() === b.valueOf()) ?? check(is("RegExp"), (a, b) => a.source === b.source && a.flags === b.flags) ?? check(isArrayBufferLike, (a, b) => {
212
- if (a.byteLength !== b.byteLength) return false;
213
- const viewA = new Uint8Array(a);
214
- const viewB = new Uint8Array(b);
215
- for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
216
- return true;
217
- }) ?? Object.keys({
218
- ...a,
219
- ...b
220
- }).every((key) => deepEqual(a[key], b[key], strict));
221
- }
222
- //#endregion
223
- //#region ../../../vendor/cosmokit/src/time.ts
224
- let Time;
225
- (function(_Time) {
226
- _Time.millisecond = 1;
227
- const second = _Time.second = 1e3;
228
- const minute = _Time.minute = second * 60;
229
- const hour = _Time.hour = minute * 60;
230
- const day = _Time.day = hour * 24;
231
- const week = _Time.week = day * 7;
232
- let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
233
- function setTimezoneOffset(offset) {
234
- timezoneOffset = offset;
235
- }
236
- _Time.setTimezoneOffset = setTimezoneOffset;
237
- function getTimezoneOffset() {
238
- return timezoneOffset;
239
- }
240
- _Time.getTimezoneOffset = getTimezoneOffset;
241
- function getDateNumber(date = /* @__PURE__ */ new Date(), offset) {
242
- if (typeof date === "number") date = new Date(date);
243
- if (offset === void 0) offset = timezoneOffset;
244
- return Math.floor((date.valueOf() / minute - offset) / 1440);
245
- }
246
- _Time.getDateNumber = getDateNumber;
247
- function fromDateNumber(value, offset) {
248
- const date = new Date(value * day);
249
- if (offset === void 0) offset = timezoneOffset;
250
- return new Date(+date + offset * minute);
251
- }
252
- _Time.fromDateNumber = fromDateNumber;
253
- const numeric = /\d+(?:\.\d+)?/.source;
254
- const timeRegExp = new RegExp(`^${[
255
- "w(?:eek(?:s)?)?",
256
- "d(?:ay(?:s)?)?",
257
- "h(?:our(?:s)?)?",
258
- "m(?:in(?:ute)?(?:s)?)?",
259
- "s(?:ec(?:ond)?(?:s)?)?"
260
- ].map((unit) => `(${numeric}${unit})?`).join("")}$`);
261
- function parseTime(source) {
262
- const capture = timeRegExp.exec(source);
263
- if (!capture) return 0;
264
- return (parseFloat(capture[1]) * week || 0) + (parseFloat(capture[2]) * day || 0) + (parseFloat(capture[3]) * hour || 0) + (parseFloat(capture[4]) * minute || 0) + (parseFloat(capture[5]) * second || 0);
265
- }
266
- _Time.parseTime = parseTime;
267
- function parseDate(date) {
268
- const parsed = parseTime(date);
269
- if (parsed) date = Date.now() + parsed;
270
- else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).toLocaleDateString()}-${date}`;
271
- else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`;
272
- return date ? new Date(date) : /* @__PURE__ */ new Date();
273
- }
274
- _Time.parseDate = parseDate;
275
- function format(ms) {
276
- const abs = Math.abs(ms);
277
- if (abs >= day - hour / 2) return Math.round(ms / day) + "d";
278
- else if (abs >= hour - minute / 2) return Math.round(ms / hour) + "h";
279
- else if (abs >= minute - second / 2) return Math.round(ms / minute) + "m";
280
- else if (abs >= second) return Math.round(ms / second) + "s";
281
- return ms + "ms";
282
- }
283
- _Time.format = format;
284
- function toDigits(source, length = 2) {
285
- return source.toString().padStart(length, "0");
286
- }
287
- _Time.toDigits = toDigits;
288
- function template(template, time = /* @__PURE__ */ new Date()) {
289
- return template.replace("yyyy", time.getFullYear().toString()).replace("yy", time.getFullYear().toString().slice(2)).replace("MM", toDigits(time.getMonth() + 1)).replace("dd", toDigits(time.getDate())).replace("hh", toDigits(time.getHours())).replace("mm", toDigits(time.getMinutes())).replace("ss", toDigits(time.getSeconds())).replace("SSS", toDigits(time.getMilliseconds(), 3));
290
- }
291
- _Time.template = template;
292
- })(Time || (Time = {}));
293
- //#endregion
294
- //#region ../../../vendor/schemastery/src/index.ts
295
- const kSchema = Symbol.for("schemastery");
296
- const kValidationError = Symbol.for("ValidationError");
297
- globalThis.__schemastery_index__ ??= 0;
298
- globalThis.__schemastery_refs__ = void 0;
299
- var ValidationError = class extends TypeError {
300
- options;
301
- name = "ValidationError";
302
- constructor(message, options) {
303
- let prefix = "$";
304
- for (const segment of options.path || []) if (typeof segment === "string") prefix += "." + segment;
305
- else if (typeof segment === "number") prefix += "[" + segment + "]";
306
- else if (typeof segment === "symbol") prefix += `[Symbol(${segment.toString()})]`;
307
- if (prefix.startsWith(".")) prefix = prefix.slice(1);
308
- super((prefix === "$" ? "" : `${prefix} `) + message);
309
- this.options = options;
310
- }
311
- static is(error) {
312
- return !!error?.[kValidationError];
313
- }
314
- };
315
- Object.defineProperty(ValidationError.prototype, kValidationError, { value: true });
316
- const Schema = function(options) {
317
- const schema = function(data, options = {}) {
318
- return Schema.resolve(data, schema, options)[0];
319
- };
320
- if (options.refs) {
321
- const refs = mapValues(options.refs, (options) => new Schema(options));
322
- const getRef = (uid) => refs[uid];
323
- for (const key in refs) {
324
- const options = refs[key];
325
- options.sKey = getRef(options.sKey);
326
- options.inner = getRef(options.inner);
327
- options.list = options.list && options.list.map(getRef);
328
- options.dict = options.dict && mapValues(options.dict, getRef);
329
- }
330
- return refs[options.uid];
331
- }
332
- Object.assign(schema, options);
333
- if (typeof schema.callback === "string") try {
334
- schema.callback = new Function("return " + schema.callback)();
335
- } catch {}
336
- Object.defineProperty(schema, "uid", { value: globalThis.__schemastery_index__++ });
337
- Object.setPrototypeOf(schema, Schema.prototype);
338
- schema.meta ||= {};
339
- schema.toString = schema.toString.bind(schema);
340
- return schema;
341
- };
342
- Schema.prototype = Object.create(Function.prototype);
343
- Schema.prototype[kSchema] = true;
344
- Object.defineProperty(Schema.prototype, "~standard", { get() {
345
- return {
346
- version: 1,
347
- vendor: "schemastery",
348
- validate: (value) => {
349
- try {
350
- return { value: Schema.resolve(value, this, {})[0] };
351
- } catch (error) {
352
- if (ValidationError.is(error)) return { issues: [{
353
- message: error.message,
354
- path: error.options.path
355
- }] };
356
- throw error;
357
- }
358
- }
359
- };
360
- } });
361
- Schema.ValidationError = ValidationError;
362
- Schema.prototype.toJSON = function toJSON() {
363
- if (globalThis.__schemastery_refs__) {
364
- globalThis.__schemastery_refs__[this.uid] ??= JSON.parse(JSON.stringify({ ...this }));
365
- return this.uid;
366
- }
367
- globalThis.__schemastery_refs__ = { [this.uid]: { ...this } };
368
- globalThis.__schemastery_refs__[this.uid] = JSON.parse(JSON.stringify({ ...this }));
369
- const result = {
370
- uid: this.uid,
371
- refs: globalThis.__schemastery_refs__
372
- };
373
- globalThis.__schemastery_refs__ = void 0;
374
- return result;
375
- };
376
- Schema.prototype.set = function set(key, value) {
377
- this.dict[key] = value;
378
- return this;
379
- };
380
- Schema.prototype.push = function push(value) {
381
- this.list.push(value);
382
- return this;
383
- };
384
- function mergeDesc(original, messages) {
385
- const result = typeof original === "string" ? { "": original } : { ...original };
386
- for (const locale in messages) {
387
- const value = messages[locale];
388
- if (value?.$description || value?.$desc) result[locale] = value.$description || value.$desc;
389
- else if (typeof value === "string") result[locale] = value;
390
- }
391
- return result;
392
- }
393
- function getInner(value) {
394
- return value?.$value ?? value?.$inner;
395
- }
396
- function extractKeys(data) {
397
- return filterKeys(data ?? {}, (key) => !key.startsWith("$"));
398
- }
399
- Schema.prototype.i18n = function i18n(messages) {
400
- const schema = Schema(this);
401
- const desc = mergeDesc(schema.meta.description, messages);
402
- if (Object.keys(desc).length) schema.meta.description = desc;
403
- if (schema.dict) schema.dict = mapValues(schema.dict, (inner, key) => {
404
- return inner.i18n(mapValues(messages, (data) => getInner(data)?.[key] ?? data?.[key]));
405
- });
406
- if (schema.list) schema.list = schema.list.map((inner, index) => {
407
- return inner.i18n(mapValues(messages, (data = {}) => {
408
- if (Array.isArray(getInner(data))) return getInner(data)[index];
409
- if (Array.isArray(data)) return data[index];
410
- return extractKeys(data);
411
- }));
412
- });
413
- if (schema.inner) schema.inner = schema.inner.i18n(mapValues(messages, (data) => {
414
- if (getInner(data)) return getInner(data);
415
- return extractKeys(data);
416
- }));
417
- if (schema.sKey) schema.sKey = schema.sKey.i18n(mapValues(messages, (data) => data?.$key));
418
- return schema;
419
- };
420
- Schema.prototype.extra = function extra(key, value) {
421
- const schema = Schema(this);
422
- schema.meta = {
423
- ...schema.meta,
424
- [key]: value
425
- };
426
- return schema;
427
- };
428
- for (const key of [
429
- "required",
430
- "disabled",
431
- "collapse",
432
- "hidden",
433
- "loose"
434
- ]) Object.assign(Schema.prototype, { [key](value = true) {
435
- const schema = Schema(this);
436
- schema.meta = {
437
- ...schema.meta,
438
- [key]: value
439
- };
440
- return schema;
441
- } });
442
- Schema.prototype.deprecated = function deprecated() {
443
- const schema = Schema(this);
444
- schema.meta.badges ||= [];
445
- schema.meta.badges.push({
446
- text: "deprecated",
447
- type: "danger"
448
- });
449
- return schema;
450
- };
451
- Schema.prototype.experimental = function experimental() {
452
- const schema = Schema(this);
453
- schema.meta.badges ||= [];
454
- schema.meta.badges.push({
455
- text: "experimental",
456
- type: "warning"
457
- });
458
- return schema;
459
- };
460
- Schema.prototype.pattern = function pattern(regexp) {
461
- const schema = Schema(this);
462
- const pattern = pick(regexp, ["source", "flags"]);
463
- schema.meta = {
464
- ...schema.meta,
465
- pattern
466
- };
467
- return schema;
468
- };
469
- Schema.prototype.simplify = function simplify(value) {
470
- if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
471
- if (isNullable(value)) return value;
472
- if (this.type === "object" || this.type === "dict") {
473
- const result = {};
474
- for (const key in value) {
475
- const item = (this.type === "object" ? this.dict[key] : this.inner)?.simplify(value[key]);
476
- if (this.type === "dict" || !isNullable(item)) result[key] = item;
477
- }
478
- if (deepEqual(result, this.meta.default, this.type === "dict")) return null;
479
- return result;
480
- } else if (this.type === "array" || this.type === "tuple") {
481
- const result = [];
482
- value.forEach((value, index) => {
483
- const schema = this.type === "array" ? this.inner : this.list[index];
484
- const item = schema ? schema.simplify(value) : value;
485
- result.push(item);
486
- });
487
- return result;
488
- } else if (this.type === "intersect") {
489
- const result = {};
490
- for (const item of this.list) Object.assign(result, item.simplify(value));
491
- return result;
492
- } else if (this.type === "union") for (const schema of this.list) try {
493
- Schema.resolve(value, schema, {});
494
- return schema.simplify(value);
495
- } catch {}
496
- return value;
497
- };
498
- Schema.prototype.toString = function toString(inline) {
499
- return formatters[this.type]?.(this, inline) ?? `Schema<${this.type}>`;
500
- };
501
- Schema.prototype.role = function role(role, extra) {
502
- const schema = Schema(this);
503
- schema.meta = {
504
- ...schema.meta,
505
- role,
506
- extra
507
- };
508
- return schema;
509
- };
510
- for (const key of [
511
- "default",
512
- "link",
513
- "comment",
514
- "description",
515
- "max",
516
- "min",
517
- "step"
518
- ]) Object.assign(Schema.prototype, { [key](value) {
519
- const schema = Schema(this);
520
- schema.meta = {
521
- ...schema.meta,
522
- [key]: value
523
- };
524
- return schema;
525
- } });
526
- const resolvers = {};
527
- Schema.extend = function extend(type, resolve) {
528
- resolvers[type] = resolve;
529
- };
530
- Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
531
- if (!schema) return [data];
532
- if (options.ignore?.(data, schema)) return [data];
533
- if (isNullable(data) && schema.type !== "lazy") {
534
- if (schema.meta.required) throw new ValidationError(`missing required value`, options);
535
- let current = schema;
536
- let fallback = schema.meta.default;
537
- while (current?.type === "intersect" && isNullable(fallback)) {
538
- current = current.list[0];
539
- fallback = current?.meta.default;
540
- }
541
- if (isNullable(fallback)) return [data];
542
- data = clone(fallback);
543
- }
544
- const callback = resolvers[schema.type];
545
- if (!callback) throw new ValidationError(`unsupported type "${schema.type}"`, options);
546
- try {
547
- return callback(data, schema, options, strict);
548
- } catch (error) {
549
- if (!schema.meta.loose) throw error;
550
- return [schema.meta.default];
551
- }
552
- };
553
- Schema.from = function from(source) {
554
- if (isNullable(source)) return Schema.any();
555
- else if ([
556
- "string",
557
- "number",
558
- "boolean"
559
- ].includes(typeof source)) return Schema.const(source).required();
560
- else if (source[kSchema]) return source;
561
- else if (typeof source === "function") switch (source) {
562
- case String: return Schema.string().required();
563
- case Number: return Schema.number().required();
564
- case Boolean: return Schema.boolean().required();
565
- case Function: return Schema.function().required();
566
- default: return Schema.is(source).required();
567
- }
568
- else throw new TypeError(`cannot infer schema from ${source}`);
569
- };
570
- Schema.lazy = function lazy(builder) {
571
- const toJSON = () => {
572
- if (!schema.inner[kSchema]) {
573
- schema.inner = schema.builder();
574
- schema.inner.meta = {
575
- ...schema.meta,
576
- ...schema.inner.meta
577
- };
578
- }
579
- return schema.inner.toJSON();
580
- };
581
- const schema = new Schema({
582
- type: "lazy",
583
- builder,
584
- inner: { toJSON }
585
- });
586
- return schema;
587
- };
588
- Schema.natural = function natural() {
589
- return Schema.number().step(1).min(0);
590
- };
591
- Schema.percent = function percent() {
592
- return Schema.number().step(.01).min(0).max(1).role("slider");
593
- };
594
- Schema.date = function date() {
595
- return Schema.union([Schema.is(Date), Schema.transform(Schema.string().role("datetime"), (value, options) => {
596
- const date = new Date(value);
597
- if (isNaN(+date)) throw new ValidationError(`invalid date "${value}"`, options);
598
- return date;
599
- }, true)]);
600
- };
601
- Schema.regExp = function regExp(flag = "") {
602
- return Schema.union([Schema.is(RegExp), Schema.transform(Schema.string().role("regexp", { flag }), (value, options) => {
603
- try {
604
- return new RegExp(value, flag);
605
- } catch (e) {
606
- throw new ValidationError(e.message, options);
607
- }
608
- }, true)]);
609
- };
610
- Schema.arrayBuffer = function arrayBuffer(encoding) {
611
- return Schema.union([
612
- Schema.is(ArrayBuffer),
613
- Schema.is(SharedArrayBuffer),
614
- Schema.transform(Schema.any(), (value, options) => {
615
- if (Binary.isSource(value)) return Binary.fromSource(value);
616
- throw new ValidationError(`expected ArrayBufferSource but got ${value}`, options);
617
- }, true),
618
- ...encoding ? [Schema.transform(Schema.string(), (value, options) => {
619
- try {
620
- return encoding === "base64" ? Binary.fromBase64(value) : Binary.fromHex(value);
621
- } catch (e) {
622
- throw new ValidationError(e.message, options);
623
- }
624
- }, true)] : []
625
- ]);
626
- };
627
- Schema.extend("lazy", (data, schema, options, strict) => {
628
- if (!schema.inner[kSchema]) {
629
- schema.inner = schema.builder();
630
- schema.inner.meta = {
631
- ...schema.meta,
632
- ...schema.inner.meta
633
- };
634
- }
635
- return Schema.resolve(data, schema.inner, options, strict);
636
- });
637
- Schema.extend("any", (data) => {
638
- return [data];
639
- });
640
- Schema.extend("never", (data, _, options) => {
641
- throw new ValidationError(`expected nullable but got ${data}`, options);
642
- });
643
- Schema.extend("const", (data, { value }, options) => {
644
- if (deepEqual(data, value)) return [value];
645
- throw new ValidationError(`expected ${value} but got ${data}`, options);
646
- });
647
- function checkWithinRange(data, meta, description, options, skipMin = false) {
648
- const { max = Infinity, min = -Infinity } = meta;
649
- if (data > max) throw new ValidationError(`expected ${description} <= ${max} but got ${data}`, options);
650
- if (data < min && !skipMin) throw new ValidationError(`expected ${description} >= ${min} but got ${data}`, options);
651
- }
652
- Schema.extend("string", (data, { meta }, options) => {
653
- if (typeof data !== "string") throw new ValidationError(`expected string but got ${data}`, options);
654
- if (meta.pattern) {
655
- const regexp = new RegExp(meta.pattern.source, meta.pattern.flags);
656
- if (!regexp.test(data)) throw new ValidationError(`expect string to match regexp ${regexp}`, options);
657
- }
658
- checkWithinRange(data.length, meta, "string length", options);
659
- return [data];
660
- });
661
- function decimalShift(data, digits) {
662
- const str = data.toString();
663
- if (str.includes("e")) return data * Math.pow(10, digits);
664
- const index = str.indexOf(".");
665
- if (index === -1) return data * Math.pow(10, digits);
666
- const frac = str.slice(index + 1);
667
- const integer = str.slice(0, index);
668
- if (frac.length <= digits) return +(integer + frac.padEnd(digits, "0"));
669
- return +(integer + frac.slice(0, digits) + "." + frac.slice(digits));
670
- }
671
- function isMultipleOf(data, min, step) {
672
- step = Math.abs(step);
673
- if (!/^\d+\.\d+$/.test(step.toString())) return (data - min) % step === 0;
674
- const index = step.toString().indexOf(".");
675
- const digits = step.toString().slice(index + 1).length;
676
- return Math.abs(decimalShift(data, digits) - decimalShift(min, digits)) % decimalShift(step, digits) === 0;
677
- }
678
- Schema.extend("number", (data, { meta }, options) => {
679
- if (typeof data !== "number") throw new ValidationError(`expected number but got ${data}`, options);
680
- checkWithinRange(data, meta, "number", options);
681
- const { step } = meta;
682
- if (step && !isMultipleOf(data, meta.min ?? 0, step)) throw new ValidationError(`expected number multiple of ${step} but got ${data}`, options);
683
- return [data];
684
- });
685
- Schema.extend("boolean", (data, _, options) => {
686
- if (typeof data === "boolean") return [data];
687
- throw new ValidationError(`expected boolean but got ${data}`, options);
688
- });
689
- Schema.extend("bitset", (data, { bits, meta }, options) => {
690
- let value = 0, keys = [];
691
- if (typeof data === "number") {
692
- value = data;
693
- for (const key in bits) if (data & bits[key]) keys.push(key);
694
- } else if (Array.isArray(data)) {
695
- keys = data;
696
- for (const key of keys) {
697
- if (typeof key !== "string") throw new ValidationError(`expected string but got ${key}`, options);
698
- if (key in bits) value |= bits[key];
699
- }
700
- } else throw new ValidationError(`expected number or array but got ${data}`, options);
701
- if (value === meta.default) return [value];
702
- return [value, keys];
703
- });
704
- Schema.extend("function", (data, _, options) => {
705
- if (typeof data === "function") return [data];
706
- throw new ValidationError(`expected function but got ${data}`, options);
707
- });
708
- Schema.extend("is", (data, { constructor }, options) => {
709
- if (typeof constructor === "function") {
710
- if (data instanceof constructor) return [data];
711
- throw new ValidationError(`expected ${constructor.name} but got ${data}`, options);
712
- } else {
713
- if (isNullable(data)) throw new ValidationError(`expected ${constructor} but got ${data}`, options);
714
- let prototype = Object.getPrototypeOf(data);
715
- while (prototype) {
716
- if (prototype.constructor?.name === constructor) return [data];
717
- prototype = Object.getPrototypeOf(prototype);
718
- }
719
- throw new ValidationError(`expected ${constructor} but got ${data}`, options);
720
- }
721
- });
722
- function property(data, key, schema, options) {
723
- try {
724
- const [value, adapted] = Schema.resolve(data[key], schema, {
725
- ...options,
726
- path: [...options.path || [], key]
727
- });
728
- if (adapted !== void 0) data[key] = adapted;
729
- return value;
730
- } catch (e) {
731
- if (!options?.autofix) throw e;
732
- delete data[key];
733
- return schema.meta.default;
734
- }
735
- }
736
- Schema.extend("array", (data, { inner, meta }, options) => {
737
- if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
738
- checkWithinRange(data.length, meta, "array length", options, !isNullable(inner.meta.default));
739
- return [data.map((_, index) => property(data, index, inner, options))];
740
- });
741
- Schema.extend("dict", (data, { inner, sKey }, options, strict) => {
742
- if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
743
- const result = {};
744
- for (const key in data) {
745
- let rKey;
746
- try {
747
- rKey = Schema.resolve(key, sKey, options)[0];
748
- } catch (error) {
749
- if (strict) continue;
750
- throw error;
751
- }
752
- result[rKey] = property(data, key, inner, options);
753
- data[rKey] = data[key];
754
- if (key !== rKey) delete data[key];
755
- }
756
- return [result];
757
- });
758
- Schema.extend("tuple", (data, { list }, options, strict) => {
759
- if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
760
- const result = list.map((inner, index) => property(data, index, inner, options));
761
- if (strict) return [result];
762
- result.push(...data.slice(list.length));
763
- return [result];
764
- });
765
- function merge(result, data) {
766
- for (const key in data) {
767
- if (key in result) continue;
768
- result[key] = data[key];
769
- }
770
- }
771
- Schema.extend("object", (data, { dict }, options, strict) => {
772
- if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
773
- const result = {};
774
- for (const key in dict) {
775
- const value = property(data, key, dict[key], options);
776
- if (!isNullable(value) || key in data) result[key] = value;
777
- }
778
- if (!strict) merge(result, data);
779
- return [result];
780
- });
781
- Schema.extend("union", (data, { list, toString }, options, strict) => {
782
- const messages = [];
783
- for (const inner of list) try {
784
- return Schema.resolve(data, inner, options, strict);
785
- } catch (error) {
786
- messages.push(error);
787
- }
788
- throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
789
- });
790
- Schema.extend("intersect", (data, { list, toString }, options, strict) => {
791
- if (!list.length) return [data];
792
- let result;
793
- for (const inner of list) {
794
- const value = Schema.resolve(data, inner, options, true)[0];
795
- if (isNullable(value)) continue;
796
- if (isNullable(result)) result = value;
797
- else if (typeof result !== typeof value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
798
- else if (typeof value === "object") merge(result ??= {}, value);
799
- else if (result !== value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
800
- }
801
- if (!strict && isPlainObject(data)) merge(result, data);
802
- return [result];
803
- });
804
- Schema.extend("transform", (data, { inner, callback, preserve }, options) => {
805
- const [result, adapted = data] = Schema.resolve(data, inner, options, true);
806
- if (preserve) return [callback(result)];
807
- else return [callback(result), callback(adapted)];
808
- });
809
- const formatters = {};
810
- function defineMethod(name, keys, format) {
811
- formatters[name] = format;
812
- Object.assign(Schema, { [name](...args) {
813
- const schema = new Schema({ type: name });
814
- keys.forEach((key, index) => {
815
- switch (key) {
816
- case "sKey":
817
- schema.sKey = args[index] ?? Schema.string();
818
- break;
819
- case "inner":
820
- schema.inner = Schema.from(args[index]);
821
- break;
822
- case "list":
823
- schema.list = args[index].map(Schema.from);
824
- break;
825
- case "dict":
826
- schema.dict = mapValues(args[index], Schema.from);
827
- break;
828
- case "bits":
829
- schema.bits = {};
830
- for (const key in args[index]) {
831
- if (typeof args[index][key] !== "number") continue;
832
- schema.bits[key] = args[index][key];
833
- }
834
- break;
835
- case "callback": {
836
- const callback = schema.callback = args[index];
837
- callback["toJSON"] ||= () => callback.toString();
838
- break;
839
- }
840
- case "constructor": {
841
- const constructor = schema.constructor = args[index];
842
- if (typeof constructor === "function") constructor["toJSON"] ||= () => constructor["name"];
843
- break;
844
- }
845
- default: schema[key] = args[index];
846
- }
847
- });
848
- if (name === "object" || name === "dict") schema.meta.default = {};
849
- else if (name === "array" || name === "tuple") schema.meta.default = [];
850
- else if (name === "bitset") schema.meta.default = 0;
851
- return schema;
852
- } });
853
- }
854
- defineMethod("is", ["constructor"], ({ constructor }) => {
855
- if (typeof constructor === "function") return constructor.name;
856
- else return constructor;
857
- });
858
- defineMethod("any", [], () => "any");
859
- defineMethod("never", [], () => "never");
860
- defineMethod("const", ["value"], ({ value }) => typeof value === "string" ? JSON.stringify(value) : value);
861
- defineMethod("string", [], () => "string");
862
- defineMethod("number", [], () => "number");
863
- defineMethod("boolean", [], () => "boolean");
864
- defineMethod("bitset", ["bits"], () => "bitset");
865
- defineMethod("function", [], () => "function");
866
- defineMethod("array", ["inner"], ({ inner }) => `${inner.toString(true)}[]`);
867
- defineMethod("dict", ["inner", "sKey"], ({ inner, sKey }) => `{ [key: ${sKey.toString()}]: ${inner.toString()} }`);
868
- defineMethod("tuple", ["list"], ({ list }) => `[${list.map((inner) => inner.toString()).join(", ")}]`);
869
- defineMethod("object", ["dict"], ({ dict }) => {
870
- if (Object.keys(dict).length === 0) return "{}";
871
- return `{ ${Object.entries(dict).map(([key, inner]) => {
872
- return `${key}${inner.meta.required ? "" : "?"}: ${inner.toString()}`;
873
- }).join(", ")} }`;
874
- });
875
- defineMethod("union", ["list"], ({ list }, inline) => {
876
- const result = list.map(({ toString: format }) => format()).join(" | ");
877
- return inline ? `(${result})` : result;
878
- });
879
- defineMethod("intersect", ["list"], ({ list }) => {
880
- return `${list.map((inner) => inner.toString(true)).join(" & ")}`;
881
- });
882
- defineMethod("transform", [
883
- "inner",
884
- "callback",
885
- "preserve"
886
- ], ({ inner }, isInner) => inner.toString(isInner));
887
- //#endregion
888
97
  //#region ../../util/timeout/src/index.ts
889
98
  /** Largest delay Node schedules without clamping it to one millisecond. */
890
99
  const MAX_TIMER_DELAY_MS = 2147483647;
@@ -924,22 +133,22 @@ const DEFAULT_RETRYABLE_CODES = Object.freeze([
924
133
  "TIMEOUT",
925
134
  "TRANSPORT"
926
135
  ]);
927
- const backoffSchema = Schema.object({
928
- initialDelayMs: Schema.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_INITIAL_DELAY_MS),
929
- maxDelayMs: Schema.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_MAX_DELAY_MS),
930
- jitterRatio: Schema.number().min(0).max(1).default(DEFAULT_JITTER_RATIO)
136
+ const backoffSchema = z.object({
137
+ initialDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_INITIAL_DELAY_MS),
138
+ maxDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_MAX_DELAY_MS),
139
+ jitterRatio: z.number().min(0).max(1).default(DEFAULT_JITTER_RATIO)
931
140
  });
932
- const normalPolicySchema = Schema.object({
933
- mode: Schema.const("normal").required(),
934
- maxRetries: Schema.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_MAX_RETRIES),
935
- retryableCodes: Schema.array(Schema.string()).default([...DEFAULT_RETRYABLE_CODES]),
141
+ const normalPolicySchema = z.object({
142
+ mode: z.const("normal").required(),
143
+ maxRetries: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_MAX_RETRIES),
144
+ retryableCodes: z.array(z.string()).default([...DEFAULT_RETRYABLE_CODES]),
936
145
  backoff: backoffSchema
937
146
  });
938
- const alwaysPolicySchema = Schema.object({
939
- mode: Schema.const("always").required(),
147
+ const alwaysPolicySchema = z.object({
148
+ mode: z.const("always").required(),
940
149
  backoff: backoffSchema
941
150
  });
942
- Schema.union([normalPolicySchema, alwaysPolicySchema]);
151
+ z.union([normalPolicySchema, alwaysPolicySchema]);
943
152
  //#endregion
944
153
  //#region ../../llm/llm/src/attribution.ts
945
154
  /**
@@ -1261,7 +470,7 @@ var SkillLayer = class {
1261
470
  }
1262
471
  };
1263
472
  (class extends Service {
1264
- static Config = Schema.object({ collectCacheMaxEntries: Schema.number().default(DEFAULT_COLLECT_CACHE_ENTRIES) });
473
+ static Config = z.object({ collectCacheMaxEntries: z.number().default(DEFAULT_COLLECT_CACHE_ENTRIES) });
1265
474
  collectCacheMaxEntries;
1266
475
  layers = new ScopedLayers((scope) => new SkillLayer(scope), () => {
1267
476
  this.invalidateCache();
@@ -2157,21 +1366,25 @@ function configPath(env = process.env) {
2157
1366
  return join(configDir(env), "config.json");
2158
1367
  }
2159
1368
  /**
2160
- * Read the configured default with diagnostics: environment variable first,
2161
- * then the config file, then `full`. A missing config file is normal and
2162
- * yields no issue; a broken one yields the fallback mode plus one issue for
2163
- * the caller to warn about once.
1369
+ * Read the configured default with diagnostics. Priority:
1370
+ * `PONYTAIL_DEFAULT_MODE` → Cordis profile `defaultMode` → user config file →
1371
+ * `full`. A missing config file is normal and yields no issue; a broken one
1372
+ * yields the fallback mode plus one issue for the caller to warn about once.
1373
+ * @param env - the process environment to read.
1374
+ * @param profileMode - the validated Cordis profile `defaultMode`, or `null`
1375
+ * when the profile config is absent or invalid (invalid values are reported
1376
+ * by the caller; this function only consumes valid ones).
2164
1377
  */
2165
- function readDefaultModeInfo(env = process.env) {
1378
+ function readDefaultModeInfo(env = process.env, profileMode = null) {
2166
1379
  const path = configPath(env);
2167
1380
  const envMode = normalizeRuntimeMode(env.PONYTAIL_DEFAULT_MODE);
2168
1381
  let configText;
2169
1382
  try {
2170
1383
  configText = readFileSync(path, "utf8");
2171
1384
  } catch (error) {
2172
- if (error.code === "ENOENT") return { mode: envMode ?? "full" };
1385
+ if (error.code === "ENOENT") return { mode: envMode ?? profileMode ?? "full" };
2173
1386
  return {
2174
- mode: envMode ?? "full",
1387
+ mode: envMode ?? profileMode ?? "full",
2175
1388
  issue: {
2176
1389
  kind: "read",
2177
1390
  detail: `${path}: ${error.message}`
@@ -2203,6 +1416,10 @@ function readDefaultModeInfo(env = process.env) {
2203
1416
  mode: envMode,
2204
1417
  ...configIssue ? { issue: configIssue } : {}
2205
1418
  };
1419
+ if (profileMode) return {
1420
+ mode: profileMode,
1421
+ ...configIssue ? { issue: configIssue } : {}
1422
+ };
2206
1423
  if (configIssue) return {
2207
1424
  mode: DEFAULT_MODE,
2208
1425
  issue: configIssue
@@ -2211,10 +1428,19 @@ function readDefaultModeInfo(env = process.env) {
2211
1428
  }
2212
1429
  /**
2213
1430
  * Read the configured default for this host: environment variable first, then
2214
- * the config file, then `full`.
1431
+ * the Cordis profile `defaultMode`, then the user config file, then `full`.
1432
+ */
1433
+ function readDefaultMode(env = process.env, profileMode = null) {
1434
+ return readDefaultModeInfo(env, profileMode).mode;
1435
+ }
1436
+ /**
1437
+ * Why a `saved` default is not the effective one — for the `/ponytail default`
1438
+ * result message. `null` means the saved value is effective.
2215
1439
  */
2216
- function readDefaultMode(env = process.env) {
2217
- return readDefaultModeInfo(env).mode;
1440
+ function defaultOverrideReason(env, profileMode) {
1441
+ if (normalizeRuntimeMode(env.PONYTAIL_DEFAULT_MODE)) return "PONYTAIL_DEFAULT_MODE";
1442
+ if (profileMode) return "profile configuration";
1443
+ return null;
2218
1444
  }
2219
1445
  /**
2220
1446
  * Persist a new default level to the config file, preserving other fields.
@@ -2305,55 +1531,102 @@ function isSubagentSession(header) {
2305
1531
  //#endregion
2306
1532
  //#region lib/types/instructions.js
2307
1533
  /**
2308
- * Build the mode-filtered ponytail ruleset. Ported from the upstream
2309
- * `hooks/ponytail-instructions.js`, so the injected text is byte-for-byte the
2310
- * same ruleset every other host emits, filtered to the active intensity.
1534
+ * Structured ponytail ruleset composition. Each intensity is built from
1535
+ * explicit fragments — common rules, a never-cut safety boundary list, and
1536
+ * the mode's own rules — instead of filtering one Markdown body with regexes.
1537
+ * The three intensities therefore differ in their actual instructions, not
1538
+ * just in a table row.
2311
1539
  *
2312
1540
  * @module @deepseek-ai/dsh-ponytail
2313
1541
  */
1542
+ /** Shared identity line, carried by every non-`off` mode. */
1543
+ const INTRO = "You are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written.";
2314
1544
  /**
2315
- * Keep a line of the skill body only when it belongs to every mode or to the
2316
- * active one. Both shape-sensitive spots (the intensity table rows and the
2317
- * quoted worked examples) are keyed by a mode name; ordinary rules survive
2318
- * verbatim, even ones whose prose starts with a mode-looking word.
1545
+ * Understanding-and-reuse baseline, identical in every non-`off` mode.
2319
1546
  */
2320
- function filterSkillBodyForMode(body, mode) {
2321
- const effective = normalizeRuntimeMode(mode) ?? "full";
2322
- return body.split(/\r?\n/).filter((line) => {
2323
- const tableLabel = line.match(/^\|\s*\*\*(.+?)\*\*\s*\|/);
2324
- if (tableLabel) {
2325
- const labelMode = normalizeRuntimeMode(tableLabel[1]);
2326
- if (labelMode) return labelMode === effective;
2327
- }
2328
- const exampleLabel = line.match(/^-\s*([^:]+):\s*"/);
2329
- if (exampleLabel) {
2330
- const labelMode = normalizeRuntimeMode(exampleLabel[1]);
2331
- if (labelMode) return labelMode === effective;
2332
- }
2333
- return true;
2334
- }).join("\n");
2335
- }
2336
- /** Minimal instruction set if the skill body can't be read (parity fallback). */
2337
- function fallbackInstructions(mode) {
2338
- return "PONYTAIL MODE ACTIVE — level: " + mode + "\n\nYou are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written.\n\n## Persistence\n\nACTIVE EVERY RESPONSE. No drift back to over-building. Still active if unsure. Off only: \"stop ponytail\" / \"normal mode\".\n\nCurrent level: **" + mode + "**. Switch: `/ponytail lite|full|ultra`.\n\n## The ladder\n\nBefore any code, stop at the first rung that holds (the ladder runs after you understand the problem, not instead of it — read the code it touches and trace the real flow first):\n1. Does this need to be built at all? (YAGNI)\n2. Does it already exist in this codebase? Reuse what is already here, do not re-write it.\n3. Does the standard library do this? Use it.\n4. Does a native platform feature cover it? Use it.\n5. Does an already-installed dependency solve it? Use it.\n6. Can this be one line? Make it one line.\n7. Only then: write the minimum code that works.\n\nBug fix = root cause, not symptom: grep every caller of the function you touch and fix the shared function once (a smaller diff than one guard per caller); patching only the path the ticket names leaves a sibling caller broken.\n\n## Rules\n\nNo abstractions that were not requested. No avoidable dependencies. No boilerplate nobody asked for. Deletion over addition. Boring over clever. Fewest files possible. Ship the lazy version and question the complex request in the same response — never stall. Between two same-size stdlib options, pick the one correct on edge cases. Mark deliberate simplifications that cut a real corner with a known ceiling, using a `ponytail:` comment that names the ceiling and upgrade path.\n\n## Output\n\nCode first. Then at most three short lines: what was skipped, when to add it. If the explanation is longer than the code, delete the explanation. Explanation the user explicitly asked for is not debt, give it in full.\n\n## When NOT to be lazy\n\nNever simplify away: understanding the problem (read it fully and trace the real flow before picking a rung — a small diff you do not understand is just laziness dressed up as efficiency), input validation at trust boundaries, error handling that prevents data loss, security measures, accessibility basics, the calibration real hardware needs (the platform is never the spec ideal), anything the user explicitly asked to keep. Lazy code without its check is unfinished: non-trivial logic leaves ONE runnable check behind (assert-based demo/self-check or one small test file; no frameworks). Trivial one-liners need no test.\n\n## Boundaries\n\nPonytail governs what you build, not how you talk. \"stop ponytail\" or \"normal mode\": revert. Level persists until changed.";
1547
+ const COMMON_RULES = [
1548
+ "Understand the problem before choosing a solution: read the code the change touches and trace the real flow end to end. Laziness that skips comprehension ships a confident wrong fix.",
1549
+ "Reuse what already exists in this codebase before writing anything new.",
1550
+ "Reach for the standard library, platform-native features, and already-installed dependencies before custom code.",
1551
+ "A non-trivial change leaves ONE minimal runnable check behind (an assert-based self-check or one small test file; no frameworks). Trivial one-liners need no test.",
1552
+ "Explain briefly, but never omit the key decisions."
1553
+ ].join("\n");
1554
+ /**
1555
+ * The never-cut list. Every non-`off` mode keeps these; intensities tune how
1556
+ * aggressively code is minimized, never what may be dropped.
1557
+ */
1558
+ const SAFETY_BOUNDARIES = [
1559
+ "Never cut, in any mode:",
1560
+ "- Input validation at trust boundaries.",
1561
+ "- Error handling that prevents data loss.",
1562
+ "- Security measures.",
1563
+ "- Accessibility basics.",
1564
+ "- Explicit acceptance criteria the user asked for.",
1565
+ "- Understanding the problem and tracing the real flow first.",
1566
+ "- \"Minimal diff\" is not a substitute for \"correct fix\"."
1567
+ ].join("\n");
1568
+ const MODE_RULES = {
1569
+ lite: [
1570
+ "Complete what is explicitly asked, including every acceptance criterion.",
1571
+ "Prefer reuse, the standard library, native features, and installed dependencies.",
1572
+ "You may name a simpler alternative in one line, but do not challenge or reject an explicit requirement.",
1573
+ "Output may be a little more complete than full; never cut an acceptance item to save lines."
1574
+ ].join("\n"),
1575
+ full: [
1576
+ "The ladder — stop at the first rung that holds:",
1577
+ "1. Does this need to exist at all? (YAGNI)",
1578
+ "2. Does it already exist in this codebase? Reuse it.",
1579
+ "3. Does the standard library do it? Use it.",
1580
+ "4. Does a native platform feature cover it? Use it.",
1581
+ "5. Does an already-installed dependency solve it? Use it.",
1582
+ "6. Can this be one line? Make it one line.",
1583
+ "7. Only then: the minimum code that works.",
1584
+ "Default to the shortest correct implementation; prefer deletion and reuse.",
1585
+ "Fix root causes, not symptoms: one guard in the shared function beats a guard in every caller."
1586
+ ].join("\n"),
1587
+ ultra: [
1588
+ "YAGNI extremist: default to deletion before addition.",
1589
+ "Actively question speculative features, caches, abstractions, configuration, and new dependencies.",
1590
+ "Prefer one-liners, the standard library, and native capabilities.",
1591
+ "Minimize files, dependencies, and code — but never the safety boundaries or acceptance criteria above.",
1592
+ "For a complex request: ship the minimal correct version first and state what the full version would require.",
1593
+ "Ultra is not \"refuse everything\": honor explicit user requirements."
1594
+ ].join("\n")
1595
+ };
1596
+ const MODE_LABELS = {
1597
+ lite: "Lite",
1598
+ full: "Full",
1599
+ ultra: "Ultra"
1600
+ };
1601
+ /** Compose the complete section text for one intensity. */
1602
+ function render(effective) {
1603
+ return [
1604
+ `PONYTAIL MODE ACTIVE — level: ${effective}`,
1605
+ "",
1606
+ INTRO,
1607
+ "",
1608
+ "## Common rules (all modes)",
1609
+ COMMON_RULES,
1610
+ "",
1611
+ "## Safety boundaries (never cut)",
1612
+ SAFETY_BOUNDARIES,
1613
+ "",
1614
+ `## ${MODE_LABELS[effective]} rules`,
1615
+ MODE_RULES[effective]
1616
+ ].join("\n");
2339
1617
  }
2340
1618
  /**
2341
- * The full injected ruleset for one intensity: the "PONYTAIL MODE ACTIVE"
2342
- * header plus the body filtered down to that mode's rows and examples.
2343
- * Returns an empty string for `off` (ponytail contributes nothing).
1619
+ * The injected ruleset for one intensity, composed from the structured
1620
+ * fragments above. Returns an empty string for `off` (ponytail contributes
1621
+ * nothing). Renders are pure per mode and cached so every turn's bytes stay
1622
+ * identical.
2344
1623
  */
2345
1624
  function getPonytailInstructions(mode) {
2346
1625
  const effective = normalizeRuntimeMode(mode) ?? "full";
2347
1626
  if (effective === "off") return "";
2348
1627
  const cached = instructionCache.get(effective);
2349
1628
  if (cached !== void 0) return cached;
2350
- let body;
2351
- try {
2352
- body = filterSkillBodyForMode(PONYTAIL_SKILL_BODY, effective);
2353
- } catch {
2354
- return fallbackInstructions(effective);
2355
- }
2356
- const rendered = "PONYTAIL MODE ACTIVE — level: " + effective + "\n\n" + body;
1629
+ const rendered = render(effective);
2357
1630
  instructionCache.set(effective, rendered);
2358
1631
  return rendered;
2359
1632
  }
@@ -2451,7 +1724,7 @@ function registerCommands(deps, commandCtx) {
2451
1724
  kind: "error",
2452
1725
  text: "Usage: /ponytail default [lite|full|ultra|off]"
2453
1726
  };
2454
- const effective = readDefaultMode();
1727
+ const effective = readDefaultMode(process.env, deps.profileMode);
2455
1728
  deps.setDefault(effective);
2456
1729
  if (written === effective) {
2457
1730
  agent.steer(createUserMessage({
@@ -2469,10 +1742,11 @@ function registerCommands(deps, commandCtx) {
2469
1742
  text: `Ponytail default set — new sessions start in ${written}.`
2470
1743
  };
2471
1744
  }
1745
+ const reason = defaultOverrideReason(process.env, deps.profileMode) ?? "PONYTAIL_DEFAULT_MODE";
2472
1746
  agent.steer(createUserMessage({
2473
1747
  content: [{
2474
1748
  type: "text",
2475
- text: `PONYTAIL DEFAULT SET — saved ${written}, effective ${effective} (PONYTAIL_DEFAULT_MODE).`
1749
+ text: `PONYTAIL DEFAULT SET — saved ${written}, effective ${effective} (${reason}).`
2476
1750
  }],
2477
1751
  source: {
2478
1752
  kind: "plugin",
@@ -2481,7 +1755,7 @@ function registerCommands(deps, commandCtx) {
2481
1755
  }));
2482
1756
  return {
2483
1757
  kind: "success",
2484
- text: `Saved default: ${written}. Effective default: ${effective}, overridden by PONYTAIL_DEFAULT_MODE.`
1758
+ text: `Saved default: ${written}. Effective default: ${effective}, overridden by ${reason}.`
2485
1759
  };
2486
1760
  }
2487
1761
  if (input === "status") return {
@@ -2590,7 +1864,9 @@ function descriptionFor(skill) {
2590
1864
  * Register the always-on ruleset section, the runtime skills, the slash
2591
1865
  * commands, and the plain-text deactivation listener.
2592
1866
  */
2593
- function apply(ctx) {
1867
+ function apply(ctx, config = {}) {
1868
+ const profileMode = normalizeRuntimeMode(config.defaultMode);
1869
+ if (config.defaultMode !== void 0 && profileMode === null) ctx.logger.warn(`[ponytail] profile config defaultMode is not lite|full|ultra|off: ${JSON.stringify(config.defaultMode)}; falling back`);
2594
1870
  let defaultMode = null;
2595
1871
  const warned = /* @__PURE__ */ new Set();
2596
1872
  const warnOnce = (key, message) => {
@@ -2599,7 +1875,7 @@ function apply(ctx) {
2599
1875
  ctx.logger.warn(`[ponytail] ${message}`);
2600
1876
  };
2601
1877
  const refreshDefault = () => {
2602
- const resolution = readDefaultModeInfo();
1878
+ const resolution = readDefaultModeInfo(process.env, profileMode);
2603
1879
  if (resolution.issue) warnOnce(`default:${resolution.issue.kind}`, `${resolution.issue.detail}; using ${resolution.mode}`);
2604
1880
  defaultMode = resolution.mode;
2605
1881
  return defaultMode;
@@ -2614,7 +1890,7 @@ function apply(ctx) {
2614
1890
  if (matcherResult.invalid) warnOnce("matcher:invalid", "PONYTAIL_SUBAGENT_MATCHER is not a valid regular expression; ignoring it (fail-open).");
2615
1891
  const configFile = configPath();
2616
1892
  const onConfigChange = () => {
2617
- const resolution = readDefaultModeInfo();
1893
+ const resolution = readDefaultModeInfo(process.env, profileMode);
2618
1894
  if (resolution.issue) {
2619
1895
  warnOnce(`config:${resolution.issue.kind}`, `${resolution.issue.detail}; keeping the previous default`);
2620
1896
  return;
@@ -2644,6 +1920,7 @@ function apply(ctx) {
2644
1920
  registerCommands({
2645
1921
  ctx,
2646
1922
  store,
1923
+ profileMode,
2647
1924
  defaultMode: readDefault,
2648
1925
  setDefault
2649
1926
  }, commandCtx);