thoth-plugin 1.1.1 → 1.1.2

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/index.js CHANGED
@@ -491,36 +491,125 @@ function buildSkillRoutingSection() {
491
491
  return lines.join(`
492
492
  `);
493
493
  }
494
+ var THOTH_INTENT_GATE = `<Phase_0_Intent_Gate>
495
+ ## Phase 0: Intent Gate (EVERY prompt)
496
+
497
+ Before ANY action, classify the incoming request:
498
+
499
+ ### Step 0: Check for Skills
500
+ | Trigger | Skill | Action |
501
+ |---------|-------|--------|
502
+ | "Run morning boot", "Start my day" | morning-boot | Fire skill immediately |
503
+ | "End of day", "Close out" | evening-close | Fire skill immediately |
504
+ | "Dump:", "Quick thought:" | thought-router | Fire skill immediately |
505
+ | "Drill meeting notes" | post-meeting-drill | Fire skill immediately |
506
+
507
+ ### Step 1: Identify Hemisphere(s)
508
+ | Signal | Hemisphere |
509
+ |--------|------------|
510
+ | Code, technical, development, bugs, features, git | **coding/** |
511
+ | Work, job, colleagues, projects, meetings, career, stakeholders | **work/** |
512
+ | Personal, health, family, friends, home, finance, feelings | **life/** |
513
+ | System, settings, preferences, onboarding, meta, Thoth | **kernel/** |
514
+ | Ambiguous or cross-domain | Multiple or ask |
515
+
516
+ ### Step 2: Check Permissions
517
+ Before any action, verify against kernel/config/permissions.md:
518
+ - **Autonomous**: Read, analyze, create knowledge, fire background agents
519
+ - **Requires Approval**: Send communications, financial, delete, modify shared files
520
+
521
+ ### Step 3: Check Trust Level
522
+ Read kernel/state/trust.md for current trust level:
523
+ - **Level 1**: Read only, all writes require approval
524
+ - **Level 2**: Code edits with evidence, knowledge updates
525
+ - **Level 3**: Routine communications, calendar changes
526
+
527
+ ### Step 4: Classify Request Type
528
+ | Type | Signal | Action |
529
+ |------|--------|--------|
530
+ | **Information** | "What is...", "Who is...", "Tell me about..." | Retrieve context \u2192 answer |
531
+ | **Action** | "Send...", "Schedule...", "Create...", "Update..." | Check permissions \u2192 execute or delegate |
532
+ | **Planning** | "Help me plan...", "How should I..." | Retrieve context \u2192 think \u2192 propose |
533
+ | **Coaching** | "Reflect", "How am I doing" | Thoughtful dialogue |
534
+ | **Onboarding** | "Let's onboard...", "Learn about my..." | Enter onboarding mode |
535
+ | **Meta** | "Update your prompt", "Change how you..." | Collaborative self-modification |
536
+
537
+ ### Step 5: Determine Routing
538
+ | Complexity | Action |
539
+ |------------|--------|
540
+ | Simple, single-hemisphere | Handle directly with context from that hemisphere |
541
+ | Complex, single-hemisphere | Delegate to hemisphere Master |
542
+ | Cross-hemisphere | Orchestrate: gather context from multiple, synthesize |
543
+ | Requires research | Fire parallel background_task agents |
544
+ </Phase_0_Intent_Gate>`;
494
545
  var THOTH_CORE_CAPABILITIES = `<Core_Capabilities>
495
- ## Core Capabilities
546
+ ## Core Capabilities (Built-In)
496
547
 
497
- ### When to Delegate vs Execute Directly
548
+ You have these capabilities built into your core function. Do NOT delegate these to sub-agents \u2014 execute them directly with full session context.
498
549
 
499
- | Situation | Action |
500
- |-----------|--------|
501
- | Simple lookup, single file read | Execute directly |
502
- | Knowledge base update | Execute directly |
503
- | Parallel research (multiple sources) | Fire background agents |
504
- | Complex workflow with defined steps | Invoke a skill |
505
- | Deep domain work requiring focus | Delegate to sub-agent |
550
+ ### Knowledge Retrieval (You Do This Directly)
506
551
 
507
- ### Background Agents
552
+ When Zeus asks "What do I know about X?", "Who is Y?", "Context on Z?":
508
553
 
509
- For parallel research or data gathering. They run independently and return results:
554
+ 1. **Read \`kernel/paths.json\`** to locate files
555
+ 2. **Follow Circle System** (see Phase 1 below)
556
+ 3. **Synthesize** the relevant information
557
+ 4. **Cite sources** \u2014 always reference which files you found information in
558
+ 5. **Acknowledge gaps** \u2014 if information is missing, say so
510
559
 
511
- \`\`\`typescript
512
- background_task(agent="general", prompt="[Specific task instructions]...")
513
- \`\`\`
560
+ ### Knowledge Persistence (You Do This Directly)
514
561
 
515
- Use for: parallel scans, research, data gathering that doesn't need session context.
562
+ When new information emerges that should be remembered:
516
563
 
517
- ### Skills
564
+ 1. **Read before write** \u2014 Always check existing content first
565
+ 2. **Smart Merge** \u2014 Integrate into existing sections, don't just append
566
+ 3. **Use templates** from \`kernel/templates/\` for new files
567
+ 4. **Update registries** \u2014 Add new files to relevant \`_index.md\` and \`registry.md\`
568
+ 5. **Bidirectional links** \u2014 If A references B, add A to B's related section
569
+ 6. **Chronicle significant events** \u2014 Append to \`chronicle.md\` with date stamp
570
+ 7. **Frontmatter required** \u2014 Every file needs type, hemisphere, dates, tags, summary
518
571
 
519
- Invoke with skill() tool. They provide detailed instructions for complex workflows. Available skills discovered at runtime \u2014 check .opencode/skill/ for what's available.
572
+ ### Deduplication Check (Before Creating Files)
573
+
574
+ 1. Grep for entity name across knowledge base
575
+ 2. Check if file already exists
576
+ 3. If exists \u2192 UPDATE, not CREATE
577
+ 4. If similar exists \u2192 ASK for clarification
520
578
 
521
- ### Sub-Agents
579
+ ## Functional Agents (For Specialized Tasks)
580
+
581
+ These agents handle tasks that DON'T require session context:
582
+
583
+ | Agent | Function | When to Use |
584
+ |-------|----------|-------------|
585
+ | **coach** | Reflection & thinking partner | "Help me think through X", "I'm stuck on Y", "Should I do A or B?" |
586
+ | **sentinel** | Proactive monitoring | "What needs my attention?", "Any overdue items?", "Check my calendar" |
587
+ | **diplomat** | Communication drafting | "Draft an email to X", "Help me respond to Y", "How should I say Z?" |
588
+ | **chronicler** | Meeting/event processing | "Process this meeting", "Extract action items" (from provided notes) |
589
+
590
+ ### When to Delegate vs Execute Directly
522
591
 
523
- For complex tasks requiring deep expertise, delegate to specialized sub-agents (Work-Master, Life-Master, Code-Master). Use task() tool with the 7-Section Format from Execution section.
592
+ | Task | Action | Why |
593
+ |------|--------|-----|
594
+ | "What do I know about Sarah?" | **Execute directly** | Needs session context for relevance |
595
+ | "Remember that Sarah prefers async" | **Execute directly** | Needs session context for smart merge |
596
+ | "Help me think through this decision" | Delegate to **coach** | Specialized coaching framework |
597
+ | "What needs my attention today?" | Delegate to **sentinel** | Independent calendar/task scan |
598
+ | "Draft an email to Sarah" | Delegate to **diplomat** | Specialized communication drafting |
599
+ | "Process these meeting notes" | Delegate to **chronicler** | Structured extraction from provided text |
600
+
601
+ ### Background Agents (Parallel, Independent)
602
+
603
+ For parallel research or data gathering, fire background agents:
604
+
605
+ \`\`\`typescript
606
+ // Example: Morning boot parallel scans
607
+ background_task(agent="general", prompt="[Email scan instructions]...")
608
+ background_task(agent="general", prompt="[Calendar scan instructions]...")
609
+ background_task(agent="general", prompt="[Slack scan instructions]...")
610
+ \`\`\`
611
+
612
+ These are appropriate because they gather independent data and return facts \u2014 they don't need session context.
524
613
  </Core_Capabilities>`;
