-
Notifications
You must be signed in to change notification settings - Fork 0
feat(reqstool): add OpenSpec ↔ reqstool dogfooding layer #68
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
2 commits
Select commit
Hold shift + click to select a range
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,32 @@ | ||
| # 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-java-gradle-plugin | ||
|
|
||
| # 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: | ||
| # Matches the existing GRADLE_PLUGIN_NNN / SVC_GRADLE_PLUGIN_NNN ID convention. | ||
| plugin: | ||
| path: docs/reqstool | ||
| req_prefix: "GRADLE_PLUGIN_" | ||
| svc_prefix: "SVC_GRADLE_PLUGIN_" |
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,10 @@ | ||
| # yaml-language-server: $schema=https://raw.githubusercontent.com/reqstool/reqstool-client/main/src/reqstool/resources/schemas/v1/reqstool_config.schema.json | ||
|
|
||
| language: java | ||
| build: gradle | ||
| resources: | ||
| requirements: requirements.yml | ||
| software_verification_cases: software_verification_cases.yml | ||
| annotations: ../../build/reqstool/annotations.yml | ||
| test_results: | ||
| - ../../build/test-results/test/*.xml |
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,41 @@ | ||
| # yaml-language-server: $schema=https://raw.githubusercontent.com/reqstool/reqstool-client/main/src/reqstool/resources/schemas/v1/requirements.schema.json | ||
| --- | ||
| metadata: | ||
| urn: reqstool-java-gradle-plugin | ||
| variant: microservice | ||
| title: Reqstool Java Gradle Plugin | ||
| url: https://github.com/reqstool/reqstool-java-gradle-plugin | ||
|
|
||
| requirements: | ||
| - id: GRADLE_PLUGIN_001 | ||
| title: Annotation combination | ||
| significance: shall | ||
| description: >- | ||
| The plugin shall combine requirement implementation annotations and SVC test | ||
| annotations gathered from multiple source sets into a single annotations | ||
| dataset, merging test entries for SVC IDs that appear in more than one | ||
| source set. | ||
| categories: | ||
| - functional-suitability | ||
| revision: "0.1.0" | ||
|
|
||
| - id: GRADLE_PLUGIN_002 | ||
| title: Zip artifact assembly | ||
| significance: shall | ||
| description: >- | ||
| The plugin shall assemble a reqstool dataset ZIP artifact containing the | ||
| requirements dataset files, the combined annotations file, matching test | ||
| result files, and a generated reqstool_config.yml. | ||
| categories: | ||
| - functional-suitability | ||
| revision: "0.1.0" | ||
|
|
||
| - id: GRADLE_PLUGIN_003 | ||
| title: Skip flags | ||
| significance: shall | ||
| description: >- | ||
| The plugin shall support skip flags that bypass task execution entirely | ||
| or bypass only ZIP artifact assembly, while still combining annotations. | ||
| categories: | ||
| - functional-suitability | ||
| revision: "0.1.0" |
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 @@ | ||
| # yaml-language-server: $schema=https://raw.githubusercontent.com/reqstool/reqstool-client/main/src/reqstool/resources/schemas/v1/software_verification_cases.schema.json | ||
|
|
||
| cases: | ||
| - id: SVC_GRADLE_PLUGIN_001 | ||
| requirement_ids: ["GRADLE_PLUGIN_001"] | ||
| title: "Verify implementation and test annotations are combined and merged across source sets" | ||
| verification: automated-test | ||
| revision: "0.1.0" | ||
|
|
||
| - id: SVC_GRADLE_PLUGIN_002 | ||
| requirement_ids: ["GRADLE_PLUGIN_002"] | ||
| title: "Verify the zip artifact is assembled with the mandatory and optional reqstool resources" | ||
| verification: automated-test | ||
| revision: "0.1.0" | ||
|
|
||
| - id: SVC_GRADLE_PLUGIN_003 | ||
| requirement_ids: ["GRADLE_PLUGIN_003"] | ||
| title: "Verify skip and skipAssembleZipArtifact each bypass their respective step" | ||
| verification: automated-test | ||
| revision: "0.1.0" |
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,15 @@ | ||
| # Annotation Combination 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/`. | ||
|
|
||
| ## Requirements | ||
|
|
||
| ### Requirement: GRADLE_PLUGIN_001 | ||
| The system SHALL implement GRADLE_PLUGIN_001. | ||
|
|
||
| #### Scenario: SVC_GRADLE_PLUGIN_001 | ||
| The system SHALL pass SVC_GRADLE_PLUGIN_001. |
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,15 @@ | ||
| # Skip Flags 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/`. | ||
|
|
||
| ## Requirements | ||
|
|
||
| ### Requirement: GRADLE_PLUGIN_003 | ||
| The system SHALL implement GRADLE_PLUGIN_003. | ||
|
|
||
| #### Scenario: SVC_GRADLE_PLUGIN_003 | ||
| The system SHALL pass SVC_GRADLE_PLUGIN_003. |
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,15 @@ | ||
| # Zip Assembly 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/`. | ||
|
|
||
| ## Requirements | ||
|
|
||
| ### Requirement: GRADLE_PLUGIN_002 | ||
| The system SHALL implement GRADLE_PLUGIN_002. | ||
|
|
||
| #### Scenario: SVC_GRADLE_PLUGIN_002 | ||
| The system SHALL pass SVC_GRADLE_PLUGIN_002. |
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
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.