@rashidee/co2 1.3.12 → 1.3.14
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/dist/.co2-dat/app.db +0 -0
- package/dist/.co2-dat/app.db-shm +0 -0
- package/dist/.co2-dat/app.db-wal +0 -0
- package/dist/index.js +145 -9
- package/package.json +1 -1
- package/plugin/.claude-plugin/marketplace.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/README.md +3 -1
- package/plugin/SKILLS.md +5 -2
- package/plugin/skills/conductor-feature-develop/SKILL.md +13 -0
- package/plugin/skills/conductor-feature-prepare/SKILL.md +39 -11
- package/plugin/skills/specgen-custom/SKILL.md +442 -0
- package/plugin/skills/specgen-custom/references/spec-template.md +271 -0
- package/plugin/skills/specgen-custom/references/stack-doc-template.md +154 -0
- package/static/assets/{abnfDiagram-VRR7QNED-Qm0Wk_H_.js → abnfDiagram-VRR7QNED-D8RafkEP.js} +1 -1
- package/static/assets/{arc-Cfk6Kt0D.js → arc-Coo8djJL.js} +1 -1
- package/static/assets/{architectureDiagram-ZJ3FMSHR-DQTlZaUK.js → architectureDiagram-ZJ3FMSHR-D7fGTL3b.js} +1 -1
- package/static/assets/{blockDiagram-677ZJIJ3-D-4ibdoX.js → blockDiagram-677ZJIJ3-CjI7s86S.js} +1 -1
- package/static/assets/{c4Diagram-LMCZKHZV-CHuqcdJm.js → c4Diagram-LMCZKHZV-Ao3qCcFi.js} +1 -1
- package/static/assets/channel-Ci11n54j.js +1 -0
- package/static/assets/{chunk-2Q5K7J3B-MB9pWQe1.js → chunk-2Q5K7J3B-lUK_s3cG.js} +1 -1
- package/static/assets/{chunk-32BRIVSS-BttYlwU1.js → chunk-32BRIVSS-PA889NNs.js} +1 -1
- package/static/assets/{chunk-5VM5RSS4-oyt0Tagd.js → chunk-5VM5RSS4-CxACsgKF.js} +1 -1
- package/static/assets/{chunk-EX3LRPZG-DaTxdTWl.js → chunk-EX3LRPZG-BuSTKl3j.js} +1 -1
- package/static/assets/{chunk-JWPE2WC7-CNgDxPAD.js → chunk-JWPE2WC7-CuKFDdbM.js} +1 -1
- package/static/assets/{chunk-MOJQB5TN-CoAudO3B.js → chunk-MOJQB5TN-CVXUJZuG.js} +1 -1
- package/static/assets/{chunk-RYQCIY6F-BrkDEEha.js → chunk-RYQCIY6F-DrMs0YPQ.js} +1 -1
- package/static/assets/{chunk-V7JOEXUC-D14rGvBO.js → chunk-V7JOEXUC-DJopV6W1.js} +1 -1
- package/static/assets/{chunk-VR4S4FIN-CAtas2vF.js → chunk-VR4S4FIN-CcNBYiui.js} +1 -1
- package/static/assets/{chunk-XXDRQBXY-DcVAnNUQ.js → chunk-XXDRQBXY-CCFhxbN4.js} +1 -1
- package/static/assets/classDiagram-OUVF2IWQ-D8JGj9_e.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-D8JGj9_e.js +1 -0
- package/static/assets/{cose-bilkent-JH36ORCC-B7y4MI6Z.js → cose-bilkent-JH36ORCC-DXoH0ywa.js} +1 -1
- package/static/assets/{cynefin-VYW2F7L2-D2mnk-iJ.js → cynefin-VYW2F7L2-DH8W0rE6.js} +1 -1
- package/static/assets/{cynefinDiagram-TSTJHNR4-DNzQJhG7.js → cynefinDiagram-TSTJHNR4-BW_IHnv8.js} +1 -1
- package/static/assets/{dagre-VKFMJZFB-NMSyTo8N.js → dagre-VKFMJZFB-Biw6PsWK.js} +1 -1
- package/static/assets/{diagram-FQU43EPY-CdqS5Ike.js → diagram-FQU43EPY-TtNOYp5j.js} +1 -1
- package/static/assets/{diagram-G47NLZAW-Bsb4T5JI.js → diagram-G47NLZAW-CQk6Jt_6.js} +1 -1
- package/static/assets/{diagram-NH7WQ7WH-Z-PyzBSC.js → diagram-NH7WQ7WH-DTY1BO9B.js} +1 -1
- package/static/assets/{diagram-OA4YK3LP-BZBTYbxg.js → diagram-OA4YK3LP-DEnO6Eyl.js} +1 -1
- package/static/assets/{diagram-WEI45ONY-BL2LA5H_.js → diagram-WEI45ONY-WKF4avWj.js} +1 -1
- package/static/assets/{ebnfDiagram-CCIWWBDH-BGtukMeo.js → ebnfDiagram-CCIWWBDH-K4uJ1i6v.js} +1 -1
- package/static/assets/{erDiagram-Q63AITRT-B6OJ4YCl.js → erDiagram-Q63AITRT-B4wRa3fY.js} +1 -1
- package/static/assets/{flowDiagram-23GEKE2U-BBkMgJ1i.js → flowDiagram-23GEKE2U-SnSUCnHl.js} +1 -1
- package/static/assets/{ganttDiagram-NO4QXBWP-Cv8KHFBD.js → ganttDiagram-NO4QXBWP-CsdfAP2-.js} +1 -1
- package/static/assets/{gitGraphDiagram-IHSO6WYX-hYxDNjWx.js → gitGraphDiagram-IHSO6WYX-C8xab2KC.js} +1 -1
- package/static/assets/{index-DKI3BgkF.css → index-D58A6Kf1.css} +1 -1
- package/static/assets/{index-DnYGyYET.js → index-D74-2ULy.js} +93 -93
- package/static/assets/{infoDiagram-FWYZ7A6U-WuQIiedv.js → infoDiagram-FWYZ7A6U-CjUSYyJV.js} +1 -1
- package/static/assets/{ishikawaDiagram-FXEZZL3T-BJGOD-IY.js → ishikawaDiagram-FXEZZL3T-DGEbhYY4.js} +1 -1
- package/static/assets/{journeyDiagram-5HDEW3XC-Ba_NXseS.js → journeyDiagram-5HDEW3XC-Bn8WZeF-.js} +1 -1
- package/static/assets/{kanban-definition-HUTT4EX6-C5RMm9uF.js → kanban-definition-HUTT4EX6-DD9QNPz9.js} +1 -1
- package/static/assets/{linear-CLKppNoj.js → linear-CREoNC-Z.js} +1 -1
- package/static/assets/{mindmap-definition-LN4V7U3C-ClSG_qmQ.js → mindmap-definition-LN4V7U3C-BLCKbp6n.js} +1 -1
- package/static/assets/{pegDiagram-2B236MQR-C_JEZqk3.js → pegDiagram-2B236MQR-DLyLfiFi.js} +1 -1
- package/static/assets/{pieDiagram-ENE6RG2P-D0lx7wDi.js → pieDiagram-ENE6RG2P-B7v1-Mow.js} +1 -1
- package/static/assets/{quadrantDiagram-ABIIQ3AL-BBKy0PVs.js → quadrantDiagram-ABIIQ3AL-Dnt728yI.js} +1 -1
- package/static/assets/{railroadDiagram-RFXS5EU6-C1Badu5q.js → railroadDiagram-RFXS5EU6-BZ0m-G9m.js} +1 -1
- package/static/assets/{requirementDiagram-TGXJPOKE-1j59jBQH.js → requirementDiagram-TGXJPOKE-3SCMjIUR.js} +1 -1
- package/static/assets/{sankeyDiagram-HTMAVEWB-BLR5S8aZ.js → sankeyDiagram-HTMAVEWB-BoK8Wh3S.js} +1 -1
- package/static/assets/{sequenceDiagram-DBY2YBRQ-BlqdK4qm.js → sequenceDiagram-DBY2YBRQ-B89bqSYK.js} +1 -1
- package/static/assets/{sizeCapture-X5ZJPWSS-C27ndGIT.js → sizeCapture-X5ZJPWSS-BVTk4iE2.js} +1 -1
- package/static/assets/{stateDiagram-2N3HPSRC-DOu4536_.js → stateDiagram-2N3HPSRC-Q5DLz7JU.js} +1 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-CU_q9WoS.js +1 -0
- package/static/assets/{swimlanes-5IMT3BWC-BpGitR9A.js → swimlanes-5IMT3BWC-1LqBZGZu.js} +2 -2
- package/static/assets/swimlanesDiagram-G3AALYLV-CilhyDV0.js +8 -0
- package/static/assets/{timeline-definition-FHXFAJF6-JIvdjH7z.js → timeline-definition-FHXFAJF6-Bk8mm9PQ.js} +1 -1
- package/static/assets/{vennDiagram-L72KCM5P-DulnqLWm.js → vennDiagram-L72KCM5P-qS4AIIXn.js} +1 -1
- package/static/assets/{wardleyDiagram-EHGQE667--MhZNL8M.js → wardleyDiagram-EHGQE667-BjnQ0zER.js} +1 -1
- package/static/assets/{xychartDiagram-FW5EYKEG-CjnNgnMQ.js → xychartDiagram-FW5EYKEG-Xtl5bYjl.js} +1 -1
- package/static/index.html +2 -2
- package/static/assets/channel-H9DRrduO.js +0 -1
- package/static/assets/classDiagram-OUVF2IWQ-DRNPM383.js +0 -1
- package/static/assets/classDiagram-v2-EOCWNBFH-DRNPM383.js +0 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-DvP1ZCp8.js +0 -1
- package/static/assets/swimlanesDiagram-G3AALYLV-B5cf18Ey.js +0 -8
package/dist/.co2-dat/app.db
CHANGED
|
Binary file
|
package/dist/.co2-dat/app.db-shm
CHANGED
|
Binary file
|
package/dist/.co2-dat/app.db-wal
CHANGED
|
Binary file
|
package/dist/index.js
CHANGED
|
@@ -6809,7 +6809,7 @@ var {
|
|
|
6809
6809
|
// package.json
|
|
6810
6810
|
var package_default = {
|
|
6811
6811
|
name: "@rashidee/co2",
|
|
6812
|
-
version: "1.3.
|
|
6812
|
+
version: "1.3.14",
|
|
6813
6813
|
description: "Compound Context Studio \u2014 self-hosted team context authoring for CO2 projects",
|
|
6814
6814
|
type: "module",
|
|
6815
6815
|
license: "MIT",
|
|
@@ -25398,7 +25398,7 @@ function scanProject(db, workspacePath, folderName) {
|
|
|
25398
25398
|
import { join as join22, relative as relative2 } from "path";
|
|
25399
25399
|
|
|
25400
25400
|
// ../server/src/features/workflow/service.ts
|
|
25401
|
-
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
|
|
25401
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync15, statSync as statSync6, writeFileSync as writeFileSync13 } from "fs";
|
|
25402
25402
|
import { join as join21, relative, resolve as resolve2, sep } from "path";
|
|
25403
25403
|
var ACTIVE_RUN_STATUSES = ["pending", "in_progress"];
|
|
25404
25404
|
function svcError14(code, status) {
|
|
@@ -25855,6 +25855,125 @@ function rescanWorkflowRun(db, workspacePath, workflowMarkdownId, watcherInterva
|
|
|
25855
25855
|
}
|
|
25856
25856
|
return getWorkflowRun(db, workflowMarkdownId, workspacePath);
|
|
25857
25857
|
}
|
|
25858
|
+
var DISCOVERABLE_CONDUCTOR_TYPES = ["prepare", "develop", "quality"];
|
|
25859
|
+
function rescanProjectWorkflows(db, workspacePath, projectId, user, watcherIntervalMs) {
|
|
25860
|
+
const project = db.select().from(projects).where(eq(projects.id, projectId)).get();
|
|
25861
|
+
if (!project) throw svcError14("PROJECT_NOT_FOUND", 404);
|
|
25862
|
+
const projectDir2 = join21(workspacePath, project.workspaceFolderName);
|
|
25863
|
+
const nonTerminal = db.select({ id: conductorRuns.id, wfmId: conductorRuns.workflowMarkdownId }).from(conductorRuns).where(
|
|
25864
|
+
and(
|
|
25865
|
+
eq(conductorRuns.projectId, projectId),
|
|
25866
|
+
inArray(conductorRuns.status, [...ACTIVE_RUN_STATUSES])
|
|
25867
|
+
)
|
|
25868
|
+
).all();
|
|
25869
|
+
const rescannedRuns = nonTerminal.length;
|
|
25870
|
+
const wfmIds = new Set(nonTerminal.map((r) => r.wfmId).filter((x) => x !== null));
|
|
25871
|
+
for (const wfmId of wfmIds) rescanWorkflowRun(db, workspacePath, wfmId, watcherIntervalMs);
|
|
25872
|
+
const apps = db.select({ id: applications.id, name: applications.name }).from(applications).where(and(eq(applications.projectId, projectId), eq(applications.isDeleted, 0))).all();
|
|
25873
|
+
const currentVersionId = ensureBaselineVersion(db, projectId, user.id);
|
|
25874
|
+
const semver = db.select({ semver: prdVersions.semver }).from(prdVersions).where(eq(prdVersions.id, currentVersionId)).get()?.semver ?? null;
|
|
25875
|
+
let discoveredRuns = 0;
|
|
25876
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
25877
|
+
for (const app of apps) {
|
|
25878
|
+
for (const conductorType of DISCOVERABLE_CONDUCTOR_TYPES) {
|
|
25879
|
+
const trackingFilePath = resolveRunTrackingFilePath(projectDir2, app.name, conductorType);
|
|
25880
|
+
const fileStatus = parseTrackingStatus(trackingFilePath);
|
|
25881
|
+
if (fileStatus === "unknown") continue;
|
|
25882
|
+
const aliases = conductorType === "quality" ? ["quality", "defect"] : [conductorType];
|
|
25883
|
+
const known = db.select({
|
|
25884
|
+
status: conductorRuns.status,
|
|
25885
|
+
prdVersionId: conductorRuns.prdVersionId,
|
|
25886
|
+
createdAt: conductorRuns.createdAt,
|
|
25887
|
+
completedAt: conductorRuns.completedAt
|
|
25888
|
+
}).from(conductorRuns).where(
|
|
25889
|
+
and(
|
|
25890
|
+
eq(conductorRuns.applicationId, app.id),
|
|
25891
|
+
inArray(conductorRuns.conductorType, aliases)
|
|
25892
|
+
)
|
|
25893
|
+
).all();
|
|
25894
|
+
const knownForVersion = known.filter(
|
|
25895
|
+
(r) => r.prdVersionId === null || r.prdVersionId === currentVersionId
|
|
25896
|
+
);
|
|
25897
|
+
if (fileStatus === "COMPLETED" && knownForVersion.some((r) => r.status === "completed"))
|
|
25898
|
+
continue;
|
|
25899
|
+
if (fileStatus === "IN PROGRESS" && known.some((r) => r.status === "pending" || r.status === "in_progress"))
|
|
25900
|
+
continue;
|
|
25901
|
+
if (fileStatus === "FAILED" && knownForVersion.some((r) => r.status === "failed")) continue;
|
|
25902
|
+
let mtimeMs;
|
|
25903
|
+
try {
|
|
25904
|
+
mtimeMs = statSync6(trackingFilePath).mtimeMs;
|
|
25905
|
+
} catch {
|
|
25906
|
+
continue;
|
|
25907
|
+
}
|
|
25908
|
+
const latestKnownMs = known.reduce(
|
|
25909
|
+
(acc, r) => Math.max(
|
|
25910
|
+
acc,
|
|
25911
|
+
Date.parse(r.createdAt) || 0,
|
|
25912
|
+
r.completedAt ? Date.parse(r.completedAt) || 0 : 0
|
|
25913
|
+
),
|
|
25914
|
+
0
|
|
25915
|
+
);
|
|
25916
|
+
if (latestKnownMs > 0 && mtimeMs <= latestKnownMs) continue;
|
|
25917
|
+
const mtimeIso = new Date(mtimeMs).toISOString();
|
|
25918
|
+
const wfm = db.insert(workflowMarkdowns).values({
|
|
25919
|
+
projectId,
|
|
25920
|
+
conductorType,
|
|
25921
|
+
// The master the discovery was read from — Run History's path link previews it.
|
|
25922
|
+
filePath: relative(projectDir2, trackingFilePath).replace(/\\/g, "/"),
|
|
25923
|
+
versionLabel: semver,
|
|
25924
|
+
createdBy: user.id,
|
|
25925
|
+
createdByName: user.username,
|
|
25926
|
+
createdAt: now,
|
|
25927
|
+
updatedAt: now
|
|
25928
|
+
}).returning({ id: workflowMarkdowns.id }).get();
|
|
25929
|
+
const run = db.insert(conductorRuns).values({
|
|
25930
|
+
projectId,
|
|
25931
|
+
applicationId: app.id,
|
|
25932
|
+
conductorType,
|
|
25933
|
+
prdVersionId: currentVersionId,
|
|
25934
|
+
trackingFilePath,
|
|
25935
|
+
status: fileStatus === "COMPLETED" ? "completed" : fileStatus === "FAILED" ? "failed" : "in_progress",
|
|
25936
|
+
startedBy: user.id,
|
|
25937
|
+
startedAt: mtimeIso,
|
|
25938
|
+
completedAt: fileStatus === "IN PROGRESS" ? null : mtimeIso,
|
|
25939
|
+
workflowMarkdownId: wfm.id,
|
|
25940
|
+
priorPhaseSatisfied: 1,
|
|
25941
|
+
gitOperationStatus: "skipped",
|
|
25942
|
+
// terminal execution — the user owns its git flow
|
|
25943
|
+
createdAt: now
|
|
25944
|
+
}).returning({ id: conductorRuns.id }).get();
|
|
25945
|
+
discoveredRuns++;
|
|
25946
|
+
if (fileStatus === "COMPLETED") {
|
|
25947
|
+
try {
|
|
25948
|
+
registerConductorArtifacts(db, workspacePath, run.id);
|
|
25949
|
+
} catch {
|
|
25950
|
+
}
|
|
25951
|
+
syncDefectRunStatuses(db, run.id);
|
|
25952
|
+
} else if (fileStatus === "IN PROGRESS") {
|
|
25953
|
+
const appName = app.name;
|
|
25954
|
+
startWatcher(
|
|
25955
|
+
db,
|
|
25956
|
+
run.id,
|
|
25957
|
+
() => resolveRunTrackingFilePath(projectDir2, appName, conductorType),
|
|
25958
|
+
user.id,
|
|
25959
|
+
watcherIntervalMs,
|
|
25960
|
+
(id, terminalStatus) => {
|
|
25961
|
+
lastArtifactSweepAt.delete(id);
|
|
25962
|
+
if (terminalStatus === "COMPLETED") {
|
|
25963
|
+
try {
|
|
25964
|
+
registerConductorArtifacts(db, workspacePath, id);
|
|
25965
|
+
} catch {
|
|
25966
|
+
}
|
|
25967
|
+
syncDefectRunStatuses(db, id);
|
|
25968
|
+
}
|
|
25969
|
+
},
|
|
25970
|
+
(id) => sweepRunArtifactsThrottled(db, workspacePath, id)
|
|
25971
|
+
);
|
|
25972
|
+
}
|
|
25973
|
+
}
|
|
25974
|
+
}
|
|
25975
|
+
return { rescannedRuns, discoveredRuns };
|
|
25976
|
+
}
|
|
25858
25977
|
function getWorkflowRun(db, workflowMarkdownId, workspacePath) {
|
|
25859
25978
|
const wfm = db.select().from(workflowMarkdowns).where(eq(workflowMarkdowns.id, workflowMarkdownId)).get();
|
|
25860
25979
|
if (!wfm) throw svcError14("RUN_NOT_FOUND", 404);
|
|
@@ -25952,7 +26071,9 @@ function toDetail(db, projectFolder, wfm, workspacePath) {
|
|
|
25952
26071
|
status: rollupStatus(runs.map((r) => r.status)),
|
|
25953
26072
|
applications: appBreakdown,
|
|
25954
26073
|
filePath: wfm.filePath,
|
|
25955
|
-
|
|
26074
|
+
// Only generated markdowns (workflow/<user>/<ts>.md) carry an Execute prompt — a row
|
|
26075
|
+
// discovered from a tracking master (rescanProjectWorkflows) has nothing to re-run.
|
|
26076
|
+
executeCommand: wfm.filePath.startsWith("workflow/") ? buildExecuteCommand(projectFolder, wfm.filePath) : "",
|
|
25956
26077
|
versionLabel: wfm.versionLabel,
|
|
25957
26078
|
gitOperationStatus: git4?.gitOperationStatus ?? "not_started",
|
|
25958
26079
|
gitBranchCreated: git4?.gitBranchCreated ?? null,
|
|
@@ -26469,7 +26590,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
26469
26590
|
}
|
|
26470
26591
|
|
|
26471
26592
|
// ../server/src/features/shared-context/service.ts
|
|
26472
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as
|
|
26593
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync7, writeFileSync as writeFileSync14 } from "fs";
|
|
26473
26594
|
import { join as join23, resolve as resolve3, sep as sep2 } from "path";
|
|
26474
26595
|
function svcError16(code, status) {
|
|
26475
26596
|
return Object.assign(new Error(code), { code, status });
|
|
@@ -26527,7 +26648,7 @@ function walk(dir, relBase) {
|
|
|
26527
26648
|
children: walk(join23(dir, entry.name), rel)
|
|
26528
26649
|
});
|
|
26529
26650
|
} else if (entry.isFile()) {
|
|
26530
|
-
const stat =
|
|
26651
|
+
const stat = statSync7(join23(dir, entry.name));
|
|
26531
26652
|
nodes.push({
|
|
26532
26653
|
name: entry.name,
|
|
26533
26654
|
path: rel,
|
|
@@ -26566,7 +26687,7 @@ function uploadFile(db, workspacePath, projectId, folder, file) {
|
|
|
26566
26687
|
function getFileContent(db, workspacePath, projectId, relPath) {
|
|
26567
26688
|
const root = resolveSharedRoot(db, workspacePath, projectId);
|
|
26568
26689
|
const full = safeResolve(root, relPath);
|
|
26569
|
-
if (!existsSync21(full) || !
|
|
26690
|
+
if (!existsSync21(full) || !statSync7(full).isFile()) throw svcError16("NOT_FOUND", 404);
|
|
26570
26691
|
const name = relPath.split("/").pop() ?? relPath;
|
|
26571
26692
|
return { mimeType: mimeForName(name), bytes: readFileSync16(full), name };
|
|
26572
26693
|
}
|
|
@@ -26574,7 +26695,7 @@ function saveMarkdown(db, workspacePath, projectId, relPath, content) {
|
|
|
26574
26695
|
if (mimeForName(relPath) !== "text/markdown") throw svcError16("NOT_MARKDOWN", 400);
|
|
26575
26696
|
const root = resolveSharedRoot(db, workspacePath, projectId);
|
|
26576
26697
|
const full = safeResolve(root, relPath);
|
|
26577
|
-
if (!existsSync21(full) || !
|
|
26698
|
+
if (!existsSync21(full) || !statSync7(full).isFile()) throw svcError16("NOT_FOUND", 404);
|
|
26578
26699
|
writeFileSync14(full, content, "utf8");
|
|
26579
26700
|
}
|
|
26580
26701
|
|
|
@@ -27149,6 +27270,21 @@ function workflowRoutes(db, workspacePath) {
|
|
|
27149
27270
|
return errorResponse16(c, e);
|
|
27150
27271
|
}
|
|
27151
27272
|
});
|
|
27273
|
+
app.post("/rescan-all", (c) => {
|
|
27274
|
+
const projectId = Number(c.req.query("projectId"));
|
|
27275
|
+
if (Number.isNaN(projectId)) return c.json({ code: "INVALID_ID" }, 400);
|
|
27276
|
+
const user = c.get("user");
|
|
27277
|
+
try {
|
|
27278
|
+
return c.json(
|
|
27279
|
+
rescanProjectWorkflows(db, workspacePath, projectId, {
|
|
27280
|
+
id: user.id,
|
|
27281
|
+
username: user.username
|
|
27282
|
+
})
|
|
27283
|
+
);
|
|
27284
|
+
} catch (e) {
|
|
27285
|
+
return errorResponse16(c, e);
|
|
27286
|
+
}
|
|
27287
|
+
});
|
|
27152
27288
|
app.post("/runs/:id/rescan", (c) => {
|
|
27153
27289
|
const id = Number(c.req.param("id"));
|
|
27154
27290
|
if (Number.isNaN(id)) return c.json({ code: "INVALID_ID" }, 400);
|
|
@@ -27663,7 +27799,7 @@ function loggerMiddleware() {
|
|
|
27663
27799
|
}
|
|
27664
27800
|
|
|
27665
27801
|
// ../server/src/lib/static.ts
|
|
27666
|
-
import { existsSync as existsSync24, readFileSync as readFileSync19, statSync as
|
|
27802
|
+
import { existsSync as existsSync24, readFileSync as readFileSync19, statSync as statSync8 } from "fs";
|
|
27667
27803
|
import { extname, join as join26, normalize, resolve as resolve4, sep as sep3 } from "path";
|
|
27668
27804
|
var MIME = {
|
|
27669
27805
|
".html": "text/html; charset=utf-8",
|
|
@@ -27701,7 +27837,7 @@ function serveSpa(staticDir) {
|
|
|
27701
27837
|
}
|
|
27702
27838
|
const rel = path === "/" ? "/index.html" : decodeURIComponent(path);
|
|
27703
27839
|
const filePath = safeResolve2(staticDir, rel);
|
|
27704
|
-
if (filePath && existsSync24(filePath) &&
|
|
27840
|
+
if (filePath && existsSync24(filePath) && statSync8(filePath).isFile()) {
|
|
27705
27841
|
const body = readFileSync19(filePath);
|
|
27706
27842
|
return c.body(body, 200, {
|
|
27707
27843
|
"Content-Type": MIME[extname(filePath)] ?? "application/octet-stream"
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"name": "co2-skills",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"description": "Compound Context Skills — generates data models, HTML mockups, technical specifications, test specifications, and orchestrates full-stack development from Agile user stories.",
|
|
15
|
-
"version": "1.
|
|
15
|
+
"version": "1.7.0",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "Rashidee M.Rashid"
|
|
18
18
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "co2-skills",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Compound Context Skills — an opinionated context engineering methodology for AI-driven software development. Generates data models, HTML mockups, technical specifications, test specifications, and orchestrates full-stack development from Agile user stories.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rashidee M.Rashid"
|
package/plugin/README.md
CHANGED
|
@@ -229,6 +229,7 @@ Example skill invocation for technical specification generation:
|
|
|
229
229
|
/specgen-flutter-riverpod <app_name> ## For generating Flutter 3 mobile application technical specification with Riverpod, Hive, Dio, go_router and Firebase Messaging.
|
|
230
230
|
/specgen-ts-cli <app_name> ## For generating Node.js CLI application technical specification with TypeScript, Commander.js and tsup.
|
|
231
231
|
/specgen-node-cli-web <app_name> ## For generating self-hosted Node.js web application technical specification distributed as a global npm CLI — Hono, embedded React SPA, Drizzle + SQLite, hand-rolled sessions and tsup.
|
|
232
|
+
/specgen-custom <app_name> ## For generating a technical specification for an arbitrary user-defined stack from a custom stack spec markdown (also the automatic fallback when no specgen matches).
|
|
232
233
|
~~~
|
|
233
234
|
|
|
234
235
|
- Input:
|
|
@@ -556,7 +557,7 @@ We welcome contributions from the community! Here are some ways you can contribu
|
|
|
556
557
|
|
|
557
558
|
## Specification Generator Skills (`specgen-*`)
|
|
558
559
|
|
|
559
|
-
This is the **highest impact area** for contribution. The CO2 workflow is designed to be technology-agnostic, but each `specgen-*` skill targets a specific technology stack. We currently support:
|
|
560
|
+
This is the **highest impact area** for contribution. The CO2 workflow is designed to be technology-agnostic, but each `specgen-*` skill targets a specific technology stack. Stacks not covered by a dedicated variant are served by `specgen-custom` (driven by a user-authored stack spec markdown), but a contributed stack-specific specgen always produces a deeper specification and remains preferred. We currently support:
|
|
560
561
|
|
|
561
562
|
| Skill | Technology Stack |
|
|
562
563
|
|-------|-----------------|
|
|
@@ -570,6 +571,7 @@ This is the **highest impact area** for contribution. The CO2 workflow is design
|
|
|
570
571
|
| `specgen-ts-cli` | Node.js CLI + TypeScript + Commander.js + tsup + pkg |
|
|
571
572
|
| `specgen-node-cli-web` | Self-hosted Node.js web app as a global npm CLI — Node 22 + commander 12 + Hono 4 + embedded React 19/Vite 6 SPA + Tailwind CSS v4 + shadcn/ui + Drizzle ORM + SQLite + hand-rolled scrypt sessions + tsup + Biome + Vitest + Playwright |
|
|
572
573
|
| `specgen-sdk-java` | Java SDK library — Maven Multi-Release fat JAR (JDK 8 baseline + JDK 11+ overlay) + OkHttp |
|
|
574
|
+
| `specgen-custom` | Any stack described by a user-authored custom stack spec markdown (authoring template: `skills/specgen-custom/references/stack-doc-template.md`) — an explicit declaration overrides the built-in variants; also the automatic fallback when no variant matches |
|
|
573
575
|
|
|
574
576
|
We are looking for contributors to create new `specgen-*` skills for other technology stacks, such as:
|
|
575
577
|
|
package/plugin/SKILLS.md
CHANGED
|
@@ -237,7 +237,9 @@ All specgens share the same contract: **Invoke** `/specgen-<variant> <applicatio
|
|
|
237
237
|
UI-bearing stacks); **Output** `context/specification/` — root `SPECIFICATION.md` + one
|
|
238
238
|
self-contained `SPEC.md` per module (or per command for CLI stacks), with complete code
|
|
239
239
|
samples and traceability tables. **Pick exactly ONE variant, matching the application's
|
|
240
|
-
technology stack** (declared in `CLAUDE.md` / PRD Architecture Principle)
|
|
240
|
+
technology stack** (declared in `CLAUDE.md` / PRD Architecture Principle). A custom stack
|
|
241
|
+
spec declared as ``Stack per custom stack spec at `<path>` `` always wins over the built-in
|
|
242
|
+
variants, and `specgen-custom` is the fallback when nothing matches:
|
|
241
243
|
|
|
242
244
|
| Skill | Pick when the application is… |
|
|
243
245
|
|-------|-------------------------------|
|
|
@@ -250,6 +252,7 @@ technology stack** (declared in `CLAUDE.md` / PRD Architecture Principle):
|
|
|
250
252
|
| `specgen-ts-cli` | Distributable Node.js CLI **tool** (TypeScript 5, Commander 12, tsup, Zod, chalk/ora; optional prompts, conf/cosmiconfig, SQLite, execa, got, @yao-pkg/pkg binaries) — terminal-only, no web UI |
|
|
251
253
|
| `specgen-node-cli-web` | Self-hosted **web application distributed as a global npm CLI** — installed `npm i -g`, started `<app> start`, opened at `http://IP:PORT`. Single Node 22 process: Hono 4 API + embedded pre-built React 19/Vite 6 SPA (Tailwind v4, shadcn/ui), Drizzle + SQLite, hand-rolled scrypt sessions with first-run admin + forced password change, tsup, Biome, Vitest + Playwright with built-binary smoke test |
|
|
252
254
|
| `specgen-sdk-java` | Java SDK **library** wrapping a remote REST API — Maven Multi-Release fat JAR, JDK 8 baseline + JDK 11+ overlay, OkHttp as sole runtime dependency |
|
|
255
|
+
| `specgen-custom` | ANY other stack — driven by a user-authored custom stack spec markdown (declared in PRD.md/CLAUDE.md as ``Stack per custom stack spec at `<path>` ``, authoring template in `references/stack-doc-template.md`). An explicit declaration OVERRIDES the built-in variants; also the automatic fallback (with `[TODO]`-annotated defaults) when no variant matches |
|
|
253
256
|
|
|
254
257
|
> **`specgen-ts-cli` vs `specgen-node-cli-web`:** if the deliverable is a terminal tool with
|
|
255
258
|
> commands and flags → `specgen-ts-cli`. If the deliverable is a browser-accessed web app that
|
|
@@ -406,7 +409,7 @@ implemented** (empty stub — no SKILL.md). Do not invoke; fall back to `specgen
|
|
|
406
409
|
| "prepare everything for development" | `conductor-feature-prepare` |
|
|
407
410
|
| "design the data model / ERD" | `modelgen-relational` or `modelgen-nosql` (by datastore) |
|
|
408
411
|
| "generate mockups / UI screens" | `mockgen-tailwind` or `mockgen-shadcn` (by stack) |
|
|
409
|
-
| "write the technical spec" | the ONE `specgen-*` matching the stack (see table) |
|
|
412
|
+
| "write the technical spec" | the ONE `specgen-*` matching the stack (see table); a declared custom stack spec → `specgen-custom`, which is also the fallback when nothing matches |
|
|
410
413
|
| "generate the test plan" | `testgen-functional` |
|
|
411
414
|
| "set up databases/queues in k8s" | `util-preparek8senv` |
|
|
412
415
|
| "plan deployment / plan cicd" | `util-plancicd` |
|
|
@@ -389,6 +389,11 @@ files to understand what has already been completed.
|
|
|
389
389
|
version (e.g., `${APP_VERSION:1.0.4}`)
|
|
390
390
|
- **config/app.php** (Laravel): Update the default in `env('APP_VERSION', '1.0.0')`
|
|
391
391
|
to the new version
|
|
392
|
+
- **Custom / other stacks** (SPECIFICATION.md generated by `specgen-custom`): Update
|
|
393
|
+
the manifest file/field and environment variable named in SPECIFICATION.md's
|
|
394
|
+
**Application Version Configuration** subsection. If the spec names none, update
|
|
395
|
+
the project's primary manifest and add a `[TODO]` in IMPLEMENTATION_MASTER.md
|
|
396
|
+
noting the gap.
|
|
392
397
|
The version displayed in the application footer (or API info endpoint) MUST reflect
|
|
393
398
|
the new version after this update.
|
|
394
399
|
3. Proceed to Phase 3 (Implementation) for the affected modules.
|
|
@@ -496,6 +501,8 @@ subdirectory. Instead:
|
|
|
496
501
|
then move ALL files (including dotfiles) from that temp directory up to `<source-code-path>/`,
|
|
497
502
|
then remove the empty temp directory. This avoids overwriting the existing `context/` folder.
|
|
498
503
|
- For Spring Boot: Same approach — scaffold into a temp dir, then move files up.
|
|
504
|
+
- For any other stack whose scaffolding tool creates a nested project directory: same
|
|
505
|
+
temp-dir-then-move approach.
|
|
499
506
|
- NEVER use the project slug/name as the target directory if it would create a nested folder.
|
|
500
507
|
|
|
501
508
|
**Scaffolding Checklist** (adapt to the technology stack from SPECIFICATION.md):
|
|
@@ -511,6 +518,9 @@ subdirectory. Instead:
|
|
|
511
518
|
- **Laravel**: Set `version` in `composer.json` and `APP_VERSION` in `.env`
|
|
512
519
|
- **React / Node.js**: Set `version` in `package.json` and `VITE_APP_VERSION` in
|
|
513
520
|
`.env.development` (or `APP_VERSION` in `.env` for Node.js backends)
|
|
521
|
+
- **Custom / other stacks**: Use the manifest file/field and environment variable named in
|
|
522
|
+
SPECIFICATION.md's **Application Version Configuration** subsection (a spec generated by
|
|
523
|
+
`specgen-custom` always names them)
|
|
514
524
|
- The version in the manifest MUST match the version in the environment variable
|
|
515
525
|
- For multi-version processing, this version will be updated during each version increment
|
|
516
526
|
in the Phase 0 resume check
|
|
@@ -1057,6 +1067,9 @@ Extracted from IMPLEMENTATION_MASTER.md execution order and module details.>
|
|
|
1057
1067
|
- For **Spring Boot** apps: use Maven/Gradle commands, `src/main/java` paths, `application.yml` config
|
|
1058
1068
|
- For **Laravel** apps: use Composer/Artisan commands, `app/` paths, `.env` config
|
|
1059
1069
|
- For **REST API** apps: omit view/template sections, focus on API endpoints and Swagger/OpenAPI docs
|
|
1070
|
+
- For **custom stacks** (SPECIFICATION.md generated by `specgen-custom`): every command, path
|
|
1071
|
+
and config reference comes from SPECIFICATION.md's Build, Run & Packaging and Project
|
|
1072
|
+
Structure sections — never from stack assumptions
|
|
1060
1073
|
- Include only sections that are relevant to the actual specification — do NOT add sections for
|
|
1061
1074
|
features that are not part of the spec (e.g., skip messaging section if no messaging is configured)
|
|
1062
1075
|
|
|
@@ -319,8 +319,10 @@ record the result for use throughout the version loop:
|
|
|
319
319
|
decisions and resolved-file-path propagation to mockgen-* and specgen-*. No additional action
|
|
320
320
|
needed; Step 1.5 is the canonical handler.
|
|
321
321
|
- **`# Architecture Principle`** — Already consumed by Step 1.3 (Infer Database Type) and
|
|
322
|
-
Step 1.7 (Infer Technology Stack) as the **primary signal** for routing decisions.
|
|
323
|
-
(
|
|
322
|
+
Step 1.7 (Infer Technology Stack) as the **primary signal** for routing decisions. It may
|
|
323
|
+
also declare a custom stack spec file (``Stack per custom stack spec at `<path>` `` or a
|
|
324
|
+
markdown link to the stack file), which Step 1.7 treats as the **overriding** signal and
|
|
325
|
+
routes to `specgen-custom`. Sub-skills (modelgen-*, specgen-*) read it directly from PRD.md.
|
|
324
326
|
- **`# High Level Process Flow`** — **Detect presence here** so all downstream skills are aware:
|
|
325
327
|
1. Check PRD.md for a `# High Level Process Flow` section. If present, record:
|
|
326
328
|
- The list of named flows (e.g., "Country submission flow", "Worker registration flow")
|
|
@@ -384,6 +386,10 @@ tracker that this skill — and any resumed Ralph Loop iteration — reads to kn
|
|
|
384
386
|
| Spec Generator | TBD | - |
|
|
385
387
|
|
|
386
388
|
> Filled in by the inference steps (1.3, 1.5, 1.7) as the pipeline runs.
|
|
389
|
+
> When a custom stack spec is used, record Technology Stack = "Custom (per `<path>`)" and
|
|
390
|
+
> Spec Generator = "specgen-custom (explicit)" or "specgen-custom (fallback)"; the Source
|
|
391
|
+
> column names the declaration (`PRD.md # Architecture Principle (custom stack spec)`,
|
|
392
|
+
> `CLAUDE.md entry (custom stack spec)`) or `fallback (no specgen matched)`.
|
|
387
393
|
|
|
388
394
|
---
|
|
389
395
|
|
|
@@ -514,31 +520,53 @@ applies to all items regardless of version.
|
|
|
514
520
|
|
|
515
521
|
#### Step 1.7: Infer Technology Stack
|
|
516
522
|
|
|
517
|
-
1. **Check
|
|
523
|
+
1. **Check for an explicit custom stack spec declaration first** (overrides all other signals):
|
|
524
|
+
- In PRD.md `# Architecture Principle`: a statement matching ``Stack per custom stack spec at `<path>` ``
|
|
525
|
+
or a markdown link to a custom stack spec markdown file (e.g., `[STACK.md](reference/STACK.md)`)
|
|
526
|
+
- Else in the application's CLAUDE.md entry (under `# Custom Applications`): the same
|
|
527
|
+
``Stack per custom stack spec at `<path>` `` convention
|
|
528
|
+
- If found: resolve the path (relative to PRD.md first, then to the project root) and verify
|
|
529
|
+
the file exists — if it does NOT exist, **STOP** and report the dangling declaration to the
|
|
530
|
+
user. If it exists, record **Technology Stack = Custom (per `<path>`)** with the resolved
|
|
531
|
+
path and **skip items 2–3** — the stack doc is authoritative.
|
|
532
|
+
2. **Check PRD.md `# Architecture Principle` section** (primary signal):
|
|
518
533
|
- If Architecture Principle explicitly mentions a framework (e.g., "Spring Boot", "Laravel"), use it
|
|
519
534
|
- If it mentions a view engine (e.g., "JTE", "Blade"), use it
|
|
520
535
|
- If it mentions frontend tooling (e.g., "HTMX", "Alpine.js", "Vite"), use it
|
|
521
536
|
- If it mentions architectural style (e.g., "monolithic", "REST API"), use it to disambiguate specgen variant
|
|
522
|
-
|
|
537
|
+
3. **Fallback to CLAUDE.md** (if Architecture Principle is absent or incomplete):
|
|
523
538
|
- Read application details from CLAUDE.md (already in context)
|
|
524
539
|
- Identify the technology stack:
|
|
525
540
|
- Framework (Laravel, Spring Boot, etc.)
|
|
526
541
|
- ORM (Eloquent, JPA/Hibernate, etc.)
|
|
527
542
|
- View engine (Blade, JTE, etc.)
|
|
528
543
|
- Frontend enhancement (HTMX, Alpine.js, etc.)
|
|
529
|
-
|
|
544
|
+
4. Record the inferred technology stack (or the resolved custom stack doc path) for the next step
|
|
530
545
|
|
|
531
546
|
#### Step 1.8: Generate Technical Specification
|
|
532
547
|
|
|
533
548
|
1. **Check if artifacts exist**: List files in `<app_folder>/context/specification/`
|
|
534
549
|
- If `.md` files already exist AND this is NOT a version increment → **SKIP** this step
|
|
535
550
|
- If this IS a version increment, **proceed** to re-invoke the skill with version/module args
|
|
536
|
-
2. Determine the appropriate specification generator
|
|
537
|
-
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
-
|
|
541
|
-
|
|
551
|
+
2. Determine the appropriate specification generator, **in this priority order**:
|
|
552
|
+
- **a. Explicit custom stack spec (from Step 1.7)** — if Step 1.7 resolved a custom stack doc,
|
|
553
|
+
use `specgen-custom` regardless of whether a built-in specgen would also match the stack:
|
|
554
|
+
`Skill(skill: "co2-skills:specgen-custom", args: "<app_folder> [version:<version>] [module:<module>] stack:<resolved-stack-doc-path>")`
|
|
555
|
+
- **b. Built-in specgen match** — otherwise, check available skills matching the `specgen-*`
|
|
556
|
+
pattern (excluding `specgen-custom`) and match against the inferred technology stack.
|
|
557
|
+
Only route to a variant that actually exists as an invokable skill (has a SKILL.md):
|
|
558
|
+
- For Laravel + Eloquent + Blade + HTMX → `Skill(skill: "co2-skills:specgen-laravel-eloquent-bladehtmx", args: "<app_folder> [version:<version>] [module:<module>]")`
|
|
559
|
+
- For Spring Boot + JPA + JTE + HTMX → `Skill(skill: "co2-skills:specgen-spring-jpa-jtehtmx", args: "<app_folder> [version:<version>] [module:<module>]")`
|
|
560
|
+
- For a self-hosted Node.js web app distributed as a global npm CLI (Hono + embedded React SPA + SQLite) → `Skill(skill: "co2-skills:specgen-node-cli-web", args: "<app_folder> [version:<version>] [module:<module>]")`
|
|
561
|
+
- Match the skill name against the inferred technology stack components
|
|
562
|
+
- **c. No match — fallback to specgen-custom** — if NO built-in specgen matches the inferred
|
|
563
|
+
stack, do NOT stop and do NOT force-fit the closest variant. Invoke:
|
|
564
|
+
`Skill(skill: "co2-skills:specgen-custom", args: "<app_folder> [version:<version>] [module:<module>] stack-desc:\"<inferred stack summary from Step 1.7>\"")`
|
|
565
|
+
specgen-custom will first attempt to auto-discover a stack doc at its well-known paths
|
|
566
|
+
(`<app_folder>/context/reference/STACK.md`, `<app_folder>/context/STACK.md`,
|
|
567
|
+
`<root>/shared_context/STACK.md`); if none exists it generates from the inferred stack
|
|
568
|
+
description, annotating every assumption with `[TODO]`. Surface the applied defaults to
|
|
569
|
+
the user in the run summary.
|
|
542
570
|
3. Wait for the skill to complete. The output will be specification files in `<app_folder>/context/specification/`
|
|
543
571
|
|
|
544
572
|
#### Step 1.9: Generate Test Specification
|