hafez 1.3.0 → 1.4.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.
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import type { BatchOperation, CreateEntityFields, CreateKnowledgeFields, SessionLogEntry, UpdateFields } from './types.js';
2
+ import type { BatchOperation, CreateEntityFields, CreateKnowledgeFields, KnowledgeSubtype, SessionLogEntry, UpdateFields } from './types.js';
3
3
  export declare const SessionLogSchema: z.ZodObject<{
4
4
  type: z.ZodEnum<{
5
5
  [x: string]: string;
@@ -59,6 +59,9 @@ export declare const CreateKnowledgeFieldsSchema: z.ZodObject<{
59
59
  subtype: z.ZodOptional<z.ZodEnum<{
60
60
  [x: string]: string;
61
61
  }>>;
62
+ status: z.ZodOptional<z.ZodEnum<{
63
+ [x: string]: string;
64
+ }>>;
62
65
  synthesis: z.ZodOptional<z.ZodString>;
63
66
  insight: z.ZodOptional<z.ZodString>;
64
67
  description: z.ZodOptional<z.ZodString>;
@@ -142,21 +145,33 @@ export declare const BatchOperationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
142
145
  knowledge: "knowledge";
143
146
  }>;
144
147
  }, z.core.$strict>], "op">;
148
+ /** Canonicalize an operation's declared aliases.
149
+ *
150
+ * Precedence when both are present: the canonical field wins and the alias is
151
+ * dropped. Never mutates the caller's object — an op carrying no alias is
152
+ * returned as-is. */
153
+ export declare function normalizeAliases<T>(op: T): T;
154
+ /** normalizeAliases over a payload — the form the Spine and dry-run use. */
155
+ export declare function normalizeOperations(operations: BatchOperation[]): BatchOperation[];
156
+ /** What a slug resolves to. `subtype` is carried for knowledge so ops that
157
+ * gate on the plan/insight distinction see it for files already on disk AND
158
+ * for notes created earlier in the same batch. */
159
+ export interface ResolvedTarget {
160
+ kind: 'entity' | 'knowledge';
161
+ type: string;
162
+ subtype?: KnowledgeSubtype;
163
+ }
145
164
  /** Vault context handed to each spec's semantic check by validateBatchCore.
146
165
  * Simulation-aware: creates earlier in the batch count as existing. */