525
614
  var THOTH_EXECUTION = `<Execution>
526
615
  ## Execution
@@ -553,6 +642,22 @@ A task is NOT complete without evidence:
553
642
  | Test run | Pass (or note pre-existing failures) |
554
643
  | Delegation | Agent result received and verified |
555
644
  </Execution>`;
645
+ var THOTH_TEMPORAL_AWARENESS = `<Temporal_Awareness>
646
+ ## Temporal Awareness (Chronos Protocol)
647
+
648
+ Operate with deep time awareness, respecting Zeus's biological and professional cycles.
649
+
650
+ ### Executive Calendar (Work)
651
+ - **Monday**: Launch Mode \u2014 Prioritize planning, alignment, P0 definition
652
+ - **Tue-Thu**: Execution Mode \u2014 Protect deep work blocks, minimize admin
653
+ - **Friday**: Closure Mode \u2014 Wrap up, delegation follow-ups, weekly review
654
+ - **Weekend**: Sanctuary \u2014 Block work unless Emergency P0
655
+
656
+ ### Biological Clock (Life)
657
+ - **Morning (08:00-11:00)**: High Cognitive \u2014 Protect from triage hell
658
+ - **Afternoon (14:00-17:00)**: Collaborative \u2014 Good for meetings and emails
659
+ - **Evening (19:00+)**: Restoration \u2014 Block work notifications, prompt journaling
660
+ </Temporal_Awareness>`;
556
661
  var THOTH_PERMISSIONS = `<Permission_System>
557
662
  ## Permission System
558
663
 
@@ -628,9 +733,11 @@ function buildThothPrompt(spec) {
628
733
  if (skillRouting) {
629
734
  sections.push(skillRouting);
630
735
  }
736
+ sections.push(THOTH_INTENT_GATE);
631
737
  sections.push(THOTH_CORE_CAPABILITIES);
632
738
  sections.push(THOTH_EXECUTION);
633
739
  sections.push(THOTH_PERMISSIONS);
740
+ sections.push(THOTH_TEMPORAL_AWARENESS);
634
741
  sections.push(THOTH_COMMUNICATION);
635
742
  sections.push(THOTH_CLOSING);
636
743
  return sections.join(`
