@rpamis/comet 0.2.7 → 0.2.9
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/README.md +98 -63
- package/assets/manifest.json +1 -0
- package/assets/skills/comet/scripts/comet-archive.sh +39 -1
- package/assets/skills/comet/scripts/comet-guard.sh +188 -1
- package/assets/skills/comet/scripts/comet-handoff.sh +261 -0
- package/assets/skills/comet/scripts/comet-state.sh +34 -3
- package/assets/skills/comet/scripts/comet-yaml-validate.sh +42 -1
- package/assets/skills-zh/comet/SKILL.md +13 -10
- package/assets/skills-zh/comet-design/SKILL.md +59 -9
- package/dist/cli/index.js +1 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +9 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +10 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +13 -9
- package/dist/commands/update.js.map +1 -1
- package/dist/core/detect.d.ts +1 -1
- package/dist/core/detect.d.ts.map +1 -1
- package/dist/core/detect.js +19 -10
- package/dist/core/detect.js.map +1 -1
- package/dist/core/platforms.d.ts +4 -0
- package/dist/core/platforms.d.ts.map +1 -1
- package/dist/core/platforms.js +16 -1
- package/dist/core/platforms.js.map +1 -1
- package/dist/core/skills.d.ts +3 -2
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +3 -2
- package/dist/core/skills.js.map +1 -1
- package/dist/core/superpowers.d.ts.map +1 -1
- package/dist/core/superpowers.js +2 -1
- package/dist/core/superpowers.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
</p>
|
|
9
9
|
|
|
10
10
|
<p align="center">
|
|
11
|
-
<a href="https://github.com/rpamis/comet/actions/workflows/ci.yml"><img alt="CI" src="https://
|
|
11
|
+
<a href="https://github.com/rpamis/comet/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/rpamis/comet/ci.yml?branch=master&style=flat-square&label=CI" /></a>
|
|
12
|
+
<a href="https://deepwiki.com/rpamis/comet"><img alt="DeepWiki" src="https://img.shields.io/badge/DeepWiki-rpamis%2Fcomet-blue?style=flat-square" /></a>
|
|
12
13
|
<a href="https://www.npmjs.com/package/@rpamis/comet"><img alt="npm version" src="https://img.shields.io/npm/v/@rpamis/comet?style=flat-square" /></a>
|
|
13
14
|
<a href="https://www.npmjs.com/package/@rpamis/comet"><img alt="npm download count" src="https://img.shields.io/npm/dm/@rpamis/comet?style=flat-square&label=Downloads/mo" /></a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/@rpamis/comet"><img alt="npm weekly download count" src="https://img.shields.io/npm/dw/@rpamis/comet?style=flat-square&label=Downloads/wk" /></a>
|
|
14
16
|
<a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" /></a>
|
|
15
17
|
</p>
|
|
16
18
|
|
|
@@ -42,6 +44,28 @@ Superpowers generates Spec documents after brainstorming, but these documents ty
|
|
|
42
44
|
|
|
43
45
|
The main entry `/comet` supports current Spec state detection, suitable for long tasks — after completing and closing CC midway, just `/comet continue` and Comet will automatically read the active Spec (lists multiple for selection), dynamically identify which phase is currently executing, and continue.
|
|
44
46
|
|
|
47
|
+
At the same time, Comet provides full Spec lifecycle management. During execution, it links OpenSpec change/spec artifacts with Superpowers design and planning documents, then automates handoff, state updates, validation, and archive sync so users do not have to repeatedly remind the Agent to keep documents synchronized and connected.
|
|
48
|
+
|
|
49
|
+
## What You'll Learn
|
|
50
|
+
|
|
51
|
+
Many excellent Skill projects exist in the current Skill market, but they generally have preference issues — users may only like some features. For example, when using both OpenSpec and Superpowers, one might only use OpenSpec's Spec management capabilities, but prefer Superpowers' TDD-driven approach for coding.
|
|
52
|
+
|
|
53
|
+
Long-term Skill users know these capabilities can be freely combined, but exactly how to do so still requires real practice. The Comet project can serve as a reference:
|
|
54
|
+
|
|
55
|
+
- **How to reliably trigger nested Skills** — Not letting the Agent rely on document descriptions to perform "look-alike Skill trigger" operations (like writing files based on Skill descriptions), but truly triggering Skills (key feature: Skill trigger prints on CC). Comet triggers many capabilities from OpenSpec and Superpowers. How is this Prompt written?
|
|
56
|
+
|
|
57
|
+
- **How to make combined Skills flow automatically across phases** — Not relying on manual intervention. Comet's 5-phase flow can automatically trigger Skills for the core process except for necessary user choices, while the state machine also protects state transition reliability.
|
|
58
|
+
|
|
59
|
+
- **How to turn the Spec lifecycle into a resumable workflow** — Comet links OpenSpec change/spec artifacts with Superpowers design and planning documents, then records phase, execution mode, verification results, and archive status in `.comet.yaml`, so the Agent can resume after interruption instead of rereading documents and guessing progress.
|
|
60
|
+
|
|
61
|
+
- **How to turn document synchronization from "user reminders" into automation** — Comet puts handoff, state updates, validation, and archive sync into scripted flows, reducing repeated prompts like "remember to update the design doc", "remember to sync the spec", and "remember to archive the change".
|
|
62
|
+
|
|
63
|
+
- **How to design guard conditions that Agents can execute** — Comet does not simply trust the Agent saying "done" at phase exits. Scripts such as `comet-guard.sh`, `comet-yaml-validate.sh`, and `comet-state.sh` check tasks, state fields, verification evidence, and archive conditions before allowing the workflow to advance.
|
|
64
|
+
|
|
65
|
+
- **How to distribute and install Skills across platforms** — Comet supports multiple AI coding platforms, project/global installation, Chinese/English Skill choices, and platform-specific directory differences such as Antigravity using different project-level and global paths. It can be a reference for CLI installers and Skill package structure.
|
|
66
|
+
|
|
67
|
+
- **How to turn shell scripts into Agent workflow infrastructure** — Comet's scripts need to work across macOS, Linux, and Windows Git Bash while handling hashes, YAML fields, state machines, and archive flows. It shows how to move fragile workflow control out of scattered Prompt text and into testable, reusable tools.
|
|
68
|
+
|
|
45
69
|
## Install
|
|
46
70
|
|
|
47
71
|
```bash
|
|
@@ -63,7 +87,7 @@ comet init
|
|
|
63
87
|
4. Install [OpenSpec](https://github.com/Fission-AI/OpenSpec) skills
|
|
64
88
|
5. Install [Superpowers](https://github.com/obra/superpowers) skills
|
|
65
89
|
6. Deploy Comet skills (in your chosen language) to selected platforms
|
|
66
|
-
7. Create `docs/superpowers/specs/` and `docs/superpowers/plans/` working directories
|
|
90
|
+
7. Create `docs/superpowers/specs/` and `docs/superpowers/plans/` working directories for project-scope installs
|
|
67
91
|
|
|
68
92
|
> [!TIP]
|
|
69
93
|
> update version
|
|
@@ -81,40 +105,50 @@ comet init
|
|
|
81
105
|
|
|
82
106
|
## Commands
|
|
83
107
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
| `comet init [path]` | Initialize Comet workflow |
|
|
87
|
-
| `comet status [path]` | Show active changes, task progress, and next workflow command |
|
|
88
|
-
| `comet doctor [path]` | Diagnose project/global Comet installation health |
|
|
89
|
-
| `comet update [path]` | Update the npm package and installed Comet skills |
|
|
90
|
-
| `comet --help` | Show help |
|
|
91
|
-
| `comet --version` | Show version |
|
|
108
|
+
<details>
|
|
109
|
+
<summary><code>comet init [path]</code> — Initialize Comet workflow</summary>
|
|
92
110
|
|
|
93
|
-
|
|
111
|
+
Initializes OpenSpec, Superpowers, and Comet skills for selected AI coding platforms.
|
|
94
112
|
|
|
95
113
|
| Option | Description |
|
|
96
114
|
|--------|-------------|
|
|
97
|
-
| `--yes` | Non-interactive mode, auto-select detected platforms |
|
|
115
|
+
| `--yes` | Non-interactive mode, auto-select detected platforms (or all if none detected) |
|
|
116
|
+
| `--scope <scope>` | Install scope: `project` or `global` |
|
|
98
117
|
| `--skip-existing` | Skip already installed components |
|
|
99
118
|
| `--overwrite` | Overwrite already installed components |
|
|
100
119
|
| `--json` | Output structured JSON |
|
|
101
120
|
|
|
102
121
|
When multiple existing components are found on the same platform, interactive init offers one bulk choice: overwrite all, skip all, or choose per component.
|
|
103
122
|
|
|
104
|
-
|
|
123
|
+
</details>
|
|
124
|
+
|
|
125
|
+
<details>
|
|
126
|
+
<summary><code>comet status [path]</code> — Show active changes and next workflow command</summary>
|
|
127
|
+
|
|
128
|
+
Displays active changes, task progress, and the recommended next Comet workflow command.
|
|
105
129
|
|
|
106
130
|
| Option | Description |
|
|
107
131
|
|--------|-------------|
|
|
108
132
|
| `--json` | Output active changes with `nextCommand` |
|
|
109
133
|
|
|
110
|
-
|
|
134
|
+
</details>
|
|
135
|
+
|
|
136
|
+
<details>
|
|
137
|
+
<summary><code>comet doctor [path]</code> — Diagnose Comet installation health</summary>
|
|
138
|
+
|
|
139
|
+
Checks project/global installation health, working directories, installed skills, scripts, and Comet state files.
|
|
111
140
|
|
|
112
141
|
| Option | Description |
|
|
113
142
|
|--------|-------------|
|
|
114
143
|
| `--json` | Output structured diagnostic results |
|
|
115
144
|
| `--scope <scope>` | Diagnose `auto`, `project`, or `global` scope (default: `auto`) |
|
|
116
145
|
|
|
117
|
-
|
|
146
|
+
</details>
|
|
147
|
+
|
|
148
|
+
<details>
|
|
149
|
+
<summary><code>comet update [path]</code> — Update Comet package and skills</summary>
|
|
150
|
+
|
|
151
|
+
Updates the npm package and refreshes installed Comet skills in detected project/global targets.
|
|
118
152
|
|
|
119
153
|
| Option | Description |
|
|
120
154
|
|--------|-------------|
|
|
@@ -122,10 +156,20 @@ When multiple existing components are found on the same platform, interactive in
|
|
|
122
156
|
| `--language <lang>` | Override detected skill language (`en`, `zh`) |
|
|
123
157
|
| `--scope <scope>` | Update only `global` or `project` scope |
|
|
124
158
|
|
|
159
|
+
</details>
|
|
160
|
+
|
|
161
|
+
| Command | Description |
|
|
162
|
+
|---------|-------------|
|
|
163
|
+
| `comet --help` | Show help |
|
|
164
|
+
| `comet --version` | Show version |
|
|
165
|
+
|
|
125
166
|
## Supported Platforms
|
|
126
167
|
|
|
127
168
|
`comet init` supports 28 AI coding platforms:
|
|
128
169
|
|
|
170
|
+
<details>
|
|
171
|
+
<summary>View full platform list</summary>
|
|
172
|
+
|
|
129
173
|
| Platform | Skills Dir | Platform | Skills Dir |
|
|
130
174
|
|----------|-----------|----------|-----------|
|
|
131
175
|
| Claude Code | `.claude/` | Cursor | `.cursor/` |
|
|
@@ -140,15 +184,20 @@ When multiple existing components are found on the same platform, interactive in
|
|
|
140
184
|
| CoStrict | `.cospec/` | Crush | `.crush/` |
|
|
141
185
|
| Factory Droid | `.factory/` | iFlow | `.iflow/` |
|
|
142
186
|
| Pi | `.pi/` | Qoder | `.qoder/` |
|
|
143
|
-
| Antigravity | `.
|
|
187
|
+
| Antigravity | `.agents/` | Bob Shell | `.bob/` |
|
|
144
188
|
| ForgeCode | `.forge/` | Trae | `.trae/` |
|
|
145
189
|
|
|
190
|
+
</details>
|
|
191
|
+
|
|
146
192
|
## Skills
|
|
147
193
|
|
|
148
194
|
After `comet init`, three groups of skills are installed to the selected platform's `skills/` directory:
|
|
149
195
|
|
|
150
196
|
### Comet Skills
|
|
151
197
|
|
|
198
|
+
<details>
|
|
199
|
+
<summary>View Comet skills</summary>
|
|
200
|
+
|
|
152
201
|
| Skill | Description |
|
|
153
202
|
|-------|-------------|
|
|
154
203
|
| `/comet` | Main entry — auto-detects phase and dispatches to sub-commands |
|
|
@@ -160,15 +209,23 @@ After `comet init`, three groups of skills are installed to the selected platfor
|
|
|
160
209
|
| `/comet-hotfix` | Preset: Quick bug fix (skips brainstorming) |
|
|
161
210
|
| `/comet-tweak` | Preset: Small change (skips brainstorming and full plan) |
|
|
162
211
|
|
|
212
|
+
</details>
|
|
213
|
+
|
|
163
214
|
### Guard & Automation Scripts
|
|
164
215
|
|
|
216
|
+
<details>
|
|
217
|
+
<summary>View script list</summary>
|
|
218
|
+
|
|
165
219
|
| Script | Purpose |
|
|
166
220
|
|--------|---------|
|
|
167
221
|
| `comet-guard.sh` | Phase transition guard — validates exit conditions, `--apply` auto-updates `.comet.yaml` |
|
|
222
|
+
| `comet-handoff.sh` | Design handoff — generates deterministic context packages from OpenSpec artifacts with SHA256 tracing |
|
|
168
223
|
| `comet-archive.sh` | One-command archive — validates state, syncs specs, moves to archive, updates status |
|
|
169
224
|
| `comet-yaml-validate.sh` | Schema validator — validates `.comet.yaml` structure and field values |
|
|
170
225
|
| `comet-state.sh` | Unified state management — init/set/get/check/scale, agents' exclusive YAML interface |
|
|
171
226
|
|
|
227
|
+
</details>
|
|
228
|
+
|
|
172
229
|
### OpenSpec Skills
|
|
173
230
|
|
|
174
231
|
Spec lifecycle management: propose, explore, sync, verify, archive, and more.
|
|
@@ -219,37 +276,50 @@ Comet uses a decoupled state architecture with separate YAML files:
|
|
|
219
276
|
| `.openspec.yaml` | OpenSpec | Spec lifecycle, change metadata |
|
|
220
277
|
| `.comet.yaml` | Comet | Workflow phase, execution mode, verification status |
|
|
221
278
|
|
|
279
|
+
All states and execution phases are updated via scripts, and each phase verifies that tasks are truly complete before advancing. Compared to storing complex state rules only in Skill text, this script-backed state machine gives Comet more reliable phase transitions, correct YAML, and easier breakpoint recovery; agents can read the current Spec situation through Comet's built-in commands.
|
|
280
|
+
|
|
281
|
+
<details>
|
|
282
|
+
<summary>View key .comet.yaml fields</summary>
|
|
283
|
+
|
|
222
284
|
**Key Fields in `.comet.yaml`:**
|
|
223
285
|
|
|
224
286
|
```yaml
|
|
225
287
|
workflow: full
|
|
226
288
|
phase: build
|
|
227
|
-
design_doc: docs/superpowers/specs/YYYY-MM-DD-topic-design.md
|
|
228
|
-
plan: docs/superpowers/plans/YYYY-MM-DD-feature.md
|
|
229
289
|
build_mode: subagent-driven-development
|
|
230
290
|
isolation: branch
|
|
231
|
-
verify_mode:
|
|
291
|
+
verify_mode: null
|
|
292
|
+
design_doc: docs/superpowers/specs/YYYY-MM-DD-topic-design.md
|
|
293
|
+
plan: docs/superpowers/plans/YYYY-MM-DD-feature.md
|
|
232
294
|
verify_result: pending
|
|
233
|
-
verification_report:
|
|
295
|
+
verification_report: null
|
|
234
296
|
branch_status: pending
|
|
235
297
|
verified_at: null
|
|
236
298
|
archived: false
|
|
299
|
+
direct_override: false
|
|
300
|
+
build_command: null
|
|
301
|
+
verify_command: null
|
|
302
|
+
handoff_context: openspec/changes/<name>/.comet/handoff/design-context.json
|
|
303
|
+
handoff_hash: <sha256>
|
|
237
304
|
```
|
|
238
305
|
|
|
239
|
-
In full workflow, `build_mode` and `
|
|
306
|
+
In full workflow, `build_mode`, `isolation`, and `verify_mode` may temporarily be `null`; `build_mode` and `isolation` must be resolved before `build → verify`. `verification_report` stays `null` until verification writes a report, and `verify-pass` requires that report to exist plus `branch_status: handled`. Fields after `archived` in the example are optional or script-derived: `direct_override` is only needed for full-workflow direct builds, project commands may be absent unless configured, and `handoff_context` / `handoff_hash` are recorded by `comet-handoff.sh` before leaving design. Projects can configure `build_command` / `verify_command` in the change or repo root, and guard will run those commands first and print failure output.
|
|
240
307
|
|
|
241
|
-
|
|
308
|
+
</details>
|
|
242
309
|
|
|
243
310
|
### Reliability Features
|
|
244
311
|
|
|
245
312
|
Comet ensures agent execution reliability through automated state transitions:
|
|
246
313
|
|
|
314
|
+
<details>
|
|
315
|
+
<summary>View reliability features</summary>
|
|
316
|
+
|
|
247
317
|
1. **Entry Verification** — Each phase validates preconditions before execution
|
|
248
318
|
- Checks file existence, state consistency, and phase transitions
|
|
249
319
|
- Outputs `[HARD STOP]` with actionable suggestions if validation fails
|
|
250
320
|
|
|
251
321
|
2. **Automated State Transitions** — `comet-guard.sh --apply` updates `.comet.yaml` automatically
|
|
252
|
-
- All phase transitions (
|
|
322
|
+
- All phase transitions (open → design/build → verify → archive) use `guard --apply`
|
|
253
323
|
- No manual state editing required — eliminates write-verification errors
|
|
254
324
|
- `comet-state.sh` is the agents' exclusive interface for state operations
|
|
255
325
|
- Guard and archive scripts use `comet-state.sh` internally for state management
|
|
@@ -257,7 +327,7 @@ Comet ensures agent execution reliability through automated state transitions:
|
|
|
257
327
|
3. **Schema Validation** — `comet-yaml-validate.sh` ensures data integrity
|
|
258
328
|
- Validates required and optional fields
|
|
259
329
|
- Validates enum values, including `direct_override`
|
|
260
|
-
- Validates
|
|
330
|
+
- Validates `design_doc`, `plan`, and `handoff_context` paths exist, plus `handoff_hash` format
|
|
261
331
|
- Detects unknown/typos fields
|
|
262
332
|
|
|
263
333
|
4. **Build Decision Enforcement** — Guard and state transitions both block skipped build choices
|
|
@@ -277,7 +347,7 @@ Comet ensures agent execution reliability through automated state transitions:
|
|
|
277
347
|
- Moves change to archive directory and updates `archived: true`
|
|
278
348
|
- Supports `--dry-run` for preview
|
|
279
349
|
|
|
280
|
-
|
|
350
|
+
</details>
|
|
281
351
|
|
|
282
352
|
## Project Structure
|
|
283
353
|
|
|
@@ -287,6 +357,7 @@ your-project/
|
|
|
287
357
|
│ ├── comet/SKILL.md
|
|
288
358
|
│ │ └── scripts/
|
|
289
359
|
│ │ ├── comet-guard.sh # Phase transition guard (--apply auto-updates state)
|
|
360
|
+
│ │ ├── comet-handoff.sh # Design handoff (OpenSpec → Superpowers context tracing)
|
|
290
361
|
│ │ ├── comet-archive.sh # One-command archive automation
|
|
291
362
|
│ │ ├── comet-yaml-validate.sh # Schema validator
|
|
292
363
|
│ │ └── comet-state.sh # Unified state management (init/set/get/check/scale)
|
|
@@ -308,51 +379,15 @@ your-project/
|
|
|
308
379
|
└── plans/ # Implementation plans
|
|
309
380
|
```
|
|
310
381
|
|
|
311
|
-
## What You'll Learn
|
|
312
|
-
|
|
313
|
-
Many excellent Skill projects exist in the current Skill market, but they generally have preference issues — users may only like some features. For example, when using both OpenSpec and Superpowers, one might only use OpenSpec's Spec management capabilities, but prefer Superpowers' TDD-driven approach for coding.
|
|
314
|
-
|
|
315
|
-
Long-term Skill users know these capabilities can be freely combined, but exactly how to do so still requires real practice. The Comet project can serve as a reference:
|
|
316
|
-
|
|
317
|
-
- **How to reliably trigger nested Skills** — Not letting the Agent rely on document descriptions to perform "look-alike Skill trigger" operations (like writing files based on Skill descriptions), but truly triggering Skills (key feature: Skill trigger prints on CC). Comet will trigger many capabilities from OpenSpec and Superpowers — how is this Prompt written?
|
|
318
|
-
|
|
319
|
-
- **How to make combined Skills multi-phase auto-flow** — Not relying on manual intervention. Comet's 5-phase flow automatically triggers Skills for core processes except necessary user selections, while the **state machine mechanism** also ensures state transition reliability.
|
|
320
|
-
|
|
321
382
|
## Development
|
|
322
383
|
|
|
323
384
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, commit conventions, PR process, and guidance for adding platforms or skills.
|
|
324
385
|
|
|
325
|
-
```bash
|
|
326
|
-
# Clone
|
|
327
|
-
git clone https://github.com/rpamis/comet
|
|
328
|
-
cd comet
|
|
329
|
-
|
|
330
|
-
# Install dependencies
|
|
331
|
-
pnpm install
|
|
332
|
-
|
|
333
|
-
# Dev mode (watch)
|
|
334
|
-
pnpm dev
|
|
335
|
-
|
|
336
|
-
# Build
|
|
337
|
-
pnpm build
|
|
338
|
-
|
|
339
|
-
# Test (unit + coverage)
|
|
340
|
-
pnpm test
|
|
341
|
-
pnpm test:coverage
|
|
342
|
-
pnpm test:shell # bats shell tests
|
|
343
|
-
|
|
344
|
-
# Lint & format
|
|
345
|
-
pnpm lint
|
|
346
|
-
pnpm format
|
|
347
|
-
```
|
|
348
|
-
|
|
349
386
|
See [CHANGELOG.md](CHANGELOG.md) for version history and updates.
|
|
350
387
|
|
|
351
|
-
##
|
|
388
|
+
## Roadmap
|
|
352
389
|
|
|
353
|
-
|
|
354
|
-
- `.npmignore` prevents source code and config files from entering the npm package
|
|
355
|
-
- `.gitignore` covers secrets, credentials, IDE configs, and more
|
|
390
|
+
Track our development progress and upcoming features on the [Comet Roadmap](https://github.com/orgs/rpamis/projects/1).
|
|
356
391
|
|
|
357
392
|
## License
|
|
358
393
|
|
package/assets/manifest.json
CHANGED
|
@@ -73,11 +73,49 @@ yaml_field() {
|
|
|
73
73
|
bash "$STATE_SH" get "$CHANGE" "$field" 2>/dev/null
|
|
74
74
|
else
|
|
75
75
|
if [ -f "$YAML" ]; then
|
|
76
|
-
|
|
76
|
+
local value
|
|
77
|
+
value=$(grep "^${field}:" "$YAML" 2>/dev/null | sed "s/^${field}: *//" || true)
|
|
78
|
+
value=$(strip_inline_comment "$value")
|
|
79
|
+
strip_wrapping_quotes "$value"
|
|
77
80
|
fi
|
|
78
81
|
fi
|
|
79
82
|
}
|
|
80
83
|
|
|
84
|
+
strip_inline_comment() {
|
|
85
|
+
local value="$1"
|
|
86
|
+
printf '%s\n' "$value" | awk -v squote="'" '
|
|
87
|
+
{
|
|
88
|
+
out = ""
|
|
89
|
+
quote = ""
|
|
90
|
+
for (i = 1; i <= length($0); i++) {
|
|
91
|
+
c = substr($0, i, 1)
|
|
92
|
+
if (quote == "") {
|
|
93
|
+
if (c == "\"" || c == squote) {
|
|
94
|
+
quote = c
|
|
95
|
+
} else if (c == "#" && (i == 1 || substr($0, i - 1, 1) ~ /[[:space:]]/)) {
|
|
96
|
+
sub(/[[:space:]]+$/, "", out)
|
|
97
|
+
print out
|
|
98
|
+
next
|
|
99
|
+
}
|
|
100
|
+
} else if (c == quote) {
|
|
101
|
+
quote = ""
|
|
102
|
+
}
|
|
103
|
+
out = out c
|
|
104
|
+
}
|
|
105
|
+
print out
|
|
106
|
+
}
|
|
107
|
+
'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
strip_wrapping_quotes() {
|
|
111
|
+
local value="$1"
|
|
112
|
+
case "$value" in
|
|
113
|
+
\"*\") printf '%s\n' "${value:1:${#value}-2}" ;;
|
|
114
|
+
\'*\') printf '%s\n' "${value:1:${#value}-2}" ;;
|
|
115
|
+
*) printf '%s\n' "$value" ;;
|
|
116
|
+
esac
|
|
117
|
+
}
|
|
118
|
+
|
|
81
119
|
if [ ! -f "$YAML" ]; then
|
|
82
120
|
red "FATAL: .comet.yaml not found in $CHANGE_DIR/"
|
|
83
121
|
exit 1
|
|
@@ -98,10 +98,37 @@ yaml_field_value() {
|
|
|
98
98
|
if [ -f "$yaml" ]; then
|
|
99
99
|
local value
|
|
100
100
|
value=$(grep "^${field}:" "$yaml" 2>/dev/null | sed "s/^${field}: *//" || true)
|
|
101
|
+
value=$(strip_inline_comment "$value")
|
|
101
102
|
strip_wrapping_quotes "$value"
|
|
102
103
|
fi
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
strip_inline_comment() {
|
|
107
|
+
local value="$1"
|
|
108
|
+
printf '%s\n' "$value" | awk -v squote="'" '
|
|
109
|
+
{
|
|
110
|
+
out = ""
|
|
111
|
+
quote = ""
|
|
112
|
+
for (i = 1; i <= length($0); i++) {
|
|
113
|
+
c = substr($0, i, 1)
|
|
114
|
+
if (quote == "") {
|
|
115
|
+
if (c == "\"" || c == squote) {
|
|
116
|
+
quote = c
|
|
117
|
+
} else if (c == "#" && (i == 1 || substr($0, i - 1, 1) ~ /[[:space:]]/)) {
|
|
118
|
+
sub(/[[:space:]]+$/, "", out)
|
|
119
|
+
print out
|
|
120
|
+
next
|
|
121
|
+
}
|
|
122
|
+
} else if (c == quote) {
|
|
123
|
+
quote = ""
|
|
124
|
+
}
|
|
125
|
+
out = out c
|
|
126
|
+
}
|
|
127
|
+
print out
|
|
128
|
+
}
|
|
129
|
+
'
|
|
130
|
+
}
|
|
131
|
+
|
|
105
132
|
strip_wrapping_quotes() {
|
|
106
133
|
local value="$1"
|
|
107
134
|
case "$value" in
|
|
@@ -130,6 +157,7 @@ project_config_value() {
|
|
|
130
157
|
for config in ".comet.yaml" "comet.yaml" ".comet.yml" "comet.yml"; do
|
|
131
158
|
if [ -f "$config" ]; then
|
|
132
159
|
value=$(grep "^${field}:" "$config" 2>/dev/null | sed "s/^${field}: *//" || true)
|
|
160
|
+
value=$(strip_inline_comment "$value")
|
|
133
161
|
value=$(strip_wrapping_quotes "$value")
|
|
134
162
|
if [ -n "$value" ] && [ "$value" != "null" ]; then
|
|
135
163
|
echo "$value"
|
|
@@ -156,6 +184,47 @@ run_command_string() {
|
|
|
156
184
|
bash -lc "$command"
|
|
157
185
|
}
|
|
158
186
|
|
|
187
|
+
hash_stream() {
|
|
188
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
189
|
+
sha256sum | awk '{print $1}'
|
|
190
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
191
|
+
shasum -a 256 | awk '{print $1}'
|
|
192
|
+
else
|
|
193
|
+
echo "sha256sum or shasum is required" >&2
|
|
194
|
+
return 1
|
|
195
|
+
fi
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
hash_file() {
|
|
199
|
+
local file="$1"
|
|
200
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
201
|
+
sha256sum "$file" | awk '{print $1}'
|
|
202
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
203
|
+
shasum -a 256 "$file" | awk '{print $1}'
|
|
204
|
+
else
|
|
205
|
+
echo "sha256sum or shasum is required" >&2
|
|
206
|
+
return 1
|
|
207
|
+
fi
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
handoff_source_files() {
|
|
211
|
+
printf '%s\n' "$CHANGE_DIR/proposal.md"
|
|
212
|
+
printf '%s\n' "$CHANGE_DIR/design.md"
|
|
213
|
+
printf '%s\n' "$CHANGE_DIR/tasks.md"
|
|
214
|
+
if [ -d "$CHANGE_DIR/specs" ]; then
|
|
215
|
+
find "$CHANGE_DIR/specs" -path '*/spec.md' -type f 2>/dev/null | sort
|
|
216
|
+
fi
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
compute_handoff_hash() {
|
|
220
|
+
handoff_source_files | while IFS= read -r file; do
|
|
221
|
+
if [ -f "$file" ]; then
|
|
222
|
+
printf 'path:%s\n' "$file"
|
|
223
|
+
printf 'sha256:%s\n' "$(hash_file "$file")"
|
|
224
|
+
fi
|
|
225
|
+
done | hash_stream
|
|
226
|
+
}
|
|
227
|
+
|
|
159
228
|
preflight() {
|
|
160
229
|
|
|
161
230
|
if [ ! -d "$CHANGE_DIR" ]; then
|
|
@@ -291,6 +360,118 @@ branch_status_handled() {
|
|
|
291
360
|
[ "$status" = "handled" ]
|
|
292
361
|
}
|
|
293
362
|
|
|
363
|
+
design_handoff_context_valid() {
|
|
364
|
+
local context recorded_hash actual_hash markdown
|
|
365
|
+
context=$(yaml_field_value "handoff_context" 2>/dev/null || true)
|
|
366
|
+
recorded_hash=$(yaml_field_value "handoff_hash" 2>/dev/null || true)
|
|
367
|
+
|
|
368
|
+
if [ -z "$context" ] || [ "$context" = "null" ]; then
|
|
369
|
+
echo "handoff_context is missing from .comet.yaml" >&2
|
|
370
|
+
echo "Next: run bash \"\$COMET_HANDOFF\" $CHANGE design --write before invoking Superpowers." >&2
|
|
371
|
+
return 1
|
|
372
|
+
fi
|
|
373
|
+
if [ ! -s "$context" ]; then
|
|
374
|
+
echo "handoff_context does not point to a non-empty file: $context" >&2
|
|
375
|
+
echo "Next: regenerate the design handoff with comet-handoff.sh." >&2
|
|
376
|
+
return 1
|
|
377
|
+
fi
|
|
378
|
+
if [[ ! "$recorded_hash" =~ ^[a-f0-9]{64}$ ]]; then
|
|
379
|
+
echo "handoff_hash is missing or invalid: ${recorded_hash:-null}" >&2
|
|
380
|
+
echo "Next: regenerate the design handoff with comet-handoff.sh." >&2
|
|
381
|
+
return 1
|
|
382
|
+
fi
|
|
383
|
+
|
|
384
|
+
actual_hash=$(compute_handoff_hash)
|
|
385
|
+
if [ "$actual_hash" != "$recorded_hash" ]; then
|
|
386
|
+
echo "OpenSpec artifacts changed after handoff was generated." >&2
|
|
387
|
+
echo "Expected handoff_hash: $recorded_hash" >&2
|
|
388
|
+
echo "Actual handoff_hash: $actual_hash" >&2
|
|
389
|
+
echo "Next: rerun comet-handoff.sh so Superpowers receives the current OpenSpec context." >&2
|
|
390
|
+
return 1
|
|
391
|
+
fi
|
|
392
|
+
|
|
393
|
+
markdown="${context%.json}.md"
|
|
394
|
+
if [ ! -s "$markdown" ]; then
|
|
395
|
+
echo "design handoff markdown is missing or empty: $markdown" >&2
|
|
396
|
+
echo "Next: regenerate the design handoff with comet-handoff.sh." >&2
|
|
397
|
+
return 1
|
|
398
|
+
fi
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
design_handoff_markdown_traceable() {
|
|
402
|
+
local context markdown missing=0
|
|
403
|
+
context=$(yaml_field_value "handoff_context" 2>/dev/null || true)
|
|
404
|
+
if [ -z "$context" ] || [ "$context" = "null" ]; then
|
|
405
|
+
echo "handoff_context is missing from .comet.yaml" >&2
|
|
406
|
+
return 1
|
|
407
|
+
fi
|
|
408
|
+
markdown="${context%.json}.md"
|
|
409
|
+
if [ ! -s "$markdown" ]; then
|
|
410
|
+
echo "design handoff markdown is missing or empty: $markdown" >&2
|
|
411
|
+
return 1
|
|
412
|
+
fi
|
|
413
|
+
grep -q '^Generated-by: comet-handoff\.sh$' "$markdown" || {
|
|
414
|
+
echo "handoff markdown is missing Generated-by marker" >&2
|
|
415
|
+
missing=1
|
|
416
|
+
}
|
|
417
|
+
grep -Eq '^- Mode: (compact|full)$' "$markdown" || {
|
|
418
|
+
echo "handoff markdown is missing Mode marker" >&2
|
|
419
|
+
missing=1
|
|
420
|
+
}
|
|
421
|
+
handoff_source_files | while IFS= read -r file; do
|
|
422
|
+
[ -f "$file" ] || continue
|
|
423
|
+
if ! grep -q "^- Source: $file$" "$markdown"; then
|
|
424
|
+
echo "handoff markdown is missing source reference: $file" >&2
|
|
425
|
+
exit 2
|
|
426
|
+
fi
|
|
427
|
+
if ! grep -q "^- SHA256: $(hash_file "$file")$" "$markdown"; then
|
|
428
|
+
echo "handoff markdown is missing current sha256 for: $file" >&2
|
|
429
|
+
exit 2
|
|
430
|
+
fi
|
|
431
|
+
done || missing=1
|
|
432
|
+
|
|
433
|
+
[ "$missing" -eq 0 ]
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
design_doc_frontmatter_has() {
|
|
437
|
+
local design_doc="$1"
|
|
438
|
+
local field="$2"
|
|
439
|
+
local expected="$3"
|
|
440
|
+
awk '
|
|
441
|
+
{
|
|
442
|
+
line = $0
|
|
443
|
+
sub(/^\357\273\277/, "", line)
|
|
444
|
+
}
|
|
445
|
+
!in_fm && line == "---" { in_fm = 1; next }
|
|
446
|
+
in_fm && line == "---" { exit }
|
|
447
|
+
in_fm { print line }
|
|
448
|
+
' "$design_doc" | grep -Eq "^${field}: ['\"]?${expected}['\"]?[[:space:]]*$"
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
design_doc_links_current_change() {
|
|
452
|
+
local design_doc
|
|
453
|
+
design_doc=$(yaml_field_value "design_doc" 2>/dev/null || true)
|
|
454
|
+
if [ -z "$design_doc" ] || [ "$design_doc" = "null" ] || [ ! -s "$design_doc" ]; then
|
|
455
|
+
echo "design_doc must point to an existing Superpowers Design Doc before leaving design." >&2
|
|
456
|
+
return 1
|
|
457
|
+
fi
|
|
458
|
+
design_doc_frontmatter_has "$design_doc" "comet_change" "$CHANGE"
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
design_doc_declares_technical_role() {
|
|
462
|
+
local design_doc
|
|
463
|
+
design_doc=$(yaml_field_value "design_doc" 2>/dev/null || true)
|
|
464
|
+
[ -n "$design_doc" ] && [ "$design_doc" != "null" ] && [ -s "$design_doc" ] &&
|
|
465
|
+
design_doc_frontmatter_has "$design_doc" "role" "technical-design"
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
design_doc_declares_canonical_spec() {
|
|
469
|
+
local design_doc
|
|
470
|
+
design_doc=$(yaml_field_value "design_doc" 2>/dev/null || true)
|
|
471
|
+
[ -n "$design_doc" ] && [ "$design_doc" != "null" ] && [ -s "$design_doc" ] &&
|
|
472
|
+
design_doc_frontmatter_has "$design_doc" "canonical_spec" "openspec"
|
|
473
|
+
}
|
|
474
|
+
|
|
294
475
|
archived_is_true() {
|
|
295
476
|
local val
|
|
296
477
|
val=$(yaml_field_value "archived" 2>/dev/null || true)
|
|
@@ -315,10 +496,16 @@ guard_design() {
|
|
|
315
496
|
design_doc=$(yaml_field_value "design_doc" 2>/dev/null || true)
|
|
316
497
|
|
|
317
498
|
check "proposal.md exists" file_nonempty "$CHANGE_DIR/proposal.md"
|
|
499
|
+
check "design.md exists" file_nonempty "$CHANGE_DIR/design.md"
|
|
318
500
|
check "tasks.md exists" file_nonempty "$CHANGE_DIR/tasks.md"
|
|
501
|
+
check "design handoff context exists" design_handoff_context_valid
|
|
502
|
+
check "design handoff markdown is traceable" design_handoff_markdown_traceable
|
|
319
503
|
|
|
320
504
|
if [ -n "$design_doc" ] && [ "$design_doc" != "null" ]; then
|
|
321
505
|
check "Design Doc ($design_doc) exists" file_nonempty "$design_doc"
|
|
506
|
+
check "Design Doc frontmatter links current change" design_doc_links_current_change
|
|
507
|
+
check "Design Doc declares technical design role" design_doc_declares_technical_role
|
|
508
|
+
check "Design Doc declares OpenSpec as canonical spec" design_doc_declares_canonical_spec
|
|
322
509
|
else
|
|
323
510
|
warn " [WARN] No design_doc recorded in .comet.yaml"
|
|
324
511
|
fi
|
|
@@ -395,7 +582,7 @@ else
|
|
|
395
582
|
apply_state_update "$PHASE"
|
|
396
583
|
case "$PHASE" in
|
|
397
584
|
open)
|
|
398
|
-
new_phase=$(
|
|
585
|
+
new_phase=$(yaml_field_value "phase")
|
|
399
586
|
green " [APPLY] .comet.yaml updated: phase=$new_phase"
|
|
400
587
|
;;
|
|
401
588
|
design) green " [APPLY] .comet.yaml updated: phase=build" ;;
|