147
166
  export interface OpCheckContext {
148
- resolve(slug: string): {
149
- kind: 'entity' | 'knowledge';
150
- type: string;
151
- } | null;
167
+ resolve(slug: string): ResolvedTarget | null;
152
168
  existsAnywhere(slug: string): boolean;
153
169
  sessionExists(slug: string): boolean;
154
- simulate(slug: string, info: {
155
- kind: 'entity' | 'knowledge';
156
- type: string;
157
- }): void;
170
+ simulate(slug: string, info: ResolvedTarget): void;
158
171
  simulateSession(slug: string): void;
159
- validateKindFields(kind: 'entity' | 'knowledge', fields: UpdateFields): void;
172
+ /** Kind gating is subtype-aware for knowledge: a plan carries `status`,
173
+ * an insight does not (see contracts.SUBTYPE_STATUSES). */
174
+ validateKindFields(kind: 'entity' | 'knowledge', fields: UpdateFields, subtype?: KnowledgeSubtype): void;
160
175
  validateSessionLogEntry(entry: SessionLogEntry): string[];
161
176
  buildEntityFrontmatter(name: string, fields: CreateEntityFields): void;
162
177
  buildKnowledgeFrontmatter(name: string, fields: CreateKnowledgeFields): void;
@@ -166,7 +181,31 @@ export interface OpCheckResult {
166
181
  errors: string[];
167
182
  created?: boolean;
168
183
  warning?: string;
184
+ /** Error class of the primary failure, when it maps to a specific
185
+ * HafezError code. The Spine uses it so single-op callers keep NOT_FOUND /
186
+ * SLUG_EXISTS semantics (and their exit codes) through spec validation;
187
+ * batch-level failures stay VALIDATION_FAILED regardless. */
188
+ primaryCode?: 'NOT_FOUND' | 'SLUG_EXISTS';
169
189
  }
190
+ /** The one source of the missing-slug error text — specs and the Spine's
191
+ * shape-failure precedence rule (validateBatchCore) must not drift apart. */
192
+ export declare function notFoundError(slug: string): string;
193
+ /** The one op-error line format. Every surface that localises an error to its
194
+ * op — the validate phase's error list, apply's execution-time failures, and
195
+ * the CLI's dry-run report — renders it through here, so an agent parsing
196
+ * `op[i] (op slug): …` sees one shape whichever surface produced it. */
197
+ export declare function opErrorLine(index: number, op: string, slug: string, message: string): string;
198
+ /** The one "the batch was rejected, nothing landed" header — shared by the
199
+ * library's validate gate and the CLI's shape-parse gate, which reject the
200
+ * same payload at different depths and must say so identically. `batch
201
+ * --dry-run` deliberately words its own header differently: it reports a
202
+ * hypothetical ("would be applied"), not a refusal. */
203
+ export declare function batchInvalidHeader(invalidCount: number, total: number): string;
204
+ /** The one source of the duplicate-session disposition text. A duplicate
205
+ * session is not an error — the op is skipped and the batch continues — and
206
+ * dry-run (spec warning) and apply (BatchResult.warning) report it with this
207
+ * same sentence so the two can never drift. */
208
+ export declare function duplicateSessionWarning(slug: string): string;
170
209
  export interface OpSpec {
171
210
  /** Stable identifier used by `schema <name>` and the op catalog. */
172
211
  name: string;
@@ -175,13 +214,257 @@ export interface OpSpec {
175
214
  schema: z.ZodType;
176
215
  description: string;
177
216
  /** Optional fields force-included in the generated example payload. */
178
- exampleInclude: string[];
217
+ exampleInclude: readonly string[];
179
218
  check(op: any, ctx: OpCheckContext): OpCheckResult;
180
219
  }
181
- export declare const OP_SPECS: OpSpec[];
220
+ declare const OP_SPEC_ROWS: readonly [{
221
+ readonly name: "update";
222
+ readonly op: "update";
223
+ readonly schema: z.ZodObject<{
224
+ op: z.ZodLiteral<"update">;
225
+ slug: z.ZodString;
226
+ fields: z.ZodObject<{
227
+ status: z.ZodOptional<z.ZodEnum<{
228
+ [x: string]: string;
229
+ }>>;
230
+ current_state: z.ZodOptional<z.ZodString>;
231
+ session_log: z.ZodOptional<z.ZodObject<{
232
+ type: z.ZodEnum<{
233
+ [x: string]: string;
234
+ }>;
235
+ summary: z.ZodString;
236
+ body: z.ZodOptional<z.ZodString>;
237
+ agent: z.ZodString;
238
+ }, z.core.$strict>>;
239
+ brief: z.ZodOptional<z.ZodNullable<z.ZodString>>;
240
+ add_action: z.ZodOptional<z.ZodString>;
241
+ add_actions: z.ZodOptional<z.ZodArray<z.ZodString>>;
242
+ complete_action: z.ZodOptional<z.ZodString>;
243
+ remove_action: z.ZodOptional<z.ZodString>;
244
+ clear_actions: z.ZodOptional<z.ZodBoolean>;
245
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
246
+ resource: z.ZodOptional<z.ZodNullable<z.ZodString>>;
247
+ domain: z.ZodOptional<z.ZodArray<z.ZodString>>;
248
+ confidence: z.ZodOptional<z.ZodEnum<{
249
+ [x: string]: string;
250
+ }>>;
251
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
252
+ related: z.ZodOptional<z.ZodArray<z.ZodString>>;
253
+ synthesis: z.ZodOptional<z.ZodString>;
254
+ insight: z.ZodOptional<z.ZodString>;
255
+ add_evidence: z.ZodOptional<z.ZodString>;
256
+ add_source: z.ZodOptional<z.ZodString>;
257
+ }, z.core.$strict>;
258
+ }, z.core.$strict>;
259
+ readonly description: "Update an entity or knowledge note (status, brief, next actions, session log, synthesis, …). `insight` is an alias for `synthesis` (knowledge-only; ignored when `synthesis` is also given). On knowledge notes `status` is plan-subtype only (active|done); insights reject it.";
260
+ readonly exampleInclude: readonly ["status"];
261
+ readonly check: (op: Extract<BatchOperation, {
262
+ op: "update";
263
+ }>, ctx: OpCheckContext) => {
264
+ slug: string;
265
+ errors: string[];
266
+ primaryCode: "SLUG_EXISTS" | "NOT_FOUND" | undefined;
267
+ };
268
+ }, {
269
+ readonly name: "create-entity";
270
+ readonly op: "create";
271
+ readonly kind: "entity";
272
+ readonly schema: z.ZodObject<{
273
+ op: z.ZodLiteral<"create">;
274
+ kind: z.ZodLiteral<"entity">;
275
+ name: z.ZodString;
276
+ fields: z.ZodObject<{
277
+ type: z.ZodEnum<{
278
+ [x: string]: string;
279
+ }>;
280
+ purpose: z.ZodOptional<z.ZodString>;
281
+ description: z.ZodOptional<z.ZodString>;
282
+ resource: z.ZodOptional<z.ZodString>;
283
+ domain: z.ZodOptional<z.ZodArray<z.ZodString>>;
284
+ parent: z.ZodOptional<z.ZodString>;
285
+ related: z.ZodOptional<z.ZodArray<z.ZodString>>;
286
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
287
+ brief: z.ZodOptional<z.ZodString>;
288
+ add_action: z.ZodOptional<z.ZodString>;
289
+ add_actions: z.ZodOptional<z.ZodArray<z.ZodString>>;
290
+ }, z.core.$strict>;
291
+ }, z.core.$strict>;
292
+ readonly description: "Create a new entity (capture / entity / project). A field whose section the chosen type lacks is rejected, never dropped — `purpose` needs type project.";
293
+ readonly exampleInclude: readonly ["type"];
294
+ readonly check: (op: Extract<BatchOperation, {
295
+ op: "create";
296
+ kind: "entity";
297
+ }>, ctx: OpCheckContext) => {
298
+ slug: string;
299
+ errors: string[];
300
+ created: boolean;
301
+ primaryCode: "SLUG_EXISTS" | "NOT_FOUND" | undefined;
302
+ };
303
+ }, {
304
+ readonly name: "create-knowledge";
305
+ readonly op: "create";
306
+ readonly kind: "knowledge";
307
+ readonly schema: z.ZodObject<{
308
+ op: z.ZodLiteral<"create">;
309
+ kind: z.ZodLiteral<"knowledge">;
310
+ name: z.ZodString;
311
+ fields: z.ZodOptional<z.ZodObject<{
312
+ subtype: z.ZodOptional<z.ZodEnum<{
313
+ [x: string]: string;
314
+ }>>;
315
+ status: z.ZodOptional<z.ZodEnum<{
316
+ [x: string]: string;
317
+ }>>;
318
+ synthesis: z.ZodOptional<z.ZodString>;
319
+ insight: z.ZodOptional<z.ZodString>;
320
+ description: z.ZodOptional<z.ZodString>;
321
+ domain: z.ZodOptional<z.ZodArray<z.ZodString>>;
322
+ related: z.ZodOptional<z.ZodArray<z.ZodString>>;
323
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
324
+ 'session-date': z.ZodOptional<z.ZodString>;
325
+ }, z.core.$strict>>;
326
+ }, z.core.$strict>;
327
+ readonly description: "Create a new knowledge note (insight / plan subtype). `insight` is an alias for `synthesis` (ignored when `synthesis` is also given). `status` (active|done) is plan-only; `synthesis` is insight-only — plan bodies are Goal/Steps/Dependencies, and a field whose section the subtype lacks is rejected, never dropped.";
328
+ readonly exampleInclude: readonly ["subtype", "synthesis"];
329
+ readonly check: (op: Extract<BatchOperation, {
330
+ op: "create";
331
+ kind: "knowledge";
332
+ }>, ctx: OpCheckContext) => {
333
+ slug: string;
334
+ errors: string[];
335
+ created: boolean;
336
+ primaryCode: "SLUG_EXISTS" | "NOT_FOUND" | undefined;
337
+ };
338
+ }, {
339
+ readonly name: "create-session";
340
+ readonly op: "create";
341
+ readonly kind: "session";
342
+ readonly schema: z.ZodObject<{
343
+ op: z.ZodLiteral<"create">;
344
+ kind: z.ZodLiteral<"session">;
345
+ name: z.ZodString;
346
+ fields: z.ZodOptional<z.ZodObject<{
347
+ synthesis: z.ZodOptional<z.ZodString>;
348
+ insight: z.ZodOptional<z.ZodString>;
349
+ related: z.ZodOptional<z.ZodArray<z.ZodString>>;
350
+ 'session-date': z.ZodOptional<z.ZodString>;
351
+ }, z.core.$strict>>;
352
+ }, z.core.$strict>;
353
+ readonly description: "Create a new session note linked to an entity. `insight` is an alias for `synthesis` (ignored when `synthesis` is also given).";
354
+ readonly exampleInclude: readonly ["synthesis"];
355
+ readonly check: (op: Extract<BatchOperation, {
356
+ op: "create";
357
+ kind: "session";
358
+ }>, ctx: OpCheckContext) => {
359
+ slug: string;
360
+ errors: never[];
361
+ warning: string;
362
+ created?: undefined;
363
+ } | {
364
+ slug: string;
365
+ errors: never[];
366
+ created: true;
367
+ warning?: undefined;
368
+ };
369
+ }, {
370
+ readonly name: "capture";
371
+ readonly op: "capture";
372
+ readonly schema: z.ZodObject<{
373
+ op: z.ZodLiteral<"capture">;
374
+ name: z.ZodString;
375
+ notes: z.ZodOptional<z.ZodString>;
376
+ }, z.core.$strict>;
377
+ readonly description: "Quick capture — a raw inbox item with no structure required.";
378
+ readonly exampleInclude: readonly ["notes"];
379
+ readonly check: (op: Extract<BatchOperation, {
380
+ op: "capture";
381
+ }>, ctx: OpCheckContext) => {
382
+ slug: string;
383
+ errors: string[];
384
+ created: boolean;
385
+ primaryCode: "SLUG_EXISTS" | "NOT_FOUND" | undefined;
386
+ };
387
+ }, {
388
+ readonly name: "link";
389
+ readonly op: "link";
390
+ readonly schema: z.ZodObject<{
391
+ op: z.ZodLiteral<"link">;
392
+ slug: z.ZodString;
393
+ target: z.ZodString;
394
+ relation: z.ZodEnum<{
395
+ parent: "parent";
396
+ related: "related";
397
+ }>;
398
+ }, z.core.$strict>;
399
+ readonly description: "Add a parent or related link between two entities.";
400
+ readonly exampleInclude: readonly [];
401
+ readonly check: (op: Extract<BatchOperation, {
402
+ op: "link";
403
+ }>, ctx: OpCheckContext) => {
404
+ slug: string;
405
+ errors: string[];
406
+ primaryCode: "SLUG_EXISTS" | "NOT_FOUND" | undefined;
407
+ };
408
+ }, {
409
+ readonly name: "unlink";
410
+ readonly op: "unlink";
411
+ readonly schema: z.ZodObject<{
412
+ op: z.ZodLiteral<"unlink">;
413
+ slug: z.ZodString;
414
+ target: z.ZodString;
415
+ relation: z.ZodEnum<{
416
+ parent: "parent";
417
+ related: "related";
418
+ }>;
419
+ }, z.core.$strict>;
420
+ readonly description: "Remove a parent or related link between two entities.";
421
+ readonly exampleInclude: readonly [];
422
+ readonly check: (op: Extract<BatchOperation, {
423
+ op: "unlink";
424
+ }>, ctx: OpCheckContext) => {
425
+ slug: string;
426
+ errors: string[];
427
+ primaryCode: "SLUG_EXISTS" | "NOT_FOUND" | undefined;
428
+ };
429
+ }, {
430
+ readonly name: "promote";
431
+ readonly op: "promote";
432
+ readonly schema: z.ZodObject<{
433
+ op: z.ZodLiteral<"promote">;
434
+ slug: z.ZodString;
435
+ target: z.ZodEnum<{
436
+ entity: "entity";
437
+ project: "project";
438
+ knowledge: "knowledge";
439
+ }>;
440
+ }, z.core.$strict>;
441
+ readonly description: "Promote a capture to entity/project/knowledge, or an entity to project.";
442
+ readonly exampleInclude: readonly [];
443
+ readonly check: (op: Extract<BatchOperation, {
444
+ op: "promote";
445
+ }>, ctx: OpCheckContext) => {
446
+ slug: string;
447
+ errors: string[];
448
+ primaryCode: "SLUG_EXISTS" | "NOT_FOUND" | undefined;
449
+ };
450
+ }];
451
+ /** Every op the table defines. The Apply table (src/apply.ts) is keyed by this
452
+ * union, so a spec row added here without an apply arm fails to build. */
453
+ export type OpSpecName = (typeof OP_SPEC_ROWS)[number]['name'];
454
+ export declare const OP_SPECS: readonly (OpSpec & {
455
+ name: OpSpecName;
456
+ })[];
182
457
  /** Find the spec for a (shape-valid) operation. */
183
- export declare function specFor(op: BatchOperation): OpSpec;
458
+ export declare function specFor(op: BatchOperation): OpSpec & {
459
+ name: OpSpecName;
460
+ };
184
461
  /** Op names for error text ("expected one of […]") — derived, never hardcoded. */
185
462
  export declare function validOpNames(): string[];
463
+ /** Ops whose payload addresses an existing file by `slug` — derived from the
464
+ * Zod shapes, so a new slug-bearing op picks up slug-level rules (the
465
+ * NOT_FOUND precedence on shape failure in validateBatchCore) without a
466
+ * hand-kept list. */
467
+ export declare function slugBearingOpNames(): string[];
186
468
  /** Create kinds for error text — derived from the table. */
187
469
  export declare function validCreateKinds(): string[];
470
+ export {};