lobsidian 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +186 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +24 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/validate.d.ts +2 -0
- package/dist/commands/validate.js +35 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/lib/content.d.ts +11 -0
- package/dist/lib/content.js +80 -0
- package/dist/lib/content.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +29 -0
- package/dist/lib/frontmatter.js +372 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/scaffold.d.ts +7 -0
- package/dist/lib/scaffold.js +105 -0
- package/dist/lib/scaffold.js.map +1 -0
- package/dist/lib/slug.d.ts +13 -0
- package/dist/lib/slug.js +96 -0
- package/dist/lib/slug.js.map +1 -0
- package/dist/lib/validate/checks.d.ts +23 -0
- package/dist/lib/validate/checks.js +777 -0
- package/dist/lib/validate/checks.js.map +1 -0
- package/dist/lib/validate/fix.d.ts +7 -0
- package/dist/lib/validate/fix.js +75 -0
- package/dist/lib/validate/fix.js.map +1 -0
- package/dist/lib/validate/index.d.ts +37 -0
- package/dist/lib/validate/index.js +88 -0
- package/dist/lib/validate/index.js.map +1 -0
- package/dist/lib/validate/report.d.ts +3 -0
- package/dist/lib/validate/report.js +77 -0
- package/dist/lib/validate/report.js.map +1 -0
- package/dist/lib/vault.d.ts +28 -0
- package/dist/lib/vault.js +119 -0
- package/dist/lib/vault.js.map +1 -0
- package/dist/lib/wikilink.d.ts +45 -0
- package/dist/lib/wikilink.js +156 -0
- package/dist/lib/wikilink.js.map +1 -0
- package/embedded/agent.md +91 -0
- package/embedded/config.md.tmpl +48 -0
- package/embedded/dashboard.md +35 -0
- package/embedded/domains/default.md.tmpl +44 -0
- package/embedded/examples/example-cdn-caching.md +53 -0
- package/embedded/examples/example-configure-cloudfront.md +56 -0
- package/embedded/examples/example-website-performance.md +36 -0
- package/embedded/gitignore +4 -0
- package/embedded/procedures/cascade.md +33 -0
- package/embedded/procedures/context-extraction.md +43 -0
- package/embedded/procedures/facilitation.md +43 -0
- package/embedded/procedures/review-cycle.md +47 -0
- package/embedded/start-here.md +60 -0
- package/embedded/templates/decision.md +29 -0
- package/embedded/templates/domain.md +46 -0
- package/embedded/templates/log.md +12 -0
- package/embedded/templates/motive.md +25 -0
- package/embedded/templates/note.md +13 -0
- package/embedded/templates/task.md +39 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lobsidian Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.jpg" alt="Lobsidian" width="480">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# Lobsidian
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/lobsidian)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
**Structured accountability for AI agents.** Plain markdown files that are simultaneously the agent's memory and the team's knowledge base.
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx lobsidian init ./my-project
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
That's it. Open `my-project/Start Here.md` to see what was created, or point an AI agent at `my-project/_lobsidian/AGENT.md` and it immediately knows how to operate.
|
|
19
|
+
|
|
20
|
+
To install globally:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g lobsidian
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Requires Node.js 18+.
|
|
27
|
+
|
|
28
|
+
## Why Lobsidian?
|
|
29
|
+
|
|
30
|
+
Existing AI memory tools (Mem0, Zep, Letta) solve what an agent can *recall*. Lobsidian solves what an agent is *accountable for* -- who delegated that authority, and how improvements surface.
|
|
31
|
+
|
|
32
|
+
Give an AI agent access to files and it will *do things*. But:
|
|
33
|
+
|
|
34
|
+
- **Who said it should?** No record of why work was started or who approved it.
|
|
35
|
+
- **Is it working?** No success criteria, no review dates, no feedback loop.
|
|
36
|
+
- **What's the scope?** Agents drift. Without boundaries, they reorganize your entire vault.
|
|
37
|
+
- **Where's the audit trail?** Actions happen, but the reasoning disappears with the conversation.
|
|
38
|
+
|
|
39
|
+
Lobsidian fixes this with a simple protocol: every piece of work traces a chain from **motive** (why) to **decision** (what, with human consent) to **task** (concrete work, with outcome). Decisions carry review dates, closing the loop.
|
|
40
|
+
|
|
41
|
+
## How It Works
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
MOTIVE (why) "Website is slow, losing visitors"
|
|
45
|
+
|
|
|
46
|
+
DECISION (what) "Implement CDN caching"
|
|
47
|
+
| success_criteria: "Lighthouse > 90"
|
|
48
|
+
| review_date: 2026-05-20
|
|
49
|
+
| status: proposed -> [human consents] -> active
|
|
50
|
+
|
|
|
51
|
+
TASK (work) "Configure CloudFront distribution"
|
|
52
|
+
| assignee: my-agent
|
|
53
|
+
| outcome: "Lighthouse 94, load time 1.4s"
|
|
54
|
+
|
|
|
55
|
+
REVIEW Is the motive still relevant?
|
|
56
|
+
Did the decision meet its criteria?
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Every file is a markdown file with YAML frontmatter. Frontmatter is the API. Folders are human convenience.
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
### Create a Vault
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
lobsidian init ./my-project
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The path is where Lobsidian creates the vault structure. Use any directory -- `./my-project`, `/path/to/vault`, or `.` for the current directory.
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
- `--name "My Project"` -- vault display name (defaults to directory name)
|
|
73
|
+
- `--agent "my-agent"` -- default agent name (defaults to "agent")
|
|
74
|
+
|
|
75
|
+
This creates:
|
|
76
|
+
```
|
|
77
|
+
my-project/
|
|
78
|
+
_lobsidian/ # System files (agent skill, config, templates, procedures)
|
|
79
|
+
Motives/ # Why something needs attention
|
|
80
|
+
Decisions/ # What was decided and why
|
|
81
|
+
Tasks/ # Concrete work items
|
|
82
|
+
Knowledge/ # Reference material and notes
|
|
83
|
+
Journal/ # Chronological entries
|
|
84
|
+
Inbox/ # Unprocessed items
|
|
85
|
+
Archive/ # Completed items (nothing is deleted)
|
|
86
|
+
Start Here.md # Orientation guide
|
|
87
|
+
.gitignore
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Validate a Vault
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
lobsidian validate ./my-project
|
|
94
|
+
# OK 7 files checked, 0 errors, 0 warnings
|
|
95
|
+
|
|
96
|
+
lobsidian validate ./my-project --fix # Auto-fix: stale claims, orphaned tasks
|
|
97
|
+
lobsidian validate ./my-project --json # Machine-readable output
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
21 checks covering frontmatter schemas, broken wiki-links, circular references, consent bypasses, overdue reviews, and more.
|
|
101
|
+
|
|
102
|
+
### Connect an Agent
|
|
103
|
+
|
|
104
|
+
**Agent Skills (recommended):**
|
|
105
|
+
If your platform supports [Agent Skills](https://agentskills.io), Lobsidian ships a skill file at `skills/lobsidian/SKILL.md`. No extra configuration needed -- the agent discovers it automatically.
|
|
106
|
+
|
|
107
|
+
**Manual one-liner:**
|
|
108
|
+
```bash
|
|
109
|
+
lobsidian init ./my-project --print-shim
|
|
110
|
+
```
|
|
111
|
+
Outputs a shim you can paste into your agent's startup context (e.g. `CLAUDE.md`, `.cursorrules`).
|
|
112
|
+
|
|
113
|
+
**Direct context injection:**
|
|
114
|
+
Load `_lobsidian/AGENT.md` (~1,500 tokens) into the agent's system prompt or context window. Set `LOBSIDIAN_AGENT_NAME` env var for identity.
|
|
115
|
+
|
|
116
|
+
### Integration Contract
|
|
117
|
+
|
|
118
|
+
- **File access:** read/write to the vault directory (plain markdown files only)
|
|
119
|
+
- **Startup context:** load `_lobsidian/AGENT.md` (~1,500 tokens) at agent startup
|
|
120
|
+
- **Quick setup:** `lobsidian init --print-shim` outputs a one-liner for agent config files
|
|
121
|
+
|
|
122
|
+
## What Makes This Different
|
|
123
|
+
|
|
124
|
+
**No infrastructure.** No databases, no servers, no vector stores. Just markdown files on disk. Works offline. Works with git.
|
|
125
|
+
|
|
126
|
+
**Self-documenting agents.** An agent reads one file (`_lobsidian/AGENT.md`, ~1,500 tokens) and knows the entire protocol. No training, no fine-tuning, no SDK integration.
|
|
127
|
+
|
|
128
|
+
**Consent gates.** Agent-created decisions start as `draft` or `proposed`. An agent *cannot* set `status: active` on its own decisions -- human consent is a hard requirement, not a suggestion.
|
|
129
|
+
|
|
130
|
+
**Review cycles.** Every decision carries a `review_date` and `success_criteria`. When the date arrives, the agent surfaces the review. This is the feedback loop that most AI systems lack entirely.
|
|
131
|
+
|
|
132
|
+
**Domain boundaries.** Each agent operates within a defined domain -- a set of filesystem paths with explicit permissions. Built-in WIP limits prevent agents from claiming everything at once.
|
|
133
|
+
|
|
134
|
+
**Framework-agnostic.** Lobsidian is a convention layer. It works with any agent that can read and write files.
|
|
135
|
+
|
|
136
|
+
**Human-compatible.** Everything is plain markdown. Browse motives in Obsidian. Review decisions in VS Code. Approve tasks in your terminal. The vault works without any agent at all.
|
|
137
|
+
|
|
138
|
+
## Agent Skill
|
|
139
|
+
|
|
140
|
+
Lobsidian ships an [Agent Skill](https://agentskills.io) at [`skills/lobsidian/SKILL.md`](skills/lobsidian/SKILL.md). Compatible with any platform supporting the Agent Skills standard:
|
|
141
|
+
|
|
142
|
+
- Claude Code
|
|
143
|
+
- OpenAI Codex
|
|
144
|
+
- Gemini CLI
|
|
145
|
+
- Cursor
|
|
146
|
+
- GitHub Copilot
|
|
147
|
+
- OpenClaw / ClawHub
|
|
148
|
+
- Goose
|
|
149
|
+
- Any agent supporting Agent Skills
|
|
150
|
+
|
|
151
|
+
If your platform doesn't support Agent Skills, load `_lobsidian/AGENT.md` directly into the agent's context window.
|
|
152
|
+
|
|
153
|
+
## The Protocol
|
|
154
|
+
|
|
155
|
+
### Core Types
|
|
156
|
+
|
|
157
|
+
| Type | Folder | Purpose |
|
|
158
|
+
|------|--------|---------|
|
|
159
|
+
| `motive` | `Motives/` | Why something needs attention (situation + need + effect) |
|
|
160
|
+
| `decision` | `Decisions/` | What was decided, with success criteria and review date |
|
|
161
|
+
| `task` | `Tasks/` | Concrete work with assignee, deliverables, and outcome |
|
|
162
|
+
| `note` | `Knowledge/` | Captured insights and reference material |
|
|
163
|
+
| `log` | `Journal/` | Chronological entries (daily, activity, meeting, reflection) |
|
|
164
|
+
| `domain` | `_lobsidian/domains/` | Agent scope, permissions, and boundaries |
|
|
165
|
+
|
|
166
|
+
### Status Flows
|
|
167
|
+
|
|
168
|
+
**Motives:** `open` -> `addressed` -> `obsolete`
|
|
169
|
+
|
|
170
|
+
**Decisions:** `draft` -> `proposed` -> `active` -> `under-review` -> `retired` or `superseded`
|
|
171
|
+
|
|
172
|
+
**Tasks:** `pending` -> `claimed` -> `in-progress` -> `done` or `cancelled`
|
|
173
|
+
|
|
174
|
+
## Spec
|
|
175
|
+
|
|
176
|
+
The full protocol specification is in [`spec.md`](spec.md) (~960 lines). Advanced multi-agent coordination, cascade protocols, and domain emergence are in [`spec-advanced.md`](spec-advanced.md).
|
|
177
|
+
|
|
178
|
+
The spec was developed through 15 expert agent reviews and 67 fixes across 8 versions. Design history is in [`CHANGELOG.md`](CHANGELOG.md).
|
|
179
|
+
|
|
180
|
+
## Contributing
|
|
181
|
+
|
|
182
|
+
Found a bug or have a suggestion? [Open an issue](https://github.com/cannuri/lobsidian/issues).
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
[MIT](LICENSE)
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { registerInitCommand } from './commands/init.js';
|
|
5
|
+
import { validateCommand } from './commands/validate.js';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const pkg = require('../package.json');
|
|
8
|
+
const program = new Command()
|
|
9
|
+
.name('lobsidian')
|
|
10
|
+
.description('Lobsidian — markdown-based knowledge protocol for AI agents')
|
|
11
|
+
.version(pkg.version, '-v, --version');
|
|
12
|
+
registerInitCommand(program);
|
|
13
|
+
program.addCommand(validateCommand);
|
|
14
|
+
program.parse();
|
|
15
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;AAE9D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;KAC1B,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,6DAA6D,CAAC;KAC1E,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;AAEzC,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;AAEpC,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { resolve, basename, join } from 'node:path';
|
|
2
|
+
import { defaultVars } from '../lib/content.js';
|
|
3
|
+
import { init, summary } from '../lib/scaffold.js';
|
|
4
|
+
export function registerInitCommand(program) {
|
|
5
|
+
program
|
|
6
|
+
.command('init [path]')
|
|
7
|
+
.description('Scaffold a new Lobsidian vault')
|
|
8
|
+
.option('--name <name>', 'vault name (default: directory basename)')
|
|
9
|
+
.option('--agent <agent>', 'default agent name', 'agent')
|
|
10
|
+
.option('--print-shim', 'output one-liner for agent startup context')
|
|
11
|
+
.action((pathArg, opts) => {
|
|
12
|
+
const target = resolve(pathArg ?? '.');
|
|
13
|
+
const vaultName = opts.name || basename(target);
|
|
14
|
+
const vars = defaultVars(vaultName, opts.agent);
|
|
15
|
+
const result = init(target, vars);
|
|
16
|
+
console.log(summary(result));
|
|
17
|
+
if (opts.printShim) {
|
|
18
|
+
const agentMd = join(target, '_lobsidian', 'AGENT.md');
|
|
19
|
+
console.log(`\nOn startup, read ${agentMd} and follow its conventions.`);
|
|
20
|
+
console.log(`Your Lobsidian agent name is: ${opts.agent}`);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CAAC,gCAAgC,CAAC;SAC7C,MAAM,CAAC,eAAe,EAAE,0CAA0C,CAAC;SACnE,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,EAAE,OAAO,CAAC;SACxD,MAAM,CAAC,cAAc,EAAE,4CAA4C,CAAC;SACpE,MAAM,CAAC,CAAC,OAA2B,EAAE,IAA2D,EAAE,EAAE;QACnG,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;QAEvC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAEhD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAE7B,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,sBAAsB,OAAO,8BAA8B,CAAC,CAAC;YACzE,OAAO,CAAC,GAAG,CAAC,iCAAiC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { run, formatReport, Severity } from "../lib/validate/index.js";
|
|
3
|
+
export const validateCommand = new Command("validate")
|
|
4
|
+
.description("Check a Lobsidian vault for issues")
|
|
5
|
+
.argument("[path]", "path to vault root", ".")
|
|
6
|
+
.option("--fix", "auto-fix safe issues (stale claims, orphaned tasks)", false)
|
|
7
|
+
.option("--json", "output results as JSON", false)
|
|
8
|
+
.option("--no-color", "disable colored output")
|
|
9
|
+
.option("--quiet", "show summary only", false)
|
|
10
|
+
.action((path, options) => {
|
|
11
|
+
const opts = {
|
|
12
|
+
path,
|
|
13
|
+
fix: options.fix === true,
|
|
14
|
+
json: options.json === true,
|
|
15
|
+
noColor: options.color === false, // commander negates --no-color to color=false
|
|
16
|
+
quiet: options.quiet === true,
|
|
17
|
+
};
|
|
18
|
+
const { findings, fileCount } = run(opts);
|
|
19
|
+
const output = formatReport(findings, fileCount, opts);
|
|
20
|
+
process.stdout.write(output);
|
|
21
|
+
// Exit codes: 0=clean, 1=warnings only, 2=errors
|
|
22
|
+
let hasErrors = false;
|
|
23
|
+
let hasWarnings = false;
|
|
24
|
+
for (const r of findings) {
|
|
25
|
+
if (r.severity === Severity.Error)
|
|
26
|
+
hasErrors = true;
|
|
27
|
+
else if (r.severity === Severity.Warning)
|
|
28
|
+
hasWarnings = true;
|
|
29
|
+
}
|
|
30
|
+
if (hasErrors)
|
|
31
|
+
process.exit(2);
|
|
32
|
+
if (hasWarnings)
|
|
33
|
+
process.exit(1);
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEvE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;KACnD,WAAW,CAAC,oCAAoC,CAAC;KACjD,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,EAAE,GAAG,CAAC;KAC7C,MAAM,CAAC,OAAO,EAAE,qDAAqD,EAAE,KAAK,CAAC;KAC7E,MAAM,CAAC,QAAQ,EAAE,wBAAwB,EAAE,KAAK,CAAC;KACjD,MAAM,CAAC,YAAY,EAAE,wBAAwB,CAAC;KAC9C,MAAM,CAAC,SAAS,EAAE,mBAAmB,EAAE,KAAK,CAAC;KAC7C,MAAM,CAAC,CAAC,IAAY,EAAE,OAAgC,EAAE,EAAE;IACzD,MAAM,IAAI,GAAG;QACX,IAAI;QACJ,GAAG,EAAE,OAAO,CAAC,GAAG,KAAK,IAAI;QACzB,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,IAAI;QAC3B,OAAO,EAAE,OAAO,CAAC,KAAK,KAAK,KAAK,EAAE,8CAA8C;QAChF,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI;KAC9B,CAAC;IAEF,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IACvD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAE7B,iDAAiD;IACjD,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,KAAK;YAAE,SAAS,GAAG,IAAI,CAAC;aAC/C,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,OAAO;YAAE,WAAW,GAAG,IAAI,CAAC;IAC/D,CAAC;IAED,IAAI,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,WAAW;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface TemplateVars {
|
|
2
|
+
vaultName: string;
|
|
3
|
+
agentName: string;
|
|
4
|
+
date: string;
|
|
5
|
+
reviewDate: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function defaultVars(vaultName: string, agentName: string): TemplateVars;
|
|
8
|
+
export declare function render(content: string, vars: TemplateVars, filename: string): string;
|
|
9
|
+
export declare function stripTmplExtension(filename: string): string;
|
|
10
|
+
export declare function readEmbedded(path: string): string;
|
|
11
|
+
export declare function listEmbedded(dir: string): string[];
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
// defaultVars returns TemplateVars with sensible defaults.
|
|
5
|
+
export function defaultVars(vaultName, agentName) {
|
|
6
|
+
const now = new Date();
|
|
7
|
+
const review = new Date(now);
|
|
8
|
+
review.setMonth(review.getMonth() + 3);
|
|
9
|
+
const fmt = (d) => d.getFullYear() + '-' +
|
|
10
|
+
String(d.getMonth() + 1).padStart(2, '0') + '-' +
|
|
11
|
+
String(d.getDate()).padStart(2, '0');
|
|
12
|
+
return {
|
|
13
|
+
vaultName,
|
|
14
|
+
agentName,
|
|
15
|
+
date: fmt(now),
|
|
16
|
+
reviewDate: fmt(review),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// render applies template variables to content if it contains Go-style template syntax.
|
|
20
|
+
// Files ending in .tmpl are always treated as templates.
|
|
21
|
+
// Other files are checked for {{ markers before processing.
|
|
22
|
+
export function render(content, vars, filename) {
|
|
23
|
+
const isTemplate = filename.endsWith('.tmpl');
|
|
24
|
+
if (!isTemplate && !content.includes('{{'))
|
|
25
|
+
return content;
|
|
26
|
+
return content
|
|
27
|
+
.replace(/\{\{\.VaultName\}\}/g, vars.vaultName)
|
|
28
|
+
.replace(/\{\{\.AgentName\}\}/g, vars.agentName)
|
|
29
|
+
.replace(/\{\{\.Date\}\}/g, vars.date)
|
|
30
|
+
.replace(/\{\{\.ReviewDate\}\}/g, vars.reviewDate);
|
|
31
|
+
}
|
|
32
|
+
// stripTmplExtension removes the .tmpl suffix if present.
|
|
33
|
+
export function stripTmplExtension(filename) {
|
|
34
|
+
return filename.endsWith('.tmpl') ? filename.slice(0, -5) : filename;
|
|
35
|
+
}
|
|
36
|
+
// Resolve the path to the embedded/ directory.
|
|
37
|
+
// Works both from source (src/) and compiled (dist/) locations.
|
|
38
|
+
function resolveEmbeddedDir() {
|
|
39
|
+
try {
|
|
40
|
+
// import.meta.url points to dist/lib/content.js or src/lib/content.ts
|
|
41
|
+
// embedded/ is at the package root, so ../../embedded from here
|
|
42
|
+
const thisDir = fileURLToPath(new URL('.', import.meta.url));
|
|
43
|
+
const candidate = resolve(thisDir, '..', '..', 'embedded');
|
|
44
|
+
statSync(candidate);
|
|
45
|
+
return candidate;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Fallback: walk up from __dirname-like resolution
|
|
49
|
+
const thisDir = fileURLToPath(new URL('.', import.meta.url));
|
|
50
|
+
return resolve(thisDir, '..', '..', 'embedded');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const embeddedDir = resolveEmbeddedDir();
|
|
54
|
+
// readEmbedded reads a file from the embedded directory.
|
|
55
|
+
// path is relative to embedded/, e.g. "templates/motive.md"
|
|
56
|
+
export function readEmbedded(path) {
|
|
57
|
+
return readFileSync(join(embeddedDir, path), 'utf-8');
|
|
58
|
+
}
|
|
59
|
+
// listEmbedded lists all files under a directory in the embedded filesystem.
|
|
60
|
+
// Returns paths relative to embedded/, e.g. ["templates/motive.md", ...].
|
|
61
|
+
export function listEmbedded(dir) {
|
|
62
|
+
const paths = [];
|
|
63
|
+
const base = join(embeddedDir, dir);
|
|
64
|
+
function walk(current) {
|
|
65
|
+
const entries = readdirSync(current, { withFileTypes: true });
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
const full = join(current, entry.name);
|
|
68
|
+
if (entry.isDirectory()) {
|
|
69
|
+
walk(full);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Return path relative to embeddedDir
|
|
73
|
+
paths.push(full.slice(embeddedDir.length + 1));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
walk(base);
|
|
78
|
+
return paths;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/lib/content.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type ContentType = 'motive' | 'decision' | 'task' | 'domain' | 'log' | 'note' | 'config' | 'dashboard' | 'cascade-checkpoint' | 'bootstrap-consent';
|
|
2
|
+
export declare const ALL_TYPES: ContentType[];
|
|
3
|
+
export declare const CORE_TYPES: ContentType[];
|
|
4
|
+
export declare const SYSTEM_TYPES: ContentType[];
|
|
5
|
+
export declare function isKnownType(t: string): t is ContentType;
|
|
6
|
+
export declare enum Severity {
|
|
7
|
+
Warning = 0,
|
|
8
|
+
Error = 1
|
|
9
|
+
}
|
|
10
|
+
export declare function severityString(s: Severity): string;
|
|
11
|
+
export interface ValidationResult {
|
|
12
|
+
severity: Severity;
|
|
13
|
+
path: string;
|
|
14
|
+
line: number;
|
|
15
|
+
message: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class Frontmatter {
|
|
18
|
+
fields: Record<string, unknown>;
|
|
19
|
+
fieldLines: Record<string, number>;
|
|
20
|
+
body: string;
|
|
21
|
+
rawYAML: string;
|
|
22
|
+
constructor(fields: Record<string, unknown>, fieldLines: Record<string, number>, body: string, rawYAML: string);
|
|
23
|
+
type(): ContentType | '';
|
|
24
|
+
getString(key: string): string;
|
|
25
|
+
getStringSlice(key: string): string[] | null;
|
|
26
|
+
}
|
|
27
|
+
export declare function parse(content: string): Frontmatter;
|
|
28
|
+
export declare const EXPECTED_FOLDER: Partial<Record<ContentType, string>>;
|
|
29
|
+
export declare function validate(fm: Frontmatter | null, path: string): ValidationResult[];
|