nova-spec 1.0.0 → 1.0.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.
package/AGENTS.md CHANGED
@@ -42,6 +42,19 @@ context/
42
42
 
43
43
  Memory model: one fact = one file, filename = index, explicit supersede via `> Supersedes: <old>.md`. Wisdom about memory itself lives in `decisions/`, not in `AGENTS.md`.
44
44
 
45
+ ## Auto-sync on session start
46
+
47
+ At the start of every session, check if nova-spec has pending updates by running:
48
+
49
+ ```
50
+ /nova-sync
51
+ ```
52
+
53
+ If `novaspec/.nova-manifest.json` exists and the installed version matches the latest, skip it silently. If there are updates or outdated custom overrides, report them to the user before proceeding.
54
+
55
+ > **Claude Code** handles this automatically via a `SessionStart` hook in `.claude/settings.json`.
56
+ > **OpenCode** relies on this instruction — always run `/nova-sync` at session start.
57
+
45
58
  ## Symlinks
46
59
 
47
60
  Claude Code discovers commands via `.claude/` symlinks pointing to `novaspec/`.
package/README.md CHANGED
@@ -3,53 +3,49 @@
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- <strong>Spec-Driven Development on top of Claude Code.</strong><br>
6
+ <strong>Spec-Driven Development for Claude Code and OpenCode.</strong><br>
7
7
  From a ticket to a merged PR in explicit steps, with architectural memory that doesn't decay.
8
8
  </p>
9
9
 
10
10
  <p align="center">
11
11
  <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
12
12
  <img alt="Status: experimental" src="https://img.shields.io/badge/status-experimental-orange.svg">
13
- <img alt="Built for Claude Code" src="https://img.shields.io/badge/built%20for-Claude%20Code-purple.svg">
13
+ <a href="https://www.npmjs.com/package/nova-spec"><img alt="npm" src="https://img.shields.io/npm/v/nova-spec.svg"></a>
14
+ <img alt="Built for Claude Code" src="https://img.shields.io/badge/built%20for-Claude%20Code%20%7C%20OpenCode-purple.svg">
14
15
  </p>
15
16
 
16
17
  ---
17
18
 
18
19
  ## What it is
19
20
 
20
- `nova-spec` adds seven `/nova-*` commands to Claude Code that turn a ticket into a traceable change: classify, close requirements, plan, implement task by task, review, and wrap up with commit + PR + memory update. Memory (`context/decisions/`, `context/gotchas/`, `context/services/`) lives in atomic markdown files that humans edit and `grep` finds.
21
+ `nova-spec` adds seven `/nova-*` slash commands to Claude Code (and OpenCode) that turn a ticket into a traceable change: classify, close requirements, plan, implement task by task, review, and wrap up with commit + PR + memory update.
21
22
 
22
- It's not a template or a generator. It's a set of conventions + commands that Claude Code runs as slash commands inside your repo.
23
+ Architectural memory (`context/decisions/`, `context/gotchas/`, `context/services/`) lives in atomic markdown files that humans edit and `grep` finds.
24
+
25
+ It's not a template or a generator. It's a set of conventions + commands that your AI agent runs as slash commands inside your repo.
23
26
 
24
27
  ## Who is this for
25
28
 
26
- - Developers using **Claude Code** (or OpenCode) on real projects, not toy demos.
29
+ - Developers using **Claude Code** or **OpenCode** on real projects, not toy demos.
27
30
  - Teams that want their AI agent to follow a **disciplined ticket → PR flow** instead of one-shotting code.
28
31
  - Anyone tired of **re-explaining the same architectural context** every new chat.
29
32
 
30
33
  If you only use Claude Code for one-off scripts, this is overkill. If you ship to production with it, read on.
31
34
 
32
- ## Why it exists
33
-
34
- Without discipline, an agent writes code fast and loses the *why*. The next ticket forces you to re-explain the same context. `nova-spec` enforces human checkpoints, separates spec from executable tasks, and leaves a trail in `context/` so the next ticket starts informed.
35
-
36
35
  ## Quickstart
37
36
 
38
37
  ```bash
39
- # 1. Clone nova-spec on your machine (one time only)
40
- git clone https://github.com/adansuku/nova-spec.git ~/tools/nova-spec
38
+ npx nova-spec init
39
+ ```
40
+
41
+ That's it. The interactive wizard asks where to install (global or per-project), which runtime (Claude Code, OpenCode, or both), and optionally your Jira connection. It generates a ready-to-use `config.yml` — no manual editing required.
41
42
 