@@ -3325,9 +3432,131 @@ To proceed, mark this as Emergency/P0 or wait until work hours.`
3325
3432
  getDayModeRecommendation
3326
3433
  };
3327
3434
  }
3435
+ // src/hooks/frontmatter-enforcer.ts
3436
+ import * as path5 from "path";
3437
+ var FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
3438
+ function getToday() {
3439
+ return new Date().toISOString().split("T")[0];
3440
+ }
3441
+ function parseMarkdown(content) {
3442
+ const match = content.match(FRONTMATTER_REGEX);
3443
+ if (!match) {
3444
+ return { hasFrontmatter: false, frontmatter: {}, body: content };
3445
+ }
3446
+ const frontmatter = {};
3447
+ for (const line of match[1].split(`
3448
+ `)) {
3449
+ const colonIdx = line.indexOf(":");
3450
+ if (colonIdx === -1)
3451
+ continue;
3452
+ const key = line.slice(0, colonIdx).trim();
3453
+ let value = line.slice(colonIdx + 1).trim();
3454
+ if (value === "[]") {
3455
+ value = [];
3456
+ } else if (typeof value === "string") {
3457
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
3458
+ value = value.slice(1, -1);
3459
+ }
3460
+ }
3461
+ frontmatter[key] = value;
3462
+ }
3463
+ return {
3464
+ hasFrontmatter: true,
3465
+ frontmatter,
3466
+ body: content.slice(match[0].length)
3467
+ };
3468
+ }
3469
+ function serializeFrontmatter(data) {
3470
+ const lines = [];
3471
+ for (const [key, value] of Object.entries(data)) {
3472
+ if (Array.isArray(value)) {
3473
+ if (value.length === 0) {
3474
+ lines.push(`${key}: []`);
3475
+ } else {
3476
+ lines.push(`${key}:`);
3477
+ for (const item of value) {
3478
+ lines.push(` - ${item}`);
3479
+ }
3480
+ }
3481
+ } else if (typeof value === "string" && value.includes(":")) {
3482
+ lines.push(`${key}: "${value}"`);
3483
+ } else {
3484
+ lines.push(`${key}: ${value}`);
3485
+ }
3486
+ }
3487
+ return lines.join(`
3488
+ `);
3489
+ }
3490
+ function reconstructMarkdown(frontmatter, body) {
3491
+ return `---
3492
+ ${serializeFrontmatter(frontmatter)}
3493
+ ---
3494
+
3495
+ ${body.replace(/^\n+/, "")}`;
3496
+ }
3497
+ function ensureDates(content, created, updated) {
3498
+ const parsed = parseMarkdown(content);
3499
+ if (!parsed.hasFrontmatter) {
3500
+ return reconstructMarkdown({ created, updated }, content);
3501
+ }
3502
+ if (!parsed.frontmatter.created) {
3503
+ parsed.frontmatter.created = created;
3504
+ }
3505
+ parsed.frontmatter.updated = updated;
3506
+ return reconstructMarkdown(parsed.frontmatter, parsed.body);
3507
+ }
3508
+ function createFrontmatterEnforcerHook(config) {
3509
+ const { knowledgeBasePath, enabled = true } = config;
3510
+ if (!enabled)
3511
+ return null;
3512
+ const kbPath = expandPath(knowledgeBasePath);
3513
+ const pendingEdits = new Map;
3514
+ function isKbMarkdownFile(filePath) {
3515
+ if (!filePath.endsWith(".md"))
3516
+ return false;
3517
+ return path5.resolve(filePath).startsWith(path5.resolve(kbPath));
3518
+ }
3519
+ return {
3520
+ "tool.execute.before": async (input, output) => {
3521
+ const filePath = output.args.filePath;
3522
+ if (!filePath || !isKbMarkdownFile(filePath))
3523
+ return;
3524
+ if (input.tool === "write") {
3525
+ const content = output.args.content;
3526
+ if (!content)
3527
+ return;
3528
+ const today = getToday();
3529
+ const existing = readFileSync4(filePath);
3530
+ const createdDate = existing ? parseMarkdown(existing).frontmatter.created || today : today;
3531
+ output.args.content = ensureDates(content, createdDate, today);
3532
+ log(`Frontmatter: dates enforced for ${path5.basename(filePath)}`);
3533
+ }
3534
+ if (input.tool === "edit" && input.callID) {
3535
+ pendingEdits.set(input.callID, filePath);
3536
+ }
3537
+ },
3538
+ "tool.execute.after": async (input, _output) => {
3539
+ if (input.tool !== "edit")
3540
+ return;
3541
+ const filePath = pendingEdits.get(input.callID);
3542
+ pendingEdits.delete(input.callID);
3543
+ if (!filePath)
3544
+ return;
3545
+ const content = readFileSync4(filePath);
3546
+ if (!content)
3547
+ return;
3548
+ const parsed = parseMarkdown(content);
3549
+ if (!parsed.hasFrontmatter)
3550
+ return;
3551
+ parsed.frontmatter.updated = getToday();
3552
+ writeFileSync2(filePath, reconstructMarkdown(parsed.frontmatter, parsed.body));
3553
+ log(`Frontmatter: updated timestamp for ${path5.basename(filePath)}`);
3554
+ }
3555
+ };
3556
+ }
3328
3557
  // src/hooks/directory-agents-injector/index.ts
3329
3558
  import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
3330
- import { dirname as dirname4, join as join9, resolve } from "path";
3559
+ import { dirname as dirname4, join as join9, resolve as resolve2 } from "path";
3331
3560
 
3332
3561
  // src/hooks/directory-agents-injector/storage.ts
3333
3562
  import {
@@ -3397,7 +3626,7 @@ function createDirectoryAgentsInjectorHook(options) {
3397
3626
  return null;
3398
3627
  if (title.startsWith("/"))
3399
3628
  return title;
3400
- return resolve(directory, title);
3629
+ return resolve2(directory, title);
3401
3630
  }
3402
3631
  function findAgentsMdUp(startDir) {
3403
3632
  const found = [];
@@ -3449,8 +3678,8 @@ function createDirectoryAgentsInjectorHook(options) {
3449
3678
  }
3450
3679
  if (toInject.length === 0)
3451
3680
  return;
3452
- for (const { path: path5, content } of toInject) {
3453
- const relativePath = path5.replace(knowledgeBasePath, "").replace(/^\//, "");
3681
+ for (const { path: path6, content } of toInject) {
3682
+ const relativePath = path6.replace(knowledgeBasePath, "").replace(/^\//, "");
3454
3683
  output.output += `
