-
Notifications
You must be signed in to change notification settings - Fork 1
feat(openspec): bootstrap OpenSpec spec layer for the demo's status showcase #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
b5e86ec
feat(openspec): bootstrap OpenSpec spec layer for the demo's status s…
jimisola 64d67d7
ci(build): validate OpenSpec specs, clarify reqstool-ai prefix comment
jimisola 4e03ef4
ci(build): matrix reqstool CI across PyPI and reqstool-client@main
jimisola 6cbd004
fix(ci): pin Python 3.13 for reqstool install step
jimisola ff3e563
ci(build): use shared reqstool/openspec workflows from reqstool/.github
jimisola 19256d4
fix(ci): call install-reqstool explicitly, per reqstool/.github#48
jimisola 50723f9
ci(build): re-pin reqstool/.github to 5bdf4e5
jimisola 9f93a0c
Merge branch 'main' into feat/openspec-dogfooding
jimisola 5d111f4
fix(reqstool): use 0.1.0 as the first revision, per semver
jimisola 977a81e
fix(security): add explicit permissions block to build.yml
jimisola File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "mcpServers": { | ||
| "reqstool": { | ||
| "command": "reqstool", | ||
| "args": ["mcp"] | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # reqstool-ai configuration | ||
| # | ||
| # This file tells reqstool-ai skills where to find your reqstool files | ||
| # and how to generate IDs for new requirements and SVCs. | ||
| # | ||
| # Place this file at: .reqstool-ai.yaml (project root) | ||
|
|
||
| # Project URN — matches the urn in your reqstool YAML files | ||
| urn: reqstool-demo | ||
|
|
||
| # Revision string for new requirements and SVCs | ||
| revision: "0.1.0" | ||
|
|
||
| # System-level reqstool directory (contains the SSOT requirements and SVCs) | ||
| system: | ||
| path: docs/reqstool | ||
|
|
||
| # Subproject modules — each module imports a subset of requirements/SVCs via filters | ||
| # | ||
| # Required fields per module: | ||
| # path — path to the module's reqstool directory (contains filter files) | ||
| # req_prefix — prefix for requirement IDs belonging to this module (e.g., CORE_) | ||
| # svc_prefix — prefix for SVC IDs belonging to this module (e.g., SVC_CORE_) | ||
| # | ||
| # Add as many modules as your project has. The module name (key) is used in | ||
| # commands like `/reqstool:status core` and `/reqstool:add-req core`. | ||
| modules: | ||
| # Domain-specific prefixes: this demo's requirement IDs are managed manually, so | ||
| # req_prefix is empty (no auto-prefix for new requirements). SVC IDs already use a | ||
| # literal "SVC_" + number convention (SVC_010, SVC_020, ...), so svc_prefix is set. | ||
| # (REQ_PASS, REQ_MANUAL_FAIL, REQ_NOT_IMPLEMENTED, ...; SVC_010, SVC_020, ...) | ||
| demo: | ||
| path: docs/reqstool | ||
| req_prefix: "" | ||
| svc_prefix: "SVC_" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| // @reqstool-openspec-hooks: 0.1.1 | ||
| import { spawn, ChildProcess } from "child_process"; | ||
| import type { OnReadDocumentHookV1 } from "openspecui/hooks"; | ||
|
|
||
| // Minimal MCP client over stdio (JSON-RPC 2.0, newline-delimited). | ||
| // Uses only Node.js built-ins — no npm packages required. | ||
| class McpStdioClient { | ||
| private proc: ChildProcess; | ||
| private buf = ""; | ||
| private pending = new Map< | ||
| number, | ||
| { resolve: (v: unknown) => void; reject: (e: Error) => void } | ||
| >(); | ||
| private id = 1; | ||
| readonly ready: Promise<void>; | ||
|
|
||
| constructor(cwd: string) { | ||
| this.proc = spawn("reqstool", ["mcp"], { | ||
| cwd, | ||
| stdio: ["pipe", "pipe", "pipe"], | ||
| }); | ||
| this.proc.stdout!.on("data", (chunk: Buffer) => { | ||
| this.buf += chunk.toString(); | ||
| let nl: number; | ||
| while ((nl = this.buf.indexOf("\n")) !== -1) { | ||
| const line = this.buf.slice(0, nl).trim(); | ||
| this.buf = this.buf.slice(nl + 1); | ||
| if (line) this.handle(line); | ||
| } | ||
| }); | ||
| this.ready = this.init(); | ||
| } | ||
|
|
||
| private handle(line: string) { | ||
| try { | ||
| const msg = JSON.parse(line) as { id?: number; result?: unknown; error?: { message: string } }; | ||
| if (msg.id !== undefined) { | ||
| const p = this.pending.get(msg.id); | ||
| if (p) { | ||
| this.pending.delete(msg.id); | ||
| msg.error ? p.reject(new Error(msg.error.message)) : p.resolve(msg.result); | ||
| } | ||
| } | ||
| } catch (e) { | ||
| console.warn("[reqstool-openspec] Skipping non-JSON line from reqstool mcp:", e instanceof Error ? e.message : e); | ||
| } | ||
| } | ||
|
|
||
| private send(method: string, params: unknown, expectReply = true): Promise<unknown> { | ||
| if (!expectReply) { | ||
| this.proc.stdin!.write(JSON.stringify({ jsonrpc: "2.0", method, params }) + "\n"); | ||
| return Promise.resolve(); | ||
| } | ||
| const id = this.id++; | ||
| return new Promise((resolve, reject) => { | ||
| this.pending.set(id, { resolve, reject }); | ||
| this.proc.stdin!.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n"); | ||
| }); | ||
| } | ||
|
|
||
| private async init(): Promise<void> { | ||
| await this.send("initialize", { | ||
| protocolVersion: "2024-11-05", | ||
| capabilities: { tools: {} }, | ||
| clientInfo: { name: "openspecui", version: "1.0" }, | ||
| }); | ||
| this.send("notifications/initialized", {}, false); | ||
| } | ||
|
|
||
| async enrich(content: string, preset: string): Promise<string> { | ||
| await this.ready; | ||
| const result = (await this.send("tools/call", { | ||
| name: "enrich_document", | ||
| arguments: { content, preset }, | ||
| })) as { content: { text: string }[] }; | ||
| return result.content[0].text; | ||
| } | ||
|
|
||
| close() { | ||
| this.proc.stdin?.end(); | ||
| this.proc.kill(); | ||
| } | ||
| } | ||
|
|
||
| let client: McpStdioClient | null = null; | ||
|
|
||
| export const onReadDocument: OnReadDocumentHookV1 = async (ctx, read) => { | ||
| if (!client) { | ||
| client = new McpStdioClient(ctx.projectDir); | ||
| ctx.lifecycle.onDispose(() => { | ||
| client?.close(); | ||
| client = null; | ||
| }); | ||
| } | ||
|
|
||
| const result = await read(); | ||
| const preset = `openspec:${ctx.document.kind}`; | ||
|
|
||
| try { | ||
| const enriched = await client.enrich(result.markdown, preset); | ||
| return { ...result, markdown: enriched, sourceLabel: `reqstool ${preset}` }; | ||
| } catch (e) { | ||
| return { | ||
| ...result, | ||
| diagnostics: [{ level: "warning", message: `reqstool enrich failed: ${e}` }], | ||
| }; | ||
| } | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Audit Logging Specification | ||
|
|
||
| ## Purpose | ||
|
|
||
| Requirement and SVC content is owned by reqstool (single source of truth). This spec references | ||
| reqstool requirement and SVC IDs only; titles and descriptions are injected at read time via | ||
| `reqstool enrich` (or the openspecui hook). See `docs/reqstool/`. This capability demonstrates a | ||
| requirement and SVC that exist but have no verifying test at all — the **missing-test** status | ||
| case. | ||
|
|
||
| ## Requirements | ||
|
|
||
| ### Requirement: REQ_MISSING_TEST | ||
| The system SHALL implement REQ_MISSING_TEST. | ||
|
|
||
| #### Scenario: SVC_060 | ||
| The system SHALL pass SVC_060. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # Billing Specification | ||
|
|
||
| ## Purpose | ||
|
|
||
| Requirement and SVC content is owned by reqstool (single source of truth). This spec references | ||
| reqstool requirement and SVC IDs only; titles and descriptions are injected at read time via | ||
| `reqstool enrich` (or the openspecui hook). See `docs/reqstool/`. This capability demonstrates a | ||
| requirement that passes its automated test but fails manual verification — the **manual-fail** | ||
| status case. | ||
|
|
||
| ## Requirements | ||
|
|
||
| ### Requirement: REQ_MANUAL_FAIL | ||
| The system SHALL implement REQ_MANUAL_FAIL. | ||
|
|
||
| #### Scenario: SVC_020 | ||
| The system SHALL pass SVC_020. | ||
|
|
||
| #### Scenario: SVC_022 | ||
| The system SHALL pass SVC_022. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # Greeting Specification | ||
|
|
||
| ## Purpose | ||
|
|
||
| Requirement and SVC content is owned by reqstool (single source of truth). This spec references | ||
| reqstool requirement and SVC IDs only; titles and descriptions are injected at read time via | ||
| `reqstool enrich` (or the openspecui hook). See `docs/reqstool/`. This capability demonstrates a | ||
| requirement that is fully implemented, automatically tested, and manually verified — the **passing** | ||
| status case. | ||
|
|
||
| ## Requirements | ||
|
|
||
| ### Requirement: REQ_PASS | ||
| The system SHALL implement REQ_PASS. | ||
|
|
||
| #### Scenario: SVC_010 | ||
| The system SHALL pass SVC_010. | ||
|
|
||
| #### Scenario: SVC_021 | ||
| The system SHALL pass SVC_021. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Notifications Specification | ||
|
|
||
| ## Purpose | ||
|
|
||
| Requirement and SVC content is owned by reqstool (single source of truth). This spec references | ||
| reqstool requirement and SVC IDs only; titles and descriptions are injected at read time via | ||
| `reqstool enrich` (or the openspecui hook). See `docs/reqstool/`. This capability demonstrates a | ||
| requirement whose implementation is intentionally unfinished, causing its automated test to be | ||
| skipped — the **skipped-test** status case. | ||
|
|
||
| ## Requirements | ||
|
|
||
| ### Requirement: REQ_SKIPPED_TEST | ||
| The system SHALL implement REQ_SKIPPED_TEST. | ||
|
|
||
| #### Scenario: SVC_050 | ||
| The system SHALL pass SVC_050. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # Reporting Specification | ||
|
|
||
| ## Purpose | ||
|
|
||
| Requirement and SVC content is owned by reqstool (single source of truth). This spec references | ||
| reqstool requirement and SVC IDs only; titles and descriptions are injected at read time via | ||
| `reqstool enrich` (or the openspecui hook). See `docs/reqstool/`. This capability demonstrates a | ||
| requirement with an SVC but no implementation — the **not-implemented** status case. | ||
|
|
||
| ## Requirements | ||
|
|
||
| ### Requirement: REQ_NOT_IMPLEMENTED | ||
| The system SHALL implement REQ_NOT_IMPLEMENTED. | ||
|
|
||
| #### Scenario: SVC_030 | ||
| The system SHALL pass SVC_030. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.