nova-spec 1.0.2 → 1.0.4

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.
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env bash
2
+ # Guardrail: deterministic pre-review checks.
3
+ #
4
+ # Usage: bash novaspec/guardrails/review-checks.sh <ticket-id> [base-branch]
5
+ #
6
+ # Runs (in order):
7
+ # 1. diff is non-empty (committed + working tree)
8
+ # 2. every "Files to touch" entry from tasks.md appears in the diff
9
+ # 3. lint clean (if `npm run lint` / `pnpm lint` / `yarn lint` exists)
10
+ # 4. tests pass (if `npm test` / `pnpm test` / `yarn test` exists)
11
+ #
12
+ # Exits 0 if every applicable check passes, 1 if any blocking check fails,
13
+ # 2 on usage error. Skipped checks (e.g. no lint script) are reported but
14
+ # don't fail.
15
+
16
+ set -uo pipefail
17
+
18
+ if [ -z "${1:-}" ]; then
19
+ echo "Usage: $0 <ticket-id> [base-branch]" >&2
20
+ exit 2
21
+ fi
22
+
23
+ ticket="$1"
24
+ base="${2:-main}"
25
+ fail=0
26
+
27
+ # 1. Diff non-empty
28
+ diff_total="$(git diff "$base"...HEAD 2>/dev/null; git diff HEAD 2>/dev/null)"
29
+ if [ -z "$diff_total" ]; then
30
+ echo "✗ Empty diff: no committed or staged changes against $base."
31
+ fail=1
32
+ else
33
+ echo "✓ Diff is non-empty."
34
+ fi
35
+
36
+ # 2. Files to touch
37
+ tasks="context/changes/active/${ticket}/tasks.md"
38
+ if [ -f "$tasks" ]; then
39
+ # Pull every path under a "Files to touch" section. We look for lines that
40
+ # look like list items containing a path-ish token, but only between that
41
+ # heading and the next heading.
42
+ declared="$(awk '
43
+ /^#+ +Files to touch/i, /^#+ +/ {
44
+ if (NR > 1 && /^#+ +/ && !/^#+ +Files to touch/i) next_section=1
45
+ if (next_section) next
46
+ if (match($0, /[`"]?([\.a-zA-Z0-9_\/-]+\.[a-zA-Z0-9]+)[`"]?/, m)) print m[1]
47
+ }
48
+ ' "$tasks" | sort -u)"
49
+
50
+ if [ -n "$declared" ]; then
51
+ missing=""
52
+ while IFS= read -r f; do
53
+ [ -z "$f" ] && continue
54
+ if ! git diff "$base"...HEAD --name-only | grep -qxF "$f" \
55
+ && ! git diff HEAD --name-only | grep -qxF "$f"; then
56
+ missing+=" - $f"$'\n'
57
+ fi
58
+ done <<< "$declared"
59
+
60
+ if [ -n "$missing" ]; then
61
+ echo "✗ Files declared in tasks.md but missing from diff:"
62
+ printf '%s' "$missing"
63
+ fail=1
64
+ else
65
+ echo "✓ All declared files present in diff."
66
+ fi
67
+ else
68
+ echo "ℹ︎ tasks.md has no 'Files to touch' section — skipping declared-files check."
69
+ fi
70
+ else
71
+ echo "ℹ︎ No tasks.md (quick-fix path) — skipping declared-files check."
72
+ fi
73
+
74
+ # 3 + 4. Lint and test
75
+ detect_pm() {
76
+ if [ -f pnpm-lock.yaml ]; then echo pnpm
77
+ elif [ -f yarn.lock ]; then echo yarn
78
+ elif [ -f package-lock.json ] || [ -f package.json ]; then echo npm
79
+ else echo ''
80
+ fi
81
+ }
82
+
83
+ has_script() {
84
+ local pm="$1" script="$2"
85
+ if [ ! -f package.json ]; then return 1; fi
86
+ node -e "process.exit(((require('./package.json').scripts||{})['$script'])?0:1)" 2>/dev/null
87
+ }
88
+
89
+ pm="$(detect_pm)"
90
+
91
+ if [ -n "$pm" ] && has_script "$pm" lint; then
92
+ echo "Running $pm run lint…"
93
+ if "$pm" run lint --silent >/dev/null 2>&1; then
94
+ echo "✓ Lint clean."
95
+ else
96
+ echo "✗ Lint failed. Run \`$pm run lint\` and fix before review."
97
+ fail=1
98
+ fi
99
+ else
100
+ echo "ℹ︎ No lint script — skipping."
101
+ fi
102
+
103
+ if [ -n "$pm" ] && has_script "$pm" test; then
104
+ echo "Running $pm test…"
105
+ if "$pm" test --silent >/dev/null 2>&1; then
106
+ echo "✓ Tests pass."
107
+ else
108
+ echo "✗ Tests failed. Run \`$pm test\` and fix before review."
109
+ fail=1
110
+ fi
111
+ else
112
+ echo "ℹ︎ No test script — skipping."
113
+ fi
114
+
115
+ [ "$fail" -eq 0 ]
@@ -0,0 +1,14 @@
1
+ ## Ticket: <TICKET-ID> — "<title>"
2
+
3
+ Classification : <quick-fix | feature | architecture> (<estimated effort>)
4
+ Affected services : <svc-1> ✓, <svc-2> ✓
5
+ Branch created : <type>/<TICKET>-<slug> (from <base>)
6
+
7
+ Loaded context:
8
+ Services : <names with ✓ if `context/services/<name>.md` exists, ✗ otherwise>
9
+ Decisions read: <list of decision filenames, or "none">
10
+ Gotchas read : <list, or "none">
11
+ Gaps : <list of missing context, or "none">
12
+ Questions : <open questions for the user, or "none">
13
+
14
+ Next step: <next command, e.g. `/nova-spec` or `/nova-build`>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nova-spec",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Spec-Driven Development framework for Claude Code and OpenCode",
5
5
  "bin": {
6
6
  "nova-spec": "bin/nova-spec.js"
@@ -8,11 +8,18 @@
8
8
  "files": [
9
9
  "bin/",
10
10
  "lib/",
11
- "novaspec/",
12
- "AGENTS.md"
11
+ "novaspec/agents/",
12
+ "novaspec/commands/",
13
+ "novaspec/guardrails/",
14
+ "novaspec/skills/",
15
+ "novaspec/templates/",
16
+ "novaspec/config.example.yml",
17
+ "AGENTS.md",
18
+ "INSTALL.md",
19
+ "PHILOSOPHY.md"
13
20
  ],
14
21
  "scripts": {
15
- "test": "echo \"No automated tests — use smoke test in CLAUDE.md\""
22
+ "test": "node test/smoke.test.js"
16
23
  },
17
24
  "dependencies": {
18
25
  "@inquirer/prompts": "^7.0.0"
@@ -1,30 +0,0 @@
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
- }
@@ -1,23 +0,0 @@
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"
File without changes
File without changes
File without changes