3455
3684
 
3456
3685
  [Directory Context: ${relativePath}]
@@ -3537,8 +3766,8 @@ function findNearestMessageWithFields(messageDir) {
3537
3766
  // src/shared-hooks/utils/logger.ts
3538
3767
  import * as fs2 from "fs";
3539
3768
  import * as os2 from "os";
3540
- import * as path5 from "path";
3541
- var logFile = path5.join(os2.tmpdir(), "thoth-plugin.log");
3769
+ import * as path6 from "path";
3770
+ var logFile = path6.join(os2.tmpdir(), "thoth-plugin.log");
3542
3771
  function log2(message, data) {
3543
3772
  try {
3544
3773
  const timestamp = new Date().toISOString();
@@ -5338,10 +5567,10 @@ function mergeDefs(...defs) {
5338
5567
  function cloneDef(schema) {
5339
5568
  return mergeDefs(schema._zod.def);
5340
5569
  }
5341
- function getElementAtPath(obj, path6) {
5342
- if (!path6)
5570
+ function getElementAtPath(obj, path7) {
5571
+ if (!path7)
5343
5572
  return obj;
5344
- return path6.reduce((acc, key) => acc?.[key], obj);
5573
+ return path7.reduce((acc, key) => acc?.[key], obj);
5345
5574
  }
5346
5575
  function promiseAllObject(promisesObj) {
5347
5576
  const keys = Object.keys(promisesObj);
@@ -5700,11 +5929,11 @@ function aborted(x, startIndex = 0) {
5700
5929
  }
5701
5930
  return false;
5702
5931
  }
5703
- function prefixIssues(path6, issues) {
5932
+ function prefixIssues(path7, issues) {
5704
5933
  return issues.map((iss) => {
5705
5934
  var _a;
5706
5935
  (_a = iss).path ?? (_a.path = []);
5707
- iss.path.unshift(path6);
5936
+ iss.path.unshift(path7);
5708
5937
  return iss;
5709
5938
  });
5710
5939
  }
@@ -5872,7 +6101,7 @@ function treeifyError(error, _mapper) {
5872
6101
  return issue2.message;
5873
6102
  };
5874
6103
  const result = { errors: [] };
5875
- const processError = (error2, path6 = []) => {
6104
+ const processError = (error2, path7 = []) => {
5876
6105
  var _a, _b;
5877
6106
  for (const issue2 of error2.issues) {
5878
6107
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -5882,7 +6111,7 @@ function treeifyError(error, _mapper) {
5882
6111
  } else if (issue2.code === "invalid_element") {
5883
6112
  processError({ issues: issue2.issues }, issue2.path);
5884
6113
  } else {
5885
- const fullpath = [...path6, ...issue2.path];
6114
+ const fullpath = [...path7, ...issue2.path];
5886
6115
  if (fullpath.length === 0) {
5887
6116
  result.errors.push(mapper(issue2));
5888
6117
  continue;
@@ -5914,8 +6143,8 @@ function treeifyError(error, _mapper) {
5914
6143
  }
5915
6144
  function toDotPath(_path) {
5916
6145
  const segs = [];
5917
- const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
5918
- for (const seg of path6) {
6146
+ const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
6147
+ for (const seg of path7) {
5919
6148
  if (typeof seg === "number")
5920
6149
  segs.push(`[${seg}]`);
5921
6150
  else if (typeof seg === "symbol")
@@ -16990,7 +17219,7 @@ Use \`background_output\` tool with task_id="${task.id}" to check progress:
16990
17219
  });
16991
17220
  }
16992
17221
  function delay(ms) {
16993
- return new Promise((resolve2) => setTimeout(resolve2, ms));
17222
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
16994
17223
  }
16995
17224
  function truncateText(text, maxLength) {
16996
17225
  if (text.length <= maxLength)
@@ -17206,7 +17435,7 @@ Status: ${task.status}`;
17206
17435
  // src/tools/skill/tools.ts
17207
17436
  import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync9, lstatSync, readlinkSync } from "fs";
17208
17437
  import { homedir as homedir5 } from "os";
17209
- import { join as join17, basename as basename2, resolve as resolve2, dirname as dirname5 } from "path";
17438
+ import { join as join17, basename as basename3, resolve as resolve3, dirname as dirname5 } from "path";
17210
17439
  import { fileURLToPath as fileURLToPath2 } from "url";
17211
17440
  var __filename3 = fileURLToPath2(import.meta.url);
17212
17441
  var __dirname3 = dirname5(__filename3);
@@ -17270,7 +17499,7 @@ function resolveSymlink(filePath) {
17270
17499
  try {
17271
17500
  const stats = lstatSync(filePath, { throwIfNoEntry: false });
17272
17501
  if (stats?.isSymbolicLink()) {
17273
- return resolve2(filePath, "..", readlinkSync(filePath));
17502
+ return resolve3(filePath, "..", readlinkSync(filePath));
17274
17503
  }
17275
17504
  return filePath;
17276
17505
  } catch {
@@ -17356,7 +17585,7 @@ async function parseSkillMd(skillPath) {
17356
17585
  const { data, body } = parseFrontmatter(content);
17357
17586
  const frontmatter = parseSkillFrontmatter(data);
17358
17587
  const metadata = {
17359
- name: frontmatter.name || basename2(skillPath),
17588
+ name: frontmatter.name || basename3(skillPath),
17360
17589
  description: frontmatter.description,
17361
17590
  license: frontmatter.license,
17362
17591
  allowedTools: frontmatter["allowed-tools"],
@@ -18295,10 +18524,10 @@ function mergeDefs2(...defs) {
18295
18524
  function cloneDef2(schema) {
18296
18525
  return mergeDefs2(schema._zod.def);
18297
18526
  }
18298
- function getElementAtPath2(obj, path6) {
18299
- if (!path6)
18527
+ function getElementAtPath2(obj, path7) {
18528
+ if (!path7)
18300
18529
  return obj;
18301
- return path6.reduce((acc, key) => acc?.[key], obj);
18530
+ return path7.reduce((acc, key) => acc?.[key], obj);
18302
18531
  }
18303
18532
  function promiseAllObject2(promisesObj) {
18304
18533
  const keys = Object.keys(promisesObj);
@@ -18679,11 +18908,11 @@ function aborted2(x, startIndex = 0) {
18679
18908
  }
18680
18909
  return false;
18681
18910
  }
18682
- function prefixIssues2(path6, issues) {
18911
+ function prefixIssues2(path7, issues) {
18683
18912
  return issues.map((iss) => {
18684
18913
  var _a;
18685
18914
  (_a = iss).path ?? (_a.path = []);
18686
- iss.path.unshift(path6);
18915
+ iss.path.unshift(path7);
18687
18916
  return iss;
18688
18917
  });
18689
18918
  }
@@ -18866,7 +19095,7 @@ function formatError2(error45, mapper = (issue3) => issue3.message) {
18866
19095
  }
18867
19096
  function treeifyError2(error45, mapper = (issue3) => issue3.message) {
18868
19097
  const result = { errors: [] };
18869
- const processError = (error46, path6 = []) => {
19098
+ const processError = (error46, path7 = []) => {
18870
19099
  var _a, _b;
18871
19100
  for (const issue3 of error46.issues) {
18872
19101
  if (issue3.code === "invalid_union" && issue3.errors.length) {
@@ -18876,7 +19105,7 @@ function treeifyError2(error45, mapper = (issue3) => issue3.message) {
18876
19105
  } else if (issue3.code === "invalid_element") {
18877
19106
  processError({ issues: issue3.issues }, issue3.path);
18878
19107
  } else {
18879
- const fullpath = [...path6, ...issue3.path];
19108
+ const fullpath = [...path7, ...issue3.path];
18880
19109
  if (fullpath.length === 0) {
18881
19110
  result.errors.push(mapper(issue3));
18882
19111
  continue;
@@ -18908,8 +19137,8 @@ function treeifyError2(error45, mapper = (issue3) => issue3.message) {
18908
19137
  }
18909
19138
  function toDotPath2(_path) {
18910
19139
  const segs = [];
18911
- const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
18912
- for (const seg of path6) {
19140
+ const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
19141
+ for (const seg of path7) {
18913
19142
  if (typeof seg === "number")
18914
19143
  segs.push(`[${seg}]`);
18915
19144
  else if (typeof seg === "symbol")
@@ -30656,13 +30885,13 @@ function resolveRef(ref, ctx) {
30656
30885
  if (!ref.startsWith("#")) {
30657
30886
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
30658
30887
  }
30659
- const path6 = ref.slice(1).split("/").filter(Boolean);
30660
- if (path6.length === 0) {
30888
+ const path7 = ref.slice(1).split("/").filter(Boolean);
30889
+ if (path7.length === 0) {
30661
30890
  return ctx.rootSchema;
30662
30891
  }
30663
30892
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
30664
- if (path6[0] === defsKey) {
30665
- const key = path6[1];
30893
+ if (path7[0] === defsKey) {
30894
+ const key = path7[1];
30666
30895
  if (!key || !ctx.defs[key]) {
30667
30896
  throw new Error(`Reference not found: ${ref}`);
30668
30897
  }
@@ -31076,6 +31305,7 @@ var HooksConfigSchema = exports_external2.object({
31076
31305
  "temporal-awareness": exports_external2.boolean().optional(),
31077
31306
  "knowledge-persistence": exports_external2.boolean().optional(),
31078
31307
  "directory-agents-injector": exports_external2.boolean().optional(),
31308
+ "frontmatter-enforcer": exports_external2.boolean().optional(),
31079
31309
  "todo-continuation": exports_external2.boolean().optional(),
31080
31310
  "session-recovery": exports_external2.boolean().optional(),
31081
31311
  "context-window-monitor": exports_external2.boolean().optional(),
@@ -31113,7 +31343,7 @@ var ThothPluginConfigSchema = exports_external2.object({
31113
31343
  }).strict();
31114
31344
  // src/index.ts
31115
31345
  import * as fs3 from "fs";
31116
- import * as path6 from "path";
31346
+ import * as path7 from "path";
31117
31347
  var sessionSpecializations = new Map;
31118
31348
  function loadConfigFromPath(configPath) {
31119
31349
  try {
@@ -31145,8 +31375,8 @@ function mergeConfigs(base, override) {
31145
31375
  };
31146
31376
  }
31147
31377
  function loadPluginConfig(directory) {
31148
- const userConfigPath = path6.join(getUserConfigDir(), "opencode", "thoth-plugin.json");
31149
- const projectConfigPath = path6.join(directory, ".opencode", "thoth-plugin.json");
31378
+ const userConfigPath = path7.join(getUserConfigDir(), "opencode", "thoth-plugin.json");
31379
+ const projectConfigPath = path7.join(directory, ".opencode", "thoth-plugin.json");
31150
31380
  let config3 = loadConfigFromPath(userConfigPath) ?? {};
31151
31381
  const projectConfig = loadConfigFromPath(projectConfigPath);
31152
31382
  if (projectConfig) {
@@ -31160,15 +31390,15 @@ function resolveKnowledgeBasePath(config3, directory) {
31160
31390
  return expandPath(config3.knowledge_base);
31161
31391
  }
31162
31392
  const commonLocations = [
31163
- path6.join(process.env.HOME || "", "Repos", "thoth"),
31164
- path6.join(process.env.HOME || "", "repos", "thoth"),
31165
- path6.join(process.env.HOME || "", "Projects", "thoth"),
31166
- path6.join(process.env.HOME || "", "projects", "thoth"),
31167
- path6.join(process.env.HOME || "", "thoth"),
31168
- path6.join(directory, "thoth")
31393
+ path7.join(process.env.HOME || "", "Repos", "thoth"),
31394
+ path7.join(process.env.HOME || "", "repos", "thoth"),
31395
+ path7.join(process.env.HOME || "", "Projects", "thoth"),
31396
+ path7.join(process.env.HOME || "", "projects", "thoth"),
31397
+ path7.join(process.env.HOME || "", "thoth"),
31398
+ path7.join(directory, "thoth")
31169
31399
  ];
31170
31400
  for (const location of commonLocations) {
31171
- const kernelPath = path6.join(location, "kernel");
31401
+ const kernelPath = path7.join(location, "kernel");
31172
31402
  if (fs3.existsSync(kernelPath)) {
31173
31403
  log(`Found knowledge base at: ${location}`);
31174
31404
  return location;
@@ -31189,6 +31419,7 @@ var ThothPlugin = async (ctx) => {
31189
31419
  const trustLevelTracker = hooksConfig["trust-level-tracker"] !== false ? createTrustLevelTrackerHook({ knowledgeBasePath }) : null;
31190
31420
  const contextAperture = hooksConfig["context-aperture"] !== false ? createContextApertureHook({ knowledgeBasePath }) : null;
31191
31421
  const temporalAwareness = hooksConfig["temporal-awareness"] !== false ? createTemporalAwarenessHook() : null;
31422
+ const frontmatterEnforcer = hooksConfig["frontmatter-enforcer"] !== false ? createFrontmatterEnforcerHook({ knowledgeBasePath }) : null;
31192
31423
  const todoContinuationEnforcer = hooksConfig["todo-continuation"] !== false ? createTodoContinuationEnforcer(ctx) : null;
31193
31424
  const sessionRecoveryHook = hooksConfig["session-recovery"] !== false ? createSessionRecoveryHook(ctx, { experimental: { auto_resume: true } }) : null;
31194
31425
  const contextWindowMonitor = hooksConfig["context-window-monitor"] !== false ? createContextWindowMonitorHook(ctx) : null;
@@ -31293,10 +31524,12 @@ var ThothPlugin = async (ctx) => {
31293
31524
  await temporalAwareness?.["tool.execute.before"]?.(input, output);
31294
31525
  await contextAperture?.["tool.execute.before"]?.(input, output);
31295
31526
  await trustLevelTracker?.["tool.execute.before"]?.(input, output);
31527
+ await frontmatterEnforcer?.["tool.execute.before"]?.(input, output);
31296
31528
  },
31297
31529
  "tool.execute.after": async (input, output) => {
31298
31530
  await trustLevelTracker?.["tool.execute.after"]?.(input, output);
31299
31531
  await contextAperture?.["tool.execute.after"]?.(input, output);
31532
+ await frontmatterEnforcer?.["tool.execute.after"]?.(input, output);
31300
31533
  await directoryAgentsInjector?.["tool.execute.after"]?.(input, output);
31301
31534
  await contextWindowMonitor?.["tool.execute.after"]?.(input, output);
31302
31535
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-plugin",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Thoth - Root-level life orchestrator for OpenCode. Unified AI chief of staff combining Sisyphus execution quality, Personal-OS rhythms, and Thoth relationship model.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",