nova-spec 1.0.4 → 1.0.5

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
@@ -1,6 +1,6 @@
1
1
  # nova-spec — framework AGENTS.md
2
2
 
3
- > This repo uses CLAUDE.md as a stub → AGENTS.md is the instructions file.
3
+ > This repo uses `CLAUDE.md` as a stub → `AGENTS.md` is the instructions file.
4
4
 
5
5
  This repo is the **nova-spec framework itself** — a Spec-Driven Development (SDD) system for Claude Code. It's designed to be installed into other projects.
6
6
 
@@ -8,6 +8,22 @@ This repo is the **nova-spec framework itself** — a Spec-Driven Development (S
8
8
 
9
9
  Framework that orchestrates ticket-to-PR workflow with 7 slash commands: `/nova-start` → `/nova-spec` → `/nova-plan` → `/nova-build` → `/nova-review` → `/nova-wrap` → `/nova-status`.
10
10
 
11
+ ## Installation (consumer repos)
12
+
13
+ To install nova-spec into another repo, run:
14
+
15
+ ```bash
16
+ npx nova-spec init
17
+ ```
18
+
19
+ That leaves the repo usable without manual steps:
20
+
21
+ - `AGENTS.md` and `CLAUDE.md` (shim that points to `AGENTS.md`)
22
+ - `novaspec/` framework files
23
+ - `.claude/` symlinks (and optionally `.opencode/`)
24
+ - `context/` memory structure
25
+ - `novaspec/config.yml` is **gitignored** (local per-repo configuration)
26
+
11
27
  ## Key commands
12
28
 
13
29
  - `/nova-start <TICKET>` — classify ticket, create branch, load context
@@ -52,8 +68,13 @@ At the start of every session, check if nova-spec has pending updates by running
52
68
 
53
69
  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
70
 
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.
71
+ This repo (and consumer repos) use `npx nova-spec sync` to update stock files **idempotently** without clobbering local edits.
72
+ Sync records what was last shipped to the repo in `novaspec/.nova-manifest.json`, and only updates files that are still untouched locally.
73
+
74
+ Hooks are maintained in:
75
+
76
+ - `.claude/settings.local.json` (Claude Code)
77
+ - `.opencode/settings.local.json` (OpenCode)
57
78
 
58
79
  ## Symlinks
59
80
 
@@ -66,6 +87,14 @@ This repo uses itself. When modifying nova-spec:
66
87
  2. Verify symlinks work: `ls -la .claude/`
67
88
  3. Run through a full ticket cycle
68
89
 
90
+ ## Tests
91
+
92
+ Run the smoke test suite with:
93
+
94
+ ```bash
95
+ npm test
96
+ ```
97
+
69
98
  ## Reference
70
99
 
71
100
  - Full docs: [README.md](./README.md)
package/CLAUDE.md ADDED
@@ -0,0 +1,3 @@
1
+ AGENTS.md
2
+
3
+ This repository uses `AGENTS.md` as the single source of truth for working instructions.
package/PHILOSOPHY.md CHANGED
@@ -94,14 +94,14 @@ We accept living without features. We do not accept living with bloat. The cost
94
94
 
95
95
  ## Forking and customization
96
96
 
97
- **Today (known limitation).** The installer does `rm -rf novaspec/` and re-copies from source. Local edits to `novaspec/commands/`, `skills/`, `templates/`, or `agents/` are **lost on re-run**. The installer only preserves `novaspec/config.yml` (via backup) and the consumer's `context/`.
97
+ **Today.** Updates are applied with `npx nova-spec sync`, which is idempotent and will not overwrite local edits to stock framework files. Sync records the last-shipped hashes in `novaspec/.nova-manifest.json` and:
98
98
 
99
- This contradicts principle 5. We acknowledge the gap. Two practical paths today:
99
+ - Updates files that are still untouched locally
100
+ - Skips files you edited and reports them so you can merge intentionally
100
101
 
101
- - **Team fork**: clone the nova-spec repo into your team's space, customize, install from your fork. Your fork is your `SCRIPT_DIR`.
102
- - **Manual updates**: don't re-run the installer; pull updates by hand and merge edits.
102
+ This keeps the framework from being the reason you can't ship: your local edits remain yours.
103
103
 
104
- **Planned (not built).** A `team-overrides/` directory that survives re-installs. Until shipped, the fork is the answer. Do not pretend otherwise to consumers.
104
+ If you need deeper customization, commit it in your repo (or fork nova-spec). When upstream changes touch files you customized, sync will intentionally refuse to overwrite them you choose how to merge.
105
105
 
106
106
  ---
107
107
 
package/lib/forge.js CHANGED
@@ -38,7 +38,9 @@ function buildPrCommand({ forge, cli, title, body, base }) {
38
38
  return `gh pr create --base ${q(base)} --title ${q(title)} --body ${q(body)}`;
39
39
  }
40
40
  if (forge === 'gitlab' || resolvedCli === 'glab') {
41
- return `glab mr create --target-branch ${q(base)} --title ${q(title)} --description ${q(body)} --fill`;
41
+ // NOTE: Avoid `--fill` because it can trigger glab autofill/push behavior depending on user config.
42
+ // We always pass title/body explicitly, and we never implicitly push.
43
+ return `glab mr create --target-branch ${q(base)} --title ${q(title)} --description ${q(body)} --yes`;
42
44
  }
43
45
  throw new Error(`Unknown forge: ${forge}`);
44
46
  }