42
- # 2. From the repo where you want to use it
43
- cd /path/to/your-project
44
- bash ~/tools/nova-spec/install.sh
43
+ Then open your editor and run your first ticket:
45
44
 
46
- # 3. Open Claude Code and launch your first ticket
47
- claude
45
+ ```
48
46
  /nova-start PROJ-123
49
47
  ```
50
48
 
51
- Full details in [INSTALL.md](./INSTALL.md).
52
-
53
49
  ## A taste of it
54
50
 
55
51
  What `/nova-start PROJ-42` actually does:
@@ -72,7 +68,7 @@ Loaded context:
72
68
  Next step: /nova-spec
73
69
  ```
74
70
 
75
- No code yet. The agent has classified the work, created the branch, and pulled in only the architectural decisions that matter for this ticket. From here you'd move on to `/nova-spec` to close requirements, then `/nova-plan`, then `/nova-build`.
71
+ No code yet. The agent classified the work, created the branch, and pulled in only the architectural decisions that matter for this ticket.
76
72
 
77
73
  ## Flow
78
74
 
@@ -82,7 +78,6 @@ No code yet. The agent has classified the work, created the branch, and pulled i
82
78
 
83
79
  | Command | What it does |
84
80
  |---|---|
85
- | `/nova-init` | One-off bootstrap: scans the repo and generates draft `context/services/` files with TODOs |
86
81
  | `/nova-start <TICKET>` | Pulls the ticket, classifies it (quick-fix / feature / architecture), creates a branch, loads context |
87
82
  | `/nova-spec` | Closes open decisions and writes `proposal.md` |
88
83
  | `/nova-plan` | Translates the spec into `tasks.md` (plan + tasks) |
@@ -90,8 +85,32 @@ No code yet. The agent has classified the work, created the branch, and pulled i
90
85
  | `/nova-review` | Final code review against spec, conventions and decisions |
91
86
  | `/nova-wrap` | Updates memory, archives the spec, creates commit and PR |
92
87
  | `/nova-status [TICKET]` | Current status of the ticket (read-only) |
88
+ | `/nova-sync` | Updates nova-spec core to the latest version |
89
+ | `/nova-diff <name>` | Shows diff between your custom override and the new core version |
90
+
91
+ `quick-fix` tickets skip `/nova-spec` and `/nova-plan`.
92
+
93
+ ## Customizing skills and commands
94
+
95
+ Place any file in `novaspec/custom/` to override the core version — same name, your rules:
93
96
 
94
- `quick-fix` tickets skip `/nova-spec` and `/nova-plan`. `/nova-init` is optional and runs only once when installing nova-spec into an existing repo.
97
+ ```
98
+ novaspec/
99
+ ├── skills/ ← core (managed by nova-spec)
100
+ └── custom/
101
+ └── skills/
102
+ └── nova-wrap/ ← your override, same name wins
103
+ ```
104
+
105
+ Run `/nova-sync` to update the core. Your `custom/` folder is never touched.
106
+
107
+ ## Keeping up to date
108
+
109
+ ```bash
110
+ npx nova-spec sync
111
+ ```
112
+
113
+ Updates the core, preserves your custom overrides and `config.yml`, and tells you if any of your overrides have upstream changes worth reviewing.
95
114
 
96
115
  ## Principles
97
116
 
@@ -102,15 +121,10 @@ No code yet. The agent has classified the work, created the branch, and pulled i
102
121
 
103
122
  ## Documentation
104
123
 
105
- - Detailed install: [INSTALL.md](./INSTALL.md)
106
- - Internal architecture: [novaspec/README.arch.md](./novaspec/README.arch.md)
107
- - Quick reference: [novaspec/README.quickref.md](./novaspec/README.quickref.md)
124
+ - Install options: [INSTALL.md](./INSTALL.md)
125
+ - Design philosophy: [PHILOSOPHY.md](./PHILOSOPHY.md)
108
126
  - Contributing: [CONTRIBUTING.md](./CONTRIBUTING.md)
109
127
 
110
- ## See also
111
-
112
- `nova-spec` was built using itself. The full development history — including specs, decisions, gotchas and dogfooding — is preserved in the lab repo: [adansuku/nova-spec-lab](https://github.com/adansuku/nova-spec-lab).
113
-
114
128
  ## License
115
129
 
116
130
  MIT — see [LICENSE](./LICENSE).
package/lib/installer.js CHANGED
@@ -131,9 +131,10 @@ function installFiles(destDir, runtime, scope) {
131
131
  if (!fs.existsSync(notes)) fs.writeFileSync(notes, '');
132
132
  }
133
133
 
134
- // Runtime symlinks / .opencode settings
134
+ // Runtime symlinks / settings
135
135
  if (runtime === 'claude' || runtime === 'both') {
136
136
  createSymlinks(destDir, '.claude');
137
+ writeClaudeSettings(path.join(destDir, '.claude'));
137
138
  }
138
139
  if (runtime === 'opencode' || runtime === 'both') {
139
140
  createSymlinks(destDir, '.opencode');
@@ -150,13 +151,40 @@ function createSymlinks(destDir, dotDir) {
150
151
  for (const name of ['commands', 'skills', 'agents']) {
151
152
  const link = path.join(dir, name);
152
153
  const target = path.join('..', 'novaspec', name);
153
- if (fs.existsSync(link) || fs.lstatSync(link).isSymbolicLink?.()) {
154
- fs.rmSync(link, { recursive: true, force: true });
155
- }
154
+ fs.rmSync(link, { recursive: true, force: true });
156
155
  fs.symlinkSync(target, link);
157
156
  }
158
157
  }
159
158
 
159
+ function writeClaudeSettings(claudeDir) {
160
+ const settingsPath = path.join(claudeDir, 'settings.local.json');
161
+ const hook = {
162
+ hooks: {
163
+ SessionStart: [
164
+ {
165
+ hooks: [
166
+ {
167
+ type: 'command',
168
+ command: 'npx nova-spec@latest sync 2>/dev/null || true',
169
+ timeout: 30,
170
+ },
171
+ ],
172
+ },
173
+ ],
174
+ },
175
+ };
176
+
177
+ if (fs.existsSync(settingsPath)) {
178
+ const existing = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
179
+ if (existing.hooks?.SessionStart) return; // already configured
180
+ existing.hooks = existing.hooks || {};
181
+ existing.hooks.SessionStart = hook.hooks.SessionStart;
182
+ fs.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + '\n');
183
+ } else {
184
+ fs.writeFileSync(settingsPath, JSON.stringify(hook, null, 2) + '\n');
185
+ }
186
+ }
187
+
160
188
  function writeOpenCodeSettings(opencodeDir) {
161
189
  const settingsPath = path.join(opencodeDir, 'settings.local.json');
162
190
  if (!fs.existsSync(settingsPath)) {
@@ -216,6 +244,7 @@ function ensureGitignore(destDir) {
216
244
  'novaspec/custom/',
217
245
  '.env',
218
246
  'notes.md',
247
+ '.claude/settings.local.json',
219
248
  '.opencode/settings.local.json',
220
249
  '.opencode/node_modules/',
221
250
  '.DS_Store',
package/lib/sync.js CHANGED
@@ -87,6 +87,15 @@ async function sync(destDir = process.cwd()) {
87
87
  // Restore config
88
88
  if (configBackup) fs.writeFileSync(configPath, configBackup);
89
89
 
90
+ // Update AGENTS.md and CLAUDE.md (always overwrite — these are framework files)
91
+ for (const file of ['AGENTS.md', 'CLAUDE.md']) {
92
+ const src = path.join(PACKAGE_ROOT, file);
93
+ const dest = path.join(destDir, file);
94
+ if (fs.existsSync(src) && fs.existsSync(dest)) {
95
+ fs.copyFileSync(src, dest);
96
+ }
97
+ }
98
+
90
99
  // Regenerate manifest with new hashes
91
100
  const newManifest = generateManifest(novaspecDest);
92
101
 
@@ -114,6 +123,9 @@ async function sync(destDir = process.cwd()) {
114
123
  newManifest.outdated_customs = outdated;
115
124
  fs.writeFileSync(manifestPath, JSON.stringify(newManifest, null, 2) + '\n');
116
125
 
126
+ // Ensure SessionStart hook is present (idempotent)
127
+ ensureSessionStartHook(destDir);
128
+
117
129
  // Report
118
130
  console.log('\n ✓ nova-spec core updated to v' + newManifest.version + '\n');
119
131
 
@@ -144,4 +156,32 @@ function copyDirSync(src, dest) {
144
156
  }
145
157
  }
146
158
 
159
+ function ensureSessionStartHook(destDir) {
160
+ const hook = {
161
+ type: 'command',
162
+ command: 'npx nova-spec@latest sync 2>/dev/null || true',
163
+ timeout: 30,
164
+ };
165
+
166
+ const targets = [
167
+ path.join(destDir, '.claude', 'settings.local.json'),
168
+ path.join(destDir, '.opencode', 'settings.local.json'),
169
+ ].filter(p => fs.existsSync(path.dirname(p))); // only if runtime dir exists
170
+
171
+ for (const settingsPath of targets) {
172
+ let settings = {};
173
+ if (fs.existsSync(settingsPath)) {
174
+ try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch (_) {}
175
+ }
176
+
177
+ const existing = settings.hooks?.SessionStart?.[0]?.hooks?.[0];
178
+ if (existing?.command === hook.command) continue; // already up to date
179
+
180
+ // Overwrite if missing or using outdated command (e.g. without @latest)
181
+ settings.hooks = settings.hooks || {};
182
+ settings.hooks.SessionStart = [{ hooks: [hook] }];
183
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
184
+ }
185
+ }
186
+
147
187
  module.exports = { sync, generateManifest };
@@ -0,0 +1,30 @@
1
+ {
2
+ "version": "1.0.1",
3
+ "generated_at": "2026-05-10T07:46:23.881Z",
4
+ "hashes": {
5
+ "commands/nova-build": "5d9c103168f97dd60e4efbe1f9a277a7",
6
+ "commands/nova-diff": "529389d8d7d187c70de708610101009c",
7
+ "commands/nova-plan": "44709e96a49e86d808f3cad94c0bdc86",
8
+ "commands/nova-review": "aa2e4e91fe5e774ae7f374c7f3bf8882",
9
+ "commands/nova-spec": "726194e235d566dfbb81e785a6437b46",
10
+ "commands/nova-start": "d031e1958cf769864f566e9d768b1552",
11
+ "commands/nova-status": "cf0b7db18d4290479c35ed3b87fecc56",
12
+ "commands/nova-sync": "4364efbb594eeddaff7deba8d7e2a947",
13
+ "commands/nova-wrap": "846dfee6faaba65d80c082bb90e9b306",
14
+ "skills/close-requirement": {
15
+ "SKILL.md": "43d5f32320a635b15601a21ef5a0bf93"
16
+ },
17
+ "skills/jira-integration": {
18
+ "SKILL.md": "513f8b116be4bed955a8e3631bbf250c"
19
+ },
20
+ "skills/update-service-context": {
21
+ "SKILL.md": "b0788661cc61e2c6acb4bead24f48017"
22
+ },
23
+ "skills/write-decision": {
24
+ "SKILL.md": "20ea0ac9f19ec17a72d562f811c4a61e"
25
+ },
26
+ "agents/context-loader": "42a4cf797452bd76c720bd8f36e261f4",
27
+ "agents/nova-review-agent": "5658926243d4ce01cfc3b79ee8e4ad93"
28
+ },
29
+ "outdated_customs": []
30
+ }
@@ -12,7 +12,7 @@ which custom overrides may need attention.
12
12
  Execute in the terminal:
13
13
 
14
14
  ```bash
