@rashidee/co2 1.3.30 → 1.3.32
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-shm +0 -0
- package/dist/.co2-dat/app.db-wal +0 -0
- package/dist/index.js +48 -9
- package/package.json +2 -2
- package/plugin/.claude-plugin/marketplace.json +4 -4
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/skills/util-gencicdscript/SKILL.md +32 -17
- package/plugin/skills/util-gencicdscript/references/cicd-app-template.md +59 -32
- package/plugin/skills/util-plancicd/SKILL.md +65 -24
- package/static/assets/{abnfDiagram-VRR7QNED-DSKqrKDc.js → abnfDiagram-VRR7QNED-Bz30rK4w.js} +1 -1
- package/static/assets/{arc-Do0QnZNS.js → arc-K9O7tO8Y.js} +1 -1
- package/static/assets/{architectureDiagram-ZJ3FMSHR-BrhENYtw.js → architectureDiagram-ZJ3FMSHR-BdrVmNvN.js} +1 -1
- package/static/assets/{blockDiagram-677ZJIJ3-NwgL5_hH.js → blockDiagram-677ZJIJ3-DImeRXuQ.js} +1 -1
- package/static/assets/{c4Diagram-LMCZKHZV-oUcKQjlM.js → c4Diagram-LMCZKHZV-Bj4yyPGH.js} +1 -1
- package/static/assets/channel-Cam27IrS.js +1 -0
- package/static/assets/{chunk-2Q5K7J3B-DRJjUwyl.js → chunk-2Q5K7J3B-BHYOpztr.js} +1 -1
- package/static/assets/{chunk-32BRIVSS-BvjeHxaY.js → chunk-32BRIVSS-C2Wy22tQ.js} +1 -1
- package/static/assets/{chunk-5VM5RSS4-BvDzjAd8.js → chunk-5VM5RSS4-V-_fRucz.js} +1 -1
- package/static/assets/{chunk-EX3LRPZG-bQwyZkNg.js → chunk-EX3LRPZG-DxT6dnNF.js} +1 -1
- package/static/assets/{chunk-JWPE2WC7-k6i1JgiV.js → chunk-JWPE2WC7-BZ9dVylx.js} +1 -1
- package/static/assets/{chunk-MOJQB5TN-mvV9BYhY.js → chunk-MOJQB5TN-B3bjiMAY.js} +1 -1
- package/static/assets/{chunk-RYQCIY6F-Bsq_Xlu-.js → chunk-RYQCIY6F-42f9_hh5.js} +1 -1
- package/static/assets/{chunk-V7JOEXUC-BL8mbu1X.js → chunk-V7JOEXUC-DU7kGh4W.js} +1 -1
- package/static/assets/{chunk-VR4S4FIN-apJkddo6.js → chunk-VR4S4FIN-glWsMWYs.js} +1 -1
- package/static/assets/{chunk-XXDRQBXY-CHpJOuj8.js → chunk-XXDRQBXY-En74ppE-.js} +1 -1
- package/static/assets/classDiagram-OUVF2IWQ-C8kRWfsa.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-C8kRWfsa.js +1 -0
- package/static/assets/{cose-bilkent-JH36ORCC-glgAYR8Q.js → cose-bilkent-JH36ORCC-q5Mo8ofj.js} +1 -1
- package/static/assets/{cynefin-VYW2F7L2-DLa7zwol.js → cynefin-VYW2F7L2-Dzy0NABg.js} +1 -1
- package/static/assets/{cynefinDiagram-TSTJHNR4-pnruKVTm.js → cynefinDiagram-TSTJHNR4-PbQZZaRi.js} +1 -1
- package/static/assets/{dagre-VKFMJZFB-CIGjl3-R.js → dagre-VKFMJZFB-BZU8w1_Z.js} +1 -1
- package/static/assets/{diagram-FQU43EPY-c-mpXCbC.js → diagram-FQU43EPY-Bp-CZLzx.js} +1 -1
- package/static/assets/{diagram-G47NLZAW-BuPrh5jO.js → diagram-G47NLZAW-CVmpgFTR.js} +1 -1
- package/static/assets/{diagram-NH7WQ7WH-1PWVEyHf.js → diagram-NH7WQ7WH-CohligHl.js} +1 -1
- package/static/assets/{diagram-OA4YK3LP-CigJcdPy.js → diagram-OA4YK3LP-DN1i4dDH.js} +1 -1
- package/static/assets/{diagram-WEI45ONY-C5TxARN6.js → diagram-WEI45ONY-HoqeBQvE.js} +1 -1
- package/static/assets/{ebnfDiagram-CCIWWBDH-C_gWcgoL.js → ebnfDiagram-CCIWWBDH-BBKpOoys.js} +1 -1
- package/static/assets/{erDiagram-Q63AITRT-vJ1itd79.js → erDiagram-Q63AITRT-CGdT5vjr.js} +1 -1
- package/static/assets/{flowDiagram-23GEKE2U-CEAcPXPg.js → flowDiagram-23GEKE2U-DEsLeisJ.js} +1 -1
- package/static/assets/{ganttDiagram-NO4QXBWP-BYVqn4qG.js → ganttDiagram-NO4QXBWP-Dmik8C0l.js} +1 -1
- package/static/assets/{gitGraphDiagram-IHSO6WYX-DvbGmSvW.js → gitGraphDiagram-IHSO6WYX-eaEegSaF.js} +1 -1
- package/static/assets/{index-BkSOIYhD.js → index-BWq7zKXB.js} +113 -112
- package/static/assets/{index-DPxMV4aU.css → index-D6Kc-3Ei.css} +1 -1
- package/static/assets/{infoDiagram-FWYZ7A6U-jERFMF3Y.js → infoDiagram-FWYZ7A6U-D_ysDB_l.js} +1 -1
- package/static/assets/{ishikawaDiagram-FXEZZL3T-CQwH43uK.js → ishikawaDiagram-FXEZZL3T-ClxYOJnV.js} +1 -1
- package/static/assets/{journeyDiagram-5HDEW3XC-BTg2GX1B.js → journeyDiagram-5HDEW3XC-CnFPmn-M.js} +1 -1
- package/static/assets/{kanban-definition-HUTT4EX6-g16OnksI.js → kanban-definition-HUTT4EX6-DJsX2G5l.js} +1 -1
- package/static/assets/{linear-ChUnKfcH.js → linear-BWkxWbFl.js} +1 -1
- package/static/assets/{mindmap-definition-LN4V7U3C-CPITAOG_.js → mindmap-definition-LN4V7U3C-D5ENc9VY.js} +1 -1
- package/static/assets/{pegDiagram-2B236MQR-CC_POdNU.js → pegDiagram-2B236MQR-DOJqp1by.js} +1 -1
- package/static/assets/{pieDiagram-ENE6RG2P-CUjx0_TV.js → pieDiagram-ENE6RG2P-g8RqyZcx.js} +1 -1
- package/static/assets/{quadrantDiagram-ABIIQ3AL-CHuqZWI1.js → quadrantDiagram-ABIIQ3AL-BTqvdqkC.js} +1 -1
- package/static/assets/{railroadDiagram-RFXS5EU6-D7z4QxTv.js → railroadDiagram-RFXS5EU6-iq_hwy4b.js} +1 -1
- package/static/assets/{requirementDiagram-TGXJPOKE-Diw77Mxr.js → requirementDiagram-TGXJPOKE-46avlw83.js} +1 -1
- package/static/assets/{sankeyDiagram-HTMAVEWB-fJ5ooN-b.js → sankeyDiagram-HTMAVEWB-CDpxKea5.js} +1 -1
- package/static/assets/{sequenceDiagram-DBY2YBRQ-Xrc6VTEu.js → sequenceDiagram-DBY2YBRQ-DqUd93_A.js} +1 -1
- package/static/assets/{sizeCapture-X5ZJPWSS-DyGMoW5L.js → sizeCapture-X5ZJPWSS-Dg-DnkUa.js} +1 -1
- package/static/assets/{stateDiagram-2N3HPSRC-U44yT3oo.js → stateDiagram-2N3HPSRC-D6BbVr9A.js} +1 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-Dq2xOcdU.js +1 -0
- package/static/assets/{swimlanes-5IMT3BWC-CQCHPefb.js → swimlanes-5IMT3BWC-uMMz-BEY.js} +2 -2
- package/static/assets/swimlanesDiagram-G3AALYLV-BsnXjA98.js +8 -0
- package/static/assets/{timeline-definition-FHXFAJF6-CoiiYudO.js → timeline-definition-FHXFAJF6-xtE5xErE.js} +1 -1
- package/static/assets/{vennDiagram-L72KCM5P-CG5T9Zvi.js → vennDiagram-L72KCM5P-Dy_JaPOh.js} +1 -1
- package/static/assets/{wardleyDiagram-EHGQE667-Bh062b_W.js → wardleyDiagram-EHGQE667-D08p63LT.js} +1 -1
- package/static/assets/{xychartDiagram-FW5EYKEG-CJGSnpSl.js → xychartDiagram-FW5EYKEG-CWXfhK2E.js} +1 -1
- package/static/index.html +2 -2
- package/static/assets/channel-Drj2zueb.js +0 -1
- package/static/assets/classDiagram-OUVF2IWQ-D61dFXyu.js +0 -1
- package/static/assets/classDiagram-v2-EOCWNBFH-D61dFXyu.js +0 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-BTFJeVit.js +0 -1
- package/static/assets/swimlanesDiagram-G3AALYLV-VPI6E8oZ.js +0 -8
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,8 +6809,8 @@ var {
|
|
|
6809
6809
|
// package.json
|
|
6810
6810
|
var package_default = {
|
|
6811
6811
|
name: "@rashidee/co2",
|
|
6812
|
-
version: "1.3.
|
|
6813
|
-
description: "Compound Context Studio \xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xA2\xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\u2026\xC2\xA1\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xAC\xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u201A\xAC\xC5\xA1\xC3\u201A\xC2\xAC\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\x9D self-hosted team context authoring for CO2 projects",
|
|
6812
|
+
version: "1.3.32",
|
|
6813
|
+
description: "Compound Context Studio \xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xE2\u20AC\xA0\xC3\xA2\xE2\u201A\xAC\xE2\u201E\xA2\xC3\u0192\xC6\u2019\xC3\xA2\xE2\u201A\xAC\xC2\xA0\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\xA2\xE2\u20AC\u017E\xC2\xA2\xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\u2026\xC2\xA1\xC3\u0192\xC6\u2019\xC3\xA2\xE2\u201A\xAC\xC5\xA1\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xA2\xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xE2\u20AC\xA0\xC3\xA2\xE2\u201A\xAC\xE2\u201E\xA2\xC3\u0192\xC6\u2019\xC3\xA2\xE2\u201A\xAC\xC5\xA1\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xA2\xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xA2\xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\u2026\xC2\xA1\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xAC\xC3\u0192\xC6\u2019\xC3\xA2\xE2\u201A\xAC\xC2\xA6\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xA1\xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\u2026\xC2\xA1\xC3\u0192\xC6\u2019\xC3\xA2\xE2\u201A\xAC\xC5\xA1\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xAC\xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xE2\u20AC\xA0\xC3\xA2\xE2\u201A\xAC\xE2\u201E\xA2\xC3\u0192\xC6\u2019\xC3\xA2\xE2\u201A\xAC\xC5\xA1\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xA2\xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xA2\xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u201A\xAC\xC5\xA1\xC3\u201A\xC2\xAC\xC3\u0192\xE2\u20AC\xA6\xC3\u201A\xC2\xA1\xC3\u0192\xC6\u2019\xC3\xA2\xE2\u201A\xAC\xC5\xA1\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xAC\xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\u2026\xC2\xA1\xC3\u0192\xC6\u2019\xC3\xA2\xE2\u201A\xAC\xC5\xA1\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\x9D self-hosted team context authoring for CO2 projects",
|
|
6814
6814
|
type: "module",
|
|
6815
6815
|
license: "MIT",
|
|
6816
6816
|
publishConfig: {
|
|
@@ -23211,19 +23211,30 @@ function getOrCreateSetup(db, projectId, userId) {
|
|
|
23211
23211
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23212
23212
|
return db.insert(cicdSetups).values({ projectId, status: "draft", createdBy: userId, createdAt: now, updatedAt: now }).returning().get();
|
|
23213
23213
|
}
|
|
23214
|
-
function buildPlanRequestMarkdown(projectName, appNames, relativePath) {
|
|
23214
|
+
function buildPlanRequestMarkdown(projectName, appNames, relativePath, guidance) {
|
|
23215
|
+
const trimmedGuidance = guidance?.trim() ?? "";
|
|
23216
|
+
const guidanceSection = trimmedGuidance ? `## User Guidance
|
|
23217
|
+
|
|
23218
|
+
The following guidance was authored by the user and is AUTHORITATIVE \u2014 honor it over inferred
|
|
23219
|
+
defaults wherever they conflict, and note in the plan's Rationale where guidance overrode
|
|
23220
|
+
inference. \`shared_context/...\` links reference files under the project's \`shared_context/\`
|
|
23221
|
+
folder \u2014 read them for additional context.
|
|
23222
|
+
|
|
23223
|
+
${trimmedGuidance}
|
|
23224
|
+
|
|
23225
|
+
` : "";
|
|
23215
23226
|
return `# CI/CD Deployment Plan Request \u2014 ${projectName}
|
|
23216
23227
|
|
|
23217
23228
|
## Instructions
|
|
23218
23229
|
|
|
23219
23230
|
Infer a deployment plan for every application listed below. Read the project's
|
|
23220
23231
|
\`ENVIRONMENT.md\`, \`DEVTOOL.md\`, \`CLAUDE.md\` (# Custom Applications, # Port Allocation)
|
|
23221
|
-
and each application's \`context/specification/SPECIFICATION.md
|
|
23232
|
+
and each application's \`context/specification/SPECIFICATION.md\`.${trimmedGuidance ? " Honor the `## User Guidance` section below." : ""} Decide per application:
|
|
23222
23233
|
deployment method (container vs bare process), build command, start command, stop mechanism,
|
|
23223
23234
|
port and health-check URL. Write the plan to \`${CICD_PLAN_RELATIVE}\` with a top-level
|
|
23224
23235
|
\`**Status**: IN PROGRESS\` header, flipped to \`COMPLETED\` when the plan is finished.
|
|
23225
23236
|
|
|
23226
|
-
## Applications
|
|
23237
|
+
${guidanceSection}## Applications
|
|
23227
23238
|
|
|
23228
23239
|
${appNames.map((n) => `- ${n}`).join("\n")}
|
|
23229
23240
|
|
|
@@ -23234,7 +23245,13 @@ ${appNames.map((n) => `- ${n}`).join("\n")}
|
|
|
23234
23245
|
\`\`\`
|
|
23235
23246
|
`;
|
|
23236
23247
|
}
|
|
23237
|
-
function
|
|
23248
|
+
function resetToDraft(db, projectId, userId) {
|
|
23249
|
+
const setup = getOrCreateSetup(db, projectId, userId);
|
|
23250
|
+
stopCicdWatcher(setup.id, "plan");
|
|
23251
|
+
stopCicdWatcher(setup.id, "generation");
|
|
23252
|
+
db.update(cicdSetups).set({ status: "draft", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(cicdSetups.id, setup.id)).run();
|
|
23253
|
+
}
|
|
23254
|
+
function requestPlan(db, workspacePath, projectId, userId, guidance) {
|
|
23238
23255
|
const setup = getOrCreateSetup(db, projectId, userId);
|
|
23239
23256
|
stopCicdWatcher(setup.id, "generation");
|
|
23240
23257
|
const project = requireProject(db, projectId);
|
|
@@ -23250,7 +23267,8 @@ function requestPlan(db, workspacePath, projectId, userId) {
|
|
|
23250
23267
|
buildPlanRequestMarkdown(
|
|
23251
23268
|
project.name,
|
|
23252
23269
|
apps.map((a) => a.name),
|
|
23253
|
-
relative3
|
|
23270
|
+
relative3,
|
|
23271
|
+
guidance
|
|
23254
23272
|
),
|
|
23255
23273
|
"utf8"
|
|
23256
23274
|
);
|
|
@@ -23475,12 +23493,33 @@ function cicdRoutes(db, workspacePath, studioPort) {
|
|
|
23475
23493
|
return errorResponse3(c, e);
|
|
23476
23494
|
}
|
|
23477
23495
|
});
|
|
23478
|
-
app.post("/:projectId/
|
|
23496
|
+
app.post("/:projectId/reset", (c) => {
|
|
23497
|
+
const projectId = parseProjectId(c);
|
|
23498
|
+
if (projectId === null) return c.json({ code: "INVALID_ID" }, 400);
|
|
23499
|
+
const user = c.get("user");
|
|
23500
|
+
if (!user) return c.json({ code: "UNAUTHORIZED" }, 401);
|
|
23501
|
+
try {
|
|
23502
|
+
resetToDraft(db, projectId, user.id);
|
|
23503
|
+
stopCicdApp(db, projectId);
|
|
23504
|
+
return c.json({ ok: true });
|
|
23505
|
+
} catch (e) {
|
|
23506
|
+
return errorResponse3(c, e);
|
|
23507
|
+
}
|
|
23508
|
+
});
|
|
23509
|
+
app.post("/:projectId/plan", async (c) => {
|
|
23479
23510
|
const projectId = parseProjectId(c);
|
|
23480
23511
|
if (projectId === null) return c.json({ code: "INVALID_ID" }, 400);
|
|
23481
23512
|
const user = c.get("user");
|
|
23513
|
+
let guidance;
|
|
23514
|
+
try {
|
|
23515
|
+
const body = await c.req.json();
|
|
23516
|
+
if (typeof body.guidance === "string" && body.guidance.trim() !== "") {
|
|
23517
|
+
guidance = body.guidance.slice(0, 2e4);
|
|
23518
|
+
}
|
|
23519
|
+
} catch {
|
|
23520
|
+
}
|
|
23482
23521
|
try {
|
|
23483
|
-
const result = requestPlan(db, workspacePath, projectId, user.id);
|
|
23522
|
+
const result = requestPlan(db, workspacePath, projectId, user.id, guidance);
|
|
23484
23523
|
stopCicdApp(db, projectId);
|
|
23485
23524
|
return c.json(result, 201);
|
|
23486
23525
|
} catch (e) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rashidee/co2",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "Compound Context Studio
|
|
3
|
+
"version": "1.3.32",
|
|
4
|
+
"description": "Compound Context Studio  self-hosted team context authoring for CO2 projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"publishConfig": {
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
"name": "Rashidee M.Rashid"
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
|
-
"description": "Compound Context (CO2) Workflow
|
|
8
|
-
"version": "1.0.
|
|
7
|
+
"description": "Compound Context (CO2) Workflow — an opinionated context engineering methodology for AI-driven software development",
|
|
8
|
+
"version": "1.0.3"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "co2-skills",
|
|
13
13
|
"source": "./",
|
|
14
|
-
"description": "Compound Context Skills
|
|
15
|
-
"version": "1.8.
|
|
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.8.7",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "Rashidee M.Rashid"
|
|
18
18
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "co2-skills",
|
|
3
|
-
"version": "1.8.
|
|
4
|
-
"description": "Compound Context Skills
|
|
3
|
+
"version": "1.8.7",
|
|
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"
|
|
7
7
|
},
|
|
@@ -9,16 +9,19 @@ description: >
|
|
|
9
9
|
cicd/app/ — a zero-npm-dependency node:http app (server.js + cicd.config.json) rendering a
|
|
10
10
|
bare light-themed page (no header/sidebar/footer, iframe-embeddable) with one nav tab per
|
|
11
11
|
application: a controls bar (status badge, clickable app URL opening a new tab, a Config
|
|
12
|
-
button, and
|
|
12
|
+
button, and MODE-DEPENDENT lifecycle buttons from the plan's per-application execution mode:
|
|
13
|
+
`process` → Build / Start / Stop (+ Push to Docker when planned); `docker` → Build / Push to
|
|
14
|
+
Docker / Redeploy; `k8s` → Build / Update Manifest / Deploy to K8s) above that application's
|
|
15
|
+
log streamed live
|
|
13
16
|
over a built-in zero-dependency WebSocket (bounded tail + backpressure drop server-side, trimmed
|
|
14
17
|
DOM buffer client-side — no leaks/OOM). The Config button opens a modal listing the application's
|
|
15
18
|
runtime config files (from the plan's `## Application Configs`) — each with its source
|
|
16
19
|
(ENVIRONMENT.md-derived vs an existing .env/.env-* file), present/MISSING status, required keys
|
|
17
20
|
and full live content — for troubleshooting; and before Build/Start the app materializes any
|
|
18
21
|
missing plan-managed config file into the application folder so it always starts with complete
|
|
19
|
-
configuration.
|
|
20
|
-
from ENVIRONMENT.md/DEVTOOL.md
|
|
21
|
-
|
|
22
|
+
configuration. All lifecycle commands come verbatim from the approved plan (inferred
|
|
23
|
+
from ENVIRONMENT.md/DEVTOOL.md and the plan's execution modes; k8s manifests were generated by
|
|
24
|
+
depgen-k8s at planning time). Controls are disabled for any application that
|
|
22
25
|
has never completed a conductor-develop workflow run (checked against the studio's
|
|
23
26
|
/api/cicd-guard endpoint using the spawn-env token). Health/port probing marks apps already
|
|
24
27
|
running — even ones started outside this app. There is NO version selector and there are NO
|
|
@@ -67,8 +70,8 @@ Read `references/cicd-app-template.md` for the complete file templates. Produce:
|
|
|
67
70
|
|
|
68
71
|
1. `cicd/app/cicd.config.json` — from the plan's tables:
|
|
69
72
|
`{ "port": <CI/CD app port>, "generatedAt": "<current ISO-8601 UTC>", "docker": { "registry" } | null, "apps": [{ "name", "folder",
|
|
70
|
-
"
|
|
71
|
-
"dockerfile", "configs" }], "infra": [{ "id", "name",
|
|
73
|
+
"mode", "port", "build", "start", "stop", "push", "redeploy", "updateManifest", "deploy",
|
|
74
|
+
"health", "image", "dockerfile", "configs" }], "infra": [{ "id", "name",
|
|
72
75
|
"host", "port", "url", "description" }] }` — `generatedAt` MUST be the actual time you generate
|
|
73
76
|
this file (a fresh value on EVERY run — never copy a previous one): the running server pins the
|
|
74
77
|
value it booted with and shows a "restart required" banner when a later regeneration moves it
|
|
@@ -79,9 +82,12 @@ Read `references/cicd-app-template.md` for the complete file templates. Produce:
|
|
|
79
82
|
`## Dockerfiles` section, or `{ "path": "Dockerfile", "content": null }` for an `existing`
|
|
80
83
|
one (`null` entirely when the app has no image). Copy managed Dockerfile bodies verbatim.
|
|
81
84
|
Commands are copied verbatim from the plan and run as-is — there is NO `{VERSION}` token
|
|
82
|
-
or any other placeholder substitution. `
|
|
83
|
-
(`
|
|
84
|
-
|
|
85
|
+
or any other placeholder substitution. `mode` is the plan's Mode column
|
|
86
|
+
(`process|docker|k8s`); `start`/`stop`/`push`/`redeploy`/`updateManifest`/`deploy` come from
|
|
87
|
+
their plan columns, `null` wherever the cell is `-` — the generated page renders per
|
|
88
|
+
application ONLY the buttons whose command exists, per mode (`process`: Build/Start/Stop
|
|
89
|
+
+ Push; `docker`: Build/Push/Redeploy; `k8s`: Build/Update Manifest/Deploy to K8s).
|
|
90
|
+
The `infra` array comes from the plan's `## Infrastructure` table (one entry per
|
|
85
91
|
row); `url` is `null` when the Console URL cell is `-`. Emit `"infra": []` when the plan's
|
|
86
92
|
Infrastructure table is empty — never invent services the plan does not list.
|
|
87
93
|
- `configs` — the runtime config files each application reads at start, from the plan's
|
|
@@ -162,19 +168,28 @@ port, application count) and STOP — the studio starts/embeds the app.
|
|
|
162
168
|
3rd-party services, each with a live TCP-reachability dot (polled) and a console link when a
|
|
163
169
|
`url` is set. Infrastructure is never built/started/stopped/deployed (no lifecycle controls,
|
|
164
170
|
no develop gate); the link is hidden when `infra` is empty.
|
|
165
|
-
-
|
|
171
|
+
- Every lifecycle button runs the plan's command, each with the APPLICATION FOLDER
|
|
166
172
|
(`<project>/<folder>`) as working directory — command paths are relative to the application
|
|
167
|
-
folder (e.g. `java -jar target\app.jar`), never to the project root.
|
|
168
|
-
|
|
173
|
+
folder (e.g. `java -jar target\app.jar`), never to the project root.
|
|
174
|
+
- **Mode-dependent buttons (MANDATORY)**: the controls bar renders per application EXACTLY the
|
|
175
|
+
button set of its `mode` — `process`: Build / Start / Stop, plus Push to Docker when `push`
|
|
176
|
+
is set; `docker`: Build / Push to Docker / Redeploy (NO Start/Stop — Redeploy owns the
|
|
177
|
+
container lifecycle); `k8s`: Build / Update Manifest / Deploy to K8s (NO Start/Stop — pods
|
|
178
|
+
run in the cluster, and the running badge shows a neutral `k8s` phase instead of the local
|
|
179
|
+
port probe). A button whose command is `null` never renders. Rendering a Start/Stop pair for
|
|
180
|
+
a docker/k8s application is a defect — it misleads the user into thinking the app executes
|
|
181
|
+
directly on this machine.
|
|
169
182
|
- **NO version UI, NO All-apps buttons, NO command substitution**: the page carries NO
|
|
170
|
-
version dropdown or any other version choice, and NO
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
183
|
+
version dropdown or any other version choice, and NO all-application bulk buttons — every
|
|
184
|
+
lifecycle button is per-application only and always operates on the CURRENT working tree,
|
|
185
|
+
running the plan's commands verbatim (no `{VERSION}` or other token substitution). Never
|
|
186
|
+
render a version control and never check out code.
|
|
174
187
|
- **Dockerfile materialization**: before Build, when an app's `dockerfile.content` is set and
|
|
175
188
|
no file exists at its path in the application folder, the server writes it (never
|
|
176
189
|
overwriting an existing Dockerfile — mirror of the managed-config rule). Build never
|
|
177
|
-
pushes an image
|
|
190
|
+
pushes an image in `process`/`docker` mode — pushing is the Push button's command; the
|
|
191
|
+
`k8s` Build is the one exception (its plan command appends the push so the cluster pulls
|
|
192
|
+
the fresh image).
|
|
178
193
|
- Develop gate: controls stay disabled for an application that never completed a
|
|
179
194
|
conductor-develop run — checked via `${CO2_STUDIO_URL}/api/cicd-guard/${CO2_PROJECT_ID}`
|
|
180
195
|
with the CO2_GUARD_TOKEN from the spawn env (refreshed every 30 s, last answer cached).
|
|
@@ -293,24 +293,29 @@ function buildApp(app) {
|
|
|
293
293
|
})
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
296
|
+
// One-shot lifecycle commands (deploy / push / redeploy / update-manifest) share one runner:
|
|
297
|
+
// one activity slot per app, exit code surfaced as the app's error. The activity label doubles
|
|
298
|
+
// as the client badge phase while the command runs.
|
|
299
|
+
function runOnce(app, label, cmd) {
|
|
300
|
+
if (activity.has(app.name) || !cmd) return
|
|
301
|
+
activity.set(app.name, label); lastError.delete(app.name)
|
|
302
|
+
runLogged(app, cmd, (code) => {
|
|
300
303
|
activity.delete(app.name)
|
|
301
|
-
if (code !== 0) lastError.set(app.name,
|
|
302
|
-
else logLine(app,
|
|
304
|
+
if (code !== 0) lastError.set(app.name, `${label} exited ${code} — see log`)
|
|
305
|
+
else logLine(app, `${label} OK`)
|
|
303
306
|
})
|
|
304
307
|
}
|
|
308
|
+
function deployApp(app) { runOnce(app, 'deploying', app.deploy) }
|
|
309
|
+
function pushApp(app) { runOnce(app, 'pushing', app.push) }
|
|
310
|
+
function redeployApp(app) { runOnce(app, 'redeploying', app.redeploy) }
|
|
311
|
+
function updateManifestApp(app) { runOnce(app, 'updating', app.updateManifest) }
|
|
305
312
|
|
|
313
|
+
// Start exists only for mode=process apps (docker mode redeploys the container, k8s mode
|
|
314
|
+
// rolls the cluster workload) — the plan carries no start command for the other modes.
|
|
306
315
|
function startApp(app) {
|
|
307
|
-
if (running.get(app.name) || activity.has(app.name)) return
|
|
316
|
+
if (running.get(app.name) || activity.has(app.name) || !app.start) return
|
|
308
317
|
failed.delete(app.name); lastError.delete(app.name)
|
|
309
318
|
ensureConfigs(app)
|
|
310
|
-
if (app.method === 'container') {
|
|
311
|
-
runLogged(app, app.start, () => {})
|
|
312
|
-
saveState(); return
|
|
313
|
-
}
|
|
314
319
|
const child = runLogged(app, app.start, (code) => {
|
|
315
320
|
running.delete(app.name)
|
|
316
321
|
// A user-requested Stop exits nonzero (killed) — that is not a failure.
|
|
@@ -427,7 +432,8 @@ body{font-family:system-ui,sans-serif;margin:0;background:#f6f7f9;color:#1c2128;
|
|
|
427
432
|
.dot.running{background:#1c8a4a}.dot.failed{background:#c23b3b}.dot.building,.dot.deploying{background:#c9922a}
|
|
428
433
|
.bar{display:flex;gap:8px;align-items:center;padding:10px 12px;background:#fff;border-bottom:1px solid #e1e5ea;flex-wrap:wrap}
|
|
429
434
|
.badge{font-size:10px;text-transform:uppercase;border-radius:99px;padding:2px 8px;color:#fff;background:#8a94a0}
|
|
430
|
-
.badge.running{background:#1c8a4a}.badge.failed{background:#c23b3b}.badge.
|
|
435
|
+
.badge.running{background:#1c8a4a}.badge.failed{background:#c23b3b}.badge.k8s{background:#3a6fc4}
|
|
436
|
+
.badge.building,.badge.deploying,.badge.pushing,.badge.redeploying,.badge.updating{background:#c9922a}
|
|
431
437
|
.url{font-size:12px;color:#1a66d0;text-decoration:underline;word-break:break-all}
|
|
432
438
|
button{background:#fff;color:#1c2128;border:1px solid #c6cdd6;border-radius:6px;padding:4px 12px;font-size:12px;cursor:pointer}
|
|
433
439
|
button:hover:enabled{background:#eef1f5}
|
|
@@ -491,17 +497,25 @@ function renderBar(){
|
|
|
491
497
|
// Fail OPEN — only a definitive "never developed" (false) disables controls; unknown (null)
|
|
492
498
|
// leaves them enabled so an unverifiable studio never locks the user out (Defect 2c/4).
|
|
493
499
|
const gate = a.developedOnce !== false
|
|
494
|
-
const
|
|
500
|
+
const mode = a.mode || 'process'
|
|
501
|
+
const busy = a.phase==='building'||a.phase==='deploying'||a.phase==='pushing'||a.phase==='redeploying'||a.phase==='updating'
|
|
502
|
+
// Mode-dependent controls: process → Build/Start/Stop (+ Push when planned);
|
|
503
|
+
// docker → Build/Push/Redeploy; k8s → Build/Update Manifest/Deploy to K8s. Rendering a
|
|
504
|
+
// Start/Stop pair for a docker/k8s app is a defect — it misleads the user into thinking
|
|
505
|
+
// the application executes directly on this machine.
|
|
495
506
|
document.getElementById('bar').innerHTML = \`
|
|
496
507
|
<span class="badge \${a.phase}">\${a.phase}\${a.external?' (external)':''}</span>
|
|
497
|
-
<span class="meta">\${
|
|
498
|
-
|
|
508
|
+
<span class="meta">\${mode} · port \${a.port}</span>
|
|
509
|
+
\${mode!=='k8s'?\`<a class="url" href="\${url}" target="_blank" rel="noreferrer">\${url}</a>\`:''}
|
|
499
510
|
<span style="flex:1"></span>
|
|
500
511
|
<button onclick="openConfig()" title="Show this application's runtime configuration files">Config</button>
|
|
501
|
-
<button onclick="act('build')" \${!gate||busy||a.phase==='running'?'disabled':''} title="\${a.image?'Builds the app AND the docker image '+a.image+':latest':'Runs the plan build command'}">Build</button>
|
|
502
|
-
|
|
503
|
-
<button onclick="act('stop')" \${a.phase!=='running'?'disabled':''}>Stop</button
|
|
504
|
-
\${a.
|
|
512
|
+
<button onclick="act('build')" \${!gate||busy||(mode==='process'&&a.phase==='running')?'disabled':''} title="\${a.image?'Builds the app AND the docker image '+a.image+':latest':'Runs the plan build command'}">Build</button>
|
|
513
|
+
\${mode==='process'?\`<button onclick="act('start')" \${!gate||busy||a.phase==='running'?'disabled':''}>Start</button>
|
|
514
|
+
<button onclick="act('stop')" \${a.phase!=='running'?'disabled':''}>Stop</button>\`:''}
|
|
515
|
+
\${(mode==='process'&&a.push)||mode==='docker'?\`<button onclick="act('push')" \${!gate||busy||!a.push?'disabled':''}>Push to Docker</button>\`:''}
|
|
516
|
+
\${mode==='docker'?\`<button onclick="act('redeploy')" \${!gate||busy||!a.redeploy?'disabled':''}>Redeploy</button>\`:''}
|
|
517
|
+
\${mode==='k8s'?\`<button onclick="act('update-manifest')" \${!gate||busy||!a.updateManifest?'disabled':''}>Update Manifest</button>
|
|
518
|
+
<button onclick="act('deploy')" \${!gate||busy||!a.deploy?'disabled':''}>Deploy to K8s</button>\`:''}\`
|
|
505
519
|
const notice = document.getElementById('notice')
|
|
506
520
|
if (a.developedOnce === false) {
|
|
507
521
|
notice.textContent = 'This application has not been developed through the workflow yet — run conductor-feature-develop first.'
|
|
@@ -659,12 +673,17 @@ const server = http.createServer(async (req, res) => {
|
|
|
659
673
|
}
|
|
660
674
|
if (req.method === 'GET' && req.url === '/api/state') {
|
|
661
675
|
const apps = await Promise.all(loadConfig().apps.map(async (a) => {
|
|
676
|
+
const mode = a.mode || 'process'
|
|
662
677
|
const healthy = await probe(a)
|
|
663
|
-
|
|
678
|
+
// k8s apps run in the cluster, not on this machine — the local port probe is
|
|
679
|
+
// meaningless, so their idle phase is a neutral 'k8s' badge.
|
|
680
|
+
const phase = activity.get(a.name)
|
|
681
|
+
|| (mode === 'k8s' ? 'k8s' : healthy ? 'running' : failed.has(a.name) ? 'failed' : 'stopped')
|
|
664
682
|
const dev = developInfo(a)
|
|
665
683
|
return {
|
|
666
|
-
name: a.name,
|
|
667
|
-
deploy: !!a.deploy,
|
|
684
|
+
name: a.name, mode, port: a.port, phase,
|
|
685
|
+
deploy: !!a.deploy, push: !!a.push, redeploy: !!a.redeploy,
|
|
686
|
+
updateManifest: !!a.updateManifest,
|
|
668
687
|
image: a.image || null,
|
|
669
688
|
external: healthy && !running.has(a.name),
|
|
670
689
|
developedOnce: dev.val, developSource: dev.src,
|
|
@@ -689,7 +708,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
689
708
|
return json(res, 200, { configs: (app.configs || []).map((c) => readConfig(app, c)) })
|
|
690
709
|
}
|
|
691
710
|
const path = (req.url || '').split('?')[0]
|
|
692
|
-
const m = path.match(/^\/api\/apps\/([^/]+)\/(build|start|stop|deploy)$/)
|
|
711
|
+
const m = path.match(/^\/api\/apps\/([^/]+)\/(build|start|stop|deploy|push|redeploy|update-manifest)$/)
|
|
693
712
|
if (m && req.method === 'POST') {
|
|
694
713
|
const app = loadConfig().apps.find((a) => a.name === decodeURIComponent(m[1]))
|
|
695
714
|
if (!app) return json(res, 404, { error: 'unknown app' })
|
|
@@ -700,6 +719,9 @@ const server = http.createServer(async (req, res) => {
|
|
|
700
719
|
if (m[2] === 'build') buildApp(app)
|
|
701
720
|
else if (m[2] === 'start') startApp(app)
|
|
702
721
|
else if (m[2] === 'deploy') deployApp(app)
|
|
722
|
+
else if (m[2] === 'push') pushApp(app)
|
|
723
|
+
else if (m[2] === 'redeploy') redeployApp(app)
|
|
724
|
+
else if (m[2] === 'update-manifest') updateManifestApp(app)
|
|
703
725
|
else await stopApp(app)
|
|
704
726
|
return json(res, 200, { ok: true })
|
|
705
727
|
}
|
|
@@ -743,9 +765,12 @@ Top-level `"generatedAt"` is an ISO-8601 UTC timestamp **stamped fresh on every
|
|
|
743
765
|
(Defect 1): the running server pins the value it booted with and flags a "restart required" banner
|
|
744
766
|
once a regeneration moves it forward, so a stale in-place-replaced process is self-diagnosing. It
|
|
745
767
|
MUST change on each run — use the actual generation time (e.g. `new Date().toISOString()`).
|
|
746
|
-
`
|
|
747
|
-
|
|
748
|
-
`
|
|
768
|
+
`mode` is the plan's Mode column (`process|docker|k8s`) and decides the rendered button set;
|
|
769
|
+
`start`/`stop`/`push`/`redeploy`/`updateManifest`/`deploy` come from their plan columns and are
|
|
770
|
+
`null` wherever the plan cell is `-` (a `null` command's button never renders — `process` apps
|
|
771
|
+
carry start/stop and optionally push; `docker` apps carry push/redeploy; `k8s` apps carry
|
|
772
|
+
updateManifest/deploy). `infra[].url` is `null` for a headless service (Console URL cell `-`);
|
|
773
|
+
emit `"infra": []` when the plan declares no supporting services.
|
|
749
774
|
|
|
750
775
|
Docker fields (from the plan's `## Docker` / `## Dockerfiles` sections): top-level
|
|
751
776
|
`"docker": { "registry": "<repo>" }` or `null` when the plan is not dockerized. Per app,
|
|
@@ -776,13 +801,15 @@ verbatim (this is a local dev CI/CD app).
|
|
|
776
801
|
{
|
|
777
802
|
"name": "my-app",
|
|
778
803
|
"folder": "my-app",
|
|
779
|
-
"
|
|
804
|
+
"mode": "k8s",
|
|
780
805
|
"port": 3000,
|
|
781
|
-
"build": "rm -rf dist && pnpm install && pnpm build && docker build --no-cache -t registry.example.com/team/my-app:latest .",
|
|
782
|
-
"start":
|
|
783
|
-
"stop":
|
|
784
|
-
"
|
|
785
|
-
"
|
|
806
|
+
"build": "rm -rf dist && pnpm install && pnpm build && docker build --no-cache -t registry.example.com/team/my-app:latest . && docker push registry.example.com/team/my-app:latest",
|
|
807
|
+
"start": null,
|
|
808
|
+
"stop": null,
|
|
809
|
+
"push": null,
|
|
810
|
+
"redeploy": null,
|
|
811
|
+
"updateManifest": "kubectl apply -f k8s/",
|
|
812
|
+
"deploy": "kubectl rollout restart deployment/my-app",
|
|
786
813
|
"health": "http://127.0.0.1:3000/",
|
|
787
814
|
"image": "registry.example.com/team/my-app",
|
|
788
815
|
"dockerfile": {
|
|
@@ -6,11 +6,14 @@ description: >
|
|
|
6
6
|
Infer a local-deployment plan for every custom application of a CO2 project and write it to
|
|
7
7
|
cicd/CICD_PLAN.md for human review in Compound Context Studio. Reads the project's
|
|
8
8
|
ENVIRONMENT.md and DEVTOOL.md, CLAUDE.md (# Custom Applications, # Port Allocation) and each
|
|
9
|
-
application's context/specification/SPECIFICATION.md, then decides per application
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
application's context/specification/SPECIFICATION.md, then decides per application its
|
|
10
|
+
EXECUTION MODE — `process` (runs directly on this machine: Build / Start / Stop buttons, plus
|
|
11
|
+
Push-to-Docker when a registry is configured), `docker` (deployed as a docker container on
|
|
12
|
+
this machine: Build / Push-to-Docker / Redeploy buttons), or `k8s` (deployed to Kubernetes:
|
|
13
|
+
Build / Update-Manifest / Deploy-to-K8s buttons) — with the mode-appropriate commands, port
|
|
14
|
+
and health-check URL, plus a port for the generated CI/CD app itself. For every k8s-mode
|
|
15
|
+
application it TRIGGERS the depgen-k8s skill to generate the required Dockerfile + manifests
|
|
16
|
+
into `<app_folder>/k8s/` before the plan is written. When Docker AND a docker repo/registry are both
|
|
14
17
|
configured (DEVTOOL.md + ENVIRONMENT.md), it additionally plans the docker-image build path:
|
|
15
18
|
per-application image names under the repo, Build commands extended with
|
|
16
19
|
`docker build --no-cache -t <image>:latest` (clean image build, no version token — the generated
|
|
@@ -53,7 +56,7 @@ If the argument is missing or the file does not exist, STOP and tell the user to
|
|
|
53
56
|
|
|
54
57
|
| Logical file | Path |
|
|
55
58
|
|---|---|
|
|
56
|
-
| Plan request | `<argument>` |
|
|
59
|
+
| Plan request | `<argument>` (may carry a `## User Guidance` section — authoritative human input) |
|
|
57
60
|
| Environment | `ENVIRONMENT.md` (project root) |
|
|
58
61
|
| Dev tools | `DEVTOOL.md` (project root) |
|
|
59
62
|
| Project context | `CLAUDE.md` (project root; `source/CLAUDE.md` fallback) |
|
|
@@ -65,6 +68,13 @@ If the argument is missing or the file does not exist, STOP and tell the user to
|
|
|
65
68
|
## Phase 1 — Gather
|
|
66
69
|
|
|
67
70
|
1. Read the plan-request markdown; its `## Applications` list is the authoritative application set.
|
|
71
|
+
1a. **User guidance**: when the plan-request markdown carries a `## User Guidance` section, treat
|
|
72
|
+
its content as AUTHORITATIVE human input — honor it over inferred defaults wherever the two
|
|
73
|
+
conflict (e.g. a forced deployment method, specific build/start/deploy commands, target hosts,
|
|
74
|
+
ports). If the guidance contains `shared_context/...` markdown links, read those files (paths
|
|
75
|
+
relative to the project root) as additional context. Record every place guidance overrode
|
|
76
|
+
inference in the plan's `## Rationale` section (e.g. "container deployment per user guidance").
|
|
77
|
+
No `## User Guidance` section → plan exactly as before.
|
|
68
78
|
2. Read `ENVIRONMENT.md` (registries, credentials, external services) and `DEVTOOL.md`
|
|
69
79
|
(installed tools + paths: node/pnpm/docker/kubectl/python versions).
|
|
70
80
|
2a. **Docker facts**: record whether Docker is installed (DEVTOOL.md) AND whether
|
|
@@ -99,8 +109,23 @@ If the argument is missing or the file does not exist, STOP and tell the user to
|
|
|
99
109
|
root. All paths inside build/start/stop/deploy commands must therefore be relative to the
|
|
100
110
|
application folder (`mvn -q package`, `java -jar target\app.jar`) — never prefixed with the
|
|
101
111
|
application folder itself (`skolafund_middleware\target\...` is WRONG).
|
|
102
|
-
- **
|
|
103
|
-
|
|
112
|
+
- **Execution mode (drives the generated app's BUTTON SET)** — decide per application HOW this
|
|
113
|
+
machine runs/ships it: `process` | `docker` | `k8s`. Honor `## User Guidance` first; else
|
|
114
|
+
infer from ENVIRONMENT.md/DEVTOOL.md:
|
|
115
|
+
- `k8s` — the deployment target is Kubernetes (kubectl + a cluster/kubeconfig configured, or
|
|
116
|
+
guidance says so). Requires Docker + a registry (the cluster pulls the pushed image).
|
|
117
|
+
Buttons: **Build / Update Manifest / Deploy to K8s** — no Start/Stop (pods run in the
|
|
118
|
+
cluster, not on this machine).
|
|
119
|
+
- `docker` — the application runs on this machine AS a docker container (Docker installed
|
|
120
|
+
and the container is the run mode). Buttons: **Build / Push to Docker / Redeploy** — no
|
|
121
|
+
bare Start/Stop (Redeploy owns the container lifecycle).
|
|
122
|
+
- `process` — bare local process, the dev-VM default. Buttons: **Build / Start / Stop**,
|
|
123
|
+
plus **Push to Docker** only when the docker-image path (below) is active.
|
|
124
|
+
- **depgen-k8s trigger (k8s mode only, MANDATORY)**: for EVERY k8s-mode application, invoke the
|
|
125
|
+
depgen-k8s skill BEFORE writing the plan —
|
|
126
|
+
`Skill(skill: "co2-skills:depgen-k8s", args: "<application name>")` — so the required
|
|
127
|
+
Dockerfile and Kubernetes manifests exist in `<app_folder>/k8s/`; the plan's Update-Manifest/
|
|
128
|
+
Deploy commands reference those generated files. Never hand-author k8s manifests here.
|
|
104
129
|
- **Build command (ALWAYS a CLEAN build)**: the Build button must produce a from-scratch build so
|
|
105
130
|
no stale artifact from a previous run can ever be started or shipped — use the stack's CLEAN
|
|
106
131
|
lifecycle, never an incremental one:
|
|
@@ -113,21 +138,32 @@ If the argument is missing or the file does not exist, STOP and tell the user to
|
|
|
113
138
|
- Laravel/PHP with a front-end build → `rm -rf public/build && composer install && npm ci && npm run build`;
|
|
114
139
|
plain PHP with no build step → `composer install`.
|
|
115
140
|
Use tool paths from DEVTOOL.md when the tool is not on PATH.
|
|
116
|
-
- **Start command**: the stack's production-ish
|
|
117
|
-
`java -jar target/<artifact>.jar`,
|
|
118
|
-
`
|
|
141
|
+
- **Start command (`process` mode only — `-` for docker/k8s)**: the stack's production-ish
|
|
142
|
+
local start (e.g. `node dist/index.js`, `java -jar target/<artifact>.jar`,
|
|
143
|
+
`php artisan serve --port <port>`). For jar-based stacks, resolve the EXACT artifact
|
|
119
144
|
filename — read the pom/gradle build config: a Maven `<finalName>` means
|
|
120
145
|
`target/<finalName>.jar` with NO version suffix (Spring Boot repackage keeps that name and
|
|
121
146
|
leaves a `.jar.original` beside it — never reference the `.original`). Never guess
|
|
122
147
|
`<app>-<version>.jar` and never use a `target/*.jar` glob (shell-dependent, breaks on
|
|
123
148
|
Windows cmd).
|
|
124
|
-
- **Stop
|
|
125
|
-
- **
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
149
|
+
- **Stop (`process` mode only — `-` for docker/k8s)**: `process-kill`.
|
|
150
|
+
- **Per-mode lifecycle commands** (each recorded in its own plan column; `-` when the mode does
|
|
151
|
+
not use it — the generated app renders ONLY the buttons whose command exists):
|
|
152
|
+
- **Push** (`docker push <image>:latest`) — `docker` mode always; `process` mode only when
|
|
153
|
+
the docker-image path is active (the Build already produced the image). `-` for k8s
|
|
154
|
+
(pushing is folded into the k8s Build so the cluster can pull the fresh image).
|
|
155
|
+
- **Redeploy** (`docker` mode) — replace the running container with the freshly built
|
|
156
|
+
`latest` image: `docker rm -f <container> ; docker run -d --name <container>
|
|
157
|
+
-p <port>:<port> <env/volume flags> <image>:latest` (or `docker compose up -d` when a
|
|
158
|
+
compose file is the run mode). The container name defaults to the app folder name.
|
|
159
|
+
- **Update Manifest** (`k8s` mode) — apply the depgen-k8s-generated manifests to the
|
|
160
|
+
cluster: `kubectl apply -f k8s/` (namespace flag when ENVIRONMENT.md names one).
|
|
161
|
+
- **Deploy** (`k8s` mode) — roll the workload onto the freshly pushed image:
|
|
162
|
+
`kubectl rollout restart deployment/<app> -n <ns>` (match the deployment name in the
|
|
163
|
+
generated manifests). For `process`/`docker` the Deploy column is `-` — those modes ship
|
|
164
|
+
via Push/Redeploy, and a bare local process needs no deploy step beyond Start.
|
|
165
|
+
- **k8s Build** additionally appends the push — `<app build> && docker build --no-cache
|
|
166
|
+
-t <image>:latest . && docker push <image>:latest` — the cluster pulls from the registry.
|
|
131
167
|
- **Docker image** (only when Gather 2a found Docker + a docker repo — otherwise skip this
|
|
132
168
|
bullet entirely):
|
|
133
169
|
- **Image name**: `<repo>/<app_folder>` — the application folder name, lowercased, with
|
|
@@ -138,9 +174,10 @@ If the argument is missing or the file does not exist, STOP and tell the user to
|
|
|
138
174
|
clean build too, so no stale layer survives. Commands carry NO `{VERSION}` token or
|
|
139
175
|
any other placeholder — the generated CI/CD app runs them verbatim; only the current
|
|
140
176
|
working tree is ever built and images are always tagged `latest`.
|
|
141
|
-
Build never pushes — pushing
|
|
142
|
-
|
|
143
|
-
|
|
177
|
+
Build never pushes in `process`/`docker` mode — pushing is the Push button's job
|
|
178
|
+
(k8s Build is the one exception: it appends the push so the cluster can pull).
|
|
179
|
+
- **Container redeploy**: in `docker` mode, reference `<image>:latest` in the Redeploy
|
|
180
|
+
command so the freshly built tag is what runs.
|
|
144
181
|
- **Dockerfile**: if the application folder already has a `Dockerfile`, use it as-is
|
|
145
182
|
(`existing`). Otherwise author a stack-appropriate Dockerfile (node / spring-boot /
|
|
146
183
|
laravel — same stack detection as depgen-k8s) into the plan's `## Dockerfiles` section
|
|
@@ -222,9 +259,13 @@ inner ```env config block renders — the plan file itself uses a normal 3-backt
|
|
|
222
259
|
|
|
223
260
|
## Deployment Decisions
|
|
224
261
|
|
|
225
|
-
| Application | Folder |
|
|
226
|
-
|
|
227
|
-
| <name> | <folder> | process|
|
|
262
|
+
| Application | Folder | Mode | Port | Build | Start | Stop | Push | Redeploy | Update Manifest | Deploy | Health |
|
|
263
|
+
|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
264
|
+
| <name> | <folder> | process|docker|k8s | <port> | `<cmd>` | `<cmd or ->` | process-kill|- | `<cmd or ->` | `<cmd or ->` | `<cmd or ->` | `<cmd or ->` | <url> |
|
|
265
|
+
|
|
266
|
+
> **Mode → buttons**: the generated CI/CD app renders per application exactly the buttons whose
|
|
267
|
+
> command exists — `process`: Build / Start / Stop (+ Push to Docker when Push is set);
|
|
268
|
+
> `docker`: Build / Push to Docker / Redeploy; `k8s`: Build / Update Manifest / Deploy to K8s.
|
|
228
269
|
|
|
229
270
|
> **Running-detection**: the generated CI/CD app determines "is it running?" by a TCP connect to
|
|
230
271
|
> each application's **Port** on both IPv4 (`127.0.0.1`) and IPv6 (`::1`) loopback plus the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-B3bjiMAY.js";import{p as f}from"./chunk-JWPE2WC7-BZ9dVylx.js";import{_ as n,l as o}from"./index-BWq7zKXB.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-Dzy0NABg.js";var v=d().RailroadAbnf.parser.LangiumParser,l=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),i=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:i,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return l(e.element);case"AbnfOptionalGroup":return{type:"optional",element:l(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:l(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},w={parser:R,db:a,renderer:u,styles:p};export{w as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Y as ln,$ as an,a0 as y,a1 as tn,a2 as F,a3 as q,a4 as _,a5 as un,a6 as rn,a7 as J,a8 as o,a9 as j,aa as sn,ab as on,ac as fn}from"./index-
|
|
1
|
+
import{Y as ln,$ as an,a0 as y,a1 as tn,a2 as F,a3 as q,a4 as _,a5 as un,a6 as rn,a7 as J,a8 as o,a9 as j,aa as sn,ab as on,ac as fn}from"./index-BWq7zKXB.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(fn(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-an,c=A.apply(this,arguments)-an,B=un(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(un(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<rn)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(on((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-rn/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|