package/lib/installer.js CHANGED
@@ -7,6 +7,7 @@ const {
7
7
  generateManifest,
8
8
  buildHookCommand,
9
9
  HOOK_MARKER,
10
+ MANIFEST_FILE,
10
11
  } = require('./sync.js');
11
12
  const { detectForge } = require('./forge.js');
12
13
  const { listTransitionsAsync } = require('./jira.js');
@@ -15,7 +16,6 @@ const PACKAGE_ROOT = path.join(__dirname, '..');
15
16
  const NOVASPEC_SRC = path.join(PACKAGE_ROOT, 'novaspec');
16
17
  const AGENTS_SRC = path.join(PACKAGE_ROOT, 'AGENTS.md');
17
18
  const CLAUDE_MD_SRC = path.join(PACKAGE_ROOT, 'CLAUDE.md');
18
- const MANIFEST_FILE = '.nova-manifest.json';
19
19
 
20
20
  async function init() {
21
21
  console.log('\n nova-spec installer\n ───────────────────\n');
package/lib/jira.js CHANGED
@@ -79,58 +79,6 @@ async function transitionAsync({ url, email, token, ticket, transitionId }) {
79
79
  });
80
80
  }
81
81
 
82
- function runCli() {
83
- // CLI mode: node lib/jira.js <command> [args...]
84
- const [, , cmd, ...args] = process.argv;
85
- const { JIRA_URL, JIRA_EMAIL, JIRA_API_TOKEN } = process.env;
86
-
87
- if (!JIRA_URL || !JIRA_EMAIL || !JIRA_API_TOKEN) {
88
- console.error('Missing env vars: JIRA_URL, JIRA_EMAIL, JIRA_API_TOKEN');
89
- process.exit(2);
90
- }
91
-
92
- const baseArgs = { url: JIRA_URL, email: JIRA_EMAIL, token: JIRA_API_TOKEN };
93
- let promise;
94
-
95
- switch (cmd) {
96
- case 'get':
97
- if (!args[0]) {
98
- console.error('Usage: nova-jira get <TICKET>');
99
- process.exit(2);
100
- }
101
- promise = getIssueAsync({ ...baseArgs, ticket: args[0] });
102
- break;
103
- case 'transitions':
104
- if (!args[0]) {
105
- console.error('Usage: nova-jira transitions <TICKET>');
106
- process.exit(2);
107
- }
108
- promise = listTransitionsAsync({ ...baseArgs, ticket: args[0] });
109
- break;
110
- case 'transition':
111
- if (!args[0] || !args[1]) {
112
- console.error('Usage: nova-jira transition <TICKET> <TRANSITION_ID>');
113
- process.exit(2);
114
- }
115
- promise = transitionAsync({ ...baseArgs, ticket: args[0], transitionId: args[1] });
116
- break;
117
- default:
118
- console.error('Commands: get <TICKET> | transitions <TICKET> | transition <TICKET> <ID>');
119
- process.exit(2);
120
- }
121
-
122
- promise
123
- .then((r) => {
124
- if (r != null) console.log(JSON.stringify(r, null, 2));
125
- })
126
- .catch((err) => {
127
- console.error(err.message);
128
- process.exit(err.status === 401 ? 401 : err.status === 404 ? 404 : 1);
129
- });
130
- }
131
-
132
- if (require.main === module) runCli();
133
-
134
82
  module.exports = {
135
83
  getIssueAsync,
136
84
  listTransitionsAsync,
package/lib/sync.js CHANGED
@@ -245,4 +245,5 @@ module.exports = {
245
245
  collectPackageFiles,
246
246
  HOOK_MARKER,
247
247
  FRAMEWORK_FILES,
248
+ MANIFEST_FILE,
248
249
  };
@@ -27,7 +27,7 @@ If `context/` doesn't exist, return:
27
27
  ## Loaded context
28
28
  **Services**: not documented (context/ missing)
29
29
  **Decisions**: none
30
- **Gaps**: context/ structure not initialized — run `npx nova-spec init`
30
+ **Gaps**: context/ structure not initialized — run npx nova-spec init
31
31
  **Questions**: none
32
32
  ```
33
33
  And stop.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nova-spec",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Spec-Driven Development framework for Claude Code and OpenCode",
5
5
  "bin": {
6
6
  "nova-spec": "bin/nova-spec.js"
@@ -15,6 +15,7 @@
15
15
  "novaspec/templates/",
16
16
  "novaspec/config.example.yml",
17
17
  "AGENTS.md",
18
+ "CLAUDE.md",
18
19
  "INSTALL.md",
19
20
  "PHILOSOPHY.md"
20
21
  ],