@openprd/cli 0.1.19 → 0.1.21
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/.openprd/changes/openprd-control-plane-v020/.openprd.yaml +2 -0
- package/.openprd/changes/openprd-control-plane-v020/design.md +78 -0
- package/.openprd/changes/openprd-control-plane-v020/proposal.md +54 -0
- package/.openprd/changes/openprd-control-plane-v020/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/openprd-control-plane-v020/task-events.jsonl +27 -0
- package/.openprd/changes/openprd-control-plane-v020/tasks-002.md +35 -0
- package/.openprd/changes/openprd-control-plane-v020/tasks.md +427 -0
- package/.openprd/changes/remove-session-tracking-humane-approval/.openprd.yaml +2 -0
- package/.openprd/changes/remove-session-tracking-humane-approval/design.md +52 -0
- package/.openprd/changes/remove-session-tracking-humane-approval/proposal.md +35 -0
- package/.openprd/changes/remove-session-tracking-humane-approval/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/remove-session-tracking-humane-approval/task-events.jsonl +1 -0
- package/.openprd/changes/remove-session-tracking-humane-approval/tasks.md +170 -0
- package/.openprd/design/active/asset-spec.md +19 -14
- package/.openprd/design/active/direction-plan.md +19 -3
- package/.openprd/design/active/facts-sheet.md +16 -7
- package/.openprd/design/active/image-preflight.md +6 -5
- package/.openprd/design/active/review-studio-v020-directions/compare-plan.json +34 -0
- package/.openprd/design/active/review-studio-v020-directions/contact-sheet.jpg +0 -0
- package/.openprd/design/active/review-studio-v020-directions/crops/01.png +0 -0
- package/.openprd/design/active/review-studio-v020-directions/crops/02.png +0 -0
- package/.openprd/design/active/review-studio-v020-directions/crops/03.png +0 -0
- package/.openprd/design/active/review-studio-v020-directions/focus-board.template.json +69 -0
- package/.openprd/design/active/review-studio-v020-directions/parallel-board.template.json +45 -0
- package/.openprd/design/active/review-studio-v020-directions/reference-set.json +143 -0
- package/.openprd/design/active/review-studio-v020-directions/source.png +0 -0
- package/.openprd/design/active/selected-direction.md +23 -9
- package/.openprd/engagements/active/control-plane-architecture.json +203 -0
- package/.openprd/engagements/active/control-plane-intake.json +418 -0
- package/.openprd/engagements/active/prd.md +183 -119
- package/.openprd/engagements/active/review-presentation-v0018.json +176 -0
- package/.openprd/i18n-config.json +12 -0
- package/.openprd/ledger/events.jsonl +6 -0
- package/.openprd/ledger/heads/ebea1a71a9daa566f1c91b53.json +10 -0
- package/.openprd/manifest.json +21 -0
- package/AGENTS.md +1 -0
- package/README.md +2 -0
- package/README_EN.md +2 -0
- package/package.json +1 -1
- package/skills/openprd-frontend-design/SKILL.md +16 -0
- package/skills/openprd-harness/SKILL.md +1 -1
- package/skills/openprd-quality/SKILL.md +1 -1
- package/skills/openprd-requirement-intake/SKILL.md +1 -1
- package/skills/openprd-shared/SKILL.md +1 -1
- package/src/adapters/adapter-spi.js +193 -0
- package/src/adapters/capability-envelope.js +98 -0
- package/src/adapters/event-normalizer.js +55 -0
- package/src/adapters/index.js +4 -0
- package/src/adapters/install-safety.js +249 -0
- package/src/agent-canonical-content.js +4 -2
- package/src/agent-integration.js +147 -42
- package/src/cli/args.js +63 -4
- package/src/cli/gate-print.js +17 -0
- package/src/cli/quality-commands.js +18 -0
- package/src/cli/quality-print.js +10 -0
- package/src/cli/runtime-print.js +24 -0
- package/src/codex-hook-runner-template.mjs +65 -77
- package/src/codex-runtime.js +48 -5
- package/src/context/cache.js +245 -0
- package/src/context/compiler.js +438 -0
- package/src/context/constants.js +30 -0
- package/src/context/index.js +39 -0
- package/src/context/redaction.js +84 -0
- package/src/context/stable.js +69 -0
- package/src/context/telemetry.js +42 -0
- package/src/dev-standards.js +57 -0
- package/src/fleet.js +112 -95
- package/src/gates/index.js +2 -0
- package/src/gates/scoped-gates.js +256 -0
- package/src/gates/store.js +126 -0
- package/src/gates/workspace.js +41 -0
- package/src/html-artifacts.js +725 -28
- package/src/kernel/atomic-store.js +299 -0
- package/src/kernel/event-envelope.js +166 -0
- package/src/kernel/index.js +4 -0
- package/src/kernel/project-ledger.js +467 -0
- package/src/kernel/project-manifest.js +205 -0
- package/src/knowledge-v3/index.js +1 -0
- package/src/knowledge-v3/lifecycle.js +290 -0
- package/src/knowledge.js +14 -7
- package/src/openprd.js +71 -2
- package/src/review-model.js +413 -0
- package/src/review-presentation.js +1 -1
- package/src/run-harness.js +431 -37
- package/src/runtime/cli_runtime_README.md +28 -0
- package/src/runtime/errors.js +66 -0
- package/src/runtime/index.js +44 -0
- package/src/runtime/lane-schema.js +141 -0
- package/src/runtime/lane-store.js +279 -0
- package/src/runtime/task-runtime.js +449 -0
- package/src/runtime/workspace.js +179 -0
- package/src/runtime/write-set.js +206 -0
- package/src/session-binding.js +16 -3
- package/src/session-registry.js +59 -1
- package/src/upgrade/fleet-mutation.js +166 -0
- package/src/upgrade/fleet-transaction.js +398 -0
- package/src/upgrade/transaction-store.js +416 -0
- package/src/visual-compare-core.js +66 -27
- package/src/visual-compare.js +18 -12
- package/src/workspace-core.js +109 -7
- package/src/workspace-registry.js +39 -1
- package/src/workspace-workflow.js +18 -15
package/src/agent-integration.js
CHANGED
|
@@ -10,6 +10,19 @@ import { renderApprovedBenchmarkRegistrySection } from './benchmark.js';
|
|
|
10
10
|
import { timestamp } from './time.js';
|
|
11
11
|
import { upsertWorkspaceRegistryEntry } from './workspace-registry.js';
|
|
12
12
|
import { readOperationalGitignoreStatus } from './workspace-core.js';
|
|
13
|
+
import {
|
|
14
|
+
ADAPTER_CONTRACT_VERSION,
|
|
15
|
+
CAPABILITY_STATUS,
|
|
16
|
+
adapterContractDigest,
|
|
17
|
+
describeCapabilities,
|
|
18
|
+
} from './adapters/index.js';
|
|
19
|
+
import {
|
|
20
|
+
INSTALL_SAFETY_PROTOCOL_VERSION,
|
|
21
|
+
normalizeSymlinkStrategy,
|
|
22
|
+
safeAppendText,
|
|
23
|
+
safeEnsureDirectory,
|
|
24
|
+
safeWriteText,
|
|
25
|
+
} from './adapters/install-safety.js';
|
|
13
26
|
|
|
14
27
|
const PACKAGE_ROOT = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
|
|
15
28
|
const OPENPRD_AGENT_TOOLS = ['codex', 'claude', 'cursor'];
|
|
@@ -249,18 +262,16 @@ async function readJson(filePath) {
|
|
|
249
262
|
return JSON.parse(await readText(filePath));
|
|
250
263
|
}
|
|
251
264
|
|
|
252
|
-
async function writeText(filePath, text) {
|
|
253
|
-
await
|
|
254
|
-
await fs.writeFile(filePath, text, 'utf8');
|
|
265
|
+
async function writeText(filePath, text, options = {}) {
|
|
266
|
+
await safeWriteText(filePath, text, options);
|
|
255
267
|
}
|
|
256
268
|
|
|
257
|
-
async function writeJson(filePath, value) {
|
|
258
|
-
await writeText(filePath, `${JSON.stringify(value, null, 2)}\n
|
|
269
|
+
async function writeJson(filePath, value, options = {}) {
|
|
270
|
+
await writeText(filePath, `${JSON.stringify(value, null, 2)}\n`, options);
|
|
259
271
|
}
|
|
260
272
|
|
|
261
|
-
async function appendJsonl(filePath, value) {
|
|
262
|
-
await
|
|
263
|
-
await fs.appendFile(filePath, `${JSON.stringify(value)}\n`, 'utf8');
|
|
273
|
+
async function appendJsonl(filePath, value, options = {}) {
|
|
274
|
+
await safeAppendText(filePath, `${JSON.stringify(value)}\n`, options);
|
|
264
275
|
}
|
|
265
276
|
|
|
266
277
|
async function packageVersion() {
|
|
@@ -426,7 +437,7 @@ async function writeGeneratedFile(filePath, params, options, changes) {
|
|
|
426
437
|
});
|
|
427
438
|
return;
|
|
428
439
|
}
|
|
429
|
-
await writeText(filePath, body);
|
|
440
|
+
await writeText(filePath, body, options);
|
|
430
441
|
changes.push({ path: rel, status: current ? 'updated' : 'created' });
|
|
431
442
|
recordManagedFile(options, {
|
|
432
443
|
path: rel,
|
|
@@ -443,7 +454,7 @@ async function upsertTextBlockFile(filePath, id, blockBody, options, changes) {
|
|
|
443
454
|
const rel = normalizedRelativePath(options.projectRoot, filePath);
|
|
444
455
|
const current = await readText(filePath).catch(() => '');
|
|
445
456
|
const next = upsertManagedBlock(current || '', id, blockBody);
|
|
446
|
-
await writeText(filePath, next);
|
|
457
|
+
await writeText(filePath, next, options);
|
|
447
458
|
changes.push({ path: rel, status: current ? 'updated-block' : 'created' });
|
|
448
459
|
recordManagedFile(options, {
|
|
449
460
|
path: rel,
|
|
@@ -500,6 +511,7 @@ function agentContractBody() {
|
|
|
500
511
|
'5. 纯图片、封面图、配图、海报、插画、图标、贴纸、mockup 或“先看样子”请求按生图路由选工具:先判断当前对话工具面,Codex 环境用原生 `imagegen`(Image 2),Cursor 环境用内置 `GenerateImage`,两者都是工具内免费能力;都不可用或无法判断时,先读 `.openprd/harness/image-generation-preference.json` 的用户已确认偏好,没有偏好就先问用户选哪种生图方式,并把答复以 `user-confirmed` 来源写回该文件作为下次默认;绝不擅自调用用户本地或自有付费生图 API。生图工具是工具路径,不是审美豁免,生成前先写清用途、受众、气质、约束和记忆点,并用 anti-slop 避免默认紫白/蓝紫渐变、通用字体、白底卡片堆叠和无语境装饰;其中 logo、icon、avatar、badge 等开发素材在用户未明确要求场景化展示时,默认按独立素材输出(standalone asset)生成:全画布单主体,不额外添加卡片、设备框或其他展示容器;只有实际发生生图工具调用后,才能汇报生图结果、失败或限流。生图结果先当候选效果图,不要默认登记到 `.openprd/harness/visual-reviews/`。Agent 要主动确认是否符合预期、是否纳入后续效果图/实现截图对比、以及是否按此继续实现;只有确认后,才把选定方向、整张图或其中子图整理成 reference-set 并进入实现。进入实现阶段时,已有确认参考图用 `openprd visual-compare --reference/--actual`;如果参考图是一张整板、网格图或多对象组合,先运行 `openprd visual-prepare --reference <效果图> --grid <列>x<行>` 或 `--boxes <plan.json>`,确认 contact sheet 后再逐项对比;没有参考图时先判断新建界面还是修改既有界面,新建界面先按用户目标、信息架构变化、视觉决策成本和验证风险完成 3 方向方案评审,修改既有界面用 `openprd visual-compare --before/--after`;局部细节重点则补 `openprd visual-compare --board <focus-board.json>`,多方向实验则补 `openprd visual-compare --board <parallel-board.json>`;普通截图、Computer/Browser/Playwright 实测截图要作为证据时补 `openprd visual-compare --board <verification-board.json>`;同构列表、卡片、网格、表格或用户反馈没对齐时补 `openprd visual-compare --board <alignment-board.json>`,并同时覆盖容器轨道与内部内容槽位轨道;单个 logo、icon、avatar、badge、按钮图形或图片内部居中/视觉重心/偏心问题补 `openprd visual-compare --board <centering-board.json>`,并同时覆盖画布中心、主体外接框中心和视觉重心偏移。visual evidence 同时检查气质、层级、字体/色彩/表面角色和记忆点,不只检查截图是否存在。用户后续如果说“跟效果图”“不一致”“好丑”“复刻”,不能只口头说对比过了,至少先产出一份视觉证据图。如果用户目标是把工作转成可学习、可复用、可回看、可教学或可沉淀的材料,先按期望产物是否需要章节结构、证据锚点、图文讲解、检索练习、工作示例或长期阅读体验来判断;需要时优先走 `openprd learn .` 生成学习包和阅读器,不要用关键词表触发;“仙侠风格的学习材料”这类短请求也按学习型交付物处理,风格只作为 `--genre` 题材参数。',
|
|
501
512
|
'5a. 对 logo、icon、avatar、badge、贴纸、空态插画、单物件 UI 位图等开发素材,如果最终要接入 UI 并需要透明背景,默认走“候选评审 -> 资产工程化 -> 接入验证”的图标资产链路:先基于用途、受众、气质、约束和记忆点生成 3 个差异足够大的独立素材候选方向,并保持纯 `#00ff00` 绿幕、无文字、无 UI 容器、主体居中且留足裁切边距;用户选定前不写入项目文件。用户选定后再定位源图或 contact sheet,保留绿幕源图,用 `remove_chroma_key.py` 抠成透明 PNG/WebP,按真实 UI 需要裁切居中并导出 384px 或多尺寸资产;接入时按首页卡片、工具格、吸顶栏、偏好预览等实际场景分别调显示比例,而不是只换图片路径。收口时同步写回 `.openprd/design/active/asset-spec.md` 和 `selected-direction.md`,说明选中的方向、资产路径、透明产物、接入位置和验证结果;最终回复必须区分绿幕源图、透明产物和是否已经接入。',
|
|
502
513
|
'5b. 卡片宽度、间距、留白、对齐、颜色、圆角、字号、按钮或图标等轻量 UI 可视优化,仍可按 L0/L1 小范围修正推进,不自动升级成大界面 3 方向方案评审;但它是用户可见变化,动手前要有一句审美意图和记忆点,收口必须补 `visual-compare` 修改前后图、局部焦点证据板、截图实测证据板、对齐辅助线证据板或内部居中证据板,并检查气质、层级、颜色、字号、间距和表面角色是否成立。只要界面里有同构列表、卡片、网格或表格,就把容器轨道以及标题、副标题、描述、标签、状态、价格、按钮、图标、操作区等相同文案类型/相同组件槽位的对齐当作默认验收项,不等用户先投诉;只量外框、列宽或行顶不算完整对齐验收。只要任务在判断单个素材/图标/头像/徽标/按钮图形的内部居中、偏心或视觉重心,就把 centering-board 当作默认验收项;单张原始截图或主观“看起来居中”不算完整居中验收。build、package、`openprd dev-check` 和单张原始截图不能替代视觉证据。`visual-compare` 每次都会返回 `chatEmbed` markdown;生成证据板后必须把这行 `` 直接嵌入最终回复,让证据在对话流里可见,不要只报告文件路径;`openprd dev-check` 在触达界面文件且最近 24 小时没有证据板时也会输出视觉证据提醒,收口前按提醒补齐。',
|
|
514
|
+
'5c. 大界面三方向是 Agent 的默认责任,不要求用户另行提出生图;轻量 UI 微调不触发。已有界面时,Codex 原生 App 用 Computer Use、Codex 网页环境用 Chrome 插件任务专用窗口、Cursor 用可用浏览器或项目预览能力截图;三个方向必须共享同一张当前截图作为 image-to-image 参考。除非用户明确要求换风格,否则锁定字体角色、色彩系统、明暗模式、表面语言、圆角/边框/阴影、图标语言、平台框架和信息密度,只探索布局、信息层级、导航、关键路径与空间构图。冷启动没有现有界面时才使用 design brief,不伪造修改前截图。候选图先明确用途、目标用户、业务约束、核心动作、气质和唯一记忆点,并检查字体、色彩、空间、层级、表面/图标语言和动效含义;拒绝无依据蓝紫渐变、默认字体、等权白卡堆叠、过量大圆角、无语境装饰和只换颜色的伪方向。',
|
|
503
515
|
'6. 界面、页面、视觉、样式、信息架构或前端体验任务进入实现前,先读取 `$openprd-frontend-design`,并用 `.openprd/design/active/` 补齐 `facts-sheet / asset-spec / image-preflight / direction-plan / selected-direction`;空白工作区优先从 `.openprd/design/templates/` 选最近模板。若用户已经给了效果图、设计稿、参考截图或其他明确参考图,先把它当成主参考源;只有现有 starter、theme、layout 足够接近时才复用,不接近就允许偏离默认组合,以参考图为准。若当前轮用户已经把页面主题、模块范围或“直接实现”的意图说清,优先运行 `openprd run . --context --message <用户原话>`。若页面主题和模块范围已经明确,优先运行 `openprd design-starter . --starter <starter-id> --out index.html --brief "<页面主题>" --sections "<模块1|模块2|模块3>"` 起第一版真实页面;只有这个页面本来就不依赖外部产品事实、品牌素材或真实图片时,才在 active design artifacts 写清无依赖并补 `--no-external-facts --no-brand-assets --no-real-images`;若题目更像旅游、导览、展览、博物馆、城市、自然观察或案例内容页,先不要带 `--no-real-images`,让 starter 先尝试补首批真实图片;若这类冷启动即使带 message 仍短暂返回 `clarify-user`,把它当成摘要级提醒,先用 3 到 5 行 mini-plan 收口,再继续。starter 落地后默认进入 `Patch Mode`,必须直接在生成的入口文件上补丁修改;即使结构要大改,也是在同一路径内覆盖,不做 delete-first,更不要删除 `index.html` 后另起新稿。如果确实要整页重写,先把完整新稿写到 sibling draft,例如 `index.next.html`,确认内容成形后再覆盖回 `index.html`,不要让正式入口出现空窗;starter 一落地后,只允许做一轮就地对焦:快速读一次生成的入口文件和必要的 active design artifacts;这轮对焦结束后,下一步就必须是真实写入口,不要再回头搜网页、翻 `docs/basic/` 或继续模板漫游;把最后一批必要的查事实、查图、读模板动作放在口头宣布之前做完;一旦已经说“开始覆盖入口文件”或“开始整页重写”,下一步必须出现真实写文件动作,而不是继续只读浏览、压图或停在口头承诺;必要时 hook 会把这类非写入动作挡回去;`Patch Mode` 完成不等于只补合同、只下载素材或只写计划;至少要把入口文件本体改完、主要占位清掉,并把已准备好的真实图片或参考约束真正落进页面;没有明确参考方向时,不要直接落回同一种安全极简解。',
|
|
504
516
|
'6a. 写产品界面上用户能看到的文案时,必须站在当前用户视角写:这句话要说明用户现在能做什么、会发生什么、为什么值得点、下一步怎么走。除非这是面向开发者或专业技术人员的技术型产品,否则默认用普通人能看懂的语言写结果、状态、限制和行动,不要暴露 API、SDK、模型、数据库、缓存、错误码或内部模块。正反例:不要写“适合想保留全部工具入口的用户”,改写成“首页会显示所有工具入口,你可以直接选择需要的功能”;不要写“API 请求失败,错误码 500”,改写成“暂时保存失败,请稍后再试”;技术型产品可以保留必要术语,但仍要写清用户动作、影响和修复路径。',
|
|
505
517
|
'6b. 开发新功能出现新的入口、按钮、tab、卡片、空态或工具格时,默认自动配图标,不等用户提出:先复用项目已有图标体系;项目没有时按图标最佳实践路由选型(UI 图标 Phosphor,落码 Lucide/Tabler/React Icons,AI 品牌 LobeHub Icons,技术栈 Tech Icons,功能图标/插画 iconfont),把图标名、来源、用途登记到 `.openprd/design/active/asset-spec.md` 的“功能图标”行再接入。只有语义确实不需要图标或用户明确说不要时才跳过,并在收口说明原因。',
|
|
@@ -562,7 +574,7 @@ async function loadSourceSkill(skill) {
|
|
|
562
574
|
};
|
|
563
575
|
}
|
|
564
576
|
|
|
565
|
-
async function renderSkill(skill,
|
|
577
|
+
async function renderSkill(skill, projectRoot) {
|
|
566
578
|
const source = await loadSourceSkill(skill);
|
|
567
579
|
const sections = [
|
|
568
580
|
'---',
|
|
@@ -595,7 +607,7 @@ async function listFilesRecursive(rootDir) {
|
|
|
595
607
|
return files;
|
|
596
608
|
}
|
|
597
609
|
|
|
598
|
-
async function writeSourceSkillReferences(projectRoot, skill, destinationDir,
|
|
610
|
+
async function writeSourceSkillReferences(projectRoot, skill, destinationDir, version, options, changes) {
|
|
599
611
|
if (!skill.sourceSkill) {
|
|
600
612
|
return;
|
|
601
613
|
}
|
|
@@ -606,7 +618,7 @@ async function writeSourceSkillReferences(projectRoot, skill, destinationDir, ad
|
|
|
606
618
|
const body = await readText(filePath);
|
|
607
619
|
await writeGeneratedFile(
|
|
608
620
|
cjoin(destinationDir, 'references', relative),
|
|
609
|
-
{ adapter, source: `${skill.id}:reference:${relative}`, version, body },
|
|
621
|
+
{ adapter: 'shared', source: `${skill.id}:reference:${relative}`, version, body },
|
|
610
622
|
options,
|
|
611
623
|
changes,
|
|
612
624
|
);
|
|
@@ -743,7 +755,7 @@ async function writeCodexConfig(projectRoot, options, changes) {
|
|
|
743
755
|
const current = await readText(configPath).catch(() => '');
|
|
744
756
|
let next = normalizeFeatureHooks(current || '');
|
|
745
757
|
next = upsertTomlManagedBlock(next, 'CODEX-HOOKS', codexHooksTomlBlock(projectRoot, options));
|
|
746
|
-
await writeText(configPath, next);
|
|
758
|
+
await writeText(configPath, next, options);
|
|
747
759
|
changes.push({ path: rel, status: current ? 'updated' : 'created' });
|
|
748
760
|
recordManagedFile(options, {
|
|
749
761
|
path: rel,
|
|
@@ -758,7 +770,7 @@ async function writeCodexUserConfig(options, changes) {
|
|
|
758
770
|
const current = await readText(configPath).catch(() => '');
|
|
759
771
|
const next = normalizeFeatureHooks(current || '');
|
|
760
772
|
if (next !== current) {
|
|
761
|
-
await writeText(configPath, next);
|
|
773
|
+
await writeText(configPath, next, options);
|
|
762
774
|
}
|
|
763
775
|
changes.push({
|
|
764
776
|
path: displayPath(configPath),
|
|
@@ -814,7 +826,7 @@ async function writeCodexHooksJson(projectRoot, options, changes) {
|
|
|
814
826
|
delete next[eventName];
|
|
815
827
|
}
|
|
816
828
|
}
|
|
817
|
-
await writeJson(hooksPath, next);
|
|
829
|
+
await writeJson(hooksPath, next, options);
|
|
818
830
|
changes.push({ path: rel, status: existed ? 'updated' : 'created' });
|
|
819
831
|
recordManagedFile(options, {
|
|
820
832
|
path: rel,
|
|
@@ -827,15 +839,15 @@ async function writeCodexHooksJson(projectRoot, options, changes) {
|
|
|
827
839
|
async function writeCodexAdapter(projectRoot, options, changes) {
|
|
828
840
|
const version = await packageVersion();
|
|
829
841
|
for (const skill of CANONICAL_SKILLS) {
|
|
830
|
-
const body = await renderSkill(skill,
|
|
842
|
+
const body = await renderSkill(skill, projectRoot);
|
|
831
843
|
const skillDir = cjoin(projectRoot, '.codex', 'skills', skill.id);
|
|
832
844
|
await writeGeneratedFile(
|
|
833
845
|
cjoin(skillDir, 'SKILL.md'),
|
|
834
|
-
{ adapter: '
|
|
846
|
+
{ adapter: 'shared', source: skill.id, version, body },
|
|
835
847
|
options,
|
|
836
848
|
changes,
|
|
837
849
|
);
|
|
838
|
-
await writeSourceSkillReferences(projectRoot, skill, skillDir,
|
|
850
|
+
await writeSourceSkillReferences(projectRoot, skill, skillDir, version, options, changes);
|
|
839
851
|
}
|
|
840
852
|
for (const command of CANONICAL_COMMANDS) {
|
|
841
853
|
await writeGeneratedFile(
|
|
@@ -864,15 +876,15 @@ async function writeClaudeAdapter(projectRoot, options, changes) {
|
|
|
864
876
|
const version = await packageVersion();
|
|
865
877
|
await upsertTextBlockFile(cjoin(projectRoot, 'CLAUDE.md'), 'CLAUDE', agentContractBody(), options, changes);
|
|
866
878
|
for (const skill of CANONICAL_SKILLS) {
|
|
867
|
-
const body = await renderSkill(skill,
|
|
879
|
+
const body = await renderSkill(skill, projectRoot);
|
|
868
880
|
const skillDir = cjoin(projectRoot, '.claude', 'skills', skill.id);
|
|
869
881
|
await writeGeneratedFile(
|
|
870
882
|
cjoin(skillDir, 'SKILL.md'),
|
|
871
|
-
{ adapter: '
|
|
883
|
+
{ adapter: 'shared', source: skill.id, version, body },
|
|
872
884
|
options,
|
|
873
885
|
changes,
|
|
874
886
|
);
|
|
875
|
-
await writeSourceSkillReferences(projectRoot, skill, skillDir,
|
|
887
|
+
await writeSourceSkillReferences(projectRoot, skill, skillDir, version, options, changes);
|
|
876
888
|
}
|
|
877
889
|
for (const command of CANONICAL_COMMANDS) {
|
|
878
890
|
await writeGeneratedFile(
|
|
@@ -902,8 +914,8 @@ async function writeCursorAdapter(projectRoot, options, changes) {
|
|
|
902
914
|
}
|
|
903
915
|
}
|
|
904
916
|
|
|
905
|
-
async function ensureHarnessState(projectRoot) {
|
|
906
|
-
await
|
|
917
|
+
async function ensureHarnessState(projectRoot, options = {}) {
|
|
918
|
+
await safeEnsureDirectory(harnessPath(projectRoot, OPENPRD_HARNESS_DIR), options);
|
|
907
919
|
const hookStatePath = harnessPath(projectRoot, OPENPRD_HARNESS_HOOK_STATE);
|
|
908
920
|
if (!(await exists(hookStatePath))) {
|
|
909
921
|
await writeJson(hookStatePath, {
|
|
@@ -915,11 +927,11 @@ async function ensureHarnessState(projectRoot) {
|
|
|
915
927
|
suppressions: {
|
|
916
928
|
inputLock: false,
|
|
917
929
|
},
|
|
918
|
-
});
|
|
930
|
+
}, options);
|
|
919
931
|
}
|
|
920
932
|
const eventsPath = harnessPath(projectRoot, OPENPRD_HARNESS_EVENTS);
|
|
921
933
|
if (!(await exists(eventsPath))) {
|
|
922
|
-
await writeText(eventsPath, '');
|
|
934
|
+
await writeText(eventsPath, '', options);
|
|
923
935
|
}
|
|
924
936
|
const runtimeEnvironmentPath = harnessPath(projectRoot, OPENPRD_HARNESS_RUNTIME_ENVIRONMENT);
|
|
925
937
|
if (!(await exists(runtimeEnvironmentPath))) {
|
|
@@ -940,7 +952,7 @@ async function ensureHarnessState(projectRoot) {
|
|
|
940
952
|
runtimeDetection: RUNTIME_DETECTION_PROTOCOL,
|
|
941
953
|
platformCapabilityPacks: [],
|
|
942
954
|
updatedAt: null,
|
|
943
|
-
});
|
|
955
|
+
}, options);
|
|
944
956
|
}
|
|
945
957
|
}
|
|
946
958
|
|
|
@@ -1021,16 +1033,85 @@ function configuredOptionalCapabilityIds(optionalCapabilities) {
|
|
|
1021
1033
|
.map((capability) => capability.id);
|
|
1022
1034
|
}
|
|
1023
1035
|
|
|
1036
|
+
function capabilityGapsForEnvelope(envelope) {
|
|
1037
|
+
return Object.entries(envelope?.capabilities ?? {})
|
|
1038
|
+
.filter(([, status]) => typeof status === 'string' && status !== CAPABILITY_STATUS.NATIVE)
|
|
1039
|
+
.map(([capability, status]) => ({
|
|
1040
|
+
capability,
|
|
1041
|
+
status,
|
|
1042
|
+
requirement: status === CAPABILITY_STATUS.SURFACE_DEPENDENT
|
|
1043
|
+
? 'current-surface-evidence'
|
|
1044
|
+
: 'adapter-or-platform-support',
|
|
1045
|
+
}));
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
function buildAdapterProtocol(tools) {
|
|
1049
|
+
const clients = normalizeTools(tools).map((client) => {
|
|
1050
|
+
const capabilityEnvelope = describeCapabilities(client);
|
|
1051
|
+
return {
|
|
1052
|
+
client,
|
|
1053
|
+
contractVersion: ADAPTER_CONTRACT_VERSION,
|
|
1054
|
+
capabilitySchema: capabilityEnvelope.schema,
|
|
1055
|
+
lifecycleEventSchema: 'openprd.adapter-event.v1',
|
|
1056
|
+
capabilityEnvelope,
|
|
1057
|
+
capabilityGaps: capabilityGapsForEnvelope(capabilityEnvelope),
|
|
1058
|
+
};
|
|
1059
|
+
});
|
|
1060
|
+
return {
|
|
1061
|
+
schema: 'openprd.adapter-protocol.v1',
|
|
1062
|
+
protocolVersion: ADAPTER_CONTRACT_VERSION,
|
|
1063
|
+
capabilityVersion: ADAPTER_CONTRACT_VERSION,
|
|
1064
|
+
lifecycleEventVersion: ADAPTER_CONTRACT_VERSION,
|
|
1065
|
+
contractDigest: adapterContractDigest(),
|
|
1066
|
+
generatedContent: {
|
|
1067
|
+
version: 1,
|
|
1068
|
+
sharedSkillMarker: 'shared',
|
|
1069
|
+
sharedSkillConsumers: clients
|
|
1070
|
+
.map((entry) => entry.client)
|
|
1071
|
+
.filter((client) => client === 'codex' || client === 'claude'),
|
|
1072
|
+
platformSpecificKinds: ['command', 'hook', 'rule', 'config'],
|
|
1073
|
+
},
|
|
1074
|
+
clients,
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1024
1078
|
function buildPlatformCapabilityPacks(tools, optionalCapabilities = []) {
|
|
1025
1079
|
const toolSet = new Set(normalizeTools(tools));
|
|
1026
1080
|
const configuredOptional = configuredOptionalCapabilityIds(optionalCapabilities);
|
|
1027
1081
|
return PLATFORM_CAPABILITY_PACK_REGISTRY
|
|
1028
1082
|
.filter((pack) => toolSet.has(pack.client))
|
|
1029
|
-
.map((pack) =>
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1083
|
+
.map((pack) => {
|
|
1084
|
+
const capabilityEnvelope = describeCapabilities(pack.client);
|
|
1085
|
+
return {
|
|
1086
|
+
...pack,
|
|
1087
|
+
adapterContractVersion: ADAPTER_CONTRACT_VERSION,
|
|
1088
|
+
capabilityEnvelope,
|
|
1089
|
+
capabilityGaps: capabilityGapsForEnvelope(capabilityEnvelope),
|
|
1090
|
+
capabilities: pack.capabilities.map((capability) => ({ ...capability })),
|
|
1091
|
+
optionalEnhancements: configuredOptional,
|
|
1092
|
+
};
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
function installSafetyManifest(projectRoot, options = {}) {
|
|
1097
|
+
const events = Array.isArray(options.installSafetyEvents) ? options.installSafetyEvents : [];
|
|
1098
|
+
const eventPath = (filePath) => {
|
|
1099
|
+
const relative = normalizedRelativePath(projectRoot, filePath);
|
|
1100
|
+
return relative === '..' || relative.startsWith('../') ? displayPath(filePath) : relative;
|
|
1101
|
+
};
|
|
1102
|
+
return {
|
|
1103
|
+
protocolVersion: INSTALL_SAFETY_PROTOCOL_VERSION,
|
|
1104
|
+
symlinkStrategy: normalizeSymlinkStrategy(options.symlinkStrategy),
|
|
1105
|
+
materializationCount: events.length,
|
|
1106
|
+
materializations: events.map((event) => ({
|
|
1107
|
+
strategy: event.strategy,
|
|
1108
|
+
path: eventPath(event.symlinkPath),
|
|
1109
|
+
linkTarget: event.linkTarget,
|
|
1110
|
+
backupPath: displayPath(event.backupPath),
|
|
1111
|
+
materializedType: event.materializedType,
|
|
1112
|
+
at: event.at,
|
|
1113
|
+
})),
|
|
1114
|
+
};
|
|
1034
1115
|
}
|
|
1035
1116
|
|
|
1036
1117
|
async function writeInstallManifest(projectRoot, options, changes, tools, optionalCapabilities) {
|
|
@@ -1053,23 +1134,26 @@ async function writeInstallManifest(projectRoot, options, changes, tools, option
|
|
|
1053
1134
|
runtimeEnvironment: OPENPRD_HARNESS_RUNTIME_ENVIRONMENT,
|
|
1054
1135
|
driftReport: OPENPRD_HARNESS_DRIFT,
|
|
1055
1136
|
},
|
|
1137
|
+
adapterProtocol: buildAdapterProtocol(tools),
|
|
1056
1138
|
runtimeDetection: RUNTIME_DETECTION_PROTOCOL,
|
|
1057
1139
|
platformCapabilityPacks: buildPlatformCapabilityPacks(tools, optionalCapabilities),
|
|
1058
1140
|
imageGenerationRouting: IMAGE_GENERATION_ROUTING,
|
|
1141
|
+
installSafety: installSafetyManifest(projectRoot, options),
|
|
1059
1142
|
optionalCapabilities,
|
|
1060
1143
|
};
|
|
1061
1144
|
const manifestPath = harnessPath(projectRoot, OPENPRD_HARNESS_MANIFEST);
|
|
1062
1145
|
const existed = await exists(manifestPath);
|
|
1063
|
-
await writeJson(manifestPath, manifest);
|
|
1146
|
+
await writeJson(manifestPath, manifest, options);
|
|
1064
1147
|
const runtimeEnvironmentPath = harnessPath(projectRoot, OPENPRD_HARNESS_RUNTIME_ENVIRONMENT);
|
|
1065
1148
|
const runtimeEnvironment = await readJson(runtimeEnvironmentPath).catch(() => null);
|
|
1066
1149
|
await writeJson(runtimeEnvironmentPath, {
|
|
1067
1150
|
version: 1,
|
|
1068
1151
|
...(runtimeEnvironment ?? {}),
|
|
1152
|
+
adapterProtocol: manifest.adapterProtocol,
|
|
1069
1153
|
runtimeDetection: RUNTIME_DETECTION_PROTOCOL,
|
|
1070
1154
|
platformCapabilityPacks: manifest.platformCapabilityPacks,
|
|
1071
1155
|
imageGenerationRouting: IMAGE_GENERATION_ROUTING,
|
|
1072
|
-
});
|
|
1156
|
+
}, options);
|
|
1073
1157
|
const configuredCount = optionalCapabilities.filter((capability) => capability.configured).length;
|
|
1074
1158
|
changes.push({ path: OPENPRD_HARNESS_MANIFEST, status: existed ? 'updated' : 'created' });
|
|
1075
1159
|
await appendJsonl(harnessPath(projectRoot, OPENPRD_HARNESS_EVENTS), {
|
|
@@ -1082,7 +1166,11 @@ async function writeInstallManifest(projectRoot, options, changes, tools, option
|
|
|
1082
1166
|
platformCapabilityPackCount: manifest.platformCapabilityPacks.length,
|
|
1083
1167
|
optionalCapabilityConfiguredCount: configuredCount,
|
|
1084
1168
|
optionalCapabilityRecommendedCount: optionalCapabilities.length - configuredCount,
|
|
1085
|
-
|
|
1169
|
+
adapterContractVersion: manifest.adapterProtocol.protocolVersion,
|
|
1170
|
+
adapterContractDigest: manifest.adapterProtocol.contractDigest,
|
|
1171
|
+
symlinkStrategy: manifest.installSafety.symlinkStrategy,
|
|
1172
|
+
materializationCount: manifest.installSafety.materializationCount,
|
|
1173
|
+
}, options);
|
|
1086
1174
|
return manifest;
|
|
1087
1175
|
}
|
|
1088
1176
|
|
|
@@ -1113,7 +1201,7 @@ async function inspectManagedFile(projectRoot, entry) {
|
|
|
1113
1201
|
return { ...entry, ok: true, reason: 'ok' };
|
|
1114
1202
|
}
|
|
1115
1203
|
|
|
1116
|
-
async function computeDriftReport(projectRoot, tools) {
|
|
1204
|
+
async function computeDriftReport(projectRoot, tools, options = {}) {
|
|
1117
1205
|
const manifest = await readInstallManifest(projectRoot);
|
|
1118
1206
|
const checks = [];
|
|
1119
1207
|
if (!manifest) {
|
|
@@ -1124,7 +1212,7 @@ async function computeDriftReport(projectRoot, tools) {
|
|
|
1124
1212
|
errors: [`${OPENPRD_HARNESS_MANIFEST} is missing. Run openprd update .`],
|
|
1125
1213
|
checks,
|
|
1126
1214
|
};
|
|
1127
|
-
await writeJson(harnessPath(projectRoot, OPENPRD_HARNESS_DRIFT), report);
|
|
1215
|
+
await writeJson(harnessPath(projectRoot, OPENPRD_HARNESS_DRIFT), report, options);
|
|
1128
1216
|
return report;
|
|
1129
1217
|
}
|
|
1130
1218
|
for (const entry of manifest.managedFiles ?? []) {
|
|
@@ -1142,7 +1230,7 @@ async function computeDriftReport(projectRoot, tools) {
|
|
|
1142
1230
|
errors,
|
|
1143
1231
|
checks,
|
|
1144
1232
|
};
|
|
1145
|
-
await writeJson(harnessPath(projectRoot, OPENPRD_HARNESS_DRIFT), report);
|
|
1233
|
+
await writeJson(harnessPath(projectRoot, OPENPRD_HARNESS_DRIFT), report, options);
|
|
1146
1234
|
return report;
|
|
1147
1235
|
}
|
|
1148
1236
|
|
|
@@ -1151,14 +1239,22 @@ export async function setupOpenPrdAgentIntegration(projectRoot, options = {}) {
|
|
|
1151
1239
|
const hookProfile = normalizeHookProfile(options.hookProfile);
|
|
1152
1240
|
const changes = [];
|
|
1153
1241
|
const managedFiles = [];
|
|
1242
|
+
const installSafetyEvents = [];
|
|
1243
|
+
const managedWriteRoots = [projectRoot];
|
|
1244
|
+
if (options.enableUserCodexConfig) {
|
|
1245
|
+
managedWriteRoots.push(resolveCodexHome(options));
|
|
1246
|
+
}
|
|
1154
1247
|
const normalizedOptions = {
|
|
1155
1248
|
...options,
|
|
1156
1249
|
projectRoot,
|
|
1157
1250
|
hookProfile,
|
|
1158
1251
|
managedFiles,
|
|
1252
|
+
installSafetyEvents,
|
|
1253
|
+
managedWriteRoots,
|
|
1254
|
+
symlinkStrategy: normalizeSymlinkStrategy(options.symlinkStrategy),
|
|
1159
1255
|
};
|
|
1160
1256
|
|
|
1161
|
-
await ensureHarnessState(projectRoot);
|
|
1257
|
+
await ensureHarnessState(projectRoot, normalizedOptions);
|
|
1162
1258
|
await writeProjectCommandCatalog(projectRoot, normalizedOptions, changes);
|
|
1163
1259
|
await upsertTextBlockFile(cjoin(projectRoot, 'AGENTS.md'), 'AGENTS', agentContractBody(), normalizedOptions, changes);
|
|
1164
1260
|
|
|
@@ -1187,7 +1283,7 @@ export async function setupOpenPrdAgentIntegration(projectRoot, options = {}) {
|
|
|
1187
1283
|
registryPath: registry.registryPath,
|
|
1188
1284
|
status: registry.status,
|
|
1189
1285
|
knownTotal: registry.knownTotal,
|
|
1190
|
-
});
|
|
1286
|
+
}, normalizedOptions);
|
|
1191
1287
|
|
|
1192
1288
|
const doctor = await doctorOpenPrdAgentIntegration(projectRoot, {
|
|
1193
1289
|
tools,
|
|
@@ -1363,7 +1459,16 @@ async function smokeTestCodexHook(projectRoot, options = {}) {
|
|
|
1363
1459
|
export async function doctorOpenPrdAgentIntegration(projectRoot, options = {}) {
|
|
1364
1460
|
const tools = normalizeTools(options.tools);
|
|
1365
1461
|
const checks = [];
|
|
1366
|
-
|
|
1462
|
+
const managedWriteRoots = [projectRoot];
|
|
1463
|
+
if (options.enableUserCodexConfig) {
|
|
1464
|
+
managedWriteRoots.push(resolveCodexHome(options));
|
|
1465
|
+
}
|
|
1466
|
+
const safetyOptions = {
|
|
1467
|
+
...options,
|
|
1468
|
+
projectRoot,
|
|
1469
|
+
managedWriteRoots,
|
|
1470
|
+
};
|
|
1471
|
+
await ensureHarnessState(projectRoot, safetyOptions);
|
|
1367
1472
|
const manifest = await readInstallManifest(projectRoot);
|
|
1368
1473
|
const hookProfile = normalizeHookProfile(options.hookProfile ?? manifest?.hooks?.profile);
|
|
1369
1474
|
const optionalCapabilities = await collectOptionalCapabilities(projectRoot, tools, options);
|
|
@@ -1425,7 +1530,7 @@ export async function doctorOpenPrdAgentIntegration(projectRoot, options = {}) {
|
|
|
1425
1530
|
await collectGeneratedDoctorCheck(projectRoot, checks, '.cursor/rules/openprd.mdc', 'Cursor OpenPrd rule');
|
|
1426
1531
|
await collectGeneratedDoctorCheck(projectRoot, checks, '.cursor/commands/openprd-next.md', 'Cursor OpenPrd next command');
|
|
1427
1532
|
}
|
|
1428
|
-
const drift = await computeDriftReport(projectRoot, tools);
|
|
1533
|
+
const drift = await computeDriftReport(projectRoot, tools, safetyOptions);
|
|
1429
1534
|
for (const error of drift.errors) {
|
|
1430
1535
|
checks.push({ path: OPENPRD_HARNESS_DRIFT, ok: false, message: error });
|
|
1431
1536
|
}
|
package/src/cli/args.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
function parseCommandArgs(argv) {
|
|
21
21
|
const args = [...argv];
|
|
22
|
-
const flags = { json: false, force: false, fix: false, open: false, append: false, init: false, check: false, review: false, reject: false, resume: false, advance: false, verify: false, evidenceRequired: false, next: false, generate: false, validate: false, apply: false, archive: false, activate: false, close: false, keep: false, write: false, dryRun: false, repairAgent: false, allowDirtyMain: false, fleet: false, updateOpenprd: false, backfillWorkUnits: false, syncRegistry: false, setupMissing: false, doctor: false, context: false, recordHook: false, hookInject: false, plan: false, prompt: false, loopRun: false, finish: false, commit: false, html: false, template: false, failOnViolation: false, noExternalFacts: false, noBrandAssets: false, noRealImages: false, daemon: false, stop: false, bridgeOutbox: false, bridgeSent: false, bridgeFailed: false, mark: null, direction: null, autoRefactor: null, type: 'architecture', mode: 'auto', input: null, field: null, value: null, set: null, jsonFile: null, artifactMarkdown: null, contentJson: null, presentation: null, source: null, reference: null, actual: null, before: null, after: null, board: null, grid: null, boxes: null, out: null, format: null, quality: null, maxPanelWidth: null, referenceLabel: null, actualLabel: null, locale: null, classifyExternal: null, maxIterations: null, maxDepth: null, include: null, exclude: null, report: null, item: null, id: null, status: null, claim: null, notes: null, reason: null, confidence: null, threshold: null, change: null, starter: null, sections: null, brief: null, tools: 'all', hookProfile: null, templatePack: null, target: 'openprd', targetRoot: null, path: null, productType: null, title: null, owner: null, problem: null, whyNow: null, evidence: null, from: null, to: null, version: null, digest: null, workUnit: null, event: null, risk: null, outcome: null, preview: null, learn: null, genre: null, style: null, topic: null, enable: false, disable: false, agent: 'codex', agentCommand: null, worktree: null, branch: null, message: null, thread: null, threadTitle: null, conversationTitle: null, session: null, sessionTitle: null, host: null, port: null, openTarget: 'auto' };
|
|
22
|
+
const flags = { json: false, force: false, fix: false, open: false, append: false, init: false, check: false, review: false, reject: false, resume: false, advance: false, verify: false, evidenceRequired: false, next: false, generate: false, validate: false, apply: false, archive: false, activate: false, close: false, keep: false, write: false, dryRun: false, repairAgent: false, allowDirtyMain: false, fleet: false, updateOpenprd: false, backfillWorkUnits: false, syncRegistry: false, setupMissing: false, doctor: false, context: false, recordHook: false, hookInject: false, plan: false, prompt: false, loopRun: false, finish: false, commit: false, html: false, template: false, failOnViolation: false, noExternalFacts: false, noBrandAssets: false, noRealImages: false, daemon: false, stop: false, bridgeOutbox: false, bridgeSent: false, bridgeFailed: false, mark: null, direction: null, autoRefactor: null, type: 'architecture', mode: 'auto', input: null, field: null, value: null, set: null, jsonFile: null, artifactMarkdown: null, contentJson: null, presentation: null, source: null, reference: null, actual: null, before: null, after: null, board: null, grid: null, boxes: null, out: null, format: null, quality: null, maxPanelWidth: null, referenceLabel: null, actualLabel: null, locale: null, classifyExternal: null, maxIterations: null, maxDepth: null, include: null, exclude: null, report: null, item: null, id: null, status: null, claim: null, notes: null, reason: null, confidence: null, threshold: null, change: null, starter: null, sections: null, brief: null, tools: 'all', hookProfile: null, templatePack: null, target: 'openprd', targetRoot: null, path: null, productType: null, title: null, owner: null, problem: null, whyNow: null, evidence: null, from: null, to: null, version: null, digest: null, workUnit: null, event: null, risk: null, outcome: null, preview: null, learn: null, genre: null, style: null, topic: null, enable: false, disable: false, agent: 'codex', agentCommand: null, worktree: null, branch: null, message: null, thread: null, threadTitle: null, conversationTitle: null, session: null, sessionTitle: null, host: null, port: null, openTarget: 'auto', runtimeAction: null, lane: null, task: null, claimId: null, client: null, leaseMs: null, expectedRevision: null, expectedLaneRevision: null, writeSet: null, baseCommit: null, gateId: null, approvalId: null, symlinkStrategy: null, installSafetyBackupRoot: null };
|
|
23
23
|
const positionals = [];
|
|
24
24
|
|
|
25
25
|
while (args.length > 0) {
|
|
@@ -28,6 +28,62 @@ function parseCommandArgs(argv) {
|
|
|
28
28
|
flags.json = true;
|
|
29
29
|
continue;
|
|
30
30
|
}
|
|
31
|
+
if (arg === '--runtime-action' || arg === '--action') {
|
|
32
|
+
flags.runtimeAction = args.shift() ?? null;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (arg === '--lane') {
|
|
36
|
+
flags.lane = args.shift() ?? null;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (arg === '--task') {
|
|
40
|
+
flags.task = args.shift() ?? null;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (arg === '--claim-id') {
|
|
44
|
+
flags.claimId = args.shift() ?? null;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (arg === '--client') {
|
|
48
|
+
flags.client = args.shift() ?? null;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (arg === '--lease-ms') {
|
|
52
|
+
flags.leaseMs = args.shift() ?? null;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (arg === '--expected-revision') {
|
|
56
|
+
flags.expectedRevision = args.shift() ?? null;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (arg === '--expected-lane-revision') {
|
|
60
|
+
flags.expectedLaneRevision = args.shift() ?? null;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (arg === '--write-set') {
|
|
64
|
+
flags.writeSet = args.shift() ?? null;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (arg === '--base-commit') {
|
|
68
|
+
flags.baseCommit = args.shift() ?? null;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (arg === '--gate' || arg === '--gate-id') {
|
|
72
|
+
flags.gateId = args.shift() ?? null;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (arg === '--approval-id') {
|
|
76
|
+
flags.approvalId = args.shift() ?? null;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (arg === '--symlink-strategy') {
|
|
80
|
+
flags.symlinkStrategy = args.shift() ?? null;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (arg === '--install-safety-backup-root') {
|
|
84
|
+
flags.installSafetyBackupRoot = args.shift() ?? null;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
31
87
|
if (arg === '--force') {
|
|
32
88
|
flags.force = true;
|
|
33
89
|
continue;
|
|
@@ -581,14 +637,16 @@ function usage() {
|
|
|
581
637
|
'OpenPrd CLI',
|
|
582
638
|
'',
|
|
583
639
|
'Usage:',
|
|
584
|
-
' openprd init [path] [--template-pack <base|consumer|b2b|agent>] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--force]',
|
|
585
|
-
' openprd setup [path] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--force] [--json]',
|
|
586
|
-
' openprd update [path] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--force] [--json]',
|
|
640
|
+
' openprd init [path] [--template-pack <base|consumer|b2b|agent>] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--symlink-strategy <block|materialize>] [--install-safety-backup-root <path>] [--force]',
|
|
641
|
+
' openprd setup [path] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--symlink-strategy <block|materialize>] [--install-safety-backup-root <path>] [--force] [--json]',
|
|
642
|
+
' openprd update [path] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--symlink-strategy <block|materialize>] [--install-safety-backup-root <path>] [--force] [--json]',
|
|
587
643
|
' openprd self-update [--check] [--dry-run] [--json]',
|
|
588
644
|
' openprd upgrade [path] [--fleet] [--dry-run] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--max-depth <n>] [--include <csv>] [--exclude <csv>] [--report <file>] [--force] [--json]',
|
|
589
645
|
' openprd doctor [path] [--tools <all|codex,claude,cursor>] [--hook-profile <lite|guarded|full>] [--fix] [--json]',
|
|
590
646
|
' openprd fleet <root> [--dry-run|--doctor|--update-openprd|--backfill-work-units|--sync-registry|--setup-missing] [--hook-profile <lite|guarded|full>] [--max-depth <n>] [--include <csv>] [--exclude <csv>] [--report <file>] [--json]',
|
|
591
647
|
' openprd run [path] [--context|--verify|--record-hook --event <name> --risk <level> --outcome <text> --preview <text>] [--message <text>] [--json]',
|
|
648
|
+
' openprd runtime [path] --action <status|create-lane|ensure-lane|add-task|claim|heartbeat|release|finish|recover> [--lane <id>] [--task <id>] [--claim-id <id>] [--write-set <csv>] [--lease-ms <n>] [--expected-revision <n>] [--expected-lane-revision <n>] [--client <codex|claude|cursor>] [--session <id>] [--thread <id>] [--worktree <path>] [--branch <name>] [--base-commit <sha>] [--evidence <ref>] [--json]',
|
|
649
|
+
' openprd gate [path] --action <status|define|evidence|approve|evaluate> [--json-file <record.json>] [--gate <id>] [--approval-id <id>] [--json]',
|
|
592
650
|
' openprd loop [path] [--init|--plan|--next|--prompt|--run|--verify|--finish] [--change <id>] [--item <task-id-or-handle>] [--agent <codex|claude>] [--agent-command <cmd>] [--repair-agent] [--worktree <path>] [--branch <name>] [--allow-dirty-main] [--commit] [--dry-run] [--message <text>] [--json]',
|
|
593
651
|
' openprd canvas [path] [--thread <id> [--thread-title <name>]|--session <id> [--session-title <name>]] [--host <host>] [--port <number>] [--daemon|--stop] [--open] [--open-target <auto|system|codex-browser>] [--dry-run] [--json]',
|
|
594
652
|
' openprd canvas [path] --bridge-outbox [--thread <id>|--session <id>] [--claim <next|id>] [--json]',
|
|
@@ -607,6 +665,7 @@ function usage() {
|
|
|
607
665
|
' --board modes: focus-board, parallel-board, verification-board, alignment-board, centering-board',
|
|
608
666
|
' openprd design-starter [path] [--starter <content-home|product-launch|ops-dashboard>] [--out <index.html>] [--title <name>] [--brief <text>] [--sections <a|b|c>] [--no-external-facts] [--no-brand-assets] [--no-real-images] [--force] [--json]',
|
|
609
667
|
' openprd dev-check [path] <file...> [--auto-refactor <on|off>] [--json]',
|
|
668
|
+
' openprd i18n-init [path] [--locale <zh-Hans|en|...>] [--json]',
|
|
610
669
|
' openprd grow [path] [--review|--apply --id <candidate-id>|--reject --id <candidate-id>|--init|--check] [--notes <text>] [--json]',
|
|
611
670
|
' openprd benchmark <add|observe|list|approve|verify> [target-or-id] [path-for-list-or-verify] [--path <project>] [--notes <text>] [--threshold <n>] [--id <benchmark-id>] [--json]',
|
|
612
671
|
' openprd synthesize [path] [--title <text>] [--owner <text>] [--problem <text>] [--why-now <text>] [--work-unit <id>] [--target-root <path>] [--open] [--json]',
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function printGateResult(result, json) {
|
|
2
|
+
if (json) {
|
|
3
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
if (!result.ok) {
|
|
7
|
+
console.log(`OpenPrd 操作门禁:未完成(${result.error?.code ?? 'unknown'})`);
|
|
8
|
+
console.log(result.error?.message ?? '门禁操作失败。');
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const status = result.result?.status;
|
|
12
|
+
console.log(`OpenPrd 操作门禁:${status === 'pass' ? '通过' : status === 'blocked' ? '已阻断' : '已记录'}`);
|
|
13
|
+
if (result.result?.decisionId) console.log(`决策编号:${result.result.decisionId}`);
|
|
14
|
+
for (const reason of result.result?.reasons ?? []) console.log(`- ${reason.code}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { printGateResult };
|
|
@@ -218,5 +218,23 @@ export async function runQualityCommand(command, { projectPath, flags, positiona
|
|
|
218
218
|
return result.ok ? 0 : 1;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
if (command === 'i18n-init') {
|
|
222
|
+
const initProjectPath = path.resolve(flags.path ?? positionals[0] ?? process.cwd());
|
|
223
|
+
const { initI18nConfig } = await import('../dev-standards.js');
|
|
224
|
+
const result = await initI18nConfig(initProjectPath, {
|
|
225
|
+
defaultLanguage: flags.locale ?? undefined,
|
|
226
|
+
});
|
|
227
|
+
if (flags.json) {
|
|
228
|
+
console.log(JSON.stringify(result, null, 2));
|
|
229
|
+
} else {
|
|
230
|
+
console.log(`OpenPrd i18n: ${result.created ? '已创建' : '配置已存在'} ${result.configPath}`);
|
|
231
|
+
console.log(`基准语言: ${result.config.defaultLanguage}`);
|
|
232
|
+
console.log(`支持语种: ${result.config.supportedLanguages.join(', ')}`);
|
|
233
|
+
console.log(`dev-check 检查: ${result.config.enforceOnDevCheck ? '开启' : '关闭'}`);
|
|
234
|
+
console.log(`最佳实践文档: docs/basic/i18n-guidelines.md`);
|
|
235
|
+
}
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
221
239
|
return null;
|
|
222
240
|
}
|
package/src/cli/quality-print.js
CHANGED
|
@@ -123,6 +123,16 @@ function printDevelopmentStandardsResult(result, json) {
|
|
|
123
123
|
} else if (result.visualEvidence?.uiFiles?.length > 0) {
|
|
124
124
|
console.log(`- 视觉证据: 最近 24 小时已有证据板(${result.visualEvidence.freshEvidence.join(', ')});确认覆盖本轮改动并把证据图嵌入对话流。`);
|
|
125
125
|
}
|
|
126
|
+
if (result.i18nReminder?.required) {
|
|
127
|
+
console.log(`- 多语言提醒: ${result.i18nReminder.reason}`);
|
|
128
|
+
console.log(` 涉及文件: ${result.i18nReminder.textFiles.join(', ')}`);
|
|
129
|
+
if (result.i18nReminder.guideline) {
|
|
130
|
+
console.log(` 最佳实践: ${result.i18nReminder.guideline}`);
|
|
131
|
+
}
|
|
132
|
+
if (!result.i18nReminder.configExists) {
|
|
133
|
+
console.log(' 建议: 创建 .openprd/i18n-config.json 启用项目级多语言检查。');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
126
136
|
printKnowledgeReview(result.knowledgeReview);
|
|
127
137
|
for (const error of result.errors ?? []) {
|
|
128
138
|
console.log(`- ${error}`);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function printTaskRuntimeResult(result, json) {
|
|
2
|
+
if (json) {
|
|
3
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
if (!result.ok) {
|
|
7
|
+
console.log(`OpenPrd 并行任务运行时:未完成(${result.error?.code ?? 'unknown'})`);
|
|
8
|
+
console.log(result.error?.message ?? '运行时操作失败。');
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
console.log(`OpenPrd 并行任务运行时:${result.action.replace(/^runtime-/, '')}`);
|
|
12
|
+
if (result.laneId) console.log(`处理通道:${result.laneId}`);
|
|
13
|
+
if (result.taskId) console.log(`任务:${result.taskId}`);
|
|
14
|
+
const task = result.result?.task;
|
|
15
|
+
if (task) {
|
|
16
|
+
console.log(`状态:${task.status}`);
|
|
17
|
+
console.log(`修订:${task.revision}`);
|
|
18
|
+
if (task.claim?.leaseExpiresAt) console.log(`占用有效期至:${task.claim.leaseExpiresAt}`);
|
|
19
|
+
} else if (Array.isArray(result.result)) {
|
|
20
|
+
console.log(`处理通道数:${result.result.length}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { printTaskRuntimeResult };
|