@rpamis/comet 0.4.0-beta.1 → 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.
Files changed (45) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +17 -18
  3. package/assets/manifest.json +1 -1
  4. package/assets/skills/comet/reference/comet-yaml-fields.md +2 -3
  5. package/assets/skills/comet/scripts/comet-runtime.mjs +376 -103
  6. package/assets/skills/comet-build/SKILL.md +1 -9
  7. package/assets/skills-zh/comet/reference/comet-yaml-fields.md +1 -3
  8. package/assets/skills-zh/comet-build/SKILL.md +1 -9
  9. package/dist/app/cli/index.js +1 -1
  10. package/dist/app/cli/index.js.map +1 -1
  11. package/dist/app/commands/doctor.d.ts +1 -0
  12. package/dist/app/commands/doctor.d.ts.map +1 -1
  13. package/dist/app/commands/doctor.js +62 -12
  14. package/dist/app/commands/doctor.js.map +1 -1
  15. package/dist/app/commands/init.d.ts.map +1 -1
  16. package/dist/app/commands/init.js +1 -1
  17. package/dist/app/commands/init.js.map +1 -1
  18. package/dist/app/commands/update.d.ts.map +1 -1
  19. package/dist/app/commands/update.js +10 -5
  20. package/dist/app/commands/update.js.map +1 -1
  21. package/dist/domains/comet-classic/classic-guard.d.ts.map +1 -1
  22. package/dist/domains/comet-classic/classic-guard.js +44 -83
  23. package/dist/domains/comet-classic/classic-guard.js.map +1 -1
  24. package/dist/domains/comet-classic/classic-hook-guard.d.ts.map +1 -1
  25. package/dist/domains/comet-classic/classic-hook-guard.js +84 -9
  26. package/dist/domains/comet-classic/classic-hook-guard.js.map +1 -1
  27. package/dist/domains/comet-classic/classic-runtime-run.d.ts.map +1 -1
  28. package/dist/domains/comet-classic/classic-runtime-run.js +231 -4
  29. package/dist/domains/comet-classic/classic-runtime-run.js.map +1 -1
  30. package/dist/domains/comet-classic/classic-state-command.d.ts.map +1 -1
  31. package/dist/domains/comet-classic/classic-state-command.js +1 -3
  32. package/dist/domains/comet-classic/classic-state-command.js.map +1 -1
  33. package/dist/domains/comet-classic/classic-state.d.ts +1 -3
  34. package/dist/domains/comet-classic/classic-state.d.ts.map +1 -1
  35. package/dist/domains/comet-classic/classic-state.js +0 -6
  36. package/dist/domains/comet-classic/classic-state.js.map +1 -1
  37. package/dist/domains/comet-classic/classic-store.d.ts.map +1 -1
  38. package/dist/domains/comet-classic/classic-store.js +24 -7
  39. package/dist/domains/comet-classic/classic-store.js.map +1 -1
  40. package/dist/domains/dashboard/web/index.html +14 -14
  41. package/dist/domains/skill/platform-install.d.ts +2 -10
  42. package/dist/domains/skill/platform-install.d.ts.map +1 -1
  43. package/dist/domains/skill/platform-install.js +84 -19
  44. package/dist/domains/skill/platform-install.js.map +1 -1
  45. package/package.json +4 -4
