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 +32 -3
- package/CLAUDE.md +3 -0
- package/PHILOSOPHY.md +5 -5
- package/lib/forge.js +3 -1
- package/lib/installer.js +1 -1
- package/lib/jira.js +0 -52
- package/lib/sync.js +1 -0
- package/novaspec/agents/context-loader.md +1 -1
- package/package.json +2 -1
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
|
-
|
|
56
|
-
|
|
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
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
|
|
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
|
-
|
|
99
|
+
- Updates files that are still untouched locally
|
|
100
|
+
- Skips files you edited and reports them so you can merge intentionally
|
|
100
101
|
|
|
101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
@@ -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
|
|
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.
|
|
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
|
],
|