@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 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. Otherwise use normal code/text search. Use `charterleaf lint` for structural validation and mechanical active-change conflicts. These commands are helpers, not workflow gates.
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 two deterministic helpers:
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 two CLI commands are helpers, not workflow gates.
107
+ The three CLI commands are helpers, not workflow gates.
102
108
 
103
109
  ## Development
104
110
 
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onedotmint/charterleaf",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "A tiny specification layer for coding agents.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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. If the location is unknown, use the host agent's normal search tools; Charterleaf does not provide semantic search.
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: