opensddrag 0.1.1 → 0.1.2

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.
@@ -1,28 +1,21 @@
1
1
  /**
2
2
  * OpenCode command templates
3
- * These commands are identical to Claude Code commands
4
- * Installed to .opencode/commands/opsr/ when OpenCode is selected
3
+ * Installed to .opencode/commands/opsr/ when OpenCode is selected.
4
+ * Uses a compact openCodeHeader() (project_slug only) instead of the Claude Code
5
+ * IMPORTANT/tool-list/STOP block — MCP tools are auto-available in OpenCode.
5
6
  */
6
7
 
7
- export function getOpenCodeCommands(slug, serverUrl) {
8
- const header = (name) => `> **IMPORTANT — ${name}**
9
- > This command requires the **\`opensddrag\`** MCP server (${serverUrl}), configured in \`opencode.json\`.
10
- > MCP tools provided by this server: \`create_artifact\`, \`read_artifact\`, \`list_artifacts\`, \`update_artifact\`, \`validate_artifact\`, \`link_artifacts\`, \`get_relationships\`, \`search_semantic\`, \`recall_episodes\`, \`get_working_context\`, \`update_working_context\`, \`record_trace\`
11
- > **If these tools are NOT in your active tool list**: STOP immediately. Do NOT investigate or try alternatives. Tell the user: "The opensddrag MCP server is not connected. Please start it (\`docker compose up -d\`) and reload the project."
12
- > All artifact reads/writes go through these MCP tools. DO NOT create local files. DO NOT write markdown to disk.
13
- > **project_slug for every call: \`${slug}\`**
8
+ export function getOpenCodeCommands(slug, _serverUrl) {
9
+ const fm = (description) => `---\ndescription: ${description}\n---\n\n`;
14
10
 
15
- ---
16
-
17
-
18
- `;
11
+ const openCodeHeader = () => `> **project_slug for every call:** \`${slug}\`\n\n---\n\n\n`;
19
12
 
20
13
  return [
21
14
  // ── /opsr:propose ──────────────────────────────────────────────────────────
22
15
  {
23
16
  folder: "opsr",
24
17
  name: "propose",
25
- content: `${header("/opsr:propose")}## Purpose
18
+ content: `${fm("Create a named change proposal")}${openCodeHeader()}## Purpose
26
19
  Create a named change with a proposal artifact. This is the entry point for every new feature or change.
27
20
  The proposal defines WHY, WHAT changes, WHICH capabilities are affected, and the IMPACT.
28
21
  After this command, /opsr:spec and /opsr:design become available.
@@ -144,7 +137,7 @@ Tell the user:
144
137
  {
145
138
  folder: "opsr",
146
139
  name: "spec",
147
- content: `${header("/opsr:spec")}## Purpose
140
+ content: `${fm("Write capability specs with requirements and scenarios")}${openCodeHeader()}## Purpose
148
141
  Create one or more spec artifacts for the capabilities listed in a proposal.
149
142
  Each capability in "New Capabilities" or "Modified Capabilities" gets its own spec artifact.
150
143
  Specs use SHALL/MUST language and must have Scenarios with WHEN/THEN format.
@@ -245,7 +238,7 @@ Fix any validation errors before continuing.
245
238
  {
246
239
  folder: "opsr",
247
240
  name: "design",
248
- content: `${header("/opsr:design")}## Purpose
241
+ content: `${fm("Document technical decisions and architecture")}${openCodeHeader()}## Purpose
249
242
  Create a design document that captures technical decisions, architecture, trade-offs, and open questions.
250
243
  The design must be based on the proposal and spec artifacts already in the database.
251
244
 
@@ -318,7 +311,7 @@ Tell the user: "Design saved. Run \`/opsr:tasks <change-name>\` to decompose int
318
311
  {
319
312
  folder: "opsr",
320
313
  name: "tasks",
321
- content: `${header("/opsr:tasks")}## Purpose
314
+ content: `${fm("Break a design into atomic implementation tasks")}${openCodeHeader()}## Purpose
322
315
  Decompose the specs and design into atomic, verifiable task artifacts.
323
316
  Each task must map to one or more spec requirements (REQ-NNN) and be completable in under 4 hours.
324
317
  Tasks depend on BOTH specs AND design being in the database.
@@ -361,7 +354,7 @@ Tell the user: "Tasks saved. Run \`/opsr:apply <change-name>\` to start implemen
361
354
  {
362
355
  folder: "opsr",
363
356
  name: "apply",
364
- content: `${header("/opsr:apply")}## Purpose
357
+ content: `${fm("Implement the next pending task")}${openCodeHeader()}## Purpose
365
358
  Implement tasks one by one, validating each against the spec acceptance criteria before marking done.
366
359
  Read ALL planning artifacts (proposal, specs, design) as context before implementing any task.
367
360
 
@@ -419,7 +412,7 @@ For each acceptance criterion (REQ-NNN) in the task:
419
412
  {
420
413
  folder: "opsr",
421
414
  name: "verify",
422
- content: `${header("/opsr:verify")}## Purpose
415
+ content: `${fm("Validate implementation against spec requirements")}${openCodeHeader()}## Purpose
423
416
  Validate the implementation against the spec requirements and design decisions.
424
417
  Produces a structured report with CRITICAL, WARNING, and SUGGESTION severity levels.
425
418
  Does NOT modify any artifacts — read-only operation.
@@ -489,7 +482,7 @@ Output a structured report:
489
482
  {
490
483
  folder: "opsr",
491
484
  name: "sync",
492
- content: `${header("/opsr:sync")}## Purpose
485
+ content: `${fm("Merge delta specs into main specs")}${openCodeHeader()}## Purpose
493
486
  Merge delta specs (ADDED/MODIFIED/REMOVED/RENAMED sections) into the main specs stored in the database.
494
487
  Delta specs are created by /opsr:spec for MODIFIED capabilities.
495
488
  After sync, the main spec reflects all changes. This is called automatically during /opsr:archive.
@@ -548,7 +541,7 @@ Tell the user which capabilities were updated.
548
541
  {
549
542
  folder: "opsr",
550
543
  name: "archive",
551
- content: `${header("/opsr:archive")}## Purpose
544
+ content: `${fm("Finalize and archive a completed change")}${openCodeHeader()}## Purpose
552
545
  Finalize a completed change by archiving all its artifacts.
553
546
  Runs verification checks, syncs delta specs to main specs, then archives everything.
554
547
 
@@ -596,7 +589,7 @@ Show summary:
596
589
  {
597
590
  folder: "opsr",
598
591
  name: "explore",
599
- content: `${header("/opsr:explore")}## Purpose
592
+ content: `${fm("Investigate a problem without implementing anything")}${openCodeHeader()}## Purpose
600
593
  Think through a problem, idea, or question WITHOUT implementing anything.
601
594
  Explore creates understanding before committing to a proposal.
602
595
  You may create OpenSddRag artifacts to capture insights, but NEVER write application code.
@@ -644,7 +637,7 @@ When the user has enough insight to decide:
644
637
  {
645
638
  folder: "opsr",
646
639
  name: "continue",
647
- content: `${header("/opsr:continue")}## Purpose
640
+ content: `${fm("Create the next artifact in the SDD dependency chain")}${openCodeHeader()}## Purpose
648
641
  Create the NEXT single artifact in the dependency chain for a change.
649
642
  Unlike /opsr:flow which creates all artifacts, this creates ONE artifact and stops.
650
643
  Dependency order: proposal → specs → design → tasks.
@@ -685,7 +678,7 @@ After creating the artifact:
685
678
  {
686
679
  folder: "opsr",
687
680
  name: "status",
688
- content: `${header("/opsr:status")}## Purpose
681
+ content: `${fm("Show state of all in-progress changes")}${openCodeHeader()}## Purpose
689
682
  Show the current state of all in-progress changes for this project.
690
683
  Reads from the MCP server — no local files involved.
691
684
 
@@ -732,7 +725,7 @@ Then show:
732
725
  {
733
726
  folder: "opsr",
734
727
  name: "flow",
735
- content: `${header("/opsr:flow")}## Purpose
728
+ content: `${fm("Run the complete SDD flow end-to-end")}${openCodeHeader()}## Purpose
736
729
  Run the complete SDD flow end-to-end in a single session.
737
730
  ALL artifacts are saved to the database via MCP tools — no local files.
738
731
 
@@ -785,7 +778,7 @@ Follow /opsr:archive steps:
785
778
  {
786
779
  folder: "opsr",
787
780
  name: "search",
788
- content: `${header("/opsr:search")}## Purpose
781
+ content: `${fm("Semantic search over specs and past work")}${openCodeHeader()}## Purpose
789
782
  Search the SDD knowledge base using semantic similarity (pgvector).
790
783
  Use this BEFORE starting any new work to find existing specs, decisions, and past implementations.
791
784
 
@@ -807,6 +800,126 @@ Group by: this project / other projects / past actions.
807
800
 
808
801
  ## Step 5 — Offer to read the full artifact
809
802
  \`read_artifact(name="<artifact-name>", project_slug="${slug}")\`
803
+ `,
804
+ },
805
+
806
+ // ── /opsr:harness ───────────────────────────────────────────────────────────
807
+ {
808
+ folder: "opsr",
809
+ name: "harness",
810
+ content: `${fm("Manage persistent project rules (add, list, disable)")}${openCodeHeader()}## Purpose
811
+ Manage project harness rules: add new rules, list existing rules, and disable rules that are no longer needed.
812
+ Harness rules are persistent behavioral constraints injected into every agent session via \`get_working_context\` (for \`trigger="always"\` rules) and surfaced as phase-gate checklists via \`get_harness_checklist\`.
813
+
814
+ ## Input
815
+ $ARGUMENTS = one of:
816
+ - \`add\` — followed by rule fields or a natural-language description
817
+ - \`list\` — show all rules for this project
818
+ - \`disable <rule-name>\` — soft-delete a rule by name
819
+
820
+ If $ARGUMENTS is empty, show the current rules list and ask what the user wants to do.
821
+
822
+ ## Supported rule fields
823
+
824
+ | Field | Values |
825
+ |-------|--------|
826
+ | \`name\` | kebab-case slug, unique per project |
827
+ | \`trigger\` | \`always\` (every session) / \`on_apply\` / \`on_verify\` / \`on_archive\` / \`on_spec\` |
828
+ | \`category\` | \`architecture\` / \`naming\` / \`forbidden\` / \`doc-sync\` / \`verification\` |
829
+ | \`severity\` | \`error\` (MUST satisfy) / \`warning\` (SHOULD satisfy) / \`info\` (advisory) |
830
+ | \`instruction\` | free-text rule the agent must follow |
831
+ | \`metadata\` | optional JSON |
832
+ | \`enabled\` | \`true\` (default) / \`false\` (soft-delete) |
833
+
834
+ ## Step 1 — Parse the operation
835
+
836
+ If $ARGUMENTS starts with \`add\`:
837
+ → Go to **Step 2A — Add**
838
+
839
+ If $ARGUMENTS starts with \`list\`:
840
+ → Go to **Step 2B — List**
841
+
842
+ If $ARGUMENTS starts with \`disable <name>\`:
843
+ → Go to **Step 2C — Disable**
844
+
845
+ If $ARGUMENTS is empty:
846
+ → Call \`list_rules(project_slug="${slug}")\` and show the current state. Ask the user what they want to do.
847
+
848
+ ## Step 2A — Add a rule
849
+
850
+ If the user provided explicit fields after \`add\`, use them as-is.
851
+
852
+ Otherwise, ask the user for each field. **You can infer sensible defaults from natural language:**
853
+ - "always update CHANGELOG when applying" → trigger=\`on_apply\`, category=\`doc-sync\`, severity=\`warning\`
854
+ - "never do X" → trigger=\`always\`, category=\`forbidden\`, severity=\`error\`
855
+ - "use Y pattern" → trigger=\`always\`, category=\`architecture\`, severity=\`warning\`
856
+
857
+ **Show the inferred values and ask for confirmation** before calling \`add_rule\`.
858
+
859
+ Then call:
860
+ \`\`\`
861
+ add_rule(
862
+ name: "<kebab-case>",
863
+ trigger: "<always|on_apply|on_verify|on_archive|on_spec>",
864
+ category: "<architecture|naming|forbidden|doc-sync|verification>",
865
+ severity: "<error|warning|info>",
866
+ instruction: "<text>",
867
+ project_slug:"${slug}",
868
+ enabled: true
869
+ )
870
+ \`\`\`
871
+
872
+ Confirm to the user:
873
+ "Rule '<name>' added. It will [be injected into every working context | be checked during /opsr:<trigger> when the phase completes]."
874
+
875
+ Then record:
876
+ \`record_trace(action="add_rule", result_summary="Added harness rule: <name>", project_slug="${slug}")\`
877
+
878
+ ## Step 2B — List rules
879
+
880
+ Call:
881
+ \`list_rules(project_slug="${slug}", enabled_only=true)\`
882
+
883
+ Present the results **grouped by trigger**, in this order:
884
+
885
+ \`\`\`
886
+ ### Always (loaded at session start)
887
+ - [<category>:<severity>] <name> — <instruction (max 80 chars)>
888
+
889
+ ### On Apply (checked during /opsr:apply)
890
+ - ...
891
+
892
+ ### On Verify (checked during /opsr:verify)
893
+ - ...
894
+
895
+ ### On Archive (checked during /opsr:archive)
896
+ - ...
897
+
898
+ ### On Spec (checked during /opsr:spec)
899
+ - ...
900
+ \`\`\`
901
+
902
+ If the result list is empty, respond:
903
+ "No harness rules defined for this project. Run \`/opsr:harness add\` to create the first rule."
904
+
905
+ ## Step 2C — Disable a rule
906
+
907
+ Extract the rule name from $ARGUMENTS (the token after \`disable\`).
908
+
909
+ Call:
910
+ \`add_rule(name: "<name>", enabled: false, project_slug: "${slug}")\`
911
+
912
+ Confirm:
913
+ "Rule '<name>' disabled. It will no longer appear in checklists or session context. Re-add with the same name to re-enable."
914
+
915
+ Then record:
916
+ \`record_trace(action="disable_rule", result_summary="Disabled harness rule: <name>", project_slug="${slug}")\`
917
+
918
+ ## Notes
919
+
920
+ - The same \`add_rule\` MCP call is used for create, update, and soft-delete — it is idempotent on \`(project_id, name)\`.
921
+ - Disabled rules are preserved in the database and can be re-enabled by calling \`add_rule\` with the same \`name\` and \`enabled=true\`.
922
+ - Rules are project-scoped — they never leak across projects.
810
923
  `,
811
924
  },
812
925
  ];
@@ -1,3 +1,183 @@
1
+ export function getHarnessSkill({ slug, serverUrl }) {
2
+ // SKILL.md for the `opensddrag-harness` skill — installed into
3
+ // .claude/skills/opensddrag-harness/ and .agents/skills/opensddrag-harness/
4
+ // by `opensddrag init`. Drives the /opsr:harness slash command behaviorally
5
+ // for both Claude Code and OpenCode, using only the standard MCP tools
6
+ // (add_rule, list_rules, get_harness_checklist). No direct DB access, no
7
+ // local file writes for rule storage — rules are persisted via the MCP
8
+ // server's `project_rules` table.
9
+ return `---
10
+ name: opensddrag-harness
11
+ description: Manage persistent project rules — add, list, and disable behavioral constraints
12
+ ---
13
+
14
+ # OpenSddRag — Harness
15
+
16
+ This project is connected to the OpenSddRag Harness (${serverUrl}).
17
+ The Harness layer manages **project rules**: persistent, per-project behavioral
18
+ constraints that are automatically injected into every agent session.
19
+
20
+ ## Project slug: \`${slug}\`
21
+
22
+ Always pass \`project_slug: "${slug}"\` to scope rule operations to this project.
23
+
24
+ ## When to use
25
+
26
+ - Before starting any new feature, ask: "Are there harness rules I should be aware of?"
27
+ \`get_working_context(project_slug="${slug}")\` will return any \`trigger="always"\` rules
28
+ automatically — read them first.
29
+ - Use \`/opsr:harness\` (Claude Code) or invoke this skill (OpenCode) to:
30
+ - **add** a new rule (architecture, naming, forbidden, doc-sync, verification)
31
+ - **list** all rules for the project, grouped by trigger
32
+ - **disable** a rule (soft-delete — re-enable by re-adding with the same name)
33
+
34
+ ## Available operations
35
+
36
+ ### add — create or update a project rule
37
+
38
+ Call the \`add_rule\` MCP tool. Required arguments:
39
+
40
+ \`\`\`
41
+ add_rule(
42
+ name: "<kebab-case-name>",
43
+ trigger: "always" | "on_apply" | "on_verify" | "on_archive" | "on_spec",
44
+ category: "architecture" | "naming" | "forbidden" | "doc-sync" | "verification",
45
+ severity: "error" | "warning" | "info", // default: "warning"
46
+ instruction: "<free-text rule the agent must follow>",
47
+ project_slug:"${slug}",
48
+ enabled: true, // default: true (set false to soft-delete)
49
+ metadata: { ... } // optional, free-form JSON
50
+ )
51
+ \`\`\`
52
+
53
+ When the user provides a rule in natural language, infer the fields:
54
+ - "always update X when applying" → trigger=\`on_apply\`, category=\`doc-sync\`, severity=\`warning\`
55
+ - "never do Y" → trigger=\`always\`, category=\`forbidden\`, severity=\`error\`
56
+ - "use Z pattern in this project" → trigger=\`always\`, category=\`architecture\`, severity=\`warning\`
57
+
58
+ **Example 1 — architecture rule (always):**
59
+ \`\`\`
60
+ add_rule(
61
+ name: "repo-pattern-required",
62
+ trigger: "always",
63
+ category: "architecture",
64
+ severity: "error",
65
+ instruction: "All data access must go through a repository class. Do not call the ORM or DB driver directly from route handlers or service classes.",
66
+ project_slug:"${slug}"
67
+ )
68
+ \`\`\`
69
+
70
+ **Example 2 — doc-sync rule (on_apply):**
71
+ \`\`\`
72
+ add_rule(
73
+ name: "update-changelog-on-apply",
74
+ trigger: "on_apply",
75
+ category: "doc-sync",
76
+ severity: "warning",
77
+ instruction: "When applying a task that ships a user-visible change, add a one-line entry to CHANGELOG.md under the unreleased section before marking the task complete.",
78
+ project_slug:"${slug}"
79
+ )
80
+ \`\`\`
81
+
82
+ Confirm with the user after adding:
83
+ "Rule '<name>' added. It will be [injected at every session start | checked during /opsr:apply | ...]."
84
+
85
+ ### list — show all rules for this project
86
+
87
+ Call the \`list_rules\` MCP tool:
88
+
89
+ \`\`\`
90
+ list_rules(project_slug="${slug}", enabled_only=true)
91
+ \`\`\`
92
+
93
+ Group the results by \`trigger\` in the output:
94
+
95
+ \`\`\`
96
+ ### Always (loaded at session start)
97
+ - [architecture:error] repo-pattern-required — All data access must go through...
98
+
99
+ ### On Apply (checked during /opsr:apply)
100
+ - [doc-sync:warning] update-changelog-on-apply — When applying a task...
101
+
102
+ ### On Verify (checked during /opsr:verify)
103
+ - (none)
104
+
105
+ ### On Archive (checked during /opsr:archive)
106
+ - (none)
107
+
108
+ ### On Spec (checked during /opsr:spec)
109
+ - (none)
110
+ \`\`\`
111
+
112
+ If the result list is empty, respond:
113
+ "No harness rules defined for this project. Run \`/opsr:harness add\` to create the first rule."
114
+
115
+ ### disable — soft-delete a rule
116
+
117
+ Call the \`add_rule\` MCP tool with the same \`name\` and \`enabled=false\`:
118
+
119
+ \`\`\`
120
+ add_rule(name: "<rule-name>", enabled: false, project_slug: "${slug}")
121
+ \`\`\`
122
+
123
+ This is a soft-delete: the rule is preserved in the database with \`enabled=false\`
124
+ and will no longer appear in \`list_rules\` (default \`enabled_only=true\`) or in
125
+ \`get_working_context\` injection. To re-enable, call \`add_rule\` with the same
126
+ name and \`enabled=true\`.
127
+
128
+ Confirm with the user:
129
+ "Rule '<name>' disabled. It will no longer appear in checklists or session context."
130
+
131
+ ## Phase-gate checklist — get_harness_checklist
132
+
133
+ When the user runs \`/opsr:apply\`, \`/opsr:verify\`, \`/opsr:archive\`, or
134
+ \`/opsr:spec\`, the corresponding slash command will call:
135
+
136
+ \`\`\`
137
+ get_harness_checklist(trigger="on_apply"|"on_verify"|"on_archive"|"on_spec", project_slug="${slug}")
138
+ \`\`\`
139
+
140
+ …to fetch rules that must be satisfied at that phase gate. The response is an
141
+ array of {name, category, severity, instruction} objects ordered by severity
142
+ (error first) then name. Rules with \`severity="error"\` MUST be completed before
143
+ the phase is declared done; \`severity="warning"\` rules SHOULD be addressed.
144
+
145
+ You do not need to call this directly — the slash commands invoke it. But you
146
+ can use it from this skill if the user asks "what rules apply to apply?".
147
+
148
+ ## Memory of rule operations
149
+
150
+ Every add / disable operation should be recorded in episodic memory:
151
+
152
+ \`\`\`
153
+ record_trace(action="add_rule"|"disable_rule", result_summary="Added/disabled rule: <name>", project_slug="${slug}")
154
+ \`\`\`
155
+
156
+ ## MCP tools used
157
+
158
+ | Tool | Purpose |
159
+ |------|---------|
160
+ | \`add_rule\` | Upsert a rule (create, update, or soft-delete) |
161
+ | \`list_rules\` | List rules with optional filters |
162
+ | \`get_harness_checklist\` | Fetch phase-gate rules for apply/verify/archive/spec |
163
+ | \`get_working_context\` | Already returns trigger="always" rules automatically |
164
+ | \`record_trace\` | Log harness operations to episodic memory |
165
+
166
+ ## Important rules for this skill
167
+
168
+ - **Never write rule data to local files** — all rules live in the \`project_rules\` table.
169
+ - **Never translate or modify the user's rule instruction** — store it verbatim.
170
+ - **Always confirm with the user before calling \`add_rule\`** for new rules, especially when inferring fields from natural language.
171
+ - **Soft-delete is the only way to "remove" a rule** — re-adding with the same name restores it.
172
+ `;
173
+ }
174
+
175
+ export function getOpenCodeHarnessSkill({ slug, serverUrl }) {
176
+ const full = getHarnessSkill({ slug, serverUrl });
177
+ // OpenCode auto-discovers tools from opencode.json — the table is noise.
178
+ return full.replace(/\n## MCP tools used\n[\s\S]*?(?=\n## |\n$)/, '\n');
179
+ }
180
+
1
181
  export function renderSkillMd({ slug, serverUrl }) {
2
182
  return `# OpenSddRag — SDD + Harness
3
183