@entropicwarrior/sdoc 0.2.6 → 0.2.8

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 +24 -3
  2. package/package.json +3 -3
  3. package/src/sdoc.js +84 -4
package/README.md CHANGED
@@ -29,8 +29,19 @@ Markdown has no formal structure — section boundaries are ambiguous, extractio
29
29
 
30
30
  ## Quick Start
31
31
 
32
+ ### Install from the VS Code Marketplace
33
+
34
+ Search for **"SDOC - Docs for Human/Agent Teams"** by **Entropic Warrior MS Enfin** in the VS Code Extensions panel, or install from the command line:
35
+
36
+ ```bash
37
+ code --install-extension entropicwarrior-msenfin.vscode-sdoc
38
+ ```
39
+
40
+ This gives you automatic updates. If you previously installed from a `.vsix` file, uninstall that version first to avoid conflicts.
41
+
42
+ ### Build from source
43
+
32
44
  ```bash
33
- # Build and install the VS Code extension
34
45
  npm install
35
46
  npm run package
36
47
  code --install-extension dist/sdoc-*.vsix
@@ -74,7 +85,17 @@ Key resources for agents:
74
85
  | [`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
86
  | [`lexica/specification.sdoc`](https://raw.githubusercontent.com/entropicwarrior/sdoc/main/lexica/specification.sdoc) | Formal spec with EBNF grammar |
76
87
 
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.
88
+ All `.sdoc` files are designed for progressive disclosure. The JavaScript API provides three functions that let agents navigate without loading entire files:
89
+
90
+ ```javascript
91
+ const { extractAbout, listSections, extractSection } = require("@entropicwarrior/sdoc");
92
+
93
+ extractAbout(text); // ~50 tokens — what is this file about?
94
+ listSections(text); // ~50-100 tokens — what sections does it have?
95
+ extractSection(text, "error-handling"); // ~200-1000 tokens — give me just this section
96
+ ```
97
+
98
+ Total cost for a precise answer: ~750 tokens. The same lookup in Markdown requires loading the full file (5,000-50,000 tokens).
78
99
 
79
100
  ## Format at a Glance
80
101
 
@@ -159,7 +180,7 @@ Markdown-style images with optional width and alignment:
159
180
 
160
181
  ### Tables
161
182
 
162
- Pipe-delimited tables with optional flags for appearance (`borderless`, `headerless`), width (`auto`, `60%`, `400px`), and alignment (`left`, `center`, `right`). All flags compose freely.
183
+ 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
184
 
164
185
  ### Lists
165
186
 
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@entropicwarrior/sdoc",
3
- "displayName": "SDOC",
3
+ "displayName": "SDOC - Docs for Human/Agent Teams",
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",
6
- "publisher": "entropicwarrior",
5
+ "version": "0.2.8",
6
+ "publisher": "entropicwarrior-msenfin",
7
7
  "license": "MIT",
8
8
  "repository": {
9
9
  "type": "git",
package/src/sdoc.js CHANGED
@@ -2416,6 +2416,54 @@ const DEFAULT_STYLE = `
2416
2416
  pointer-events: none;
2417
2417
  }
2418
2418
 
2419
+ /* Collapsible scope toggles */
2420
+ .sdoc-heading:has(.sdoc-toggle) {
2421
+ position: relative;
2422
+ }
2423
+
2424
+ .sdoc-toggle {
2425
+ position: absolute;
2426
+ left: -1.4em;
2427
+ top: 0;
2428
+ bottom: 0;
2429
+ width: 1.2em;
2430
+ cursor: pointer;
2431
+ opacity: 0;
2432
+ transition: opacity 0.15s;
2433
+ }
2434
+
2435
+ .sdoc-toggle::before {
2436
+ content: '';
2437
+ display: block;
2438
+ width: 0.45em;
2439
+ height: 0.45em;
2440
+ border-right: 2px solid var(--sdoc-muted);
2441
+ border-bottom: 2px solid var(--sdoc-muted);
2442
+ transition: transform 0.15s;
2443
+ transform: rotate(45deg);
2444
+ position: absolute;
2445
+ top: 0.18em;
2446
+ left: 50%;
2447
+ margin-left: -0.3em;
2448
+ }
2449
+
2450
+ .sdoc-scope:hover > .sdoc-heading > .sdoc-toggle {
2451
+ opacity: 1;
2452
+ }
2453
+
2454
+ .sdoc-scope.sdoc-collapsed > .sdoc-heading > .sdoc-toggle {
2455
+ opacity: 0.6;
2456
+ }
2457
+
2458
+ .sdoc-scope.sdoc-collapsed > .sdoc-heading > .sdoc-toggle::before {
2459
+ transform: rotate(-45deg);
2460
+ margin-left: -0.15em;
2461
+ }
2462
+
2463
+ .sdoc-scope.sdoc-collapsed > .sdoc-scope-children {
2464
+ display: none;
2465
+ }
2466
+
2419
2467
  `;
2420
2468
 
2421
2469
  const PRINT_STYLE = `
@@ -2447,9 +2495,12 @@ const PRINT_STYLE = `
2447
2495
  html {
2448
2496
  font-size: 80%;
2449
2497
  }
2450
- .sdoc-copy-btn {
2498
+ .sdoc-copy-btn, .sdoc-toggle {
2451
2499
  display: none;
2452
2500
  }
2501
+ .sdoc-scope.sdoc-collapsed > .sdoc-scope-children {
2502
+ display: block;
2503
+ }
2453
2504
  body {
2454
2505
  height: auto;
2455
2506
  overflow: visible;
@@ -2476,6 +2527,10 @@ const PRINT_STYLE = `
2476
2527
  }
2477
2528
  `;
2478
2529
 
2530
+ const COLLAPSE_SCRIPT = `document.addEventListener("click",function(e){if(!e.target.classList.contains("sdoc-toggle"))return;e.stopPropagation();var s=e.target.closest(".sdoc-scope");if(s)s.classList.toggle("sdoc-collapsed")});`;
2531
+
2532
+ const COPY_SCRIPT = `document.addEventListener("click",function(e){if(!e.target.classList.contains("sdoc-copy-btn"))return;e.stopPropagation();e.preventDefault();var w=e.target.closest(".sdoc-code-wrap");if(!w)return;var c=w.querySelector("code");if(!c)return;var t=c.textContent;var b=e.target;if(navigator.clipboard){navigator.clipboard.writeText(t).then(function(){b.textContent="\\u2713";setTimeout(function(){b.textContent="\\u29C9"},1500)})}else{var a=document.createElement("textarea");a.value=t;a.style.position="fixed";a.style.opacity="0";document.body.appendChild(a);a.select();document.execCommand("copy");document.body.removeChild(a);b.textContent="\\u2713";setTimeout(function(){b.textContent="\\u29C9"},1500)}});`;
2533
+
2479
2534
  const MERMAID_CDN = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js";
2480
2535
  const KATEX_CDN_CSS = "https://cdn.jsdelivr.net/npm/katex@0.16/dist/katex.min.css";
2481
2536
  const HLJS_CDN = "https://cdn.jsdelivr.net/npm/@highlightjs/cdn-assets@11.11.1/highlight.min.js";
@@ -2574,7 +2629,8 @@ function renderHtmlDocumentFromParsed(parsed, title, options = {}) {
2574
2629
 
2575
2630
  const cssBase = options.cssOverride ?? DEFAULT_STYLE;
2576
2631
  const cssAppend = options.cssAppend ? `\n${options.cssAppend}\n${PRINT_STYLE}` : `\n${PRINT_STYLE}`;
2577
- const scriptTag = options.script ? `\n<script>${options.script}</script>` : "";
2632
+ const builtinScript = COLLAPSE_SCRIPT + COPY_SCRIPT;
2633
+ const scriptTag = options.script ? `\n<script>${options.script}</script>` : `\n<script>${builtinScript}</script>`;
2578
2634
  const mermaidTheme = options.mermaidTheme ?? "neutral";
2579
2635
  const mermaidInit = mermaidTheme === "auto"
2580
2636
  ? `var isDark=window.matchMedia("(prefers-color-scheme:dark)").matches;mermaid.initialize({startOnLoad:true,theme:isDark?"dark":"neutral",themeCSS:".node rect, .node polygon, .node circle { rx: 4; ry: 4; }"});`
@@ -2781,8 +2837,32 @@ function firstParagraphPreview(nodes, maxLen) {
2781
2837
  return "";
2782
2838
  }
2783
2839
 
2840
+ /**
2841
+ * Recursively collect all tagged (has @id) scope nodes from the content tree.
2842
+ * Skips @meta and @about. Used for deep section discovery — lets MCP clients
2843
+ * find sections nested inside top-level scopes (e.g. @pass-terminology inside
2844
+ * @pedantic-review inside @writing).
2845
+ */
2846
+ function getAllTaggedScopes(nodes) {
2847
+ const result = [];
2848
+ function walk(nodeList) {
2849
+ for (const node of nodeList) {
2850
+ if (node.type === "scope" && node.hasHeading) {
2851
+ const id = (node.id || "").toLowerCase();
2852
+ if (id !== "meta" && id !== "about") {
2853
+ result.push(node);
2854
+ }
2855
+ if (node.children) walk(node.children);
2856
+ }
2857
+ }
2858
+ }
2859
+ const doc = getDocumentScope(nodes);
2860
+ walk(doc ? doc.children : nodes);
2861
+ return result;
2862
+ }
2863
+
2784
2864
  function listSections(nodes) {
2785
- return getContentScopes(nodes).map((node) => ({
2865
+ return getAllTaggedScopes(nodes).map((node) => ({
2786
2866
  id: node.id || null,
2787
2867
  derivedId: slugify(node.title),
2788
2868
  title: node.title,
@@ -2802,7 +2882,7 @@ function collectDataBlocks(children) {
2802
2882
  }
2803
2883
 
2804
2884
  function extractSection(nodes, sectionId) {
2805
- const scopes = getContentScopes(nodes);
2885
+ const scopes = getAllTaggedScopes(nodes);
2806
2886
 
2807
2887
  function buildResult(node) {
2808
2888
  const data = collectDataBlocks(node.children || []);