@onedotmint/charterleaf 0.1.0 → 0.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/AGENTS_SNIPPET.md +9 -1
- package/README.md +8 -2
- package/lib/charterleaf.js +94 -0
- package/package.json +1 -1
- package/skills/charterleaf/SKILL.md +19 -1
package/AGENTS_SNIPPET.md
CHANGED
|
@@ -8,7 +8,7 @@ Project specifications live under `specs/`. For non-trivial architectural, behav
|
|
|
8
8
|
- `specs/changes/` — lightweight active deltas.
|
|
9
9
|
- `specs/constitution.md` — project-wide guardrails only.
|
|
10
10
|
|
|
11
|
-
When a concrete code path is known, use `charterleaf related <path>` to locate matching living specs plus active changes that affect them.
|
|
11
|
+
When a concrete code path is known, use `charterleaf related <path>` to locate matching living specs plus active changes that affect them. When the relevant code path is not yet known, use `charterleaf map` only to discover available specification scopes; do not read every listed specification. Use normal code navigation to locate the smallest relevant area, then use `charterleaf related <path>`. For broad project-level questions, start with normal project entry documents; do not expand into every spec or project-state document merely because no path was provided. Use `charterleaf lint` for structural validation and mechanical active-change conflicts. These commands are helpers, not workflow gates.
|
|
12
12
|
|
|
13
13
|
Authority is `Constitution > Active Change > Living Spec > Current Code`. Do not add authority scores or silently rewrite specs to match code.
|
|
14
14
|
|
|
@@ -16,6 +16,14 @@ Keep specs compressed and evidence-based. Prefer durable contracts and pitfalls
|
|
|
16
16
|
|
|
17
17
|
Do not create specs for temporary implementation details, current tasks, debugging notes, routine refactors, or trivial fixes with no durable implication. For a durable bug fix, use `Current` / `Expected` when useful and explicitly state what must `PRESERVE`.
|
|
18
18
|
|
|
19
|
+
After non-trivial implementation or debugging, consider whether durable project knowledge was discovered. If so, record only the durable result:
|
|
20
|
+
|
|
21
|
+
- observable behavior, contract, invariant, or non-goal → capability spec;
|
|
22
|
+
- long-lived implementation constraint, boundary, recurring pitfall, or required verification practice → engineering spec;
|
|
23
|
+
- important architectural choice, rationale, or revisit condition → decision.
|
|
24
|
+
|
|
25
|
+
If nothing durable was learned, do not modify specs. Do not record current task status, implementation progress, temporary debugging notes, one-off commands, session history, Todo items, or transient observations. Specs accumulate durable project knowledge, not development activity.
|
|
26
|
+
|
|
19
27
|
For meaningful durable behavior/contract changes, use one change file with only the needed `ADD`, `MODIFY`, `REMOVE`, and/or `PRESERVE` sections. After acceptance and implementation, merge durable results into living specs and delete the change file. Git is the history.
|
|
20
28
|
|
|
21
29
|
Charterleaf is a knowledge layer, not a workflow layer. It must not manage tasks, planning, agents, execution, review, memory, compression, semantic search, code/spec verification, release lifecycle, or Git history.
|
package/README.md
CHANGED
|
@@ -7,9 +7,10 @@ without workflow orchestration.
|
|
|
7
7
|
|
|
8
8
|
> **Keep project intent in the repo, without turning specs into a workflow.**
|
|
9
9
|
|
|
10
|
-
Charterleaf keeps durable project knowledge in Markdown and provides exactly
|
|
10
|
+
Charterleaf keeps durable project knowledge in Markdown and provides exactly three deterministic helpers:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
+
charterleaf map
|
|
13
14
|
charterleaf related <path>
|
|
14
15
|
charterleaf lint
|
|
15
16
|
```
|
|
@@ -37,10 +38,13 @@ No Pi extension or Pi runtime dependency is used.
|
|
|
37
38
|
Run from a repository root containing `specs/`:
|
|
38
39
|
|
|
39
40
|
```bash
|
|
41
|
+
charterleaf map
|
|
40
42
|
charterleaf related src/auth/session.ts
|
|
41
43
|
charterleaf lint
|
|
42
44
|
```
|
|
43
45
|
|
|
46
|
+
`map` shows a deterministic, lightweight index of the `specs/` layer when a relevant code path is not yet known. It lists specification scopes and metadata only; it does not summarize specs or recommend what to read. Use normal code navigation to find the smallest relevant area, then use `related <path>`.
|
|
47
|
+
|
|
44
48
|
`related <path>` performs deterministic path routing. It matches `applies_to` with `*` and `**`, normalizes repo-relative paths to POSIX form, then appends active changes whose `affects` reference a matched Spec ID. Output is stable; no match is success. It is not semantic search.
|
|
45
49
|
|
|
46
50
|
`lint` performs mechanical checks only: frontmatter shape, living Spec IDs, duplicate requirement IDs, change references, static `ADD` / `MODIFY` / `REMOVE` references, and conflicting active changes that touch the same requirement ID. It does not judge wording, architecture quality, or semantic code/spec drift.
|
|
@@ -74,6 +78,8 @@ Keep specs compressed and evidence-based. Prefer contracts over commentary and r
|
|
|
74
78
|
|
|
75
79
|
For bug fixes with durable behavioral implications, clarify `Current`, `Expected`, and `PRESERVE` when that reduces accidental scope. Do not create a spec for trivial fixes with no durable implication.
|
|
76
80
|
|
|
81
|
+
After non-trivial implementation or debugging, record only genuinely durable knowledge: behavior, contracts, invariants, and non-goals in capabilities; long-lived constraints, boundaries, pitfalls, or verification practices in engineering; and important architectural choices in decisions. If nothing durable was learned, do not modify specs. Specs accumulate durable project knowledge, not development activity.
|
|
82
|
+
|
|
77
83
|
Load the smallest relevant set; do not read the whole tree by default. After an accepted change is implemented, merge the durable result into living specs and delete the change file. Git is the history.
|
|
78
84
|
|
|
79
85
|
## Other agents
|
|
@@ -98,7 +104,7 @@ Charterleaf has no:
|
|
|
98
104
|
- plugin/extension runtime
|
|
99
105
|
- Git/history abstraction
|
|
100
106
|
|
|
101
|
-
The
|
|
107
|
+
The three CLI commands are helpers, not workflow gates.
|
|
102
108
|
|
|
103
109
|
## Development
|
|
104
110
|
|
package/lib/charterleaf.js
CHANGED
|
@@ -225,6 +225,84 @@ function related(repoPath, cwd = process.cwd(), streams = {}) {
|
|
|
225
225
|
return 0;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
function writeLivingSpecs(stdout, documents) {
|
|
229
|
+
for (const document of documents) {
|
|
230
|
+
const id = typeof document.frontmatter.id === 'string' ? document.frontmatter.id.trim() : '';
|
|
231
|
+
const appliesTo = Array.isArray(document.frontmatter.applies_to)
|
|
232
|
+
? document.frontmatter.applies_to.join(', ')
|
|
233
|
+
: String(document.frontmatter.applies_to || '');
|
|
234
|
+
write(stdout, ` ${id || '(missing id)'}`);
|
|
235
|
+
write(stdout, ` ${document.display}`);
|
|
236
|
+
write(stdout, ` applies_to: ${appliesTo}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function map(cwd = process.cwd(), streams = {}) {
|
|
241
|
+
const stdout = streams.stdout || process.stdout;
|
|
242
|
+
const stderr = streams.stderr || process.stderr;
|
|
243
|
+
const specRoot = path.join(cwd, 'specs');
|
|
244
|
+
if (!fs.existsSync(specRoot) || !fs.statSync(specRoot).isDirectory()) {
|
|
245
|
+
write(stderr, 'ERROR specs/: not found');
|
|
246
|
+
return 2;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const documents = [];
|
|
250
|
+
const errors = [];
|
|
251
|
+
for (const filePath of walkMarkdown(specRoot)) {
|
|
252
|
+
const display = relativeDisplay(filePath, cwd);
|
|
253
|
+
try {
|
|
254
|
+
const { frontmatter } = parseFrontmatter(fs.readFileSync(filePath, 'utf8'));
|
|
255
|
+
documents.push({
|
|
256
|
+
filePath,
|
|
257
|
+
display,
|
|
258
|
+
frontmatter: frontmatter || {},
|
|
259
|
+
relative: path.relative(specRoot, filePath).replaceAll('\\', '/'),
|
|
260
|
+
});
|
|
261
|
+
} catch (error) {
|
|
262
|
+
errors.push(`ERROR ${display}: ${error.message}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (errors.length) {
|
|
266
|
+
for (const error of errors) write(stderr, error);
|
|
267
|
+
return 2;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const constitution = [];
|
|
271
|
+
const capabilities = [];
|
|
272
|
+
const engineering = [];
|
|
273
|
+
const decisions = [];
|
|
274
|
+
const changes = [];
|
|
275
|
+
for (const document of documents) {
|
|
276
|
+
if (document.relative === 'constitution.md') constitution.push(document);
|
|
277
|
+
else if (document.relative.startsWith('capabilities/')) capabilities.push(document);
|
|
278
|
+
else if (document.relative.startsWith('engineering/')) engineering.push(document);
|
|
279
|
+
else if (document.relative.startsWith('decisions/')) decisions.push(document);
|
|
280
|
+
else if (document.relative.startsWith('changes/')) changes.push(document);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
write(stdout, 'Constitution');
|
|
284
|
+
for (const document of constitution) write(stdout, ` ${document.display}`);
|
|
285
|
+
write(stdout, '');
|
|
286
|
+
write(stdout, 'Capabilities');
|
|
287
|
+
writeLivingSpecs(stdout, capabilities);
|
|
288
|
+
write(stdout, '');
|
|
289
|
+
write(stdout, 'Engineering');
|
|
290
|
+
writeLivingSpecs(stdout, engineering);
|
|
291
|
+
write(stdout, '');
|
|
292
|
+
write(stdout, 'Decisions');
|
|
293
|
+
for (const document of decisions) write(stdout, ` ${document.display}`);
|
|
294
|
+
write(stdout, '');
|
|
295
|
+
write(stdout, 'Active changes');
|
|
296
|
+
for (const document of changes) {
|
|
297
|
+
const affects = Array.isArray(document.frontmatter.affects)
|
|
298
|
+
? document.frontmatter.affects.join(', ')
|
|
299
|
+
: String(document.frontmatter.affects || '');
|
|
300
|
+
write(stdout, ` ${document.display}`);
|
|
301
|
+
write(stdout, ` affects: ${affects}`);
|
|
302
|
+
}
|
|
303
|
+
return 0;
|
|
304
|
+
}
|
|
305
|
+
|
|
228
306
|
function isLivingSpec(filePath, specRoot) {
|
|
229
307
|
const relative = path.relative(specRoot, filePath).replaceAll('\\', '/').split('/');
|
|
230
308
|
return relative.length >= 2 && (relative[0] === 'capabilities' || relative[0] === 'engineering');
|
|
@@ -306,6 +384,12 @@ function lint(cwd = process.cwd(), streams = {}) {
|
|
|
306
384
|
ids.set(specId, filePath);
|
|
307
385
|
}
|
|
308
386
|
}
|
|
387
|
+
|
|
388
|
+
if (!Object.hasOwn(frontmatter, 'applies_to')) {
|
|
389
|
+
errors.push([display, 'missing applies_to']);
|
|
390
|
+
} else if (Array.isArray(frontmatter.applies_to) && frontmatter.applies_to.length === 0) {
|
|
391
|
+
errors.push([display, 'applies_to must contain at least one path']);
|
|
392
|
+
}
|
|
309
393
|
}
|
|
310
394
|
|
|
311
395
|
for (const field of ['applies_to', 'tags']) {
|
|
@@ -443,6 +527,7 @@ function helpText() {
|
|
|
443
527
|
'Usage: charterleaf <command> [args]',
|
|
444
528
|
'',
|
|
445
529
|
'Commands:',
|
|
530
|
+
' map show the specification map',
|
|
446
531
|
' related <path> find specs matching a repo-relative code path',
|
|
447
532
|
' lint check Charterleaf structural conventions',
|
|
448
533
|
].join('\n');
|
|
@@ -463,6 +548,14 @@ function main(argv = process.argv.slice(2), cwd = process.cwd(), streams = {}) {
|
|
|
463
548
|
if (!argv.length) return usageError('a command is required', streams);
|
|
464
549
|
|
|
465
550
|
const [command, ...rest] = argv;
|
|
551
|
+
if (command === 'map') {
|
|
552
|
+
if (rest.length === 1 && (rest[0] === '--help' || rest[0] === '-h')) {
|
|
553
|
+
write(streams.stdout || process.stdout, 'Usage: charterleaf map');
|
|
554
|
+
return 0;
|
|
555
|
+
}
|
|
556
|
+
if (rest.length) return usageError('map takes no arguments', streams);
|
|
557
|
+
return map(path.resolve(cwd), streams);
|
|
558
|
+
}
|
|
466
559
|
if (command === 'related') {
|
|
467
560
|
if (rest.length === 1 && (rest[0] === '--help' || rest[0] === '-h')) {
|
|
468
561
|
write(streams.stdout || process.stdout, 'Usage: charterleaf related <path>');
|
|
@@ -488,6 +581,7 @@ module.exports = {
|
|
|
488
581
|
globMatches,
|
|
489
582
|
lint,
|
|
490
583
|
main,
|
|
584
|
+
map,
|
|
491
585
|
normalizeRepoPath,
|
|
492
586
|
parseFrontmatter,
|
|
493
587
|
related,
|
package/package.json
CHANGED
|
@@ -13,7 +13,17 @@ When a concrete code path is known, run:
|
|
|
13
13
|
charterleaf related <path>
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Read only the returned living specs and active changes.
|
|
16
|
+
Read only the returned living specs and active changes.
|
|
17
|
+
|
|
18
|
+
When the relevant code path is not yet known, run:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
charterleaf map
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Use the map only to discover available specification scopes. Do not read every listed specification by default. Use normal code navigation to locate the smallest relevant area. Once a concrete path is known, run `charterleaf related <path>`.
|
|
25
|
+
|
|
26
|
+
For broad project-level questions, start with normal project entry documents. Do not expand into every spec or project-state document merely because no path was provided. `map` is not semantic search.
|
|
17
27
|
|
|
18
28
|
Use authority in this order:
|
|
19
29
|
|
|
@@ -25,6 +35,14 @@ Keep specs compressed and evidence-based. Prefer contracts over commentary and d
|
|
|
25
35
|
|
|
26
36
|
Do not touch specs for renames, lint cleanup, routine refactors, temporary debugging, Todo/status changes, or trivial fixes with no durable implication.
|
|
27
37
|
|
|
38
|
+
After non-trivial implementation or debugging, consider whether durable project knowledge was discovered. If so, record only the durable result:
|
|
39
|
+
|
|
40
|
+
- observable behavior, contract, invariant, or non-goal → capability spec;
|
|
41
|
+
- long-lived implementation constraint, boundary, recurring pitfall, or required verification practice → engineering spec;
|
|
42
|
+
- important architectural choice, rationale, or revisit condition → decision.
|
|
43
|
+
|
|
44
|
+
If nothing durable was learned, do not modify specs. Do not record current task status, implementation progress, temporary debugging notes, one-off commands, session history, Todo items, or transient observations. Specs accumulate durable project knowledge, not development activity.
|
|
45
|
+
|
|
28
46
|
For a durable bug fix, use `Current` and `Expected` when useful and state what must `PRESERVE`. For other durable behavior/contract changes, keep one `specs/changes/<name>.md` delta with only needed `ADD`, `MODIFY`, `REMOVE`, and/or `PRESERVE` sections.
|
|
29
47
|
|
|
30
48
|
After editing specs, run:
|