@@ -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 document = await readDocument(path4.join(changeDir, ".comet.yaml"));
8078
- const doc = documentRecord(document);
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
- const file = path4.join(changeDir, ".comet.yaml");
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
- return parseClassicStateDocument(doc, run);
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
- throw new Error("Comet classic runtime package is not installed");
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
- if (projection.unknownKeys.length > 0) {
9016
- throw new Error(
9017
- `Invalid Classic state: unknown field(s): ${projection.unknownKeys.join(", ")}`
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(changeDir) {
10343
+ async function buildPasses() {
10155
10344
  if (process.env.COMET_SKIP_BUILD === "1") return { status: 0, output: "" };
10156
- const configured = await projectConfigValue("build_command", changeDir);
10157
- if (configured) return runCommandString(configured);
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(changeDir) {
10362
+ async function verificationCommandPasses() {
10173
10363
  if (process.env.COMET_SKIP_BUILD === "1") return { status: 0, output: "" };
10174
- const configured = await projectConfigValue("verify_command", changeDir);
10175
- if (configured) return runCommandString(configured);
10176
- return buildPasses(changeDir);
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(changeDir);
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(changeDir);
10772
+ const verifyResult = await verificationCommandPasses();
10582
10773
  return verifyResult.status === 0 ? pass() : fail(verifyResult.output);
10583
10774
  }),
10584
10775
  check(
@@ -11278,6 +11469,60 @@ function blocksSourceWrites(governing) {
11278
11469
  }
11279
11470
  return governing.phase === "build" && governing.classic?.workflow === "full" && !governing.classic.designDoc;
11280
11471
  }
11472
+ function isSuperpowersArtifactPath(relativePath2) {
11473
+ return relativePath2.startsWith("docs/superpowers/");
11474
+ }
11475
+ function allowsSuperpowersArtifacts(governing) {
11476
+ return governing.phase === "design" || governing.phase === "build" || governing.phase === "verify";
11477
+ }
11478
+ function governingChangeName(governing) {
11479
+ return governing.changeDir ? path16.basename(governing.changeDir) : null;
11480
+ }
11481
+ var SUPERPOWERS_ARTIFACT_SUFFIXES = /* @__PURE__ */ new Set([
11482
+ "design",
11483
+ "plan",
11484
+ "verify",
11485
+ "verification",
11486
+ "verification-report",
11487
+ "report"
11488
+ ]);
11489
+ function escapeRegex(value) {
11490
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
11491
+ }
11492
+ function matchesRecordedSuperpowersArtifact(relativePath2, governing) {
11493
+ const artifactPaths = [
11494
+ governing.classic?.designDoc,
11495
+ governing.classic?.plan,
11496
+ governing.classic?.verificationReport
11497
+ ];
11498
+ return artifactPaths.some(
11499
+ (artifactPath) => artifactPath && normalized(artifactPath) === relativePath2
11500
+ );
11501
+ }
11502
+ function matchesSuperpowersArtifactName(relativePath2, changeName) {
11503
+ const fileName = relativePath2.split("/").at(-1) ?? relativePath2;
11504
+ const stem = fileName.replace(/\.[^.]+$/u, "");
11505
+ if (stem === changeName) return true;
11506
+ const suffixes = [...SUPERPOWERS_ARTIFACT_SUFFIXES].map(escapeRegex).join("|");
11507
+ const pattern = new RegExp(`(^|[-_.])${escapeRegex(changeName)}[-_.](${suffixes})$`, "u");
11508
+ return pattern.test(stem);
11509
+ }
11510
+ async function superpowersArtifactGoverningChange(relativePath2, projectRoot) {
11511
+ const active = await activeChanges(projectRoot);
11512
+ const recorded = active.find(
11513
+ (governing) => matchesRecordedSuperpowersArtifact(relativePath2, governing)
11514
+ );
11515
+ if (recorded) return recorded;
11516
+ const eligible = active.filter(allowsSuperpowersArtifacts);
11517
+ const named = eligible.filter((governing) => {
11518
+ const name = governingChangeName(governing);
11519
+ return name !== null && matchesSuperpowersArtifactName(relativePath2, name);
11520
+ }).sort(
11521
+ (a, b) => (governingChangeName(b)?.length ?? 0) - (governingChangeName(a)?.length ?? 0)
11522
+ )[0];
11523
+ if (named) return named;
11524
+ return null;
11525
+ }
11281
11526
  async function repoSourceGoverningChange(projectRoot) {
11282
11527
  const active = await activeChanges(projectRoot);
11283
11528
  return active.find(blocksSourceWrites) ?? active[0] ?? null;
@@ -11298,13 +11543,19 @@ async function governingChange(relativePath2, projectRoot) {
11298
11543
  return { changeDir, phase: "open", classic: null, archived: false };
11299
11544
  }
11300
11545
  }
11546
+ if (isSuperpowersArtifactPath(relativePath2)) {
11547
+ const superpowers = await superpowersArtifactGoverningChange(relativePath2, projectRoot);
11548
+ if (superpowers) return { ...superpowers, superpowersArtifact: "matched" };
11549
+ const fallback = await repoSourceGoverningChange(projectRoot);
11550
+ return fallback ? { ...fallback, superpowersArtifact: "unmatched" } : null;
11551
+ }
11301
11552
  return repoSourceGoverningChange(projectRoot);
11302
11553
  }
11303
11554
  function isRootMarkdown(relativePath2) {
11304
11555
  return !relativePath2.includes("/") && relativePath2.endsWith(".md");
11305
11556
  }
11306
11557
  function isCometConfig(relativePath2) {
11307
- return relativePath2 === ".comet.yaml" || relativePath2 === "comet.yaml" || relativePath2 === ".comet.yml" || relativePath2 === "comet.yml" || relativePath2.startsWith(".comet/") || relativePath2.includes("/.comet/");
11558
+ return relativePath2.startsWith(".comet/") || relativePath2.includes("/.comet/");
11308
11559
  }
11309
11560
  function isSuperpowersWorkspace(relativePath2) {
11310
11561
  return relativePath2 === ".superpowers" || relativePath2.startsWith(".superpowers/");
@@ -11383,6 +11634,25 @@ function blockedMissingDesignDoc(relativePath2) {
11383
11634
  ].join("\n")
11384
11635
  );
11385
11636
  }
11637
+ function blockedUnmatchedSuperpowersArtifact(relativePath2, phase) {
11638
+ return result(
11639
+ 2,
11640
+ [
11641
+ "",
11642
+ "╔══════════════════════════════════════════╗",
11643
+ "║ COMET PHASE GUARD — WRITE BLOCKED ║",
11644
+ "╚══════════════════════════════════════════╝",
11645
+ "",
11646
+ ` Current phase: ${phase}`,
11647
+ ` Target file: ${relativePath2}`,
11648
+ "",
11649
+ " BLOCKED: unmatched Superpowers artifact",
11650
+ " This docs/superpowers/ path does not match any active change artifact",
11651
+ " NEXT: record the artifact path in .comet.yaml or include the change name in the artifact filename",
11652
+ ""
11653
+ ].join("\n")
11654
+ );
11655
+ }
11386
11656
  var classicHookGuardCommand = async (args) => {
11387
11657
  const projectRoot = parseProjectRoot(args);
11388
11658
  const target = inputTarget();
@@ -11414,8 +11684,13 @@ var classicHookGuardCommand = async (args) => {
11414
11684
  const phase = governing.phase;
11415
11685
  const openSpec = openSpecAllowed(relativePath2, phase);
11416
11686
  if (openSpec) return allowed(openSpec);
11417
- if (relativePath2.startsWith("docs/superpowers/") && (phase === "design" || phase === "build" || phase === "verify")) {
11418
- return allowed(`${relativePath2} (phase: ${phase}, superpowers)`);
11687
+ if (isSuperpowersArtifactPath(relativePath2)) {
11688
+ if (governing.superpowersArtifact === "matched" && allowsSuperpowersArtifacts(governing)) {
11689
+ return allowed(`${relativePath2} (phase: ${phase}, superpowers)`);
11690
+ }
11691
+ if (governing.superpowersArtifact === "unmatched") {
11692
+ return blockedUnmatchedSuperpowersArtifact(relativePath2, phase);
11693
+ }
11419
11694
  }
11420
11695
  if (phase === "build" && governing.classic?.workflow === "full" && !governing.classic.designDoc) {
11421
11696
  return blockedMissingDesignDoc(relativePath2);
@@ -12026,8 +12301,6 @@ function sparseClassicState(record) {
12026
12301
  verifiedAt: nullableRecordString(record, "verified_at"),
12027
12302
  archived: nullableRecordBoolean(record, "archived") ?? false,
12028
12303
  directOverride: nullableRecordBoolean(record, "direct_override"),
12029
- buildCommand: nullableRecordString(record, "build_command"),
12030
- verifyCommand: nullableRecordString(record, "verify_command"),
12031
12304
  handoffContext: nullableRecordString(record, "handoff_context"),
12032
12305
  handoffHash: nullableRecordString(record, "handoff_hash"),
12033
12306
  classicProfile: enumRecordValue(record, "classic_profile", PROFILES, workflow),
@@ -12210,7 +12483,7 @@ async function init(output, name, workflow) {
12210
12483
  archived: false
12211
12484
  });
12212
12485
  await atomicWrite2(file, document.toString());
12213
- output.stderr.push(green4(`Initialized: ${label}/.comet.yaml (workflow=${workflow})`));
12486
+ output.stdout.push(green4(`Initialized: ${label}/.comet.yaml (workflow=${workflow})`));
12214
12487
  }
12215
12488
  async function requirePhase(name, expected) {
12216
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 reads project command configuration first:
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