@dawipong/opcflow 0.7.0 → 0.9.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 +1 -1
- package/README.md +1 -1
- package/dist/cli.mjs +167 -76
- package/package.json +1 -1
- package/web/dist/assets/{abnfDiagram-VRR7QNED-Cwj4KBKb.js → abnfDiagram-VRR7QNED-D19VSiUB.js} +1 -1
- package/web/dist/assets/{architectureDiagram-ZJ3FMSHR-B83QZuM-.js → architectureDiagram-ZJ3FMSHR-CJIfwLFX.js} +1 -1
- package/web/dist/assets/{chunk-EX3LRPZG-DOdm7LPI.js → chunk-EX3LRPZG-Dybqgi_R.js} +1 -1
- package/web/dist/assets/{chunk-MOJQB5TN-BrFLYoih.js → chunk-MOJQB5TN-CaHrOT4b.js} +1 -1
- package/web/dist/assets/{chunk-PUDLZKDR-D4rKEnrx.js → chunk-PUDLZKDR-BmAYd1VC.js} +1 -1
- package/web/dist/assets/{chunk-V7JOEXUC-Bb-id9Vk.js → chunk-V7JOEXUC-Dnu3ng09.js} +1 -1
- package/web/dist/assets/{classDiagram-OUVF2IWQ-XODf2seK.js → classDiagram-OUVF2IWQ-CumBxXHa.js} +1 -1
- package/web/dist/assets/{classDiagram-v2-EOCWNBFH-XODf2seK.js → classDiagram-v2-EOCWNBFH-CumBxXHa.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-TSTJHNR4-BQ_p-vVW.js → cynefinDiagram-TSTJHNR4-lP4XB4zv.js} +1 -1
- package/web/dist/assets/{dagre-VKFMJZFB-sC49sUfv.js → dagre-VKFMJZFB-Bd0-4hrw.js} +1 -1
- package/web/dist/assets/{diagram-G47NLZAW-BFZHUq5q.js → diagram-G47NLZAW-Cv5bS2zU.js} +1 -1
- package/web/dist/assets/{diagram-NH7WQ7WH-CnF3KhFt.js → diagram-NH7WQ7WH-D0t6TvWh.js} +1 -1
- package/web/dist/assets/{diagram-OA4YK3LP-DyxQbSTl.js → diagram-OA4YK3LP-4rWIlwgn.js} +1 -1
- package/web/dist/assets/{diagram-WEI45ONY-u45fnoLo.js → diagram-WEI45ONY-CwwshS2w.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-CCIWWBDH-CRWFKre6.js → ebnfDiagram-CCIWWBDH-Bh1RkpJ9.js} +1 -1
- package/web/dist/assets/{erDiagram-Q63AITRT-BWW0EF63.js → erDiagram-Q63AITRT-E5bP16-J.js} +1 -1
- package/web/dist/assets/{flowDiagram-23GEKE2U-ADg31f6W.js → flowDiagram-23GEKE2U-BAtJJjTJ.js} +1 -1
- package/web/dist/assets/{ganttDiagram-NO4QXBWP-Cs24RF6t.js → ganttDiagram-NO4QXBWP-BiuqaxNH.js} +1 -1
- package/web/dist/assets/{index-Cf-76g9P.js → index-xKffKKoc.js} +66 -66
- package/web/dist/assets/{infoDiagram-FWYZ7A6U-C2mnh_tb.js → infoDiagram-FWYZ7A6U-Co1V0oYE.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-FXEZZL3T-B_eQA-ns.js → ishikawaDiagram-FXEZZL3T-BR58gOGl.js} +1 -1
- package/web/dist/assets/{kanban-definition-HUTT4EX6-ov2PISLL.js → kanban-definition-HUTT4EX6-Cb1c-R71.js} +1 -1
- package/web/dist/assets/{linear-BcjVZ8a_.js → linear-DceE_QuD.js} +1 -1
- package/web/dist/assets/{mindmap-definition-LN4V7U3C-0NmEERgv.js → mindmap-definition-LN4V7U3C-CqJlgVe3.js} +1 -1
- package/web/dist/assets/{pegDiagram-2B236MQR-CiAlAiVi.js → pegDiagram-2B236MQR-B-LFtMLT.js} +1 -1
- package/web/dist/assets/{pieDiagram-ENE6RG2P-inbfmWLn.js → pieDiagram-ENE6RG2P-ko7PN4ZC.js} +1 -1
- package/web/dist/assets/{quadrantDiagram-ABIIQ3AL-BG-L4IlR.js → quadrantDiagram-ABIIQ3AL-DtEPofEc.js} +1 -1
- package/web/dist/assets/{railroadDiagram-RFXS5EU6-zGdHxvjI.js → railroadDiagram-RFXS5EU6-B-5wzc7X.js} +1 -1
- package/web/dist/assets/{requirementDiagram-TGXJPOKE-DYBqltr1.js → requirementDiagram-TGXJPOKE-B3MZnN4A.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-DBY2YBRQ-yoZvzY4D.js → sequenceDiagram-DBY2YBRQ-IIdjO_05.js} +1 -1
- package/web/dist/assets/{stateDiagram-2N3HPSRC-UXu_N6p7.js → stateDiagram-2N3HPSRC-BmkTfvco.js} +1 -1
- package/web/dist/assets/{stateDiagram-v2-6OUMAXLB-K2x-LOU8.js → stateDiagram-v2-6OUMAXLB-DIGnr8jK.js} +1 -1
- package/web/dist/assets/{swimlanes-5IMT3BWC-CJml4vNZ.js → swimlanes-5IMT3BWC-Dy8N-IiF.js} +1 -1
- package/web/dist/assets/{swimlanesDiagram-G3AALYLV-BJTq-tR6.js → swimlanesDiagram-G3AALYLV-7ULR3aAR.js} +1 -1
- package/web/dist/assets/{timeline-definition-FHXFAJF6-DmHx8Bri.js → timeline-definition-FHXFAJF6-BGFbzOm6.js} +1 -1
- package/web/dist/assets/{vennDiagram-L72KCM5P-jIe_jCw3.js → vennDiagram-L72KCM5P-D81cM-yT.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-EHGQE667-DEJFp47s.js → wardleyDiagram-EHGQE667-DW4lo8Mc.js} +1 -1
- package/web/dist/assets/{xychartDiagram-FW5EYKEG-DBBG2OXr.js → xychartDiagram-FW5EYKEG-BBauB7MN.js} +1 -1
- package/web/dist/index.html +1 -1
package/README.en.md
CHANGED
|
@@ -174,7 +174,7 @@ Generated by `init`, hand-edited afterwards — each project's coordinate system
|
|
|
174
174
|
mermaid / HTML-prototype iframe / code rendering, the **review-queue diff** (approved version vs.
|
|
175
175
|
current), the **artifact relation graph** (derived edges + manually declared edges: drag to connect,
|
|
176
176
|
manual edges unbindable, derived edges reconciled by scan; manual edges join invalidation propagation),
|
|
177
|
-
an event timeline, and live SSE refresh. Approve, reject, and thumbs-up/down prototypes right
|
|
177
|
+
an event timeline, and live SSE refresh. Set your **actor identity** in the header (when a team shares one workbench, approvals/feedback/edges are attributed per actor). Approve, reject, and thumbs-up/down prototypes right
|
|
178
178
|
here.
|
|
179
179
|
|
|
180
180
|
## Scripts
|
package/README.md
CHANGED
|
@@ -161,7 +161,7 @@ flowchart LR
|
|
|
161
161
|
`opcflow serve` 起在 `http://127.0.0.1:5620`:artifact 树(状态实时变色)、markdown / mermaid / HTML 原型
|
|
162
162
|
iframe / 代码渲染、**待审队列 diff**(approved 版本 vs 当前)、**产出物关系图**(自动推导边 + 手动声明边:
|
|
163
163
|
画布上拖线即声明、手动边可解绑、推导边由 scan 对账维护;手动边同样参与失效传播)、事件时间线、
|
|
164
|
-
SSE 实时刷新。审批、打回、给原型 👍👎
|
|
164
|
+
SSE 实时刷新。审批、打回、给原型 👍👎 都在这里点;头部可设置**操作人身份**(多人共用一个工作台时,审批/反馈/建边按 actor 区分留痕)。
|
|
165
165
|
|
|
166
166
|
## 脚本
|
|
167
167
|
|
package/dist/cli.mjs
CHANGED
|
@@ -506,6 +506,9 @@ var init_platforms = __esm({
|
|
|
506
506
|
agentsDir: ".claude/agents",
|
|
507
507
|
skillsDir: ".claude/skills",
|
|
508
508
|
hooksScanDir: ".claude/hooks",
|
|
509
|
+
rulesDir: null,
|
|
510
|
+
// 规则载体是 CLAUDE.md(手改大文件,刻意不追)
|
|
511
|
+
memoryDir: ".claude/agent-memory",
|
|
509
512
|
defaultModel: "opus",
|
|
510
513
|
memoryBlock: (role, lang) => claudeMemory(role, lang),
|
|
511
514
|
projectDirEnvVar: "CLAUDE_PROJECT_DIR",
|
|
@@ -555,6 +558,9 @@ var init_platforms = __esm({
|
|
|
555
558
|
agentsDir: ".codex/agents",
|
|
556
559
|
skillsDir: ".agents/skills",
|
|
557
560
|
hooksScanDir: null,
|
|
561
|
+
rulesDir: null,
|
|
562
|
+
// 规则/记忆载体都是 AGENTS.md(手改大文件,刻意不追)
|
|
563
|
+
memoryDir: null,
|
|
558
564
|
defaultModel: "gpt-5.1-codex",
|
|
559
565
|
memoryBlock: (_role, lang) => agentsMdMemory("Codex", lang),
|
|
560
566
|
projectDirEnvVar: "CODEX_PROJECT_DIR",
|
|
@@ -601,6 +607,9 @@ var init_platforms = __esm({
|
|
|
601
607
|
agentsDir: ".opencode/agents",
|
|
602
608
|
skillsDir: ".opencode/skills",
|
|
603
609
|
hooksScanDir: null,
|
|
610
|
+
rulesDir: null,
|
|
611
|
+
// 同 codex:AGENTS.md 承载,不追
|
|
612
|
+
memoryDir: null,
|
|
604
613
|
defaultModel: "anthropic/claude-opus-4-8",
|
|
605
614
|
memoryBlock: (_role, lang) => agentsMdMemory("OpenCode", lang),
|
|
606
615
|
projectDirEnvVar: "OPENCODE_PROJECT_DIR",
|
|
@@ -675,6 +684,9 @@ export const opcflow = async () => ({
|
|
|
675
684
|
agentsDir: ".cursor/agents",
|
|
676
685
|
skillsDir: ".cursor/skills",
|
|
677
686
|
hooksScanDir: null,
|
|
687
|
+
rulesDir: ".cursor/rules",
|
|
688
|
+
memoryDir: null,
|
|
689
|
+
// Cursor 原生 Memories 非文件形态,无法登记跟踪
|
|
678
690
|
defaultModel: "claude-opus-4-8",
|
|
679
691
|
memoryBlock: (_role, lang) => cursorMemory(lang),
|
|
680
692
|
projectDirEnvVar: "CURSOR_PROJECT_DIR",
|
|
@@ -755,6 +767,14 @@ function getKindRegistry(config) {
|
|
|
755
767
|
...merged["hook-script"],
|
|
756
768
|
pathPatterns: uniq(adapters.map((a) => a.hooksScanDir).filter((d) => !!d).map((d) => `${d}/`))
|
|
757
769
|
};
|
|
770
|
+
merged.rule = {
|
|
771
|
+
...merged.rule,
|
|
772
|
+
pathPatterns: uniq(adapters.map((a) => a.rulesDir).filter((d) => !!d).map((d) => `${d}/`))
|
|
773
|
+
};
|
|
774
|
+
merged.memory = {
|
|
775
|
+
...merged.memory,
|
|
776
|
+
pathPatterns: uniq(adapters.map((a) => a.memoryDir).filter((d) => !!d).map((d) => `${d}/`))
|
|
777
|
+
};
|
|
758
778
|
return merged;
|
|
759
779
|
}
|
|
760
780
|
function kindSpec(config, kind) {
|
|
@@ -803,6 +823,10 @@ var init_kind = __esm({
|
|
|
803
823
|
skill: { level: "project", approval: "human", parents: [], drivesStale: false, hashMode: "text-normalize", retrieval: "full", meta: true, pathPatterns: [".claude/skills/"] },
|
|
804
824
|
"hook-script": { level: "project", approval: "human", parents: [], drivesStale: false, hashMode: "text-normalize", retrieval: "full", meta: true, pathPatterns: [".claude/hooks/"] },
|
|
805
825
|
plan: { level: "project", approval: "human", parents: [], drivesStale: false, hashMode: "text-normalize", retrieval: "summary", meta: true, pathPatterns: ["docs/workbench/PLAN.md"] },
|
|
826
|
+
// 规则/记忆:平台原生读取生效的内容(approval:none——opcflow 只登记+跟踪变更进 draft,不设审卡);
|
|
827
|
+
// pathPatterns 按目标平台的 rulesDir/memoryDir 动态展开(getKindRegistry),仅目录文件形态可追
|
|
828
|
+
rule: { level: "project", approval: "none", parents: [], drivesStale: false, hashMode: "text-normalize", retrieval: "full", meta: true },
|
|
829
|
+
memory: { level: "project", approval: "none", parents: [], drivesStale: false, hashMode: "text-normalize", retrieval: "summary", meta: true },
|
|
806
830
|
baseline: { level: "project", approval: "human", parents: [], drivesStale: true, hashMode: "text-normalize", retrieval: "summary", pathPatterns: ["ARCHITECTURE.md", "TECH.md"] },
|
|
807
831
|
project: { level: "project", approval: "human", parents: [], drivesStale: true, hashMode: "text-normalize", retrieval: "summary", pathPatterns: ["{prd}/project.md"] },
|
|
808
832
|
roles: { level: "project", approval: "human", parents: ["project"], drivesStale: true, hashMode: "text-normalize", retrieval: "full", pathPatterns: ["{prd}/roles.md"] },
|
|
@@ -837,6 +861,71 @@ var init_kind = __esm({
|
|
|
837
861
|
}
|
|
838
862
|
});
|
|
839
863
|
|
|
864
|
+
// core/roles.ts
|
|
865
|
+
function getRoleRegistry(config) {
|
|
866
|
+
const merged = {};
|
|
867
|
+
for (const [role, spec] of Object.entries(DEFAULT_ROLE_REGISTRY)) merged[role] = { ...spec };
|
|
868
|
+
for (const [role, kinds] of Object.entries(config.roleProduces ?? {})) {
|
|
869
|
+
merged[role] = { ...merged[role] ?? { produces: [] }, produces: kinds };
|
|
870
|
+
}
|
|
871
|
+
for (const [role, override] of Object.entries(config.roles ?? {})) {
|
|
872
|
+
merged[role] = { ...merged[role] ?? { produces: [] }, ...override };
|
|
873
|
+
}
|
|
874
|
+
return merged;
|
|
875
|
+
}
|
|
876
|
+
function ownerRoleOf(config, kind) {
|
|
877
|
+
for (const [role, spec] of Object.entries(getRoleRegistry(config))) {
|
|
878
|
+
if (spec.produces.includes(kind)) return role;
|
|
879
|
+
}
|
|
880
|
+
if (kind === "code") return "developer";
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
var DEFAULT_ROLE_REGISTRY;
|
|
884
|
+
var init_roles = __esm({
|
|
885
|
+
"core/roles.ts"() {
|
|
886
|
+
"use strict";
|
|
887
|
+
init_kind();
|
|
888
|
+
DEFAULT_ROLE_REGISTRY = {
|
|
889
|
+
"product-manager": {
|
|
890
|
+
produces: ["project", "roles", "glossary", "flow", "module-prd", "page-prd"]
|
|
891
|
+
},
|
|
892
|
+
architect: {
|
|
893
|
+
produces: ["db-doc", "api-doc"],
|
|
894
|
+
requires: [{ desc: "PM \u4EA7\u51FA(\u6A21\u5757/\u9875\u9762 PRD)", kinds: PM_KINDS }],
|
|
895
|
+
dispatch: [
|
|
896
|
+
{ at: "module", endpoint: "common", content: "\u8BBE\u8BA1 {module} \u6A21\u5757\u6570\u636E\u5E93" },
|
|
897
|
+
{ at: "module", endpoint: "service", content: "\u8BBE\u8BA1 {module} \u6A21\u5757 API \u6587\u6863" }
|
|
898
|
+
]
|
|
899
|
+
},
|
|
900
|
+
designer: {
|
|
901
|
+
produces: ["design-system", "design-prompt", "prototype"],
|
|
902
|
+
requires: [{ desc: "PM \u4EA7\u51FA(\u6A21\u5757/\u9875\u9762 PRD)", kinds: PM_KINDS }],
|
|
903
|
+
dispatch: [
|
|
904
|
+
{ at: "endpoint", ifMissingKind: "design-system", produces: ["design-system"], content: "\u5EFA\u7ACB {endpoint} \u7AEF\u8BBE\u8BA1\u7CFB\u7EDF(\u8BE5\u7AEF\u9996\u4E2A\u9875\u9762\u8BBE\u8BA1\u524D\u7F6E)" },
|
|
905
|
+
{ at: "page", produces: ["design-prompt", "prototype"], content: "\u8BBE\u8BA1 {endpoint}/{page} \u9875\u9762(\u63D0\u793A\u8BCD+\u539F\u578B)" }
|
|
906
|
+
]
|
|
907
|
+
},
|
|
908
|
+
developer: {
|
|
909
|
+
produces: ["code"],
|
|
910
|
+
requires: [
|
|
911
|
+
{ desc: "\u6570\u636E\u5E93\u6587\u6863", kinds: ["db-doc"] },
|
|
912
|
+
{ desc: "API \u6587\u6863", kinds: ["api-doc"], when: { endpointNot: "service" } },
|
|
913
|
+
{ desc: "designer {endpoint} \u8BBE\u8BA1\u7A3F", kinds: ["design-prompt", "prototype"], when: { endpointNot: "service" } }
|
|
914
|
+
],
|
|
915
|
+
dispatch: [
|
|
916
|
+
{ at: "module", endpoint: "service", content: "\u5B9E\u73B0 {module} \u6A21\u5757 service \u7AEF" },
|
|
917
|
+
{ at: "page", content: "\u5B9E\u73B0 {endpoint}/{page} \u9875\u9762" }
|
|
918
|
+
],
|
|
919
|
+
onQaFail: "rework"
|
|
920
|
+
},
|
|
921
|
+
qa: {
|
|
922
|
+
produces: ["acceptance"],
|
|
923
|
+
dispatch: [{ at: "page", type: "qa", content: "\u9A8C\u6536 {endpoint}/{page} \u9875\u9762" }]
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
|
|
840
929
|
// core/derive.ts
|
|
841
930
|
var derive_exports = {};
|
|
842
931
|
__export(derive_exports, {
|
|
@@ -974,31 +1063,22 @@ function levelFilter(level, task) {
|
|
|
974
1063
|
}
|
|
975
1064
|
}
|
|
976
1065
|
function producedKinds(ctx, task) {
|
|
977
|
-
const
|
|
978
|
-
if (
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
return
|
|
982
|
-
}
|
|
983
|
-
function existRequirements(task) {
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
return [{ desc: "\u6570\u636E\u5E93\u6587\u6863", level: "exist", kinds: ["db-doc"], filter: { module: m } }];
|
|
992
|
-
}
|
|
993
|
-
return [
|
|
994
|
-
{ desc: "\u6570\u636E\u5E93\u6587\u6863", level: "exist", kinds: ["db-doc"], filter: { module: m } },
|
|
995
|
-
{ desc: "API \u6587\u6863", level: "exist", kinds: ["api-doc"], filter: { module: m } },
|
|
996
|
-
{ desc: `designer ${task.endpoint} \u8BBE\u8BA1\u7A3F`, level: "exist", kinds: ["design-prompt", "prototype"], filter: { module: m, endpoint: task.endpoint } }
|
|
997
|
-
];
|
|
998
|
-
}
|
|
999
|
-
default:
|
|
1000
|
-
return [];
|
|
1066
|
+
const spec = getRoleRegistry(ctx.config)[task.role];
|
|
1067
|
+
if (!spec) return [];
|
|
1068
|
+
const wantPage = !!task.page;
|
|
1069
|
+
const rule = (spec.dispatch ?? []).find((d) => (wantPage ? d.at === "page" : d.at !== "page") && d.produces);
|
|
1070
|
+
return rule?.produces ?? spec.produces;
|
|
1071
|
+
}
|
|
1072
|
+
function existRequirements(ctx, task) {
|
|
1073
|
+
const spec = getRoleRegistry(ctx.config)[task.role];
|
|
1074
|
+
const reqs = [];
|
|
1075
|
+
for (const r of spec?.requires ?? []) {
|
|
1076
|
+
if (r.when?.endpoint && task.endpoint !== r.when.endpoint) continue;
|
|
1077
|
+
if (r.when?.endpointNot && task.endpoint === r.when.endpointNot) continue;
|
|
1078
|
+
const filter = r.kinds.includes("design-prompt") || r.kinds.includes("prototype") ? { module: task.module, endpoint: task.endpoint } : { module: task.module };
|
|
1079
|
+
reqs.push({ desc: r.desc.replaceAll("{endpoint}", task.endpoint ?? ""), level: "exist", kinds: r.kinds, filter });
|
|
1001
1080
|
}
|
|
1081
|
+
return reqs;
|
|
1002
1082
|
}
|
|
1003
1083
|
function approvedRequirements(ctx, task) {
|
|
1004
1084
|
const registry = getKindRegistry(ctx.config);
|
|
@@ -1027,8 +1107,7 @@ function approvedRequirements(ctx, task) {
|
|
|
1027
1107
|
}
|
|
1028
1108
|
function claimRequirements(ctx, task) {
|
|
1029
1109
|
if (task.type !== "build" && task.type !== "qa") return [];
|
|
1030
|
-
|
|
1031
|
-
return [...existRequirements(task), ...approvedRequirements(ctx, task)];
|
|
1110
|
+
return [...existRequirements(ctx, task), ...approvedRequirements(ctx, task)];
|
|
1032
1111
|
}
|
|
1033
1112
|
function requirementSatisfied(ctx, req, matched) {
|
|
1034
1113
|
if (req.level === "exist") return matched.length > 0;
|
|
@@ -1154,6 +1233,7 @@ var init_gates = __esm({
|
|
|
1154
1233
|
init_derive();
|
|
1155
1234
|
init_kind();
|
|
1156
1235
|
init_lints();
|
|
1236
|
+
init_roles();
|
|
1157
1237
|
}
|
|
1158
1238
|
});
|
|
1159
1239
|
|
|
@@ -1800,11 +1880,7 @@ var init_artifact_commands = __esm({
|
|
|
1800
1880
|
import { existsSync as existsSync6 } from "node:fs";
|
|
1801
1881
|
import { join as join7 } from "node:path";
|
|
1802
1882
|
function ownerRole(ctx, kind) {
|
|
1803
|
-
|
|
1804
|
-
if (kinds.includes(kind)) return role;
|
|
1805
|
-
}
|
|
1806
|
-
if (kind === "code") return "developer";
|
|
1807
|
-
return null;
|
|
1883
|
+
return ownerRoleOf(ctx.config, kind);
|
|
1808
1884
|
}
|
|
1809
1885
|
function openReviewTargetKeys(ctx, artifactId) {
|
|
1810
1886
|
const rows = ctx.db.prepare(
|
|
@@ -1948,15 +2024,20 @@ var init_sync_command = __esm({
|
|
|
1948
2024
|
init_events();
|
|
1949
2025
|
init_git();
|
|
1950
2026
|
init_kind();
|
|
2027
|
+
init_roles();
|
|
1951
2028
|
init_artifact_commands();
|
|
1952
2029
|
}
|
|
1953
2030
|
});
|
|
1954
2031
|
|
|
1955
2032
|
// core/commands/task.commands.ts
|
|
2033
|
+
function getRoleRegistryOwner(ctx, kind) {
|
|
2034
|
+
return ownerRoleOf(ctx.config, kind) ?? "qa";
|
|
2035
|
+
}
|
|
1956
2036
|
function createTask(ctx, p) {
|
|
1957
2037
|
const type = p.type ?? "build";
|
|
1958
2038
|
if (!VALID_TYPES.includes(type)) throw new Error(`\u65E0\u6548\u7684\u4EFB\u52A1\u7C7B\u578B: ${type},\u53EF\u9009\u503C: ${VALID_TYPES.join(", ")}`);
|
|
1959
|
-
|
|
2039
|
+
const validRoles = Object.keys(getRoleRegistry(ctx.config));
|
|
2040
|
+
if (!validRoles.includes(p.role)) throw new Error(`\u65E0\u6548\u7684\u89D2\u8272: ${p.role},\u53EF\u9009\u503C: ${validRoles.join(", ")}`);
|
|
1960
2041
|
const module = normalizeModule(p.module, ctx.config);
|
|
1961
2042
|
const tx = ctx.db.transaction(() => {
|
|
1962
2043
|
const result = ctx.db.prepare(
|
|
@@ -2051,11 +2132,12 @@ function updateTask(ctx, { id, status, operator, force = false }) {
|
|
|
2051
2132
|
return { id, warnings };
|
|
2052
2133
|
}
|
|
2053
2134
|
function spawnFollowupQa(ctx, reworkTask) {
|
|
2135
|
+
const qaRole = getRoleRegistryOwner(ctx, "acceptance");
|
|
2054
2136
|
const tx = ctx.db.transaction(() => {
|
|
2055
2137
|
const result = ctx.db.prepare(
|
|
2056
2138
|
`INSERT INTO tasks (module, role, endpoint, page, type, status, assignee, creator, content)
|
|
2057
|
-
VALUES (?,
|
|
2058
|
-
).run(reworkTask.module, reworkTask.endpoint, reworkTask.page, `[\u590D\u9A8C] rework #${reworkTask.id} \u5DF2\u5B8C\u6210,\u8BF7\u91CD\u65B0\u9A8C\u6536`);
|
|
2139
|
+
VALUES (?, ?, ?, ?, 'qa', 'pending', ?, 'system', ?)`
|
|
2140
|
+
).run(reworkTask.module, qaRole, reworkTask.endpoint, reworkTask.page, qaRole, `[\u590D\u9A8C] rework #${reworkTask.id} \u5DF2\u5B8C\u6210,\u8BF7\u91CD\u65B0\u9A8C\u6536`);
|
|
2059
2141
|
const qaId = result.lastInsertRowid;
|
|
2060
2142
|
logEvent(ctx.db, {
|
|
2061
2143
|
entityType: "task",
|
|
@@ -2215,7 +2297,7 @@ function getTaskDetail(ctx, id) {
|
|
|
2215
2297
|
}
|
|
2216
2298
|
return { task, events, outputs, staleness: taskStaleness(ctx.db, id) };
|
|
2217
2299
|
}
|
|
2218
|
-
var VALID_STATUS, VALID_TYPES
|
|
2300
|
+
var VALID_STATUS, VALID_TYPES;
|
|
2219
2301
|
var init_task_commands = __esm({
|
|
2220
2302
|
"core/commands/task.commands.ts"() {
|
|
2221
2303
|
"use strict";
|
|
@@ -2225,11 +2307,11 @@ var init_task_commands = __esm({
|
|
|
2225
2307
|
init_git();
|
|
2226
2308
|
init_gh();
|
|
2227
2309
|
init_kind();
|
|
2310
|
+
init_roles();
|
|
2228
2311
|
init_artifact_commands();
|
|
2229
2312
|
init_sync_command();
|
|
2230
2313
|
VALID_STATUS = ["pending", "in_progress", "completed", "cancelled"];
|
|
2231
2314
|
VALID_TYPES = ["build", "review", "qa", "hotfix", "baseline", "legacy", "rework"];
|
|
2232
|
-
VALID_ROLES = ["product-manager", "architect", "designer", "developer", "qa"];
|
|
2233
2315
|
}
|
|
2234
2316
|
});
|
|
2235
2317
|
|
|
@@ -2402,6 +2484,8 @@ function metaSources(config) {
|
|
|
2402
2484
|
add({ dir: a.agentsDir, filter: (n) => n.endsWith(".md") || n.endsWith(".toml") });
|
|
2403
2485
|
add({ dir: a.skillsDir, filter: (n) => n === "SKILL.md", recursive: true });
|
|
2404
2486
|
if (a.hooksScanDir) add({ dir: a.hooksScanDir, recursive: true });
|
|
2487
|
+
if (a.rulesDir) add({ dir: a.rulesDir, filter: (n) => n.endsWith(".md") || n.endsWith(".mdc"), recursive: true });
|
|
2488
|
+
if (a.memoryDir) add({ dir: a.memoryDir, filter: (n) => n.endsWith(".md"), recursive: true });
|
|
2405
2489
|
}
|
|
2406
2490
|
add({ dir: "docs/workbench", filter: (n) => n === "PLAN.md" });
|
|
2407
2491
|
return sources;
|
|
@@ -2752,7 +2836,8 @@ var init_scan_command = __esm({
|
|
|
2752
2836
|
// core/commands/qa.command.ts
|
|
2753
2837
|
function recordQaResult(ctx, p) {
|
|
2754
2838
|
const task = getTaskRow(ctx, p.id);
|
|
2755
|
-
|
|
2839
|
+
const acceptanceRole = ownerRoleOf(ctx.config, "acceptance") ?? "qa";
|
|
2840
|
+
if (task.role !== acceptanceRole) throw new Error(`\u4EFB\u52A1 #${p.id} \u4E0D\u662F ${acceptanceRole} \u4EFB\u52A1`);
|
|
2756
2841
|
if (task.assignee !== p.operator) throw new Error(`\u53EA\u6709\u6267\u884C\u4EBA\u624D\u80FD\u8BB0\u5F55\u9A8C\u6536\u7ED3\u679C`);
|
|
2757
2842
|
if (p.result === "fail" && !p.reason?.trim()) throw new Error(`\u9A8C\u6536\u4E0D\u901A\u8FC7\u5FC5\u987B\u9644\u539F\u56E0,\u5B83\u5C06\u6210\u4E3A rework \u4EFB\u52A1\u7684\u5185\u5BB9`);
|
|
2758
2843
|
const eventName = p.result === "pass" ? "qa_passed" : "qa_failed";
|
|
@@ -2780,11 +2865,12 @@ function recordQaResult(ctx, p) {
|
|
|
2780
2865
|
}
|
|
2781
2866
|
return { reworkTaskId: null };
|
|
2782
2867
|
}
|
|
2868
|
+
const reworkRole = ownerRoleOf(ctx.config, "code") ?? "developer";
|
|
2783
2869
|
const tx2 = ctx.db.transaction(() => {
|
|
2784
2870
|
const result = ctx.db.prepare(
|
|
2785
2871
|
`INSERT INTO tasks (module, role, endpoint, page, type, status, assignee, creator, content)
|
|
2786
|
-
VALUES (?,
|
|
2787
|
-
).run(task.module, task.endpoint, task.page, `[\u8FD4\u5DE5] QA #${task.id} \u9A8C\u6536\u4E0D\u901A\u8FC7:${p.reason}`);
|
|
2872
|
+
VALUES (?, ?, ?, ?, 'rework', 'pending', ?, 'system', ?)`
|
|
2873
|
+
).run(task.module, reworkRole, task.endpoint, task.page, reworkRole, `[\u8FD4\u5DE5] QA #${task.id} \u9A8C\u6536\u4E0D\u901A\u8FC7:${p.reason}`);
|
|
2788
2874
|
const reworkId = result.lastInsertRowid;
|
|
2789
2875
|
logEvent(ctx.db, {
|
|
2790
2876
|
entityType: "task",
|
|
@@ -2804,6 +2890,7 @@ var init_qa_command = __esm({
|
|
|
2804
2890
|
"core/commands/qa.command.ts"() {
|
|
2805
2891
|
"use strict";
|
|
2806
2892
|
init_events();
|
|
2893
|
+
init_roles();
|
|
2807
2894
|
init_artifact_commands();
|
|
2808
2895
|
init_task_commands();
|
|
2809
2896
|
}
|
|
@@ -3019,35 +3106,25 @@ function planModule(ctx, moduleRaw, creator = "product-manager") {
|
|
|
3019
3106
|
}
|
|
3020
3107
|
const pagePrds = byKind("page-prd").filter((a) => a.endpoint && a.page && existsSync10(join12(ctx.root, a.path)));
|
|
3021
3108
|
const endpoints = [...new Set(pagePrds.map((a) => a.endpoint))];
|
|
3022
|
-
const
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3109
|
+
const registry = getRoleRegistry(ctx.config);
|
|
3110
|
+
const interp = (tpl, v) => tpl.replaceAll("{module}", v.module).replaceAll("{endpoint}", v.endpoint ?? "").replaceAll("{page}", v.page ?? "");
|
|
3111
|
+
const desired = [];
|
|
3112
|
+
for (const role of ctx.config.pipeline) {
|
|
3113
|
+
for (const d of registry[role]?.dispatch ?? []) {
|
|
3114
|
+
if (d.at === "module") {
|
|
3115
|
+
desired.push({ role, endpoint: d.endpoint ?? null, page: null, type: d.type ?? "build", assignee: role, content: interp(d.content, { module }) });
|
|
3116
|
+
} else if (d.at === "endpoint") {
|
|
3117
|
+
for (const endpoint of endpoints) {
|
|
3118
|
+
if (d.ifMissingKind && artifacts.some((a) => a.kind === d.ifMissingKind && a.endpoint === endpoint)) continue;
|
|
3119
|
+
desired.push({ role, endpoint, page: null, type: d.type ?? "build", assignee: role, content: interp(d.content, { module, endpoint }) });
|
|
3120
|
+
}
|
|
3121
|
+
} else {
|
|
3122
|
+
for (const prd of pagePrds) {
|
|
3123
|
+
desired.push({ role, endpoint: prd.endpoint, page: prd.page, type: d.type ?? "build", assignee: role, content: interp(d.content, { module, endpoint: prd.endpoint, page: prd.page }) });
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3038
3126
|
}
|
|
3039
3127
|
}
|
|
3040
|
-
for (const prd of pagePrds) {
|
|
3041
|
-
desired.push(
|
|
3042
|
-
{ role: "designer", endpoint: prd.endpoint, page: prd.page, type: "build", assignee: "designer", content: `\u8BBE\u8BA1 ${prd.endpoint}/${prd.page} \u9875\u9762(\u63D0\u793A\u8BCD+\u539F\u578B)` },
|
|
3043
|
-
{ role: "developer", endpoint: prd.endpoint, page: prd.page, type: "build", assignee: "developer", content: `\u5B9E\u73B0 ${prd.endpoint}/${prd.page} \u9875\u9762` },
|
|
3044
|
-
{ role: "qa", endpoint: prd.endpoint, page: prd.page, type: "qa", assignee: "qa", content: `\u9A8C\u6536 ${prd.endpoint}/${prd.page} \u9875\u9762` }
|
|
3045
|
-
);
|
|
3046
|
-
}
|
|
3047
|
-
const activeRoles = new Set(ctx.config.pipeline);
|
|
3048
|
-
const filtered = desired.filter((d) => activeRoles.has(d.role));
|
|
3049
|
-
desired.length = 0;
|
|
3050
|
-
desired.push(...filtered);
|
|
3051
3128
|
const exists = ctx.db.prepare(
|
|
3052
3129
|
`SELECT COUNT(*) AS c FROM tasks
|
|
3053
3130
|
WHERE module IS ? AND role = ? AND endpoint IS ? AND page IS ? AND type = ? AND status != 'cancelled'`
|
|
@@ -3092,6 +3169,7 @@ var init_plan_command = __esm({
|
|
|
3092
3169
|
init_derive();
|
|
3093
3170
|
init_events();
|
|
3094
3171
|
init_kind();
|
|
3172
|
+
init_roles();
|
|
3095
3173
|
init_task_commands();
|
|
3096
3174
|
}
|
|
3097
3175
|
});
|
|
@@ -3142,7 +3220,7 @@ var init_install_hooks_command = __esm({
|
|
|
3142
3220
|
});
|
|
3143
3221
|
|
|
3144
3222
|
// core/commands/gen-agents.command.ts
|
|
3145
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync6,
|
|
3223
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3146
3224
|
import { dirname as dirname3, join as join14 } from "node:path";
|
|
3147
3225
|
function expandPath(ctx, kind) {
|
|
3148
3226
|
const spec = getKindRegistry(ctx.config)[kind];
|
|
@@ -3211,13 +3289,19 @@ function genAgents(ctx, templatesDir) {
|
|
|
3211
3289
|
return content;
|
|
3212
3290
|
};
|
|
3213
3291
|
const adapters = resolvePlatforms(ctx.config.platforms);
|
|
3214
|
-
const
|
|
3292
|
+
const registry = getRoleRegistry(ctx.config);
|
|
3215
3293
|
const written = [];
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3294
|
+
const projectTplDir = join14(ctx.root, "docs/workbench/templates/agents", lang);
|
|
3295
|
+
for (const role of [...new Set(ctx.config.pipeline)]) {
|
|
3296
|
+
if (!registry[role]) {
|
|
3297
|
+
throw new Error(`pipeline \u89D2\u8272 ${role} \u672A\u5728\u89D2\u8272\u6CE8\u518C\u8868\u4E2D\u5B9A\u4E49(\u5185\u7F6E\u6216 config.roles)`);
|
|
3298
|
+
}
|
|
3299
|
+
const candidates = [join14(projectTplDir, `${role}.md`), join14(dir, `${role}.md`)];
|
|
3300
|
+
const file = candidates.find((f) => existsSync12(f));
|
|
3301
|
+
if (!file) {
|
|
3302
|
+
throw new Error(`\u89D2\u8272 ${role} \u7F3A\u5C11 agent \u6A21\u677F:\u81EA\u5B9A\u4E49\u89D2\u8272\u8BF7\u63D0\u4F9B docs/workbench/templates/agents/${lang}/${role}.md`);
|
|
3303
|
+
}
|
|
3304
|
+
const tpl = parseTemplate(readFileSync6(file, "utf-8"));
|
|
3221
3305
|
for (const adapter of adapters) {
|
|
3222
3306
|
const memory = adapter.memoryBlock(role, lang);
|
|
3223
3307
|
const spec = {
|
|
@@ -3242,6 +3326,7 @@ var init_gen_agents_command = __esm({
|
|
|
3242
3326
|
"use strict";
|
|
3243
3327
|
init_config();
|
|
3244
3328
|
init_kind();
|
|
3329
|
+
init_roles();
|
|
3245
3330
|
init_platforms();
|
|
3246
3331
|
TRUST_PROTOCOL_ZH = `## \u4FE1\u4EFB\u534F\u8BAE(\u6700\u9AD8\u4F18\u5148\u7EA7)
|
|
3247
3332
|
|
|
@@ -3365,11 +3450,13 @@ function intakeIssues(ctx) {
|
|
|
3365
3450
|
continue;
|
|
3366
3451
|
}
|
|
3367
3452
|
const isBug = issue.labels.some((l) => l.name.toLowerCase() === "bug");
|
|
3453
|
+
const bugRole = ctx.config.intake?.bugRole ?? "developer";
|
|
3454
|
+
const defaultRole = ctx.config.intake?.defaultRole ?? "product-manager";
|
|
3368
3455
|
const id = createTask(ctx, {
|
|
3369
|
-
role: isBug ?
|
|
3370
|
-
endpoint:
|
|
3456
|
+
role: isBug ? bugRole : defaultRole,
|
|
3457
|
+
endpoint: null,
|
|
3371
3458
|
type: isBug ? "hotfix" : "build",
|
|
3372
|
-
assignee: isBug ?
|
|
3459
|
+
assignee: isBug ? bugRole : defaultRole,
|
|
3373
3460
|
creator: "intake",
|
|
3374
3461
|
content: `[gh#${issue.number}] ${issue.title}
|
|
3375
3462
|
${issue.url}`,
|
|
@@ -3553,6 +3640,7 @@ __export(core_exports, {
|
|
|
3553
3640
|
CONFIG_FILENAME: () => CONFIG_FILENAME,
|
|
3554
3641
|
CURRENT_SCHEMA_VERSION: () => CURRENT_SCHEMA_VERSION,
|
|
3555
3642
|
DEFAULT_KIND_REGISTRY: () => DEFAULT_KIND_REGISTRY,
|
|
3643
|
+
DEFAULT_ROLE_REGISTRY: () => DEFAULT_ROLE_REGISTRY,
|
|
3556
3644
|
KIND_TIERS: () => KIND_TIERS,
|
|
3557
3645
|
META_KINDS: () => META_KINDS,
|
|
3558
3646
|
MIGRATIONS: () => MIGRATIONS,
|
|
@@ -3586,6 +3674,7 @@ __export(core_exports, {
|
|
|
3586
3674
|
genAgents: () => genAgents,
|
|
3587
3675
|
getAdapter: () => getAdapter,
|
|
3588
3676
|
getKindRegistry: () => getKindRegistry,
|
|
3677
|
+
getRoleRegistry: () => getRoleRegistry,
|
|
3589
3678
|
getTaskDetail: () => getTaskDetail,
|
|
3590
3679
|
getTaskRow: () => getTaskRow,
|
|
3591
3680
|
gitHead: () => gitHead,
|
|
@@ -3614,6 +3703,7 @@ __export(core_exports, {
|
|
|
3614
3703
|
openWorkbench: () => openWorkbench,
|
|
3615
3704
|
openWorkbenchAt: () => openWorkbenchAt,
|
|
3616
3705
|
ownerRole: () => ownerRole,
|
|
3706
|
+
ownerRoleOf: () => ownerRoleOf,
|
|
3617
3707
|
planModule: () => planModule,
|
|
3618
3708
|
prototypeEndorsed: () => prototypeEndorsed,
|
|
3619
3709
|
recordNote: () => recordNote,
|
|
@@ -3651,6 +3741,7 @@ var init_core = __esm({
|
|
|
3651
3741
|
init_hash();
|
|
3652
3742
|
init_events();
|
|
3653
3743
|
init_kind();
|
|
3744
|
+
init_roles();
|
|
3654
3745
|
init_platforms();
|
|
3655
3746
|
init_derive();
|
|
3656
3747
|
init_lints();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dawipong/opcflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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-Cwj4KBKb.js → abnfDiagram-VRR7QNED-D19VSiUB.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{E as e}from"./src-Bp5IDfvJ.js";import{n as t}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import"./chunk-WYO6CB5R-C0vPAP7X.js";import"./index-
|
|
1
|
+
import{E as e}from"./src-Bp5IDfvJ.js";import{n as t}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import"./chunk-WYO6CB5R-C0vPAP7X.js";import"./index-xKffKKoc.js";import{m as n,t as r}from"./mermaid-parser.core-CICKlqAN.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CaHrOT4b.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};
|