@rpamis/comet 0.4.0-beta.2 → 0.4.0-beta.3
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 +17 -18
- package/assets/manifest.json +1 -1
- package/assets/skills/comet/reference/comet-yaml-fields.md +2 -3
- package/assets/skills/comet/scripts/comet-runtime.mjs +290 -101
- package/assets/skills/comet-build/SKILL.md +1 -9
- package/assets/skills-zh/comet/reference/comet-yaml-fields.md +1 -3
- package/assets/skills-zh/comet-build/SKILL.md +1 -9
- package/dist/app/cli/index.js +1 -1
- package/dist/app/cli/index.js.map +1 -1
- package/dist/app/commands/doctor.d.ts +1 -0
- package/dist/app/commands/doctor.d.ts.map +1 -1
- package/dist/app/commands/doctor.js +62 -12
- package/dist/app/commands/doctor.js.map +1 -1
- package/dist/domains/comet-classic/classic-guard.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-guard.js +44 -83
- package/dist/domains/comet-classic/classic-guard.js.map +1 -1
- package/dist/domains/comet-classic/classic-hook-guard.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-hook-guard.js +1 -6
- package/dist/domains/comet-classic/classic-hook-guard.js.map +1 -1
- package/dist/domains/comet-classic/classic-runtime-run.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-runtime-run.js +231 -4
- package/dist/domains/comet-classic/classic-runtime-run.js.map +1 -1
- package/dist/domains/comet-classic/classic-state-command.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-state-command.js +1 -3
- package/dist/domains/comet-classic/classic-state-command.js.map +1 -1
- package/dist/domains/comet-classic/classic-state.d.ts +1 -3
- package/dist/domains/comet-classic/classic-state.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-state.js +0 -6
- package/dist/domains/comet-classic/classic-state.js.map +1 -1
- package/dist/domains/comet-classic/classic-store.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-store.js +24 -7
- package/dist/domains/comet-classic/classic-store.js.map +1 -1
- package/dist/domains/dashboard/web/index.html +14 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,8 +101,8 @@ Many Comet capabilities have parallels in current industry practice.
|
|
|
101
101
|
from necessary user choices, Comet's five-phase flow can trigger core Skills automatically while the state machine keeps
|
|
102
102
|
transitions reliable.
|
|
103
103
|
- **How to make a Spec lifecycle resumable** — Comet links OpenSpec change/spec artifacts with Superpowers design and
|
|
104
|
-
plan documents, then records phase, execution mode, verification result, and archive status in
|
|
105
|
-
agent can continue after interruption instead of rereading documents and guessing progress.
|
|
104
|
+
plan documents, then records phase, execution mode, verification result, and archive status in each change's
|
|
105
|
+
`.comet.yaml`, so an agent can continue after interruption instead of rereading documents and guessing progress.
|
|
106
106
|
- **How to turn doc synchronization from reminders into automation** — Comet scripts handoff, state updates, validation,
|
|
107
107
|
and archive sync, reducing repeated prompts such as "remember to update the design doc" or "remember to archive the
|
|
108
108
|
change."
|
|
@@ -483,17 +483,17 @@ Comet uses a decoupled state architecture with separate files
|
|
|
483
483
|
<details>
|
|
484
484
|
<summary>View State Management</summary>
|
|
485
485
|
|
|
486
|
-
| File
|
|
487
|
-
|
|
|
488
|
-
| `.openspec.yaml`
|
|
489
|
-
|
|
|
490
|
-
| `.comet/run-state.json`
|
|
491
|
-
| `.comet/state-events.jsonl`
|
|
486
|
+
| File | Owner | Purpose |
|
|
487
|
+
| ----------------------------------------- | -------- | --------------------------------------------------- |
|
|
488
|
+
| `.openspec.yaml` | OpenSpec | Spec lifecycle, change metadata |
|
|
489
|
+
| `openspec/changes/<name>/.comet.yaml` | Comet | Workflow phase, execution mode, verification status |
|
|
490
|
+
| `.comet/run-state.json` | Engine | Run identity and execution state (machine-owned) |
|
|
491
|
+
| `.comet/state-events.jsonl` | Comet | Append-only state transition audit log |
|
|
492
492
|
|
|
493
|
-
`.comet.yaml` stores Classic workflow state and only keeps `run_id` as the link to the Engine Run.
|
|
494
|
-
state lives in `.comet/run-state.json` with camelCase fields such as `currentStep`,
|
|
495
|
-
Run fields left in YAML are migrated after compatibility reads, and `skill` is no
|
|
496
|
-
`.comet.yaml` field.
|
|
493
|
+
Each change-level `.comet.yaml` stores Classic workflow state and only keeps `run_id` as the link to the Engine Run.
|
|
494
|
+
Machine-owned Engine state lives in the change's `.comet/run-state.json` with camelCase fields such as `currentStep`,
|
|
495
|
+
`status`, and `iteration`. Legacy Run fields left in YAML are migrated after compatibility reads, and `skill` is no
|
|
496
|
+
longer a valid current `.comet.yaml` field. Project defaults live in `.comet/config.yaml`.
|
|
497
497
|
|
|
498
498
|
Phase progression is handled consistently by the TypeScript transition table, `comet-state transition`,
|
|
499
499
|
`comet-guard --apply`, and archive commands. Each successful progression appends an audit event to
|
|
@@ -505,9 +505,9 @@ breakpoint recovery. Agents can use Comet commands to know which phase the curre
|
|
|
505
505
|
</details>
|
|
506
506
|
|
|
507
507
|
<details>
|
|
508
|
-
<summary>View key .comet.yaml fields</summary>
|
|
508
|
+
<summary>View key change .comet.yaml fields</summary>
|
|
509
509
|
|
|
510
|
-
**Key Fields in `.comet.yaml`:**
|
|
510
|
+
**Key Fields in change `.comet.yaml`:**
|
|
511
511
|
|
|
512
512
|
```yaml
|
|
513
513
|
workflow: full # Workflow type: full | tweak | hotfix
|
|
@@ -532,15 +532,13 @@ branch_status: pending # Branch handling statu
|
|
|
532
532
|
verified_at: null # Verification timestamp; null before verification passes
|
|
533
533
|
archived: false # Archived changes are blocked from further mutation
|
|
534
534
|
direct_override: null # Must be true when a full workflow chooses direct build
|
|
535
|
-
build_command: null # Optional build command; may also live in repo-root config
|
|
536
|
-
verify_command: null # Optional verify command; may also live in repo-root config
|
|
537
535
|
handoff_context: null # Design handoff context path written by comet-handoff.mjs
|
|
538
536
|
handoff_hash: null # SHA256 for handoff_context; 64 hex chars when present
|
|
539
537
|
classic_profile: full # Machine-maintained Classic profile
|
|
540
538
|
classic_migration: 1 # Machine-maintained migration version
|
|
541
539
|
```
|
|
542
540
|
|
|
543
|
-
Current `.comet.yaml` no longer contains `skill`; legacy Run fields in YAML are migrated to `.comet/run-state.json`.
|
|
541
|
+
Current change `.comet.yaml` no longer contains `skill`; legacy Run fields in YAML are migrated to `.comet/run-state.json`.
|
|
544
542
|
|
|
545
543
|
</details>
|
|
546
544
|
|
|
@@ -666,7 +664,8 @@ manual handoff. Phase advancement itself always happens — this setting only af
|
|
|
666
664
|
| `true` | Auto-invoke the next skill after each phase (default) |
|
|
667
665
|
| `false` | Pause after each phase; user manually triggers the next skill |
|
|
668
666
|
|
|
669
|
-
Three-layer configuration with precedence: `COMET_AUTO_TRANSITION` env var > `.comet/config.yaml` (project) >
|
|
667
|
+
Three-layer configuration with precedence: `COMET_AUTO_TRANSITION` env var > `.comet/config.yaml` (project) > change
|
|
668
|
+
`.comet.yaml`.
|
|
670
669
|
|
|
671
670
|
See [AUTO-TRANSITION.md](docs/AUTO-TRANSITION.md) for configuration details, workflow mapping, and FAQ.
|
|
672
671
|
|
package/assets/manifest.json
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Canonical path: `comet/reference/comet-yaml-fields.md`
|
|
4
4
|
|
|
5
|
-
This file is the field reference for
|
|
5
|
+
This file is the field reference for each change-level `.comet.yaml` state file under `openspec/changes/<name>/`.
|
|
6
|
+
Consult on demand; not loaded inline with skills. Project defaults live in `.comet/config.yaml`.
|
|
6
7
|
|
|
7
8
|
## Example
|
|
8
9
|
|
|
@@ -59,8 +60,6 @@ archived: false
|
|
|
59
60
|
| Field | Meaning |
|
|
60
61
|
|-------|---------|
|
|
61
62
|
| `direct_override` | `true`/`false`. Full workflow must explicitly set to `true` to use `build_mode: direct` |
|
|
62
|
-
| `build_command` | Project build command. Guard runs this first; supports restricted command words/quotes/paths plus `&&` sequential steps; rejects `;`, pipes, bare `&`, `$`, and backticks |
|
|
63
|
-
| `verify_command` | Project verify command. Verify guard runs this first; same restricted command grammar as `build_command`; falls back to build command when unset |
|
|
64
63
|
|
|
65
64
|
## State Machine Hard Constraints
|
|
66
65
|
|
|
@@ -7827,8 +7827,6 @@ var CLASSIC_WIRE_KEYS = [
|
|
|
7827
7827
|
"verified_at",
|
|
7828
7828
|
"archived",
|
|
7829
7829
|
"direct_override",
|
|
7830
|
-
"build_command",
|
|
7831
|
-
"verify_command",
|
|
7832
7830
|
"handoff_context",
|
|
7833
7831
|
"handoff_hash",
|
|
7834
7832
|
"classic_profile",
|
|
@@ -7937,8 +7935,6 @@ function classicStateFromDocument(doc) {
|
|
|
7937
7935
|
verifiedAt: nullableString(doc, "verified_at"),
|
|
7938
7936
|
archived: booleanValue(doc, "archived", false),
|
|
7939
7937
|
directOverride: booleanValue(doc, "direct_override"),
|
|
7940
|
-
buildCommand: nullableString(doc, "build_command"),
|
|
7941
|
-
verifyCommand: nullableString(doc, "verify_command"),
|
|
7942
7938
|
handoffContext: relativePath(doc, "handoff_context"),
|
|
7943
7939
|
handoffHash: sha256(doc, "handoff_hash"),
|
|
7944
7940
|
classicProfile: enumValue(doc, "classic_profile", CLASSIC_PROFILES),
|
|
@@ -8002,8 +7998,6 @@ function classicStateToDocument(state) {
|
|
|
8002
7998
|
verified_at: state.verifiedAt,
|
|
8003
7999
|
archived: state.archived,
|
|
8004
8000
|
direct_override: state.directOverride,
|
|
8005
|
-
build_command: state.buildCommand,
|
|
8006
|
-
verify_command: state.verifyCommand,
|
|
8007
8001
|
handoff_context: state.handoffContext,
|
|
8008
8002
|
handoff_hash: state.handoffHash,
|
|
8009
8003
|
classic_profile: state.classicProfile,
|
|
@@ -8058,6 +8052,16 @@ function stripLegacyRunFields(document) {
|
|
|
8058
8052
|
];
|
|
8059
8053
|
for (const key of LEGACY_RUN_KEYS) document.delete(key);
|
|
8060
8054
|
}
|
|
8055
|
+
function stripLegacyCommandFields(document) {
|
|
8056
|
+
let changed = false;
|
|
8057
|
+
for (const key of ["build_command", "verify_command"]) {
|
|
8058
|
+
if (document.has(key)) {
|
|
8059
|
+
document.delete(key);
|
|
8060
|
+
changed = true;
|
|
8061
|
+
}
|
|
8062
|
+
}
|
|
8063
|
+
return changed;
|
|
8064
|
+
}
|
|
8061
8065
|
async function readDocument(file) {
|
|
8062
8066
|
let source;
|
|
8063
8067
|
try {
|
|
@@ -8074,8 +8078,11 @@ async function readDocument(file) {
|
|
|
8074
8078
|
return document;
|
|
8075
8079
|
}
|
|
8076
8080
|
async function readClassicState(changeDir) {
|
|
8077
|
-
const
|
|
8078
|
-
const
|
|
8081
|
+
const file = path4.join(changeDir, ".comet.yaml");
|
|
8082
|
+
const document = await readDocument(file);
|
|
8083
|
+
let doc = documentRecord(document);
|
|
8084
|
+
let migrated = stripLegacyCommandFields(document);
|
|
8085
|
+
if (migrated) doc = documentRecord(document);
|
|
8079
8086
|
let run = await readRunState(changeDir);
|
|
8080
8087
|
if (!run && doc.run_id && doc.skill) {
|
|
8081
8088
|
const { runStateFromDocument: runStateFromDocument2 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
@@ -8083,13 +8090,15 @@ async function readClassicState(changeDir) {
|
|
|
8083
8090
|
if (run) {
|
|
8084
8091
|
await writeRunState(changeDir, run);
|
|
8085
8092
|
stripLegacyRunFields(document);
|
|
8086
|
-
|
|
8087
|
-
const temporary = path4.join(changeDir, `.comet.yaml.${randomUUID2()}.tmp`);
|
|
8088
|
-
await fs4.writeFile(temporary, document.toString(), "utf8");
|
|
8089
|
-
await fs4.rename(temporary, file);
|
|
8093
|
+
migrated = true;
|
|
8090
8094
|
}
|
|
8091
8095
|
}
|
|
8092
|
-
|
|
8096
|
+
if (migrated) {
|
|
8097
|
+
const temporary = path4.join(changeDir, `.comet.yaml.${randomUUID2()}.tmp`);
|
|
8098
|
+
await fs4.writeFile(temporary, document.toString(), "utf8");
|
|
8099
|
+
await fs4.rename(temporary, file);
|
|
8100
|
+
}
|
|
8101
|
+
return parseClassicStateDocument(documentRecord(document), run);
|
|
8093
8102
|
}
|
|
8094
8103
|
async function readLegacyState(changeDir) {
|
|
8095
8104
|
const document = await readDocument(path4.join(changeDir, ".comet.yaml"));
|
|
@@ -8980,6 +8989,230 @@ async function fileExists2(file) {
|
|
|
8980
8989
|
throw error;
|
|
8981
8990
|
}
|
|
8982
8991
|
}
|
|
8992
|
+
function embeddedClassicRuntimePackage(root) {
|
|
8993
|
+
return {
|
|
8994
|
+
root,
|
|
8995
|
+
packageKind: "runtime",
|
|
8996
|
+
definition: {
|
|
8997
|
+
apiVersion: "comet/v1alpha1",
|
|
8998
|
+
kind: "Skill",
|
|
8999
|
+
metadata: {
|
|
9000
|
+
name: "comet-classic",
|
|
9001
|
+
version: "1",
|
|
9002
|
+
description: "Internal compatibility orchestration for classic Comet full, hotfix, and tweak workflows"
|
|
9003
|
+
},
|
|
9004
|
+
goal: {
|
|
9005
|
+
statement: "Advance or restore a classic Comet Run without changing the user command surface",
|
|
9006
|
+
inputs: [
|
|
9007
|
+
{
|
|
9008
|
+
name: "classic-state",
|
|
9009
|
+
description: "Validated ClassicState consistent with the Run projection",
|
|
9010
|
+
required: true
|
|
9011
|
+
},
|
|
9012
|
+
{
|
|
9013
|
+
name: "evidence",
|
|
9014
|
+
description: "Structured evidence produced by the Classic Evidence collector",
|
|
9015
|
+
required: true
|
|
9016
|
+
}
|
|
9017
|
+
],
|
|
9018
|
+
outputs: [
|
|
9019
|
+
{
|
|
9020
|
+
name: "run-state",
|
|
9021
|
+
description: "Atomically synchronized Classic and Run state",
|
|
9022
|
+
required: true
|
|
9023
|
+
}
|
|
9024
|
+
],
|
|
9025
|
+
success: [
|
|
9026
|
+
"Legacy fields and Run fields remain consistent",
|
|
9027
|
+
"Every step invokes only a declared public Comet Skill",
|
|
9028
|
+
"The completed state passes its completion eval"
|
|
9029
|
+
]
|
|
9030
|
+
},
|
|
9031
|
+
orchestration: {
|
|
9032
|
+
mode: "deterministic",
|
|
9033
|
+
entry: "full.open",
|
|
9034
|
+
steps: [
|
|
9035
|
+
{
|
|
9036
|
+
id: "full.open",
|
|
9037
|
+
action: { type: "invoke_skill", ref: "comet-open" },
|
|
9038
|
+
next: "full.design.handoff"
|
|
9039
|
+
},
|
|
9040
|
+
{
|
|
9041
|
+
id: "full.design.handoff",
|
|
9042
|
+
action: { type: "invoke_skill", ref: "comet-design" },
|
|
9043
|
+
next: "full.design.document"
|
|
9044
|
+
},
|
|
9045
|
+
{
|
|
9046
|
+
id: "full.design.document",
|
|
9047
|
+
action: { type: "invoke_skill", ref: "comet-design" },
|
|
9048
|
+
next: "full.build.plan"
|
|
9049
|
+
},
|
|
9050
|
+
{
|
|
9051
|
+
id: "full.build.plan",
|
|
9052
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9053
|
+
next: "full.build.plan-ready"
|
|
9054
|
+
},
|
|
9055
|
+
{
|
|
9056
|
+
id: "full.build.plan-ready",
|
|
9057
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9058
|
+
next: "full.build.configure"
|
|
9059
|
+
},
|
|
9060
|
+
{
|
|
9061
|
+
id: "full.build.configure",
|
|
9062
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9063
|
+
next: "full.build.execute"
|
|
9064
|
+
},
|
|
9065
|
+
{
|
|
9066
|
+
id: "full.build.execute",
|
|
9067
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9068
|
+
next: "full.build.complete"
|
|
9069
|
+
},
|
|
9070
|
+
{
|
|
9071
|
+
id: "full.build.complete",
|
|
9072
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9073
|
+
next: "full.verify.run"
|
|
9074
|
+
},
|
|
9075
|
+
{
|
|
9076
|
+
id: "full.build.fix",
|
|
9077
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9078
|
+
next: "full.build.execute"
|
|
9079
|
+
},
|
|
9080
|
+
{
|
|
9081
|
+
id: "full.verify.run",
|
|
9082
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9083
|
+
next: "full.verify.branch"
|
|
9084
|
+
},
|
|
9085
|
+
{
|
|
9086
|
+
id: "full.verify.branch",
|
|
9087
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9088
|
+
next: "full.archive.confirm"
|
|
9089
|
+
},
|
|
9090
|
+
{
|
|
9091
|
+
id: "full.archive.confirm",
|
|
9092
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9093
|
+
next: "full.archive.execute"
|
|
9094
|
+
},
|
|
9095
|
+
{
|
|
9096
|
+
id: "full.archive.execute",
|
|
9097
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9098
|
+
next: "completed"
|
|
9099
|
+
},
|
|
9100
|
+
{
|
|
9101
|
+
id: "hotfix.open",
|
|
9102
|
+
action: { type: "invoke_skill", ref: "comet-hotfix" },
|
|
9103
|
+
next: "hotfix.build.execute"
|
|
9104
|
+
},
|
|
9105
|
+
{
|
|
9106
|
+
id: "hotfix.build.execute",
|
|
9107
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9108
|
+
next: "hotfix.build.complete"
|
|
9109
|
+
},
|
|
9110
|
+
{
|
|
9111
|
+
id: "hotfix.build.complete",
|
|
9112
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9113
|
+
next: "hotfix.verify.run"
|
|
9114
|
+
},
|
|
9115
|
+
{
|
|
9116
|
+
id: "hotfix.verify.run",
|
|
9117
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9118
|
+
next: "hotfix.verify.branch"
|
|
9119
|
+
},
|
|
9120
|
+
{
|
|
9121
|
+
id: "hotfix.verify.branch",
|
|
9122
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9123
|
+
next: "hotfix.archive.confirm"
|
|
9124
|
+
},
|
|
9125
|
+
{
|
|
9126
|
+
id: "hotfix.archive.confirm",
|
|
9127
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9128
|
+
next: "hotfix.archive.execute"
|
|
9129
|
+
},
|
|
9130
|
+
{
|
|
9131
|
+
id: "hotfix.archive.execute",
|
|
9132
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9133
|
+
next: "completed"
|
|
9134
|
+
},
|
|
9135
|
+
{
|
|
9136
|
+
id: "tweak.open",
|
|
9137
|
+
action: { type: "invoke_skill", ref: "comet-tweak" },
|
|
9138
|
+
next: "tweak.build.execute"
|
|
9139
|
+
},
|
|
9140
|
+
{
|
|
9141
|
+
id: "tweak.build.execute",
|
|
9142
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9143
|
+
next: "tweak.build.complete"
|
|
9144
|
+
},
|
|
9145
|
+
{
|
|
9146
|
+
id: "tweak.build.complete",
|
|
9147
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9148
|
+
next: "tweak.verify.run"
|
|
9149
|
+
},
|
|
9150
|
+
{
|
|
9151
|
+
id: "tweak.verify.run",
|
|
9152
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9153
|
+
next: "tweak.verify.branch"
|
|
9154
|
+
},
|
|
9155
|
+
{
|
|
9156
|
+
id: "tweak.verify.branch",
|
|
9157
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9158
|
+
next: "tweak.archive.confirm"
|
|
9159
|
+
},
|
|
9160
|
+
{
|
|
9161
|
+
id: "tweak.archive.confirm",
|
|
9162
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9163
|
+
next: "tweak.archive.execute"
|
|
9164
|
+
},
|
|
9165
|
+
{
|
|
9166
|
+
id: "tweak.archive.execute",
|
|
9167
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9168
|
+
next: "completed"
|
|
9169
|
+
},
|
|
9170
|
+
{
|
|
9171
|
+
id: "completed",
|
|
9172
|
+
action: { type: "checkpoint" },
|
|
9173
|
+
completionEvals: ["classic-completed"]
|
|
9174
|
+
}
|
|
9175
|
+
]
|
|
9176
|
+
},
|
|
9177
|
+
skills: [
|
|
9178
|
+
{ id: "comet-open" },
|
|
9179
|
+
{ id: "comet-design" },
|
|
9180
|
+
{ id: "comet-build" },
|
|
9181
|
+
{ id: "comet-verify" },
|
|
9182
|
+
{ id: "comet-archive" },
|
|
9183
|
+
{ id: "comet-hotfix" },
|
|
9184
|
+
{ id: "comet-tweak" }
|
|
9185
|
+
],
|
|
9186
|
+
agents: [],
|
|
9187
|
+
tools: []
|
|
9188
|
+
},
|
|
9189
|
+
guardrails: {
|
|
9190
|
+
allowedSkills: [
|
|
9191
|
+
"comet-open",
|
|
9192
|
+
"comet-design",
|
|
9193
|
+
"comet-build",
|
|
9194
|
+
"comet-verify",
|
|
9195
|
+
"comet-archive",
|
|
9196
|
+
"comet-hotfix",
|
|
9197
|
+
"comet-tweak"
|
|
9198
|
+
],
|
|
9199
|
+
allowedAgents: [],
|
|
9200
|
+
allowedTools: [],
|
|
9201
|
+
maxIterations: 500,
|
|
9202
|
+
maxRetriesPerAction: 3,
|
|
9203
|
+
confirmationRequiredFor: []
|
|
9204
|
+
},
|
|
9205
|
+
evals: [
|
|
9206
|
+
{
|
|
9207
|
+
id: "classic-completed",
|
|
9208
|
+
scope: "completion",
|
|
9209
|
+
type: "state_equals",
|
|
9210
|
+
field: "status",
|
|
9211
|
+
equals: "completed"
|
|
9212
|
+
}
|
|
9213
|
+
]
|
|
9214
|
+
};
|
|
9215
|
+
}
|
|
8983
9216
|
async function classicRuntimeRoot() {
|
|
8984
9217
|
const runtimeDirectory = path10.dirname(fileURLToPath(import.meta.url));
|
|
8985
9218
|
const candidates = [
|
|
@@ -8996,7 +9229,7 @@ async function classicRuntimeRoot() {
|
|
|
8996
9229
|
for (const candidate of candidates) {
|
|
8997
9230
|
if (await directoryExists(candidate)) return candidate;
|
|
8998
9231
|
}
|
|
8999
|
-
|
|
9232
|
+
return null;
|
|
9000
9233
|
}
|
|
9001
9234
|
async function loadClassicRuntimePackage(root) {
|
|
9002
9235
|
if (await fileExists2(path10.join(root, "skill.yaml"))) {
|
|
@@ -9007,15 +9240,14 @@ async function loadClassicRuntimePackage(root) {
|
|
|
9007
9240
|
async function ensureClassicRuntimeRun(changeDir) {
|
|
9008
9241
|
const root = await classicRuntimeRoot();
|
|
9009
9242
|
return ensureClassicRun(changeDir, {
|
|
9010
|
-
skillPackage: await loadClassicRuntimePackage(root)
|
|
9243
|
+
skillPackage: root ? await loadClassicRuntimePackage(root) : embeddedClassicRuntimePackage(path10.dirname(fileURLToPath(import.meta.url)))
|
|
9011
9244
|
});
|
|
9012
9245
|
}
|
|
9013
9246
|
async function ensureStrictClassicRuntimeRun(changeDir) {
|
|
9014
9247
|
const projection = await readClassicState(changeDir);
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
);
|
|
9248
|
+
const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
|
|
9249
|
+
if (unknownKeys.length > 0) {
|
|
9250
|
+
throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`);
|
|
9019
9251
|
}
|
|
9020
9252
|
return ensureClassicRuntimeRun(changeDir);
|
|
9021
9253
|
}
|
|
@@ -9939,13 +10171,7 @@ async function readField(changeDir, field2) {
|
|
|
9939
10171
|
async function projectConfigValue(field2, changeDir) {
|
|
9940
10172
|
const changeValue = await readField(changeDir, field2);
|
|
9941
10173
|
if (changeValue && changeValue !== "null") return changeValue;
|
|
9942
|
-
for (const config of [
|
|
9943
|
-
".comet/config.yaml",
|
|
9944
|
-
".comet.yaml",
|
|
9945
|
-
"comet.yaml",
|
|
9946
|
-
".comet.yml",
|
|
9947
|
-
"comet.yml"
|
|
9948
|
-
]) {
|
|
10174
|
+
for (const config of [".comet/config.yaml"]) {
|
|
9949
10175
|
if (!await exists4(config)) continue;
|
|
9950
10176
|
for (const line of (await fs13.readFile(config, "utf8")).split(/\r?\n/u)) {
|
|
9951
10177
|
if (new RegExp(`^${field2}:`, "u").test(line)) {
|
|
@@ -10001,68 +10227,6 @@ Next: regenerate or rewrite this artifact in English while preserving necessary
|
|
|
10001
10227
|
}
|
|
10002
10228
|
return pass();
|
|
10003
10229
|
}
|
|
10004
|
-
function runCommandString(command) {
|
|
10005
|
-
if (!command) return { status: 1, output: red2("ERROR: build/verify command is empty") };
|
|
10006
|
-
const split = splitCommandChain(command);
|
|
10007
|
-
if (typeof split === "string") {
|
|
10008
|
-
return {
|
|
10009
|
-
status: 1,
|
|
10010
|
-
output: `${red2(`ERROR: build/verify command contains shell metacharacters: ${command}`)}
|
|
10011
|
-
${red2(
|
|
10012
|
-
split
|
|
10013
|
-
)}`
|
|
10014
|
-
};
|
|
10015
|
-
}
|
|
10016
|
-
const output = [];
|
|
10017
|
-
for (const part of split) {
|
|
10018
|
-
const segment = part.trim();
|
|
10019
|
-
if (!segment) {
|
|
10020
|
-
return { status: 1, output: red2("ERROR: build/verify command contains an empty && step") };
|
|
10021
|
-
}
|
|
10022
|
-
const result3 = spawnSync2(segment, { shell: true, encoding: "utf8", timeout: 3e5 });
|
|
10023
|
-
const combined = `${result3.stdout ?? ""}${result3.stderr ?? ""}`.replace(/\n+$/u, "");
|
|
10024
|
-
output.push(`${red2(`+ ${segment}`)}${combined ? `
|
|
10025
|
-
${combined}` : ""}`);
|
|
10026
|
-
if (result3.status !== 0) {
|
|
10027
|
-
return { status: result3.status ?? 1, output: output.join("\n") };
|
|
10028
|
-
}
|
|
10029
|
-
}
|
|
10030
|
-
return { status: 0, output: output.join("\n") };
|
|
10031
|
-
}
|
|
10032
|
-
function splitCommandChain(command) {
|
|
10033
|
-
const parts = [];
|
|
10034
|
-
let current = "";
|
|
10035
|
-
let quote = "";
|
|
10036
|
-
for (let i = 0; i < command.length; i += 1) {
|
|
10037
|
-
const c = command[i];
|
|
10038
|
-
if (c === "$" || c === "`") {
|
|
10039
|
-
return "Allowed: command words, quotes, paths, and && between sequential commands";
|
|
10040
|
-
}
|
|
10041
|
-
if (quote) {
|
|
10042
|
-
current += c;
|
|
10043
|
-
if (c === quote) quote = "";
|
|
10044
|
-
continue;
|
|
10045
|
-
}
|
|
10046
|
-
if (c === '"' || c === "'") {
|
|
10047
|
-
quote = c;
|
|
10048
|
-
current += c;
|
|
10049
|
-
continue;
|
|
10050
|
-
}
|
|
10051
|
-
if (c === "&" && command[i + 1] === "&") {
|
|
10052
|
-
parts.push(current);
|
|
10053
|
-
current = "";
|
|
10054
|
-
i += 1;
|
|
10055
|
-
continue;
|
|
10056
|
-
}
|
|
10057
|
-
if (c === ";" || c === "|" || c === "&") {
|
|
10058
|
-
return "Allowed: command words, quotes, paths, and && between sequential commands";
|
|
10059
|
-
}
|
|
10060
|
-
current += c;
|
|
10061
|
-
}
|
|
10062
|
-
if (quote) return "Command has an unmatched quote";
|
|
10063
|
-
parts.push(current);
|
|
10064
|
-
return parts;
|
|
10065
|
-
}
|
|
10066
10230
|
function hashFile(file) {
|
|
10067
10231
|
return createHash4("sha256").update(readFileSync(file)).digest("hex");
|
|
10068
10232
|
}
|
|
@@ -10100,6 +10264,13 @@ async function preflight(changeDir, name) {
|
|
|
10100
10264
|
`);
|
|
10101
10265
|
throw new GuardFailure(red2("FATAL: .comet.yaml schema validation failed"));
|
|
10102
10266
|
}
|
|
10267
|
+
const projection = await readClassicState(changeDir);
|
|
10268
|
+
const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
|
|
10269
|
+
if (unknownKeys.length > 0) {
|
|
10270
|
+
throw new GuardFailure(
|
|
10271
|
+
red2(`FATAL: .comet.yaml has unknown field(s): ${unknownKeys.join(", ")}`)
|
|
10272
|
+
);
|
|
10273
|
+
}
|
|
10103
10274
|
}
|
|
10104
10275
|
function pushCheck(output, outcome) {
|
|
10105
10276
|
if (outcome.passed) {
|
|
@@ -10144,6 +10315,24 @@ async function runChecks(output, builders) {
|
|
|
10144
10315
|
}
|
|
10145
10316
|
return blocked2;
|
|
10146
10317
|
}
|
|
10318
|
+
async function removedProjectCommandField(field2) {
|
|
10319
|
+
const config = path14.join(".comet", "config.yaml");
|
|
10320
|
+
if (!await exists4(config)) return false;
|
|
10321
|
+
const document = (0, import_yaml4.parseDocument)(await fs13.readFile(config, "utf8"));
|
|
10322
|
+
if (document.errors.length > 0) {
|
|
10323
|
+
throw new Error(
|
|
10324
|
+
`.comet/config.yaml is invalid YAML (${document.errors[0].message}); cannot check for removed "${field2}" field. Fix the config and retry.`
|
|
10325
|
+
);
|
|
10326
|
+
}
|
|
10327
|
+
const value = document.toJS();
|
|
10328
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, field2);
|
|
10329
|
+
}
|
|
10330
|
+
function removedProjectCommandRun(field2) {
|
|
10331
|
+
return {
|
|
10332
|
+
status: 1,
|
|
10333
|
+
output: `${field2} has been removed from .comet/config.yaml. Delete this field and run any required ${field2 === "build_command" ? "build" : "verification"} command manually before retrying.`
|
|
10334
|
+
};
|
|
10335
|
+
}
|
|
10147
10336
|
function runInferred(command) {
|
|
10148
10337
|
const result3 = spawnSync2(command, { shell: true, encoding: "utf8", timeout: 3e5 });
|
|
10149
10338
|
return {
|
|
@@ -10151,10 +10340,11 @@ function runInferred(command) {
|
|
|
10151
10340
|
output: `${result3.stdout ?? ""}${result3.stderr ?? ""}`.replace(/\n+$/u, "")
|
|
10152
10341
|
};
|
|
10153
10342
|
}
|
|
10154
|
-
async function buildPasses(
|
|
10343
|
+
async function buildPasses() {
|
|
10155
10344
|
if (process.env.COMET_SKIP_BUILD === "1") return { status: 0, output: "" };
|
|
10156
|
-
|
|
10157
|
-
|
|
10345
|
+
if (await removedProjectCommandField("build_command")) {
|
|
10346
|
+
return removedProjectCommandRun("build_command");
|
|
10347
|
+
}
|
|
10158
10348
|
if (await exists4("package.json") && /"build"/u.test(await fs13.readFile("package.json", "utf8"))) {
|
|
10159
10349
|
return runInferred("npm run build");
|
|
10160
10350
|
}
|
|
@@ -10169,11 +10359,12 @@ async function buildPasses(changeDir) {
|
|
|
10169
10359
|
if (await exists4("Cargo.toml")) return runInferred("cargo build");
|
|
10170
10360
|
return { status: 1, output: "" };
|
|
10171
10361
|
}
|
|
10172
|
-
async function verificationCommandPasses(
|
|
10362
|
+
async function verificationCommandPasses() {
|
|
10173
10363
|
if (process.env.COMET_SKIP_BUILD === "1") return { status: 0, output: "" };
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10364
|
+
if (await removedProjectCommandField("verify_command")) {
|
|
10365
|
+
return removedProjectCommandRun("verify_command");
|
|
10366
|
+
}
|
|
10367
|
+
return buildPasses();
|
|
10177
10368
|
}
|
|
10178
10369
|
async function tasksAllDone(changeDir) {
|
|
10179
10370
|
const tasks = path14.join(changeDir, "tasks.md");
|
|
@@ -10567,7 +10758,7 @@ async function guardBuildChecks(output, changeDir, change) {
|
|
|
10567
10758
|
// Build check runs last — only after all config checks pass — to avoid
|
|
10568
10759
|
// wasting time on a build that would be rejected by a config failure.
|
|
10569
10760
|
check("Build passes", async () => {
|
|
10570
|
-
const buildResult = await buildPasses(
|
|
10761
|
+
const buildResult = await buildPasses();
|
|
10571
10762
|
return buildResult.status === 0 ? pass() : fail(buildResult.output);
|
|
10572
10763
|
})
|
|
10573
10764
|
]);
|
|
@@ -10578,7 +10769,7 @@ async function guardVerifyChecks(output, changeDir) {
|
|
|
10578
10769
|
// Verification command runs after tasks check — no point running tests
|
|
10579
10770
|
// if tasks.md is incomplete.
|
|
10580
10771
|
check("Verification passes", async () => {
|
|
10581
|
-
const verifyResult = await verificationCommandPasses(
|
|
10772
|
+
const verifyResult = await verificationCommandPasses();
|
|
10582
10773
|
return verifyResult.status === 0 ? pass() : fail(verifyResult.output);
|
|
10583
10774
|
}),
|
|
10584
10775
|
check(
|
|
@@ -11364,7 +11555,7 @@ function isRootMarkdown(relativePath2) {
|
|
|
11364
11555
|
return !relativePath2.includes("/") && relativePath2.endsWith(".md");
|
|
11365
11556
|
}
|
|
11366
11557
|
function isCometConfig(relativePath2) {
|
|
11367
|
-
return relativePath2
|
|
11558
|
+
return relativePath2.startsWith(".comet/") || relativePath2.includes("/.comet/");
|
|
11368
11559
|
}
|
|
11369
11560
|
function isSuperpowersWorkspace(relativePath2) {
|
|
11370
11561
|
return relativePath2 === ".superpowers" || relativePath2.startsWith(".superpowers/");
|
|
@@ -12110,8 +12301,6 @@ function sparseClassicState(record) {
|
|
|
12110
12301
|
verifiedAt: nullableRecordString(record, "verified_at"),
|
|
12111
12302
|
archived: nullableRecordBoolean(record, "archived") ?? false,
|
|
12112
12303
|
directOverride: nullableRecordBoolean(record, "direct_override"),
|
|
12113
|
-
buildCommand: nullableRecordString(record, "build_command"),
|
|
12114
|
-
verifyCommand: nullableRecordString(record, "verify_command"),
|
|
12115
12304
|
handoffContext: nullableRecordString(record, "handoff_context"),
|
|
12116
12305
|
handoffHash: nullableRecordString(record, "handoff_hash"),
|
|
12117
12306
|
classicProfile: enumRecordValue(record, "classic_profile", PROFILES, workflow),
|
|
@@ -12294,7 +12483,7 @@ async function init(output, name, workflow) {
|
|
|
12294
12483
|
archived: false
|
|
12295
12484
|
});
|
|
12296
12485
|
await atomicWrite2(file, document.toString());
|
|
12297
|
-
output.
|
|
12486
|
+
output.stdout.push(green4(`Initialized: ${label}/.comet.yaml (workflow=${workflow})`));
|
|
12298
12487
|
}
|
|
12299
12488
|
async function requirePhase(name, expected) {
|
|
12300
12489
|
const actual = await readField3(name, "phase");
|
|
@@ -284,15 +284,7 @@ Build is the longest phase and may span many tasks. To support resume after cont
|
|
|
284
284
|
- Code review has been completed per the `executing-plans` review gate (Section "Execute plan") for the chosen `review_mode`: under `standard` or `thorough`, code review has been requested and CRITICAL review findings fixed or non-CRITICAL acceptance rationale recorded; under `review_mode: off`, the reason for skipping automatic code review has been recorded in a persistent artifact
|
|
285
285
|
- **Phase guard**: Run `node "$COMET_GUARD" <change-name> build --apply`; after all PASS, state advances to `phase: verify`
|
|
286
286
|
|
|
287
|
-
Guard
|
|
288
|
-
|
|
289
|
-
```yaml
|
|
290
|
-
build_command: <build command>
|
|
291
|
-
verify_command: <verify command>
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
Configuration can live in the change `.comet.yaml`, or in repo-root `.comet.yaml` / `comet.yaml` / `.comet.yml` / `comet.yml`.
|
|
295
|
-
Configured commands use a restricted shell grammar: command words, quotes, paths, and `&&` for sequential steps are allowed; `;`, pipes, bare `&`, `$`, and backticks are rejected. Only when no command is configured does guard fall back to `npm run build`, Maven, or Cargo auto-detection. When a command fails, guard prints the command output as evidence for debugging.
|
|
287
|
+
Guard runs the inferred project build check (`npm run build`, Maven, or Cargo when detected). When the inferred command fails, guard prints the command output as evidence for debugging.
|
|
296
288
|
|
|
297
289
|
Before exit, run guard to auto-transition:
|
|
298
290
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
规范路径:`comet/reference/comet-yaml-fields.md`
|
|
4
4
|
|
|
5
|
-
本文件是 `.comet.yaml` 状态文件的字段参考。按需查阅,不随 skill
|
|
5
|
+
本文件是 `openspec/changes/<name>/` 下每个 change 级 `.comet.yaml` 状态文件的字段参考。按需查阅,不随 skill 一次性加载。项目级默认配置只放在 `.comet/config.yaml`。
|
|
6
6
|
|
|
7
7
|
## 示例
|
|
8
8
|
|
|
@@ -59,8 +59,6 @@ archived: false
|
|
|
59
59
|
| 字段 | 含义 |
|
|
60
60
|
|------|------|
|
|
61
61
|
| `direct_override` | `true`/`false`。full workflow 如需使用 `build_mode: direct`,必须显式设为 `true` |
|
|
62
|
-
| `build_command` | 项目构建命令。guard 优先运行;支持命令词、引号、路径和 `&&` 顺序步骤;拒绝 `;`、管道、裸 `&`、`$` 和反引号 |
|
|
63
|
-
| `verify_command` | 项目验证命令。verify guard 优先运行;使用与 `build_command` 相同的受限命令语法,未配置时回退到构建命令 |
|
|
64
62
|
|
|
65
63
|
## 状态机硬约束
|
|
66
64
|
|