15
- npx nova-spec sync
15
+ npx nova-spec@latest sync
16
16
  ```
17
17
 
18
18
  Show the output to the user as-is.
@@ -43,4 +43,4 @@ If everything is clean:
43
43
 
44
44
  - Don't modify any custom files.
45
45
  - Don't auto-merge or apply changes.
46
- - If `npx nova-spec sync` fails, show the error and stop.
46
+ - If `npx nova-spec@latest sync` fails, show the error and stop.
@@ -0,0 +1,23 @@
1
+ # nova-spec — project configuration
2
+ # This file is gitignored — do not push it to the repo.
3
+
4
+ branch:
5
+ pattern: "{type}/{ticket}-{slug}"
6
+ types:
7
+ bugfix: bugfix
8
+ hotfix: hotfix
9
+ feature: feature
10
+ documentation: docs
11
+ refactor: refactor
12
+ chore: chore
13
+ architecture: arch
14
+ ticket_case: upper
15
+ base: main
16
+
17
+ jira:
18
+ skill: "jira-integration"
19
+ url: https://your-workspace.atlassian.net
20
+ project: PROJ
21
+ email:
22
+ token: ${JIRA_API_TOKEN}
23
+ done_transition_id: "41"
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "nova-spec",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Spec-Driven Development framework for Claude Code and OpenCode",
5
5
  "bin": {
6
- "nova-spec": "./bin/nova-spec.js"
6
+ "nova-spec": "bin/nova-spec.js"
7
7
  },
8
8
  "files": [
9
9
  "bin/",
@@ -23,7 +23,7 @@
23
23
  "license": "MIT",
24
24
  "repository": {
25
25
  "type": "git",
26
- "url": "https://github.com/Adansuku/nova-spec"
26
+ "url": "git+https://github.com/Adansuku/nova-spec.git"
27
27
  },
28
28
  "keywords": [
29
29
  "claude-code",