thoth-plugin 1.1.2 → 1.2.1
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/config/schema.d.ts +4 -0
- package/dist/defaults/skill/gardener/SKILL.md +243 -17
- package/dist/defaults/skill/onboarding/SKILL.md +207 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/read-confirmation.d.ts +34 -0
- package/dist/hooks/write-confirmation.d.ts +34 -0
- package/dist/index.js +131 -38
- package/package.json +1 -1
package/dist/config/schema.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ declare const HooksConfigSchema: z.ZodObject<{
|
|
|
13
13
|
"knowledge-persistence": z.ZodOptional<z.ZodBoolean>;
|
|
14
14
|
"directory-agents-injector": z.ZodOptional<z.ZodBoolean>;
|
|
15
15
|
"frontmatter-enforcer": z.ZodOptional<z.ZodBoolean>;
|
|
16
|
+
"read-confirmation": z.ZodOptional<z.ZodBoolean>;
|
|
17
|
+
"write-confirmation": z.ZodOptional<z.ZodBoolean>;
|
|
16
18
|
"todo-continuation": z.ZodOptional<z.ZodBoolean>;
|
|
17
19
|
"session-recovery": z.ZodOptional<z.ZodBoolean>;
|
|
18
20
|
"context-window-monitor": z.ZodOptional<z.ZodBoolean>;
|
|
@@ -67,6 +69,8 @@ export declare const ThothPluginConfigSchema: z.ZodObject<{
|
|
|
67
69
|
"knowledge-persistence": z.ZodOptional<z.ZodBoolean>;
|
|
68
70
|
"directory-agents-injector": z.ZodOptional<z.ZodBoolean>;
|
|
69
71
|
"frontmatter-enforcer": z.ZodOptional<z.ZodBoolean>;
|
|
72
|
+
"read-confirmation": z.ZodOptional<z.ZodBoolean>;
|
|
73
|
+
"write-confirmation": z.ZodOptional<z.ZodBoolean>;
|
|
70
74
|
"todo-continuation": z.ZodOptional<z.ZodBoolean>;
|
|
71
75
|
"session-recovery": z.ZodOptional<z.ZodBoolean>;
|
|
72
76
|
"context-window-monitor": z.ZodOptional<z.ZodBoolean>;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gardener
|
|
3
3
|
description: Use when knowledge base health needs checking, broken links need fixing, orphan files need registering, or cross-references between related files are missing
|
|
4
|
+
triggers:
|
|
5
|
+
- "Check knowledge base"
|
|
6
|
+
- "Run gardener"
|
|
7
|
+
- "KB health"
|
|
8
|
+
- "Fix broken links"
|
|
9
|
+
- "Check for orphan files"
|
|
4
10
|
---
|
|
5
11
|
|
|
6
12
|
# Gardener Skill
|
|
@@ -58,12 +64,92 @@ The canonical frontmatter schema is defined in `kernel/config/frontmatter-schema
|
|
|
58
64
|
|
|
59
65
|
---
|
|
60
66
|
|
|
67
|
+
## Severity Levels
|
|
68
|
+
|
|
69
|
+
All issues are classified by severity to prioritize repair work:
|
|
70
|
+
|
|
71
|
+
| Severity | Symbol | Meaning | Action |
|
|
72
|
+
|----------|--------|---------|--------|
|
|
73
|
+
| **CRITICAL** | `[C]` | Data integrity at risk, navigation broken | Fix immediately |
|
|
74
|
+
| **ERROR** | `[E]` | Functionality impaired, links broken | Fix soon |
|
|
75
|
+
| **WARNING** | `[W]` | Best practices violated, maintenance debt | Fix when convenient |
|
|
76
|
+
| **INFO** | `[I]` | Suggestions for improvement | Optional |
|
|
77
|
+
|
|
78
|
+
### Severity by Issue Type
|
|
79
|
+
|
|
80
|
+
| Issue | Default Severity | Escalation Condition |
|
|
81
|
+
|-------|------------------|----------------------|
|
|
82
|
+
| Missing required frontmatter field | ERROR | CRITICAL if `type` missing |
|
|
83
|
+
| Invalid frontmatter value | WARNING | ERROR if `status` or `priority` |
|
|
84
|
+
| Broken internal link | ERROR | CRITICAL if in registry/index |
|
|
85
|
+
| Missing bidirectional link | WARNING | — |
|
|
86
|
+
| Orphan file (not indexed) | WARNING | ERROR if in people/ or projects/ |
|
|
87
|
+
| Registry ghost (indexed but missing) | CRITICAL | — |
|
|
88
|
+
| Stale _index.md (files not listed) | WARNING | ERROR if >5 files missing |
|
|
89
|
+
| Frontmatter schema violation | ERROR | — |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
61
93
|
## Mode 1: Health Check
|
|
62
94
|
|
|
63
|
-
### Step 1:
|
|
95
|
+
### Step 1: Scan All Categories
|
|
96
|
+
|
|
97
|
+
Perform these checks systematically:
|
|
98
|
+
|
|
99
|
+
#### 1.1 Frontmatter Validation
|
|
100
|
+
|
|
101
|
+
For each `.md` file in the knowledge base:
|
|
64
102
|
|
|
65
|
-
```
|
|
66
|
-
|
|
103
|
+
```
|
|
104
|
+
CHECK: Has frontmatter block (--- ... ---)
|
|
105
|
+
CHECK: Has required fields: type, hemisphere, created, updated
|
|
106
|
+
CHECK: type value is valid (person, project, task, note, reference, etc.)
|
|
107
|
+
CHECK: hemisphere value matches path (work/, life/, coding/, kernel/)
|
|
108
|
+
CHECK: Type-specific required fields present:
|
|
109
|
+
- person: relationship
|
|
110
|
+
- project: status
|
|
111
|
+
- task: status, priority
|
|
112
|
+
CHECK: Values are valid per schema:
|
|
113
|
+
- status (project): planning|active|on-hold|completed|cancelled
|
|
114
|
+
- status (task): pending|in-progress|done|cancelled|blocked
|
|
115
|
+
- priority: P0|P1|P2|P3
|
|
116
|
+
- health: green|yellow|red
|
|
117
|
+
- relationship: manager|peer|report|stakeholder|friend|family
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### 1.2 Link Integrity
|
|
121
|
+
|
|
122
|
+
For each `[[wikilink]]` and `[markdown](link)`:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
CHECK: Target file exists
|
|
126
|
+
CHECK: Target path is correct (not moved/renamed)
|
|
127
|
+
CHECK: Bidirectional: if A links to B, does B link to A?
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### 1.3 Index Coverage
|
|
131
|
+
|
|
132
|
+
For each `_index.md` file:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
CHECK: All files in same directory are listed
|
|
136
|
+
CHECK: All listed files actually exist (no ghosts)
|
|
137
|
+
CHECK: File summaries are present and accurate
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
For `registry.md`:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
CHECK: All hemispheres represented
|
|
144
|
+
CHECK: Key entity counts are accurate
|
|
145
|
+
CHECK: Last updated date is recent
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### 1.4 Orphan Detection
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
CHECK: Every .md file (except _index.md, registry.md) is listed in its _index.md
|
|
152
|
+
CHECK: Every entity file has at least one incoming link
|
|
67
153
|
```
|
|
68
154
|
|
|
69
155
|
### Step 2: Synthesize Report
|
|
@@ -73,21 +159,34 @@ npx tsx scripts/gardener-scan.ts --verbose
|
|
|
73
159
|
|
|
74
160
|
**Scanned**: {timestamp}
|
|
75
161
|
**Total Files**: {count}
|
|
76
|
-
**Overall Health**: {
|
|
77
|
-
|
|
78
|
-
### Summary
|
|
79
|
-
|
|
|
80
|
-
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
86
|
-
###
|
|
87
|
-
|
|
162
|
+
**Overall Health**: {HEALTHY|NEEDS-ATTENTION|CRITICAL}
|
|
163
|
+
|
|
164
|
+
### Summary by Severity
|
|
165
|
+
| Severity | Count | Categories |
|
|
166
|
+
|----------|-------|------------|
|
|
167
|
+
| CRITICAL | X | {list} |
|
|
168
|
+
| ERROR | X | {list} |
|
|
169
|
+
| WARNING | X | {list} |
|
|
170
|
+
| INFO | X | {list} |
|
|
171
|
+
|
|
172
|
+
### Issue Breakdown
|
|
173
|
+
| Category | [C] | [E] | [W] | [I] |
|
|
174
|
+
|----------|-----|-----|-----|-----|
|
|
175
|
+
| Frontmatter Issues | X | X | X | X |
|
|
176
|
+
| Broken Links | X | X | X | X |
|
|
177
|
+
| Missing Bidirectional | — | — | X | X |
|
|
178
|
+
| Orphan Files | — | X | X | — |
|
|
179
|
+
| Index Staleness | — | X | X | — |
|
|
180
|
+
| Registry Ghosts | X | — | — | — |
|
|
181
|
+
|
|
182
|
+
### Critical Issues (Must Fix)
|
|
183
|
+
{List all CRITICAL items with file path and specific issue}
|
|
184
|
+
|
|
185
|
+
### Errors (Should Fix)
|
|
186
|
+
{List top 10 ERROR items}
|
|
88
187
|
|
|
89
188
|
### Recommendations
|
|
90
|
-
{3-5 prioritized actions}
|
|
189
|
+
{3-5 prioritized actions based on findings}
|
|
91
190
|
```
|
|
92
191
|
|
|
93
192
|
---
|
|
@@ -228,6 +327,87 @@ For each approved link:
|
|
|
228
327
|
|
|
229
328
|
---
|
|
230
329
|
|
|
330
|
+
## Bidirectional Link Verification
|
|
331
|
+
|
|
332
|
+
A healthy knowledge base has bidirectional links: if A references B, B should reference A.
|
|
333
|
+
|
|
334
|
+
### Detection Algorithm
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
For each file A:
|
|
338
|
+
For each outgoing link to file B:
|
|
339
|
+
Check if B has any link back to A
|
|
340
|
+
If not: flag as "Missing bidirectional: B should link to A"
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Severity Classification
|
|
344
|
+
|
|
345
|
+
| Situation | Severity |
|
|
346
|
+
|-----------|----------|
|
|
347
|
+
| Person A mentions Person B, B doesn't mention A | WARNING |
|
|
348
|
+
| Project links to stakeholder, stakeholder doesn't link to project | WARNING |
|
|
349
|
+
| Registry/index links to file, file doesn't link back | INFO (one-way is OK) |
|
|
350
|
+
| Two files in `related:` frontmatter but no body links | WARNING |
|
|
351
|
+
|
|
352
|
+
### Repair Suggestion Format
|
|
353
|
+
|
|
354
|
+
```markdown
|
|
355
|
+
### Missing Bidirectional Links
|
|
356
|
+
|
|
357
|
+
| Source | Target | Evidence | Suggested Fix |
|
|
358
|
+
|--------|--------|----------|---------------|
|
|
359
|
+
| work/people/alice.md | work/people/bob.md | Alice mentions Bob (line 23) | Add `[[alice]]` to bob.md Related section |
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## Index Staleness Detection
|
|
365
|
+
|
|
366
|
+
Every directory with content files should have an `_index.md` that lists all files.
|
|
367
|
+
|
|
368
|
+
### Detection Algorithm
|
|
369
|
+
|
|
370
|
+
```
|
|
371
|
+
For each directory with _index.md:
|
|
372
|
+
List all .md files in directory (excluding _index.md)
|
|
373
|
+
Parse _index.md for file references
|
|
374
|
+
|
|
375
|
+
STALE if:
|
|
376
|
+
- File exists but not in _index.md (orphan)
|
|
377
|
+
- File in _index.md but doesn't exist (ghost)
|
|
378
|
+
- File count mismatch > 0
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Severity Classification
|
|
382
|
+
|
|
383
|
+
| Situation | Severity |
|
|
384
|
+
|-----------|----------|
|
|
385
|
+
| 1-2 files missing from _index.md | WARNING |
|
|
386
|
+
| 3-5 files missing from _index.md | WARNING |
|
|
387
|
+
| >5 files missing from _index.md | ERROR |
|
|
388
|
+
| Ghost entry (listed but doesn't exist) | ERROR |
|
|
389
|
+
| _index.md missing entirely in content directory | ERROR |
|
|
390
|
+
|
|
391
|
+
### Report Format
|
|
392
|
+
|
|
393
|
+
```markdown
|
|
394
|
+
### Index Staleness Report
|
|
395
|
+
|
|
396
|
+
| Directory | Files | Indexed | Missing | Ghosts | Severity |
|
|
397
|
+
|-----------|-------|---------|---------|--------|----------|
|
|
398
|
+
| work/people/ | 15 | 12 | 3 | 0 | [W] |
|
|
399
|
+
| work/projects/ | 8 | 8 | 0 | 1 | [E] |
|
|
400
|
+
|
|
401
|
+
#### Missing from Index
|
|
402
|
+
- work/people/new-person.md (created 2026-01-09)
|
|
403
|
+
- work/people/another.md (created 2026-01-08)
|
|
404
|
+
|
|
405
|
+
#### Ghost Entries (file doesn't exist)
|
|
406
|
+
- work/projects/deleted-project.md (remove from _index.md)
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
231
411
|
## Red Flags - STOP
|
|
232
412
|
|
|
233
413
|
- About to add 50+ links without review
|
|
@@ -280,4 +460,50 @@ Before completing cross-reference mode:
|
|
|
280
460
|
|
|
281
461
|
---
|
|
282
462
|
|
|
283
|
-
|
|
463
|
+
## Frontmatter Validation Details
|
|
464
|
+
|
|
465
|
+
### Required Fields by Type
|
|
466
|
+
|
|
467
|
+
| File Type | Required Fields | Optional Fields |
|
|
468
|
+
|-----------|-----------------|-----------------|
|
|
469
|
+
| **All files** | `type`, `hemisphere`, `created`, `updated` | `tags`, `summary`, `related` |
|
|
470
|
+
| **person** | + `relationship` | `email`, `slack`, `role`, `company` |
|
|
471
|
+
| **project** | + `status` | `priority`, `health`, `due`, `stakeholders` |
|
|
472
|
+
| **task** | + `status`, `priority` | `due`, `project`, `assignee` |
|
|
473
|
+
|
|
474
|
+
### Validation Error Examples
|
|
475
|
+
|
|
476
|
+
```markdown
|
|
477
|
+
### Frontmatter Validation Errors
|
|
478
|
+
|
|
479
|
+
| File | Issue | Severity | Fix |
|
|
480
|
+
|------|-------|----------|-----|
|
|
481
|
+
| work/people/alice.md | Missing `relationship` field | [E] | Add `relationship: peer` |
|
|
482
|
+
| work/projects/foo.md | Invalid status: "wip" | [E] | Change to `status: active` |
|
|
483
|
+
| life/notes/random.md | Missing `type` field | [C] | Add `type: note` |
|
|
484
|
+
| work/people/bob.md | hemisphere: "work" but path is life/ | [W] | Update to `hemisphere: life` |
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Auto-Fixable Issues
|
|
488
|
+
|
|
489
|
+
The following can be auto-fixed with `/gardener fix`:
|
|
490
|
+
|
|
491
|
+
| Issue | Auto-Fix Action |
|
|
492
|
+
|-------|-----------------|
|
|
493
|
+
| Missing `created` | Set to file creation date |
|
|
494
|
+
| Missing `updated` | Set to file modification date |
|
|
495
|
+
| Missing `hemisphere` | Infer from file path |
|
|
496
|
+
| Incorrect `hemisphere` | Correct to match path |
|
|
497
|
+
|
|
498
|
+
### Manual-Fix Required
|
|
499
|
+
|
|
500
|
+
| Issue | Why Manual |
|
|
501
|
+
|-------|------------|
|
|
502
|
+
| Missing `type` | Cannot infer content type |
|
|
503
|
+
| Missing `relationship` | Cannot guess relationship |
|
|
504
|
+
| Missing `status` | Cannot guess project/task state |
|
|
505
|
+
| Invalid enum value | Need user to choose correct value |
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
*Gardener v4.0 | Part of Thoth Knowledge Management System*
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: onboarding
|
|
3
|
+
description: Structured onboarding for new domains using breadth-before-depth discovery
|
|
4
|
+
triggers:
|
|
5
|
+
- "Let's onboard"
|
|
6
|
+
- "New domain"
|
|
7
|
+
- "Help me set up"
|
|
8
|
+
- "Onboard my"
|
|
9
|
+
- "Learn about my"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Onboarding Skill
|
|
13
|
+
|
|
14
|
+
You are entering **Onboarding Mode**. Your role is to systematically learn about a new domain of Zeus's life while avoiding the depth trap.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Philosophy
|
|
19
|
+
|
|
20
|
+
1. **Breadth before depth** — Map the landscape before diving deep
|
|
21
|
+
2. **Step back pattern** — After 5-10 minutes on any topic, ask "What else?"
|
|
22
|
+
3. **Prevent premature action** — Focus on understanding, not doing
|
|
23
|
+
4. **Structured but conversational** — Have a framework, follow Zeus's energy
|
|
24
|
+
5. **Persist as you go** — Create knowledge files for important entities discovered
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Protocol
|
|
29
|
+
|
|
30
|
+
### Phase 1: Orient (Start Here)
|
|
31
|
+
|
|
32
|
+
Ask these questions before anything else:
|
|
33
|
+
|
|
34
|
+
1. **Domain**: What domain are we onboarding? (work, life, specific project, specific area)
|
|
35
|
+
2. **Goal**: What's the goal of this onboarding? (understand context, set up tracking, prepare for something)
|
|
36
|
+
3. **Sources**: What systems/data sources are available? (email, calendar, documents, nothing yet)
|
|
37
|
+
|
|
38
|
+
**Output**: Clear understanding of scope and available data.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
### Phase 2: Scan (If Data Sources Available)
|
|
43
|
+
|
|
44
|
+
If Zeus has connected email, calendar, or documents:
|
|
45
|
+
|
|
46
|
+
1. Fire parallel background agents to scan:
|
|
47
|
+
```
|
|
48
|
+
background_task(agent="general", prompt="Scan recent emails for people, projects, recurring themes...")
|
|
49
|
+
background_task(agent="general", prompt="Scan calendar for meetings, recurring events, key people...")
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
2. Synthesize findings:
|
|
53
|
+
- Key people mentioned
|
|
54
|
+
- Active projects/areas
|
|
55
|
+
- Recurring themes
|
|
56
|
+
- Open items/commitments
|
|
57
|
+
|
|
58
|
+
**Output**: Data-driven overview of the domain landscape.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
### Phase 3: Discover (Interview Mode)
|
|
63
|
+
|
|
64
|
+
Ask clarifying questions about what you found (or start here if no data sources):
|
|
65
|
+
|
|
66
|
+
| Area | Questions |
|
|
67
|
+
|------|-----------|
|
|
68
|
+
| **People** | Who are the key people? (just names for now — we'll go deeper later) |
|
|
69
|
+
| **Projects/Areas** | What are the main projects or areas of focus? |
|
|
70
|
+
| **Challenges** | What's the biggest challenge or pain point right now? |
|
|
71
|
+
| **Success** | What does success look like? What are you optimizing for? |
|
|
72
|
+
| **Gaps** | What's not working? What falls through the cracks? |
|
|
73
|
+
|
|
74
|
+
**Key rule**: Collect NAMES and TOPICS first. Don't drill into any one area yet.
|
|
75
|
+
|
|
76
|
+
**Output**: List of people, projects, challenges, and goals.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### Phase 4: Step Back (CRITICAL)
|
|
81
|
+
|
|
82
|
+
After covering one area for 5-10 minutes, ALWAYS ask one of these:
|
|
83
|
+
|
|
84
|
+
- "We've covered [X]. Before going deeper, are there other areas we should map out?"
|
|
85
|
+
- "What else is on your mind that we haven't touched?"
|
|
86
|
+
- "Is there anything blocking you that we should address first?"
|
|
87
|
+
- "Are there other people or projects I should know about before we continue?"
|
|
88
|
+
|
|
89
|
+
**NEVER** dive deep into one entity without first mapping the full landscape.
|
|
90
|
+
|
|
91
|
+
**Anti-Pattern Detection**: If you've been discussing one project or person for more than 5 minutes and haven't asked about others, STOP and ask.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
### Phase 5: Deepen (Only After Breadth)
|
|
96
|
+
|
|
97
|
+
Once the landscape is mapped, ask Zeus which area to explore first:
|
|
98
|
+
|
|
99
|
+
- "We've identified [projects A, B, C] and [people X, Y, Z]. Which would you like to detail first?"
|
|
100
|
+
- "What's most urgent or important to capture deeply?"
|
|
101
|
+
|
|
102
|
+
For each entity Zeus prioritizes:
|
|
103
|
+
|
|
104
|
+
1. Create a knowledge file using appropriate template
|
|
105
|
+
2. Capture key details, relationships, status
|
|
106
|
+
3. Add to relevant `_index.md`
|
|
107
|
+
4. Create bidirectional links to related entities
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### Phase 6: Close
|
|
112
|
+
|
|
113
|
+
Before ending the onboarding session:
|
|
114
|
+
|
|
115
|
+
1. **Summarize** what was learned:
|
|
116
|
+
- Domain scope
|
|
117
|
+
- Key entities created
|
|
118
|
+
- Relationships mapped
|
|
119
|
+
|
|
120
|
+
2. **Identify gaps** to fill later:
|
|
121
|
+
- "We didn't get to [X, Y, Z]. Should we schedule follow-up?"
|
|
122
|
+
- "I'm missing context on [A, B]. Can we cover that next time?"
|
|
123
|
+
|
|
124
|
+
3. **Set expectations**:
|
|
125
|
+
- "I'll continue learning as we interact. Correct me when I'm wrong."
|
|
126
|
+
- "Mention new people or projects as they come up — I'll capture them."
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Anti-Pattern: The Depth Trap
|
|
131
|
+
|
|
132
|
+
### WRONG
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
Zeus: "Let's onboard my work"
|
|
136
|
+
Thoth: "Tell me about Project Alpha"
|
|
137
|
+
Zeus: [explains Project Alpha for 20 minutes]
|
|
138
|
+
Thoth: [asks more Project Alpha questions]
|
|
139
|
+
[30 minutes later, deep in Project Alpha details]
|
|
140
|
+
[Never asked about other projects, colleagues, goals]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### RIGHT
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
Zeus: "Let's onboard my work"
|
|
147
|
+
Thoth: "Before we dive deep, let me understand the landscape.
|
|
148
|
+
Who are the key people you work with?"
|
|
149
|
+
Zeus: [lists 5 people]
|
|
150
|
+
Thoth: "And what are the main projects or areas you're focused on?"
|
|
151
|
+
Zeus: [lists 3 projects]
|
|
152
|
+
Thoth: "Got it. What's your biggest challenge right now?"
|
|
153
|
+
Zeus: [explains challenge]
|
|
154
|
+
Thoth: "Before we go deeper on any of these, is there anything
|
|
155
|
+
else I should know about — other projects, people, or
|
|
156
|
+
responsibilities?"
|
|
157
|
+
Zeus: [adds 2 more things]
|
|
158
|
+
Thoth: "Now, which of these would you like to detail first?"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Knowledge Creation During Onboarding
|
|
164
|
+
|
|
165
|
+
As you discover entities, create files:
|
|
166
|
+
|
|
167
|
+
| Entity Type | Template | Location |
|
|
168
|
+
|-------------|----------|----------|
|
|
169
|
+
| Person (work) | `kernel/templates/person.md` | `work/people/` |
|
|
170
|
+
| Person (life) | `kernel/templates/person.md` | `life/people/` |
|
|
171
|
+
| Project | `kernel/templates/project.md` | `{hemisphere}/projects/` |
|
|
172
|
+
| Area/Topic | Create overview file | Appropriate folder |
|
|
173
|
+
|
|
174
|
+
**Rules**:
|
|
175
|
+
- Check if entity already exists before creating (grep for name)
|
|
176
|
+
- Add to `_index.md` immediately after creation
|
|
177
|
+
- Create bidirectional links between related entities
|
|
178
|
+
- Use frontmatter with appropriate type, hemisphere, tags
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Verification Checklist
|
|
183
|
+
|
|
184
|
+
Before ending onboarding session:
|
|
185
|
+
|
|
186
|
+
- [ ] Asked about multiple areas (people, projects, challenges, goals)
|
|
187
|
+
- [ ] Used step-back pattern at least once
|
|
188
|
+
- [ ] Didn't spend more than 10 minutes on any single entity before mapping others
|
|
189
|
+
- [ ] Created knowledge files for key entities
|
|
190
|
+
- [ ] Updated relevant `_index.md` files
|
|
191
|
+
- [ ] Identified gaps for follow-up
|
|
192
|
+
- [ ] Zeus knows what to expect going forward
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Quick Reference
|
|
197
|
+
|
|
198
|
+
| Trigger | Action |
|
|
199
|
+
|---------|--------|
|
|
200
|
+
| "Let's onboard my work" | Start Phase 1 with work domain |
|
|
201
|
+
| "Help me set up life tracking" | Start Phase 1 with life domain |
|
|
202
|
+
| "New project: X" | Orient on project X specifically |
|
|
203
|
+
| "Learn about my team" | Focus on people discovery |
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
*Onboarding Skill v1.0 | Part of Thoth Knowledge Management System*
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -3,3 +3,5 @@ export { createTrustLevelTrackerHook, type TrustLevelTrackerHook, type TrustLeve
|
|
|
3
3
|
export { createContextApertureHook, type ContextApertureHook, type ContextApertureConfig, } from "./context-aperture";
|
|
4
4
|
export { createTemporalAwarenessHook, type TemporalAwarenessHook, type TemporalAwarenessConfig, } from "./temporal-awareness";
|
|
5
5
|
export { createFrontmatterEnforcerHook, type FrontmatterEnforcerHook, type FrontmatterEnforcerConfig, } from "./frontmatter-enforcer";
|
|
6
|
+
export { createReadConfirmationHook, type ReadConfirmationHook, type ReadConfirmationConfig, } from "./read-confirmation";
|
|
7
|
+
export { createWriteConfirmationHook, type WriteConfirmationHook, type WriteConfirmationConfig, } from "./write-confirmation";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read Confirmation Hook
|
|
3
|
+
*
|
|
4
|
+
* After file reads, injects a confirmation message into the conversation.
|
|
5
|
+
* This creates an audit trail and prevents hallucination about what was read.
|
|
6
|
+
*
|
|
7
|
+
* Value:
|
|
8
|
+
* - Prevents "I read file X" when it wasn't actually read
|
|
9
|
+
* - Creates audit trail of file access
|
|
10
|
+
* - Helps with context management
|
|
11
|
+
*/
|
|
12
|
+
export interface ReadConfirmationConfig {
|
|
13
|
+
knowledgeBasePath: string;
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
/** Only confirm reads within the knowledge base (default: true) */
|
|
16
|
+
kbOnly?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function createReadConfirmationHook(config: ReadConfirmationConfig): {
|
|
19
|
+
"tool.execute.before": (input: {
|
|
20
|
+
tool: string;
|
|
21
|
+
callID: string;
|
|
22
|
+
}, output: {
|
|
23
|
+
args: Record<string, unknown>;
|
|
24
|
+
}) => Promise<void>;
|
|
25
|
+
"tool.execute.after": (input: {
|
|
26
|
+
tool: string;
|
|
27
|
+
callID: string;
|
|
28
|
+
}, output: {
|
|
29
|
+
title: string;
|
|
30
|
+
output: string;
|
|
31
|
+
metadata: unknown;
|
|
32
|
+
}) => Promise<void>;
|
|
33
|
+
} | null;
|
|
34
|
+
export type ReadConfirmationHook = ReturnType<typeof createReadConfirmationHook>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write Confirmation Hook
|
|
3
|
+
*
|
|
4
|
+
* After file writes/edits, injects a confirmation message with reminders.
|
|
5
|
+
* This creates an audit trail and reinforces the Smart Merge protocol.
|
|
6
|
+
*
|
|
7
|
+
* Value:
|
|
8
|
+
* - Reminds about _index.md updates for new files
|
|
9
|
+
* - Creates audit trail of file modifications
|
|
10
|
+
* - Reinforces Smart Merge protocol
|
|
11
|
+
*/
|
|
12
|
+
export interface WriteConfirmationConfig {
|
|
13
|
+
knowledgeBasePath: string;
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
/** Only confirm writes within the knowledge base (default: true) */
|
|
16
|
+
kbOnly?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function createWriteConfirmationHook(config: WriteConfirmationConfig): {
|
|
19
|
+
"tool.execute.before": (input: {
|
|
20
|
+
tool: string;
|
|
21
|
+
callID: string;
|
|
22
|
+
}, output: {
|
|
23
|
+
args: Record<string, unknown>;
|
|
24
|
+
}) => Promise<void>;
|
|
25
|
+
"tool.execute.after": (input: {
|
|
26
|
+
tool: string;
|
|
27
|
+
callID: string;
|
|
28
|
+
}, output: {
|
|
29
|
+
title: string;
|
|
30
|
+
output: string;
|
|
31
|
+
metadata: unknown;
|
|
32
|
+
}) => Promise<void>;
|
|
33
|
+
} | null;
|
|
34
|
+
export type WriteConfirmationHook = ReturnType<typeof createWriteConfirmationHook>;
|
package/dist/index.js
CHANGED
|
@@ -3554,6 +3554,91 @@ function createFrontmatterEnforcerHook(config) {
|
|
|
3554
3554
|
}
|
|
3555
3555
|
};
|
|
3556
3556
|
}
|
|
3557
|
+
// src/hooks/read-confirmation.ts
|
|
3558
|
+
import * as path6 from "path";
|
|
3559
|
+
function createReadConfirmationHook(config) {
|
|
3560
|
+
const { knowledgeBasePath, enabled = true, kbOnly = true } = config;
|
|
3561
|
+
if (!enabled) {
|
|
3562
|
+
return null;
|
|
3563
|
+
}
|
|
3564
|
+
const kbPath = expandPath(knowledgeBasePath);
|
|
3565
|
+
const tracker = {
|
|
3566
|
+
pendingReadPaths: new Map
|
|
3567
|
+
};
|
|
3568
|
+
return {
|
|
3569
|
+
"tool.execute.before": async (input, output) => {
|
|
3570
|
+
if (input.tool !== "read")
|
|
3571
|
+
return;
|
|
3572
|
+
const filePath = output.args?.filePath;
|
|
3573
|
+
if (filePath && input.callID) {
|
|
3574
|
+
tracker.pendingReadPaths.set(input.callID, filePath);
|
|
3575
|
+
}
|
|
3576
|
+
},
|
|
3577
|
+
"tool.execute.after": async (input, output) => {
|
|
3578
|
+
if (input.tool !== "read")
|
|
3579
|
+
return;
|
|
3580
|
+
const filePath = tracker.pendingReadPaths.get(input.callID);
|
|
3581
|
+
tracker.pendingReadPaths.delete(input.callID);
|
|
3582
|
+
if (!filePath)
|
|
3583
|
+
return;
|
|
3584
|
+
if (kbOnly && !filePath.startsWith(kbPath)) {
|
|
3585
|
+
return;
|
|
3586
|
+
}
|
|
3587
|
+
const lineCount = output.output?.split(`
|
|
3588
|
+
`).length || 0;
|
|
3589
|
+
const relativePath = filePath.startsWith(kbPath) ? filePath.slice(kbPath.length + 1) : path6.basename(filePath);
|
|
3590
|
+
log(`[Read confirmed: ${relativePath} (${lineCount} lines)]`);
|
|
3591
|
+
}
|
|
3592
|
+
};
|
|
3593
|
+
}
|
|
3594
|
+
// src/hooks/write-confirmation.ts
|
|
3595
|
+
import * as path7 from "path";
|
|
3596
|
+
function createWriteConfirmationHook(config) {
|
|
3597
|
+
const { knowledgeBasePath, enabled = true, kbOnly = true } = config;
|
|
3598
|
+
if (!enabled) {
|
|
3599
|
+
return null;
|
|
3600
|
+
}
|
|
3601
|
+
const kbPath = expandPath(knowledgeBasePath);
|
|
3602
|
+
const tracker = {
|
|
3603
|
+
pendingWritePaths: new Map
|
|
3604
|
+
};
|
|
3605
|
+
return {
|
|
3606
|
+
"tool.execute.before": async (input, output) => {
|
|
3607
|
+
if (input.tool !== "write" && input.tool !== "edit")
|
|
3608
|
+
return;
|
|
3609
|
+
const filePath = output.args?.filePath;
|
|
3610
|
+
if (filePath && input.callID) {
|
|
3611
|
+
tracker.pendingWritePaths.set(input.callID, {
|
|
3612
|
+
filePath,
|
|
3613
|
+
action: input.tool
|
|
3614
|
+
});
|
|
3615
|
+
}
|
|
3616
|
+
},
|
|
3617
|
+
"tool.execute.after": async (input, output) => {
|
|
3618
|
+
if (input.tool !== "write" && input.tool !== "edit")
|
|
3619
|
+
return;
|
|
3620
|
+
const pending = tracker.pendingWritePaths.get(input.callID);
|
|
3621
|
+
tracker.pendingWritePaths.delete(input.callID);
|
|
3622
|
+
if (!pending)
|
|
3623
|
+
return;
|
|
3624
|
+
const { filePath, action } = pending;
|
|
3625
|
+
if (kbOnly && !filePath.startsWith(kbPath)) {
|
|
3626
|
+
return;
|
|
3627
|
+
}
|
|
3628
|
+
const relativePath = filePath.startsWith(kbPath) ? filePath.slice(kbPath.length + 1) : path7.basename(filePath);
|
|
3629
|
+
const actionLabel = action === "write" ? "Created/Overwrote" : "Edited";
|
|
3630
|
+
const isNewFile = action === "write";
|
|
3631
|
+
const isMarkdownFile = filePath.endsWith(".md");
|
|
3632
|
+
const isIndexFile = relativePath.includes("_index.md") || relativePath.includes("registry.md");
|
|
3633
|
+
let message = `[${actionLabel}: ${relativePath}]`;
|
|
3634
|
+
if (isNewFile && isMarkdownFile && !isIndexFile) {
|
|
3635
|
+
message += `
|
|
3636
|
+
Reminder: Update _index.md if this is a new file. Check bidirectional links.`;
|
|
3637
|
+
}
|
|
3638
|
+
log(message);
|
|
3639
|
+
}
|
|
3640
|
+
};
|
|
3641
|
+
}
|
|
3557
3642
|
// src/hooks/directory-agents-injector/index.ts
|
|
3558
3643
|
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
3559
3644
|
import { dirname as dirname4, join as join9, resolve as resolve2 } from "path";
|
|
@@ -3678,8 +3763,8 @@ function createDirectoryAgentsInjectorHook(options) {
|
|
|
3678
3763
|
}
|
|
3679
3764
|
if (toInject.length === 0)
|
|
3680
3765
|
return;
|
|
3681
|
-
for (const { path:
|
|
3682
|
-
const relativePath =
|
|
3766
|
+
for (const { path: path8, content } of toInject) {
|
|
3767
|
+
const relativePath = path8.replace(knowledgeBasePath, "").replace(/^\//, "");
|
|
3683
3768
|
output.output += `
|
|
3684
3769
|
|
|
3685
3770
|
[Directory Context: ${relativePath}]
|
|
@@ -3766,8 +3851,8 @@ function findNearestMessageWithFields(messageDir) {
|
|
|
3766
3851
|
// src/shared-hooks/utils/logger.ts
|
|
3767
3852
|
import * as fs2 from "fs";
|
|
3768
3853
|
import * as os2 from "os";
|
|
3769
|
-
import * as
|
|
3770
|
-
var logFile =
|
|
3854
|
+
import * as path8 from "path";
|
|
3855
|
+
var logFile = path8.join(os2.tmpdir(), "thoth-plugin.log");
|
|
3771
3856
|
function log2(message, data) {
|
|
3772
3857
|
try {
|
|
3773
3858
|
const timestamp = new Date().toISOString();
|
|
@@ -5567,10 +5652,10 @@ function mergeDefs(...defs) {
|
|
|
5567
5652
|
function cloneDef(schema) {
|
|
5568
5653
|
return mergeDefs(schema._zod.def);
|
|
5569
5654
|
}
|
|
5570
|
-
function getElementAtPath(obj,
|
|
5571
|
-
if (!
|
|
5655
|
+
function getElementAtPath(obj, path9) {
|
|
5656
|
+
if (!path9)
|
|
5572
5657
|
return obj;
|
|
5573
|
-
return
|
|
5658
|
+
return path9.reduce((acc, key) => acc?.[key], obj);
|
|
5574
5659
|
}
|
|
5575
5660
|
function promiseAllObject(promisesObj) {
|
|
5576
5661
|
const keys = Object.keys(promisesObj);
|
|
@@ -5929,11 +6014,11 @@ function aborted(x, startIndex = 0) {
|
|
|
5929
6014
|
}
|
|
5930
6015
|
return false;
|
|
5931
6016
|
}
|
|
5932
|
-
function prefixIssues(
|
|
6017
|
+
function prefixIssues(path9, issues) {
|
|
5933
6018
|
return issues.map((iss) => {
|
|
5934
6019
|
var _a;
|
|
5935
6020
|
(_a = iss).path ?? (_a.path = []);
|
|
5936
|
-
iss.path.unshift(
|
|
6021
|
+
iss.path.unshift(path9);
|
|
5937
6022
|
return iss;
|
|
5938
6023
|
});
|
|
5939
6024
|
}
|
|
@@ -6101,7 +6186,7 @@ function treeifyError(error, _mapper) {
|
|
|
6101
6186
|
return issue2.message;
|
|
6102
6187
|
};
|
|
6103
6188
|
const result = { errors: [] };
|
|
6104
|
-
const processError = (error2,
|
|
6189
|
+
const processError = (error2, path9 = []) => {
|
|
6105
6190
|
var _a, _b;
|
|
6106
6191
|
for (const issue2 of error2.issues) {
|
|
6107
6192
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -6111,7 +6196,7 @@ function treeifyError(error, _mapper) {
|
|
|
6111
6196
|
} else if (issue2.code === "invalid_element") {
|
|
6112
6197
|
processError({ issues: issue2.issues }, issue2.path);
|
|
6113
6198
|
} else {
|
|
6114
|
-
const fullpath = [...
|
|
6199
|
+
const fullpath = [...path9, ...issue2.path];
|
|
6115
6200
|
if (fullpath.length === 0) {
|
|
6116
6201
|
result.errors.push(mapper(issue2));
|
|
6117
6202
|
continue;
|
|
@@ -6143,8 +6228,8 @@ function treeifyError(error, _mapper) {
|
|
|
6143
6228
|
}
|
|
6144
6229
|
function toDotPath(_path) {
|
|
6145
6230
|
const segs = [];
|
|
6146
|
-
const
|
|
6147
|
-
for (const seg of
|
|
6231
|
+
const path9 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
6232
|
+
for (const seg of path9) {
|
|
6148
6233
|
if (typeof seg === "number")
|
|
6149
6234
|
segs.push(`[${seg}]`);
|
|
6150
6235
|
else if (typeof seg === "symbol")
|
|
@@ -17435,7 +17520,7 @@ Status: ${task.status}`;
|
|
|
17435
17520
|
// src/tools/skill/tools.ts
|
|
17436
17521
|
import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync9, lstatSync, readlinkSync } from "fs";
|
|
17437
17522
|
import { homedir as homedir5 } from "os";
|
|
17438
|
-
import { join as join17, basename as
|
|
17523
|
+
import { join as join17, basename as basename5, resolve as resolve3, dirname as dirname5 } from "path";
|
|
17439
17524
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
17440
17525
|
var __filename3 = fileURLToPath2(import.meta.url);
|
|
17441
17526
|
var __dirname3 = dirname5(__filename3);
|
|
@@ -17585,7 +17670,7 @@ async function parseSkillMd(skillPath) {
|
|
|
17585
17670
|
const { data, body } = parseFrontmatter(content);
|
|
17586
17671
|
const frontmatter = parseSkillFrontmatter(data);
|
|
17587
17672
|
const metadata = {
|
|
17588
|
-
name: frontmatter.name ||
|
|
17673
|
+
name: frontmatter.name || basename5(skillPath),
|
|
17589
17674
|
description: frontmatter.description,
|
|
17590
17675
|
license: frontmatter.license,
|
|
17591
17676
|
allowedTools: frontmatter["allowed-tools"],
|
|
@@ -18524,10 +18609,10 @@ function mergeDefs2(...defs) {
|
|
|
18524
18609
|
function cloneDef2(schema) {
|
|
18525
18610
|
return mergeDefs2(schema._zod.def);
|
|
18526
18611
|
}
|
|
18527
|
-
function getElementAtPath2(obj,
|
|
18528
|
-
if (!
|
|
18612
|
+
function getElementAtPath2(obj, path9) {
|
|
18613
|
+
if (!path9)
|
|
18529
18614
|
return obj;
|
|
18530
|
-
return
|
|
18615
|
+
return path9.reduce((acc, key) => acc?.[key], obj);
|
|
18531
18616
|
}
|
|
18532
18617
|
function promiseAllObject2(promisesObj) {
|
|
18533
18618
|
const keys = Object.keys(promisesObj);
|
|
@@ -18908,11 +18993,11 @@ function aborted2(x, startIndex = 0) {
|
|
|
18908
18993
|
}
|
|
18909
18994
|
return false;
|
|
18910
18995
|
}
|
|
18911
|
-
function prefixIssues2(
|
|
18996
|
+
function prefixIssues2(path9, issues) {
|
|
18912
18997
|
return issues.map((iss) => {
|
|
18913
18998
|
var _a;
|
|
18914
18999
|
(_a = iss).path ?? (_a.path = []);
|
|
18915
|
-
iss.path.unshift(
|
|
19000
|
+
iss.path.unshift(path9);
|
|
18916
19001
|
return iss;
|
|
18917
19002
|
});
|
|
18918
19003
|
}
|
|
@@ -19095,7 +19180,7 @@ function formatError2(error45, mapper = (issue3) => issue3.message) {
|
|
|
19095
19180
|
}
|
|
19096
19181
|
function treeifyError2(error45, mapper = (issue3) => issue3.message) {
|
|
19097
19182
|
const result = { errors: [] };
|
|
19098
|
-
const processError = (error46,
|
|
19183
|
+
const processError = (error46, path9 = []) => {
|
|
19099
19184
|
var _a, _b;
|
|
19100
19185
|
for (const issue3 of error46.issues) {
|
|
19101
19186
|
if (issue3.code === "invalid_union" && issue3.errors.length) {
|
|
@@ -19105,7 +19190,7 @@ function treeifyError2(error45, mapper = (issue3) => issue3.message) {
|
|
|
19105
19190
|
} else if (issue3.code === "invalid_element") {
|
|
19106
19191
|
processError({ issues: issue3.issues }, issue3.path);
|
|
19107
19192
|
} else {
|
|
19108
|
-
const fullpath = [...
|
|
19193
|
+
const fullpath = [...path9, ...issue3.path];
|
|
19109
19194
|
if (fullpath.length === 0) {
|
|
19110
19195
|
result.errors.push(mapper(issue3));
|
|
19111
19196
|
continue;
|
|
@@ -19137,8 +19222,8 @@ function treeifyError2(error45, mapper = (issue3) => issue3.message) {
|
|
|
19137
19222
|
}
|
|
19138
19223
|
function toDotPath2(_path) {
|
|
19139
19224
|
const segs = [];
|
|
19140
|
-
const
|
|
19141
|
-
for (const seg of
|
|
19225
|
+
const path9 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
19226
|
+
for (const seg of path9) {
|
|
19142
19227
|
if (typeof seg === "number")
|
|
19143
19228
|
segs.push(`[${seg}]`);
|
|
19144
19229
|
else if (typeof seg === "symbol")
|
|
@@ -30885,13 +30970,13 @@ function resolveRef(ref, ctx) {
|
|
|
30885
30970
|
if (!ref.startsWith("#")) {
|
|
30886
30971
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
30887
30972
|
}
|
|
30888
|
-
const
|
|
30889
|
-
if (
|
|
30973
|
+
const path9 = ref.slice(1).split("/").filter(Boolean);
|
|
30974
|
+
if (path9.length === 0) {
|
|
30890
30975
|
return ctx.rootSchema;
|
|
30891
30976
|
}
|
|
30892
30977
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
30893
|
-
if (
|
|
30894
|
-
const key =
|
|
30978
|
+
if (path9[0] === defsKey) {
|
|
30979
|
+
const key = path9[1];
|
|
30895
30980
|
if (!key || !ctx.defs[key]) {
|
|
30896
30981
|
throw new Error(`Reference not found: ${ref}`);
|
|
30897
30982
|
}
|
|
@@ -31306,6 +31391,8 @@ var HooksConfigSchema = exports_external2.object({
|
|
|
31306
31391
|
"knowledge-persistence": exports_external2.boolean().optional(),
|
|
31307
31392
|
"directory-agents-injector": exports_external2.boolean().optional(),
|
|
31308
31393
|
"frontmatter-enforcer": exports_external2.boolean().optional(),
|
|
31394
|
+
"read-confirmation": exports_external2.boolean().optional(),
|
|
31395
|
+
"write-confirmation": exports_external2.boolean().optional(),
|
|
31309
31396
|
"todo-continuation": exports_external2.boolean().optional(),
|
|
31310
31397
|
"session-recovery": exports_external2.boolean().optional(),
|
|
31311
31398
|
"context-window-monitor": exports_external2.boolean().optional(),
|
|
@@ -31343,7 +31430,7 @@ var ThothPluginConfigSchema = exports_external2.object({
|
|
|
31343
31430
|
}).strict();
|
|
31344
31431
|
// src/index.ts
|
|
31345
31432
|
import * as fs3 from "fs";
|
|
31346
|
-
import * as
|
|
31433
|
+
import * as path9 from "path";
|
|
31347
31434
|
var sessionSpecializations = new Map;
|
|
31348
31435
|
function loadConfigFromPath(configPath) {
|
|
31349
31436
|
try {
|
|
@@ -31375,8 +31462,8 @@ function mergeConfigs(base, override) {
|
|
|
31375
31462
|
};
|
|
31376
31463
|
}
|
|
31377
31464
|
function loadPluginConfig(directory) {
|
|
31378
|
-
const userConfigPath =
|
|
31379
|
-
const projectConfigPath =
|
|
31465
|
+
const userConfigPath = path9.join(getUserConfigDir(), "opencode", "thoth-plugin.json");
|
|
31466
|
+
const projectConfigPath = path9.join(directory, ".opencode", "thoth-plugin.json");
|
|
31380
31467
|
let config3 = loadConfigFromPath(userConfigPath) ?? {};
|
|
31381
31468
|
const projectConfig = loadConfigFromPath(projectConfigPath);
|
|
31382
31469
|
if (projectConfig) {
|
|
@@ -31390,15 +31477,15 @@ function resolveKnowledgeBasePath(config3, directory) {
|
|
|
31390
31477
|
return expandPath(config3.knowledge_base);
|
|
31391
31478
|
}
|
|
31392
31479
|
const commonLocations = [
|
|
31393
|
-
|
|
31394
|
-
|
|
31395
|
-
|
|
31396
|
-
|
|
31397
|
-
|
|
31398
|
-
|
|
31480
|
+
path9.join(process.env.HOME || "", "Repos", "thoth"),
|
|
31481
|
+
path9.join(process.env.HOME || "", "repos", "thoth"),
|
|
31482
|
+
path9.join(process.env.HOME || "", "Projects", "thoth"),
|
|
31483
|
+
path9.join(process.env.HOME || "", "projects", "thoth"),
|
|
31484
|
+
path9.join(process.env.HOME || "", "thoth"),
|
|
31485
|
+
path9.join(directory, "thoth")
|
|
31399
31486
|
];
|
|
31400
31487
|
for (const location of commonLocations) {
|
|
31401
|
-
const kernelPath =
|
|
31488
|
+
const kernelPath = path9.join(location, "kernel");
|
|
31402
31489
|
if (fs3.existsSync(kernelPath)) {
|
|
31403
31490
|
log(`Found knowledge base at: ${location}`);
|
|
31404
31491
|
return location;
|
|
@@ -31420,6 +31507,8 @@ var ThothPlugin = async (ctx) => {
|
|
|
31420
31507
|
const contextAperture = hooksConfig["context-aperture"] !== false ? createContextApertureHook({ knowledgeBasePath }) : null;
|
|
31421
31508
|
const temporalAwareness = hooksConfig["temporal-awareness"] !== false ? createTemporalAwarenessHook() : null;
|
|
31422
31509
|
const frontmatterEnforcer = hooksConfig["frontmatter-enforcer"] !== false ? createFrontmatterEnforcerHook({ knowledgeBasePath }) : null;
|
|
31510
|
+
const readConfirmation = hooksConfig["read-confirmation"] !== false ? createReadConfirmationHook({ knowledgeBasePath }) : null;
|
|
31511
|
+
const writeConfirmation = hooksConfig["write-confirmation"] !== false ? createWriteConfirmationHook({ knowledgeBasePath }) : null;
|
|
31423
31512
|
const todoContinuationEnforcer = hooksConfig["todo-continuation"] !== false ? createTodoContinuationEnforcer(ctx) : null;
|
|
31424
31513
|
const sessionRecoveryHook = hooksConfig["session-recovery"] !== false ? createSessionRecoveryHook(ctx, { experimental: { auto_resume: true } }) : null;
|
|
31425
31514
|
const contextWindowMonitor = hooksConfig["context-window-monitor"] !== false ? createContextWindowMonitorHook(ctx) : null;
|
|
@@ -31525,11 +31614,15 @@ var ThothPlugin = async (ctx) => {
|
|
|
31525
31614
|
await contextAperture?.["tool.execute.before"]?.(input, output);
|
|
31526
31615
|
await trustLevelTracker?.["tool.execute.before"]?.(input, output);
|
|
31527
31616
|
await frontmatterEnforcer?.["tool.execute.before"]?.(input, output);
|
|
31617
|
+
await readConfirmation?.["tool.execute.before"]?.(input, output);
|
|
31618
|
+
await writeConfirmation?.["tool.execute.before"]?.(input, output);
|
|
31528
31619
|
},
|
|
31529
31620
|
"tool.execute.after": async (input, output) => {
|
|
31530
31621
|
await trustLevelTracker?.["tool.execute.after"]?.(input, output);
|
|
31531
31622
|
await contextAperture?.["tool.execute.after"]?.(input, output);
|
|
31532
31623
|
await frontmatterEnforcer?.["tool.execute.after"]?.(input, output);
|
|
31624
|
+
await readConfirmation?.["tool.execute.after"]?.(input, output);
|
|
31625
|
+
await writeConfirmation?.["tool.execute.after"]?.(input, output);
|
|
31533
31626
|
await directoryAgentsInjector?.["tool.execute.after"]?.(input, output);
|
|
31534
31627
|
await contextWindowMonitor?.["tool.execute.after"]?.(input, output);
|
|
31535
31628
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thoth-plugin",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
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",
|