@dawipong/opcflow 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +2 -2
- package/README.md +2 -2
- package/dist/cli.mjs +72 -58
- package/package.json +1 -1
- package/web/dist/assets/{abnfDiagram-VRR7QNED-DctuL32B.js → abnfDiagram-VRR7QNED-DAx6EpGI.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR--07w7V0c.js → architectureDiagram-ZJ3FMSHR-Dvr2nr59.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-DQGT_sG5.js → chunk-EX3LRPZG-M3QhKBUG.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-Y0jSjq4a.js → chunk-MOJQB5TN-DXlFl9Jv.js} +1 -1
- package/web/dist/assets/{chunk-PUDLZKDR-pSIk3CZr.js → chunk-PUDLZKDR-BTWHaeTZ.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-D_2vwCJz.js → chunk-V7JOEXUC-Cbgml1c1.js} +1 -1
- package/web/dist/assets/{classDiagram-OUVF2IWQ-Ckf-YAkB.js → classDiagram-OUVF2IWQ-Blusw6t4.js} +1 -1
- package/web/dist/assets/{classDiagram-v2-EOCWNBFH-Ckf-YAkB.js → classDiagram-v2-EOCWNBFH-Blusw6t4.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-CtVxnYLa.js → cynefinDiagram-TSTJHNR4-DrrKaXmw.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-CbAaKbWe.js → dagre-VKFMJZFB-BxDsa977.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-BdfPbsL1.js → diagram-G47NLZAW-BxVnT5Li.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-wWIYA2VS.js → diagram-NH7WQ7WH-BT56mGuK.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-Chx7_D6X.js → diagram-OA4YK3LP-CezybdJA.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-DxAoHRrP.js → diagram-WEI45ONY-CO8JNWNf.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-DcnWNqZc.js → ebnfDiagram-CCIWWBDH-B-OkdWz4.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-BX0XqX5B.js → erDiagram-Q63AITRT-DQNn2L0y.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-DL5kTdXb.js → flowDiagram-23GEKE2U-CPO1BGjg.js} +1 -1
- package/web/dist/assets/{index-DVXPy-Pj.js → index-HsPwY2CU.js} +7 -7
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-BnrcoE4X.js → infoDiagram-FWYZ7A6U-DM429PMt.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-BuUSROzL.js → ishikawaDiagram-FXEZZL3T-D-nr9wXb.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-hCokh58u.js → kanban-definition-HUTT4EX6-Cq6fbJBF.js} +1 -1
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-plfH1Ve5.js → mindmap-definition-LN4V7U3C-zZ5w-lPU.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-DpbGacSD.js → pegDiagram-2B236MQR-BL13lL1j.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-DsEjdRIW.js → pieDiagram-ENE6RG2P-B1mmu34A.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-C9kLSHvN.js → railroadDiagram-RFXS5EU6-BUbf08v3.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-BslCStWL.js → requirementDiagram-TGXJPOKE-CgepHMpy.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-Cg8Nc-TM.js → sequenceDiagram-DBY2YBRQ-CkIJuJ5O.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-DB4TsO7Z.js → stateDiagram-2N3HPSRC-wX6zU3Hv.js} +1 -1
- package/web/dist/assets/{stateDiagram-v2-6OUMAXLB-SUHVLdPI.js → stateDiagram-v2-6OUMAXLB-CAdDNary.js} +1 -1
- package/web/dist/assets/{swimlanes-5IMT3BWC-CgeBbl6U.js → swimlanes-5IMT3BWC-YXgGP77C.js} +1 -1
- package/web/dist/assets/{swimlanesDiagram-G3AALYLV-BfFfe5gc.js → swimlanesDiagram-G3AALYLV--Ha4WrTi.js} +1 -1
- package/web/dist/assets/{timeline-definition-FHXFAJF6-BRSnucVn.js → timeline-definition-FHXFAJF6-CtMf684d.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-gearbXh2.js → vennDiagram-L72KCM5P-DbFoFgBM.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-DBWn9RRb.js → wardleyDiagram-EHGQE667-C3J4of-x.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-CUL1Ijyf.js → xychartDiagram-FW5EYKEG-Fbzeb84K.js} +1 -1
- package/web/dist/index.html +1 -1
package/README.en.md
CHANGED
|
@@ -33,7 +33,7 @@ contracts, thumbs-up/down outputs, and answer rulings**.
|
|
|
33
33
|
- **Change propagation** — `sync` reconciles → invalidate → dispatch review along the graph (deduped)
|
|
34
34
|
- **QA loop** — fail → auto rework → auto re-verify, no human in the loop
|
|
35
35
|
- **Write-gate hooks** — block agents from editing an approved contract (observe mode by default)
|
|
36
|
-
- **Feedback evolution** — 👍👎 and QA verdicts, half-life weighted → skill
|
|
36
|
+
- **Feedback evolution** — 👍👎 and QA verdicts, half-life weighted → candidates (AI decides skill / rule / memory) / Red Flags
|
|
37
37
|
- **Multi-platform** — one definition generates each platform's agent + MCP + hooks (see [PLATFORMS.md](PLATFORMS.md))
|
|
38
38
|
- **Visual opcflow** — tree + markdown/mermaid/prototype/code rendering + review-queue diff + live SSE
|
|
39
39
|
|
|
@@ -55,7 +55,7 @@ complementary:
|
|
|
55
55
|
| Roles | Essentially single-flow (one agent implements) | Five-role pipeline, each with its own gate and output channel |
|
|
56
56
|
| Construction constraints | The spec is advisory | Gates block tasks whose upstream isn't approved; write-gate blocks edits to approved contracts; protocol lints as checkpoints |
|
|
57
57
|
| Acceptance | Not covered | Two-stage QA + fail→rework→re-verify auto loop |
|
|
58
|
-
| Evolution | Not covered | 👍👎/verdict half-life weighting →
|
|
58
|
+
| Evolution | Not covered | 👍👎/verdict half-life weighting → candidates (skill/rule/memory) / Red Flags |
|
|
59
59
|
|
|
60
60
|
In one line: **Spec Kit treats the spec as "context for the agent"; opcflow treats it as "an
|
|
61
61
|
approved contract that can go stale, propagate, and block construction," and governs the entire drift
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
- **变更传播** —— `sync` 对账 → 失效 → 沿图派 review(去重)
|
|
32
32
|
- **QA 闭环** —— fail → 自动 rework → 自动复验,不消耗人
|
|
33
33
|
- **写门禁 hooks** —— agent 改到已 approved 契约时拦截(默认 observe 观察期)
|
|
34
|
-
- **反馈进化** —— 👍👎 与 QA verdict 半衰期加权 → skill
|
|
34
|
+
- **反馈进化** —— 👍👎 与 QA verdict 半衰期加权 → 经验候选(由 AI 判断沉淀为 skill / 规则 / 记忆)/ Red Flags
|
|
35
35
|
- **多平台** —— 一套定义生成四家平台各自的 agent + MCP + hooks(见 [PLATFORMS.md](PLATFORMS.md))
|
|
36
36
|
- **可视化工作台** —— 树 + markdown/mermaid/原型/代码渲染 + 待审队列 diff + SSE 实时
|
|
37
37
|
|
|
@@ -51,7 +51,7 @@ opcflow 接管 **spec 之后的执行与验证**,两者是不同层次、可互
|
|
|
51
51
|
| 角色 | 基本单流程(一个 agent 实现) | 五角色流水线,各有 gate 与产出通道 |
|
|
52
52
|
| 施工约束 | spec 是建议 | gate 拦住上游未批的任务;写门禁拦改已批契约;协议 lint 卡点 |
|
|
53
53
|
| 验收 | 不涉及 | 两段式 QA + fail→rework→复验自动闭环 |
|
|
54
|
-
| 进化 | 不涉及 | 👍👎/verdict 半衰期加权 → skill
|
|
54
|
+
| 进化 | 不涉及 | 👍👎/verdict 半衰期加权 → 经验候选(skill/规则/记忆)/ Red Flags |
|
|
55
55
|
|
|
56
56
|
一句话:**Spec Kit 把 spec 当"给 agent 的上下文";opcflow 把 spec 当"会失效、会传播、卡得住施工
|
|
57
57
|
的已批契约",并管住从契约到代码到验收的整条漂移。**
|
package/dist/cli.mjs
CHANGED
|
@@ -690,6 +690,7 @@ __export(kind_exports, {
|
|
|
690
690
|
META_KINDS: () => META_KINDS,
|
|
691
691
|
PM_KINDS: () => PM_KINDS,
|
|
692
692
|
contractKinds: () => contractKinds,
|
|
693
|
+
expandPattern: () => expandPattern,
|
|
693
694
|
getKindRegistry: () => getKindRegistry,
|
|
694
695
|
inferKind: () => inferKind,
|
|
695
696
|
kindSpec: () => kindSpec,
|
|
@@ -755,15 +756,15 @@ var init_kind = __esm({
|
|
|
755
756
|
project: { level: "project", approval: "human", parents: [], drivesStale: true, hashMode: "text-normalize", retrieval: "summary", pathPatterns: ["{prd}/project.md"] },
|
|
756
757
|
roles: { level: "project", approval: "human", parents: ["project"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{prd}/roles.md"] },
|
|
757
758
|
glossary: { level: "project", approval: "human", parents: ["project"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{prd}/glossary.md"] },
|
|
758
|
-
flow: { level: "module", approval: "human", parents: ["project", "roles", "glossary"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{prd}/flows/"] },
|
|
759
|
-
"module-prd": { level: "module", approval: "human", parents: ["flow"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{prd}/modules/"] },
|
|
760
|
-
"page-prd": { level: "page", approval: "human", parents: ["module-prd"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{prd}/pages/"] },
|
|
761
|
-
"db-doc": { level: "module", approval: "human", parents: ["module-prd"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{architecture}/database/"] },
|
|
762
|
-
"api-doc": { level: "module", approval: "human", parents: ["module-prd", "db-doc"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{architecture}/api/"] },
|
|
763
|
-
"design-system": { level: "endpoint", approval: "human", parents: ["baseline"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{design}/systems/"] },
|
|
764
|
-
"design-prompt": { level: "page", approval: "none", parents: ["page-prd", "api-doc", "design-system"], drivesStale: false, hashMode: "text-normalize", retrieval: "summary", pathPatterns: ["{design}/prompts/"] },
|
|
765
|
-
prototype: { level: "page", approval: "thumbs", parents: ["design-prompt", "design-system"], drivesStale: true, hashMode: "text-normalize", retrieval: "summary", pathPatterns: ["{design}/prototypes/"] },
|
|
766
|
-
acceptance: { level: "page", approval: "human", parents: ["page-prd", "prototype"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{acceptance}/"] },
|
|
759
|
+
flow: { level: "module", approval: "human", parents: ["project", "roles", "glossary"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{prd}/flows/"], coords: "{module}", defaultEndpoint: "common" },
|
|
760
|
+
"module-prd": { level: "module", approval: "human", parents: ["flow"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{prd}/modules/"], coords: "{module}", defaultEndpoint: "common" },
|
|
761
|
+
"page-prd": { level: "page", approval: "human", parents: ["module-prd"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{prd}/pages/"], coords: "{endpoint}/{module}/{page}" },
|
|
762
|
+
"db-doc": { level: "module", approval: "human", parents: ["module-prd"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{architecture}/database/"], coords: "{module}", defaultEndpoint: "common" },
|
|
763
|
+
"api-doc": { level: "module", approval: "human", parents: ["module-prd", "db-doc"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{architecture}/api/"], coords: "{module}", defaultEndpoint: "service" },
|
|
764
|
+
"design-system": { level: "endpoint", approval: "human", parents: ["baseline"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{design}/systems/"], coords: "{endpoint}" },
|
|
765
|
+
"design-prompt": { level: "page", approval: "none", parents: ["page-prd", "api-doc", "design-system"], drivesStale: false, hashMode: "text-normalize", retrieval: "summary", pathPatterns: ["{design}/prompts/"], coords: "{endpoint}/{module}/{page}" },
|
|
766
|
+
prototype: { level: "page", approval: "thumbs", parents: ["design-prompt", "design-system"], drivesStale: true, hashMode: "text-normalize", retrieval: "summary", pathPatterns: ["{design}/prototypes/"], coords: "{endpoint}/{module}/{page}" },
|
|
767
|
+
acceptance: { level: "page", approval: "human", parents: ["page-prd", "prototype"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{acceptance}/"], coords: "{endpoint}/{module}/{page}" },
|
|
767
768
|
code: { level: "module", approval: "machine", parents: ["baseline", "api-doc", "prototype"], drivesStale: false, hashMode: "directory", retrieval: "semantic" },
|
|
768
769
|
doc: { level: "module", approval: "none", parents: [], drivesStale: false, hashMode: "text-normalize", retrieval: "summary" }
|
|
769
770
|
};
|
|
@@ -2412,31 +2413,37 @@ import { existsSync as existsSync9, readdirSync as readdirSync4, statSync as sta
|
|
|
2412
2413
|
import { join as join10 } from "node:path";
|
|
2413
2414
|
function parseCoords(ctx, kind, relPath) {
|
|
2414
2415
|
const none = { module: null, endpoint: null, page: null };
|
|
2415
|
-
const
|
|
2416
|
-
const
|
|
2416
|
+
const spec = getKindRegistry(ctx.config)[kind];
|
|
2417
|
+
const grammar = spec?.coords;
|
|
2418
|
+
if (!grammar) return none;
|
|
2419
|
+
const prefix = spec.pathPatterns?.[0] ? expandPattern(spec.pathPatterns[0], ctx.config) : "";
|
|
2420
|
+
const tail = relPath.startsWith(prefix) ? relPath.slice(prefix.length) : relPath;
|
|
2421
|
+
const tailSegs = tail.split("/").filter(Boolean);
|
|
2422
|
+
const gSegs = grammar.split("/");
|
|
2423
|
+
const defEnd = spec.defaultEndpoint ?? null;
|
|
2417
2424
|
const mod = (raw) => normalizeModule(raw, ctx.config);
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
}
|
|
2425
|
+
if (gSegs.length === 1) {
|
|
2426
|
+
if (tailSegs.length === 0) return null;
|
|
2427
|
+
const leaf = stripExt(tailSegs[tailSegs.length - 1]);
|
|
2428
|
+
if (gSegs[0] === "{module}") return { module: mod(leaf), endpoint: defEnd, page: null };
|
|
2429
|
+
if (gSegs[0] === "{endpoint}") return { module: null, endpoint: leaf, page: null };
|
|
2430
|
+
return none;
|
|
2431
|
+
}
|
|
2432
|
+
const hasPage = gSegs[gSegs.length - 1] === "{page}";
|
|
2433
|
+
const fixed = hasPage ? gSegs.length - 1 : gSegs.length;
|
|
2434
|
+
if (hasPage ? tailSegs.length < gSegs.length : tailSegs.length !== gSegs.length) return null;
|
|
2435
|
+
const cap = {};
|
|
2436
|
+
for (let i = 0; i < fixed; i++) cap[gSegs[i]] = stripExt(tailSegs[i]);
|
|
2437
|
+
if (cap["{endpoint}"] !== void 0 && !ctx.config.endpoints.includes(cap["{endpoint}"])) return null;
|
|
2438
|
+
const endpoint = cap["{endpoint}"] ?? defEnd;
|
|
2439
|
+
const module = cap["{module}"] !== void 0 ? mod(cap["{module}"]) : null;
|
|
2440
|
+
let page = null;
|
|
2441
|
+
if (hasPage) {
|
|
2442
|
+
const pageSegs = tailSegs.slice(fixed);
|
|
2443
|
+
const pageTail = pageSegs.map((s, i) => i === pageSegs.length - 1 ? stripExt(s) : s).join("/");
|
|
2444
|
+
page = module ? `${module}/${pageTail}` : pageTail;
|
|
2445
|
+
}
|
|
2446
|
+
return { module, endpoint, page };
|
|
2440
2447
|
}
|
|
2441
2448
|
function isMetaPath(ctx, relPath) {
|
|
2442
2449
|
const registry = getKindRegistry(ctx.config);
|
|
@@ -2528,7 +2535,7 @@ function deriveEdges(ctx) {
|
|
|
2528
2535
|
return created;
|
|
2529
2536
|
}
|
|
2530
2537
|
function scanArtifacts(ctx, actor = "system") {
|
|
2531
|
-
const summary = { registered: 0, refreshed: 0, remapped: 0, edges: 0, skipped: [] };
|
|
2538
|
+
const summary = { registered: 0, refreshed: 0, remapped: 0, edges: 0, skipped: [], unresolved: [] };
|
|
2532
2539
|
const files = [];
|
|
2533
2540
|
for (const dir of Object.values(ctx.config.docs)) {
|
|
2534
2541
|
const abs = join10(ctx.root, dir);
|
|
@@ -2552,7 +2559,7 @@ function scanArtifacts(ctx, actor = "system") {
|
|
|
2552
2559
|
const before = row.content_hash;
|
|
2553
2560
|
const after = refreshArtifact(ctx, { id: row.id }, actor);
|
|
2554
2561
|
if (after.content_hash !== before) summary.refreshed++;
|
|
2555
|
-
if (row.kind !== kind || row.module !== coords.module || row.endpoint !== coords.endpoint || row.page !== coords.page) {
|
|
2562
|
+
if (coords && (row.kind !== kind || row.module !== coords.module || row.endpoint !== coords.endpoint || row.page !== coords.page)) {
|
|
2556
2563
|
const tx2 = ctx.db.transaction(() => {
|
|
2557
2564
|
remap.run(kind, coords.module, coords.endpoint, coords.page, row.id);
|
|
2558
2565
|
logEvent(ctx.db, {
|
|
@@ -2575,6 +2582,10 @@ function scanArtifacts(ctx, actor = "system") {
|
|
|
2575
2582
|
}
|
|
2576
2583
|
return;
|
|
2577
2584
|
}
|
|
2585
|
+
if (!coords) {
|
|
2586
|
+
summary.unresolved.push(relPath);
|
|
2587
|
+
return;
|
|
2588
|
+
}
|
|
2578
2589
|
const hash = hashPath(join10(ctx.root, relPath));
|
|
2579
2590
|
if (hash === null) return;
|
|
2580
2591
|
const tx = ctx.db.transaction(() => {
|
|
@@ -2607,6 +2618,7 @@ function scanArtifacts(ctx, actor = "system") {
|
|
|
2607
2618
|
summary.edges = deriveEdges(ctx);
|
|
2608
2619
|
return summary;
|
|
2609
2620
|
}
|
|
2621
|
+
var stripExt;
|
|
2610
2622
|
var init_scan_command = __esm({
|
|
2611
2623
|
"core/commands/scan.command.ts"() {
|
|
2612
2624
|
"use strict";
|
|
@@ -2614,6 +2626,7 @@ var init_scan_command = __esm({
|
|
|
2614
2626
|
init_hash();
|
|
2615
2627
|
init_kind();
|
|
2616
2628
|
init_artifact_commands();
|
|
2629
|
+
stripExt = (s) => s.replace(/\.(md|html)$/i, "");
|
|
2617
2630
|
}
|
|
2618
2631
|
});
|
|
2619
2632
|
|
|
@@ -2681,8 +2694,8 @@ var init_qa_command = __esm({
|
|
|
2681
2694
|
var retro_command_exports = {};
|
|
2682
2695
|
__export(retro_command_exports, {
|
|
2683
2696
|
AUTO_ACTOR_WEIGHT: () => AUTO_ACTOR_WEIGHT,
|
|
2697
|
+
CANDIDATE_THRESHOLD: () => CANDIDATE_THRESHOLD,
|
|
2684
2698
|
RED_FLAG_THRESHOLD: () => RED_FLAG_THRESHOLD,
|
|
2685
|
-
SKILL_CANDIDATE_THRESHOLD: () => SKILL_CANDIDATE_THRESHOLD,
|
|
2686
2699
|
approvalStats: () => approvalStats,
|
|
2687
2700
|
distillFeedback: () => distillFeedback,
|
|
2688
2701
|
exportEventLog: () => exportEventLog,
|
|
@@ -2696,6 +2709,8 @@ function parseDbTime(s) {
|
|
|
2696
2709
|
function distillFeedback(ctx, opts = {}) {
|
|
2697
2710
|
const now = (opts.now ?? /* @__PURE__ */ new Date()).getTime();
|
|
2698
2711
|
const halfLifeMs = ctx.config.feedbackHalfLifeDays * 24 * 60 * 60 * 1e3;
|
|
2712
|
+
const candidateThreshold = ctx.config.candidateThreshold ?? CANDIDATE_THRESHOLD;
|
|
2713
|
+
const redFlagThreshold = ctx.config.redFlagThreshold ?? RED_FLAG_THRESHOLD;
|
|
2699
2714
|
let query = `
|
|
2700
2715
|
SELECT f.artifact_id, f.verdict, f.comment, f.actor, f.created_at,
|
|
2701
2716
|
a.kind, a.module, a.endpoint, a.path
|
|
@@ -2736,13 +2751,13 @@ function distillFeedback(ctx, opts = {}) {
|
|
|
2736
2751
|
for (const g of result) {
|
|
2737
2752
|
g.posScore = Math.round(g.posScore * 100) / 100;
|
|
2738
2753
|
g.negScore = Math.round(g.negScore * 100) / 100;
|
|
2739
|
-
const pos = g.posScore >=
|
|
2740
|
-
const neg = g.negScore >=
|
|
2754
|
+
const pos = g.posScore >= candidateThreshold;
|
|
2755
|
+
const neg = g.negScore >= redFlagThreshold;
|
|
2741
2756
|
if (pos && neg) {
|
|
2742
2757
|
g.bucket = "observation";
|
|
2743
2758
|
g.reason = "mixed";
|
|
2744
2759
|
} else if (pos) {
|
|
2745
|
-
g.bucket = "
|
|
2760
|
+
g.bucket = "candidate";
|
|
2746
2761
|
} else if (neg) {
|
|
2747
2762
|
g.bucket = "red-flag";
|
|
2748
2763
|
} else {
|
|
@@ -2750,7 +2765,7 @@ function distillFeedback(ctx, opts = {}) {
|
|
|
2750
2765
|
g.reason = "insufficient";
|
|
2751
2766
|
}
|
|
2752
2767
|
}
|
|
2753
|
-
const order = {
|
|
2768
|
+
const order = { candidate: 0, "red-flag": 1, observation: 2 };
|
|
2754
2769
|
return result.sort((a, b) => order[a.bucket] - order[b.bucket] || `${a.endpoint}/${a.kind}`.localeCompare(`${b.endpoint}/${b.kind}`));
|
|
2755
2770
|
}
|
|
2756
2771
|
function approvalStats(ctx) {
|
|
@@ -2805,18 +2820,18 @@ function approvalStats(ctx) {
|
|
|
2805
2820
|
}
|
|
2806
2821
|
function runRetrospective(ctx, opts = {}) {
|
|
2807
2822
|
const groups = distillFeedback(ctx, opts);
|
|
2808
|
-
const candidates = groups.filter((g) => g.bucket === "
|
|
2823
|
+
const candidates = groups.filter((g) => g.bucket === "candidate").length;
|
|
2809
2824
|
const redFlags = groups.filter((g) => g.bucket === "red-flag").length;
|
|
2810
2825
|
const cli = ctx.config.cli;
|
|
2811
2826
|
const guidance = [];
|
|
2812
2827
|
if (candidates > 0) {
|
|
2813
2828
|
guidance.push(
|
|
2814
|
-
`\u53D1\u73B0 ${candidates} \u4E2A
|
|
2829
|
+
`\u53D1\u73B0 ${candidates} \u4E2A\u7ECF\u9A8C\u5019\u9009\u7EC4:\u5BF9\u6BCF\u7EC4\u4F9D\u636E evidence(\u8DEF\u5F84+comment)\u5224\u65AD\u8BE5\u6C89\u6DC0\u4E3A\u54EA\u4E00\u79CD,\u518D\u6309\u5BF9\u5E94\u8DEF\u5F84\u4EA7\u51FA\u2014\u2014\u8DE8\u4F1A\u8BDD\u53EF\u590D\u7528\u7684\u505A\u6CD5/\u6D41\u7A0B \u2192 skill(.claude/skills/<\u540D\u79F0>/SKILL.md;\`${cli} register-meta\` \u6CE8\u518C + \`${cli} submit --actor=<\u89D2\u8272> -- <\u8DEF\u5F84>\` \u9001\u4EBA\u5BA1,approved \u624D\u751F\u6548);\u5FC5\u987B\u59CB\u7EC8\u6210\u7ACB\u3001\u53EF grep \u7684\u786C\u7EA6\u675F \u2192 \u89C4\u5219(workbench.config.json \u7684 protocolLints \u5361\u70B9,\u6216\u5199\u5165 TECH.md / \u57FA\u7EBF\u7EA6\u5B9A);\u53EA\u5BF9\u7279\u5B9A\u89D2\u8272/\u9879\u76EE\u6709\u7528\u3001\u4E0D\u503C\u5F97\u5355\u72EC\u6210\u7BC7\u7684\u6559\u8BAD\u6216\u504F\u597D \u2192 \u8BB0\u5FC6(.claude/agent-memory/<\u89D2\u8272>/,\u66F4\u65B0 MEMORY.md \u7D22\u5F15)`
|
|
2815
2830
|
);
|
|
2816
2831
|
}
|
|
2817
2832
|
if (redFlags > 0) {
|
|
2818
2833
|
guidance.push(
|
|
2819
|
-
`\u53D1\u73B0 ${redFlags} \u4E2A Red Flags \u7EC4:\u8D1F\u4F8B comment \u5373\u7D20\u6750,\u5199\
|
|
2834
|
+
`\u53D1\u73B0 ${redFlags} \u4E2A Red Flags \u7EC4:\u8D1F\u4F8B comment \u5373\u300C\u522B\u518D\u72AF\u300D\u7684\u7D20\u6750,\u540C\u6837\u4E09\u9009\u4E00\u2014\u2014\u80FD\u673A\u5668\u67E5 \u2192 protocolLints \u5361\u70B9;\u8DE8\u4F1A\u8BDD\u901A\u7528\u5751 \u2192 \u5199\u8FDB\u5BF9\u5E94 skill \u7684 Red Flags \u7AE0\u8282;\u89D2\u8272\u4E13\u5C5E\u5751 \u2192 \u8BB0\u5FC6`
|
|
2820
2835
|
);
|
|
2821
2836
|
}
|
|
2822
2837
|
if (guidance.length === 0) {
|
|
@@ -2847,11 +2862,11 @@ function exportEventLog(ctx) {
|
|
|
2847
2862
|
files: [`${ctx.config.dataDir}/events.jsonl`, `${ctx.config.dataDir}/feedback.jsonl`]
|
|
2848
2863
|
};
|
|
2849
2864
|
}
|
|
2850
|
-
var
|
|
2865
|
+
var CANDIDATE_THRESHOLD, RED_FLAG_THRESHOLD, AUTO_ACTOR_WEIGHT;
|
|
2851
2866
|
var init_retro_command = __esm({
|
|
2852
2867
|
"core/commands/retro.command.ts"() {
|
|
2853
2868
|
"use strict";
|
|
2854
|
-
|
|
2869
|
+
CANDIDATE_THRESHOLD = 3;
|
|
2855
2870
|
RED_FLAG_THRESHOLD = 2;
|
|
2856
2871
|
AUTO_ACTOR_WEIGHT = 0.5;
|
|
2857
2872
|
}
|
|
@@ -3403,6 +3418,7 @@ var init_init_command = __esm({
|
|
|
3403
3418
|
var core_exports = {};
|
|
3404
3419
|
__export(core_exports, {
|
|
3405
3420
|
AUTO_ACTOR_WEIGHT: () => AUTO_ACTOR_WEIGHT,
|
|
3421
|
+
CANDIDATE_THRESHOLD: () => CANDIDATE_THRESHOLD,
|
|
3406
3422
|
CONFIG_FILENAME: () => CONFIG_FILENAME,
|
|
3407
3423
|
CURRENT_SCHEMA_VERSION: () => CURRENT_SCHEMA_VERSION,
|
|
3408
3424
|
DEFAULT_KIND_REGISTRY: () => DEFAULT_KIND_REGISTRY,
|
|
@@ -3414,7 +3430,6 @@ __export(core_exports, {
|
|
|
3414
3430
|
PM_KINDS: () => PM_KINDS,
|
|
3415
3431
|
RED_FLAG_THRESHOLD: () => RED_FLAG_THRESHOLD,
|
|
3416
3432
|
SCHEMA_V1: () => SCHEMA_V1,
|
|
3417
|
-
SKILL_CANDIDATE_THRESHOLD: () => SKILL_CANDIDATE_THRESHOLD,
|
|
3418
3433
|
WORKBENCH_BIN: () => WORKBENCH_BIN,
|
|
3419
3434
|
WORKBENCH_DIR: () => WORKBENCH_DIR,
|
|
3420
3435
|
addTaskInput: () => addTaskInput,
|
|
@@ -3433,6 +3448,7 @@ __export(core_exports, {
|
|
|
3433
3448
|
distillFeedback: () => distillFeedback,
|
|
3434
3449
|
ensureSchema: () => ensureSchema,
|
|
3435
3450
|
everApproved: () => everApproved,
|
|
3451
|
+
expandPattern: () => expandPattern,
|
|
3436
3452
|
exportEventLog: () => exportEventLog,
|
|
3437
3453
|
feedbackArtifact: () => feedbackArtifact,
|
|
3438
3454
|
findProjectRoot: () => findProjectRoot,
|
|
@@ -3641,7 +3657,7 @@ function buildMcpServer(ctx) {
|
|
|
3641
3657
|
server.registerTool(
|
|
3642
3658
|
"wb_retro",
|
|
3643
3659
|
{
|
|
3644
|
-
description: "retrospective \u8BC1\u636E\u5305:\u53CD\u9988\u534A\u8870\u671F\u52A0\u6743\u63D0\u70BC(
|
|
3660
|
+
description: "retrospective \u8BC1\u636E\u5305:\u53CD\u9988\u534A\u8870\u671F\u52A0\u6743\u63D0\u70BC(\u7ECF\u9A8C\u5019\u9009/Red Flags/\u89C2\u5BDF)+ \u5BA1\u6279\u541E\u5410\u62A5\u8868\u3002\u4F9D\u636E evidence \u5224\u65AD\u6BCF\u7EC4\u8BE5\u6C89\u6DC0\u4E3A\u54EA\u4E00\u79CD\u518D\u4EA7\u51FA:\u53EF\u590D\u7528\u505A\u6CD5\u2192skill(.claude/skills/<\u540D\u79F0>/SKILL.md,register-meta+submit \u9001\u4EBA\u5BA1,approved \u624D\u751F\u6548);\u80FD\u673A\u5668\u67E5\u7684\u786C\u7EA6\u675F\u2192\u89C4\u5219(workbench.config.json \u7684 protocolLints);\u89D2\u8272\u4E13\u5C5E\u6559\u8BAD\u2192\u8BB0\u5FC6(.claude/agent-memory/<\u89D2\u8272>/)\u3002\u89C1 report.guidance",
|
|
3645
3661
|
inputSchema: { module: z.string().optional() }
|
|
3646
3662
|
},
|
|
3647
3663
|
async (args) => json(runRetrospective(ctx, args))
|
|
@@ -3680,7 +3696,7 @@ __export(app_exports, {
|
|
|
3680
3696
|
import fastifyCors from "@fastify/cors";
|
|
3681
3697
|
import fastifyStatic from "@fastify/static";
|
|
3682
3698
|
import Fastify from "fastify";
|
|
3683
|
-
import { existsSync as existsSync14, readFileSync as readFileSync8, readdirSync as readdirSync7, statSync as statSync6 } from "node:fs";
|
|
3699
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync8, readdirSync as readdirSync7, statSync as statSync6 } from "node:fs";
|
|
3684
3700
|
import { isAbsolute as isAbsolute2, join as join16, normalize, relative as relative2 } from "node:path";
|
|
3685
3701
|
function getArtifact(ctx, id) {
|
|
3686
3702
|
const row = ctx.db.prepare("SELECT * FROM artifacts WHERE id = ?").get(id);
|
|
@@ -3704,6 +3720,9 @@ async function createServer(ctx) {
|
|
|
3704
3720
|
if (existsSync14(webDist)) {
|
|
3705
3721
|
await app.register(fastifyStatic, { root: webDist });
|
|
3706
3722
|
}
|
|
3723
|
+
const protoRoot = join16(ctx.root, ctx.config.docs.design, "prototypes");
|
|
3724
|
+
mkdirSync8(protoRoot, { recursive: true });
|
|
3725
|
+
await app.register(fastifyStatic, { root: protoRoot, prefix: "/proto/", decorateReply: false });
|
|
3707
3726
|
app.get("/api/meta", async () => ({
|
|
3708
3727
|
root: ctx.root,
|
|
3709
3728
|
language: ctx.config.language,
|
|
@@ -3745,15 +3764,6 @@ async function createServer(ctx) {
|
|
|
3745
3764
|
}
|
|
3746
3765
|
return { artifact, content, isDirectory, missing, feedback, events };
|
|
3747
3766
|
});
|
|
3748
|
-
app.get("/api/artifact/:id/raw", async (req, reply) => {
|
|
3749
|
-
const artifact = getArtifact(ctx, parseInt(req.params.id));
|
|
3750
|
-
const abs = guardedAbsPath(ctx, artifact);
|
|
3751
|
-
if (!existsSync14(abs) || statSync6(abs).isDirectory()) {
|
|
3752
|
-
return reply.code(404).send("not found");
|
|
3753
|
-
}
|
|
3754
|
-
reply.header("content-type", artifact.path.endsWith(".html") ? "text/html; charset=utf-8" : "text/plain; charset=utf-8");
|
|
3755
|
-
return reply.send(readFileSync8(abs));
|
|
3756
|
-
});
|
|
3757
3767
|
app.get("/api/artifact/:id/files", async (req) => {
|
|
3758
3768
|
const artifact = getArtifact(ctx, parseInt(req.params.id));
|
|
3759
3769
|
const base = guardedAbsPath(ctx, artifact);
|
|
@@ -3825,7 +3835,7 @@ data: {"cursor":${cursor2}}
|
|
|
3825
3835
|
app.get("/api/skill-candidates", async () => {
|
|
3826
3836
|
const report = runRetrospective(ctx);
|
|
3827
3837
|
return {
|
|
3828
|
-
groups: report.groups.filter((g) => g.bucket === "
|
|
3838
|
+
groups: report.groups.filter((g) => g.bucket === "candidate" || g.bucket === "red-flag"),
|
|
3829
3839
|
candidates: report.candidates,
|
|
3830
3840
|
redFlags: report.redFlags,
|
|
3831
3841
|
halfLifeDays: report.halfLifeDays,
|
|
@@ -4438,6 +4448,10 @@ async function runCommand(ctx, command, a) {
|
|
|
4438
4448
|
case "scan": {
|
|
4439
4449
|
const s = scanArtifacts(ctx, a.actor || "system");
|
|
4440
4450
|
console.log(chalk.green(`\u2705 \u626B\u63CF\u5B8C\u6210:\u65B0\u767B\u8BB0 ${s.registered},\u5185\u5BB9\u5237\u65B0 ${s.refreshed},\u91CD\u6302\u5750\u6807 ${s.remapped},\u65B0\u589E\u8FB9 ${s.edges},\u6392\u9664\u5143\u4EA7\u7269 ${s.skipped.length}`));
|
|
4451
|
+
if (s.unresolved.length > 0) {
|
|
4452
|
+
console.log(chalk.yellow(`\u26A0 ${s.unresolved.length} \u4E2A\u6587\u4EF6\u547D\u4E2D kind \u4F46\u8DEF\u5F84\u4E0D\u7B26 coords \u6587\u6CD5,\u672A\u767B\u8BB0(\u975E\u89C4\u8303/\u5E9F\u5F03\u7AEF?):`));
|
|
4453
|
+
for (const p of s.unresolved) console.log(chalk.yellow(` - ${p}`));
|
|
4454
|
+
}
|
|
4441
4455
|
break;
|
|
4442
4456
|
}
|
|
4443
4457
|
case "move": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dawipong/opcflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Spec-anchored, drift-enforced execution layer for AI coding agents — artifact DAG, trust gates, multi-role pipeline; generates agents/MCP/hooks for Claude Code, Codex, OpenCode & Cursor.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Dawi",
|
package/web/dist/assets/{abnfDiagram-VRR7QNED-DctuL32B.js → abnfDiagram-VRR7QNED-DAx6EpGI.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e}from"./src-W4W9ly-y.js";import{n as t}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import"./chunk-WYO6CB5R-BaRUDpDl.js";import"./index-
|
|
1
|
+
import{m as e}from"./src-W4W9ly-y.js";import{n as t}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import"./chunk-WYO6CB5R-BaRUDpDl.js";import"./index-HsPwY2CU.js";import{m as n,t as r}from"./mermaid-parser.core-CAAH_OSq.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-DXlFl9Jv.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";var c=n().RailroadAbnf.parser.LangiumParser,l=t(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=t(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=t(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=t(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=t(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 Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=t(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=t(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:t(t=>{o.clear(),e.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(t);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new r(n);let i=n.value;e.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),e.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
|