Files
sporel-lib-core.api-discovery/init.lua
Axel Meyer 0457d5c798 feat: add diff_surface (S2 Phase 1)
Set-difference between code-surface.public and README documented-list.
Returns { missing_docs, stale_docs } sorted, per spec section 4.2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 13:22:57 +02:00

65 lines
2.5 KiB
Lua

-- lib-core.api-discovery — Pure-Lua surface-discovery + README-parsing.
-- See: meta/docs/superpowers/specs/2026-05-16-api-doc-convention-design.md
local M = {}
-- Extracts public/private surface from Lua source code.
-- Convention: `M.<name> = function(...)` is public; `M._<name> = ...` is private.
-- Returns: { public = ["foo","bar",...], private = ["_baz",...] }
function M.parse_lua_surface(source_string)
local public = {}
local private = {}
for name in string.gmatch(source_string, "M%.([_%w]+)%s*=%s*function") do
if string.sub(name, 1, 1) == "_" then
table.insert(private, name)
else
table.insert(public, name)
end
end
return { public = public, private = private }
end
-- Extracts documented function-names from README's "## API" section.
-- Parses H3-Headers like "### `input.bind(action_name, keys)`" → "bind".
-- Convention: H3 header opens with backtick, function-name follows after optional namespace-dot.
-- Returns: { documented = ["bind","unbind",...] }
function M.parse_readme_api(markdown_string)
local documented = {}
-- Find "## API" section start (allow trailing whitespace/content)
local api_start = string.find(markdown_string, "\n## API[%s\n]")
if not api_start then
return { documented = documented }
end
-- Find next H2 (terminate API section)
local api_end = string.find(markdown_string, "\n## ", api_start + 5)
local section = string.sub(markdown_string, api_start, api_end or #markdown_string)
-- Match H3 headers: "### `[namespace.]name(...)`" — capture name portion
for line in string.gmatch(section, "###%s+`[^.`]*%.?([_%w]+)%s*[%(`]") do
table.insert(documented, line)
end
return { documented = documented }
end
-- Diffs code-surface vs README-doc.
-- Compares surface.public (set) vs readme.documented (set).
-- Returns: { missing_docs = [...], stale_docs = [...] }
function M.diff_surface(surface, readme)
local public_set = {}
for _, name in ipairs(surface.public) do public_set[name] = true end
local doc_set = {}
for _, name in ipairs(readme.documented) do doc_set[name] = true end
local missing = {}
for name in pairs(public_set) do
if not doc_set[name] then table.insert(missing, name) end
end
local stale = {}
for name in pairs(doc_set) do
if not public_set[name] then table.insert(stale, name) end
end
table.sort(missing); table.sort(stale)
return { missing_docs = missing, stale_docs = stale }
end
return M