@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.
- package/LICENSE +21 -21
- 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 +376 -103
- 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/app/commands/init.d.ts.map +1 -1
- package/dist/app/commands/init.js +1 -1
- package/dist/app/commands/init.js.map +1 -1
- package/dist/app/commands/update.d.ts.map +1 -1
- package/dist/app/commands/update.js +10 -5
- package/dist/app/commands/update.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 +84 -9
- 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/dist/domains/skill/platform-install.d.ts +2 -10
- package/dist/domains/skill/platform-install.d.ts.map +1 -1
- package/dist/domains/skill/platform-install.js +84 -19
- package/dist/domains/skill/platform-install.js.map +1 -1
- 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
|
|
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(
|
|
@@ -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
|
|
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
|
|
11418
|
-
|
|
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.
|
|
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
|
|
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
|
|