@entropicwarrior/sdoc 0.2.6 → 0.2.7

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.
Files changed (3) hide show
  1. package/README.md +12 -2
  2. package/package.json +1 -1
  3. package/src/sdoc.js +25 -2
package/README.md CHANGED
@@ -74,7 +74,17 @@ Key resources for agents:
74
74
  | [`docs/reference/sdoc-authoring.sdoc`](https://raw.githubusercontent.com/entropicwarrior/sdoc/main/docs/reference/sdoc-authoring.sdoc) | Skill document — drop into context to read/write SDOC immediately |
75
75
  | [`lexica/specification.sdoc`](https://raw.githubusercontent.com/entropicwarrior/sdoc/main/lexica/specification.sdoc) | Formal spec with EBNF grammar |
76
76
 
77
- All `.sdoc` files are designed for progressive disclosure — read the `@about` scope first (~50 tokens), then scan headings, then load only the section you need.
77
+ All `.sdoc` files are designed for progressive disclosure. The JavaScript API provides three functions that let agents navigate without loading entire files:
78
+
79
+ ```javascript
80
+ const { extractAbout, listSections, extractSection } = require("@entropicwarrior/sdoc");
81
+
82
+ extractAbout(text); // ~50 tokens — what is this file about?
83
+ listSections(text); // ~50-100 tokens — what sections does it have?
84
+ extractSection(text, "error-handling"); // ~200-1000 tokens — give me just this section
85
+ ```
86
+
87
+ Total cost for a precise answer: ~750 tokens. The same lookup in Markdown requires loading the full file (5,000-50,000 tokens).
78
88
 
79
89
  ## Format at a Glance
80
90
 
@@ -159,7 +169,7 @@ Markdown-style images with optional width and alignment:
159
169
 
160
170
  ### Tables
161
171
 
162
- Pipe-delimited tables with optional flags for appearance (`borderless`, `headerless`), width (`auto`, `60%`, `400px`), and alignment (`left`, `center`, `right`). All flags compose freely.
172
+ Pipe-delimited tables with optional flags for appearance (`borderless`, `headerless`), width (`auto`, `60%`, `400px`), and alignment (`left`, `center`, `right`). All flags compose freely. Cells starting with `=` are evaluated as formulas (`=SUM`, `=AVG`, `=COUNT`, arithmetic with A1 cell references).
163
173
 
164
174
  ### Lists
165
175
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@entropicwarrior/sdoc",
3
3
  "displayName": "SDOC",
4
4
  "description": "A plain-text documentation format with explicit brace scoping — deterministic parsing, AI-agent efficiency, and 10-50x token savings vs Markdown.",
5
- "version": "0.2.6",
5
+ "version": "0.2.7",
6
6
  "publisher": "entropicwarrior",
7
7
  "license": "MIT",
8
8
  "repository": {
package/src/sdoc.js CHANGED
@@ -2781,8 +2781,31 @@ function firstParagraphPreview(nodes, maxLen) {
2781
2781
  return "";
2782
2782
  }
2783
2783
 
2784
+ /**
2785
+ * Recursively collect all tagged (has @id) scope nodes from the content tree.
2786
+ * Skips @meta and @about. Used for deep section discovery — lets MCP clients
2787
+ * find sections nested inside top-level scopes (e.g. @pass-terminology inside
2788
+ * @pedantic-review inside @writing).
2789
+ */
2790
+ function getAllTaggedScopes(nodes) {
2791
+ const result = [];
2792
+ function walk(nodeList) {
2793
+ for (const node of nodeList) {
2794
+ if (node.type === "scope") {
2795
+ if (node.id && node.id.toLowerCase() !== "meta" && node.id.toLowerCase() !== "about") {
2796
+ result.push(node);
2797
+ }
2798
+ if (node.children) walk(node.children);
2799
+ }
2800
+ }
2801
+ }
2802
+ const doc = getDocumentScope(nodes);
2803
+ walk(doc ? doc.children : nodes);
2804
+ return result;
2805
+ }
2806
+
2784
2807
  function listSections(nodes) {
2785
- return getContentScopes(nodes).map((node) => ({
2808
+ return getAllTaggedScopes(nodes).map((node) => ({
2786
2809
  id: node.id || null,
2787
2810
  derivedId: slugify(node.title),
2788
2811
  title: node.title,
@@ -2802,7 +2825,7 @@ function collectDataBlocks(children) {
2802
2825
  }
2803
2826
 
2804
2827
  function extractSection(nodes, sectionId) {
2805
- const scopes = getContentScopes(nodes);
2828
+ const scopes = getAllTaggedScopes(nodes);
2806
2829
 
2807
2830
  function buildResult(node) {
2808
2831
  const data = collectDataBlocks(node.children || []);