fix(codex): 完善独立运行与文档转换兼容性
This commit is contained in:
@@ -18,7 +18,7 @@ description: 基于需求、现有后端代码和项目规范设计模块边界
|
|||||||
|
|
||||||
## 边界
|
## 边界
|
||||||
|
|
||||||
- 项目规则通过 `guidance` 获取;不存在的规范、框架能力和依赖接口不得猜测。
|
- 已安装 `guidance` 时用它获取项目规则;未安装时按“目标目录适用的 `AGENTS.md` → `.craftkit/agents/index.md` → `.craftkit/standards/index.md` → 命中正文”的顺序手工读取。不存在的规范、框架能力和依赖接口不得猜测。
|
||||||
- 版本从 `.craftkit/project.json`、构建文件、锁文件或源码证据确认,不默认最新版本。
|
- 版本从 `.craftkit/project.json`、构建文件、锁文件或源码证据确认,不默认最新版本。
|
||||||
- 对现有系统的设计先追踪真实调用链和数据流,区分已验证事实与建议。
|
- 对现有系统的设计先追踪真实调用链和数据流,区分已验证事实与建议。
|
||||||
- 不把接口示例、表结构草案或伪代码视为已实施行为。
|
- 不把接口示例、表结构草案或伪代码视为已实施行为。
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ description: 按当前项目真实语言、框架版本、规范和现有实现
|
|||||||
## 工作流
|
## 工作流
|
||||||
|
|
||||||
1. 检查 Git 状态,保留用户已有改动;确认目标、范围和不可修改项。
|
1. 检查 Git 状态,保留用户已有改动;确认目标、范围和不可修改项。
|
||||||
2. 读取 `AGENTS.md`、`.craftkit/project.json`,使用 `guidance` 获取项目规范,并从构建文件确认真实版本。
|
2. 读取 `AGENTS.md`、`.craftkit/project.json`,从构建文件确认真实版本。已安装 `guidance` 时用它检索规范;未安装时按“目标目录适用的 `AGENTS.md` → `.craftkit/agents/index.md` → `.craftkit/standards/index.md` → 命中正文”的顺序手工读取,不因缺少另一插件中断实现。
|
||||||
3. 追踪入口、调用链、数据流、测试和相邻稳定实现;设计不足时先补最小决策,不套用固定模板。
|
3. 追踪入口、调用链、数据流、测试和相邻稳定实现;设计不足时先补最小决策,不套用固定模板。
|
||||||
4. 实施最小完整变更,保持项目目录、依赖、异常、事务、日志和测试风格。
|
4. 实施最小完整变更,保持项目目录、依赖、异常、事务、日志和测试风格。
|
||||||
5. 执行项目已有的编译、静态检查和相关测试,分别报告未执行的真实环境验证。
|
5. 执行项目已有的编译、静态检查和相关测试,分别报告未执行的真实环境验证。
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ description: 按当前项目框架版本、组件契约、设计令牌和请求
|
|||||||
## 工作流
|
## 工作流
|
||||||
|
|
||||||
1. 检查 Git 状态并确认目标文件;从项目元数据、依赖和锁文件识别框架、构建工具及精确版本。
|
1. 检查 Git 状态并确认目标文件;从项目元数据、依赖和锁文件识别框架、构建工具及精确版本。
|
||||||
2. 使用 `guidance` 获取规范,检查相邻页面和公共封装;组件、样式、表单分别消费 `component`、`style`、`form` 的证据。
|
2. 已安装 `guidance` 时用它检索规范;未安装时按“目标目录适用的 `AGENTS.md` → `.craftkit/agents/index.md` → `.craftkit/standards/index.md` → 命中正文”的顺序手工读取。随后检查相邻页面和公共封装;组件、样式、表单分别消费 `component`、`style`、`form` 的证据。
|
||||||
3. 有设计与接口映射时消费 `design-frontend` 和 `prepare-api`;没有时只补当前实现必需的最小决策。
|
3. 有设计与接口映射时消费 `design-frontend` 和 `prepare-api`;没有时只补当前实现必需的最小决策。
|
||||||
4. 修改页面、路由、状态和 API 层,保持项目现有契约,不虚构组件、props、事件、接口或业务校验。
|
4. 修改页面、路由、状态和 API 层,保持项目现有契约,不虚构组件、props、事件、接口或业务校验。
|
||||||
5. 执行已有格式化、类型检查、测试和构建;未进行真实渲染或浏览器验证时明确说明。
|
5. 执行已有格式化、类型检查、测试和构建;未进行真实渲染或浏览器验证时明确说明。
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ description: 将 Markdown 文档转换为 Word .docx,保留常见标题、段
|
|||||||
- 只接受存在的 `.md` 或 `.markdown` 文件。
|
- 只接受存在的 `.md` 或 `.markdown` 文件。
|
||||||
- 默认在输入文件旁生成同名 `.docx`;文件已存在时停止,只有用户明确同意覆盖后才传入 `--force`。
|
- 默认在输入文件旁生成同名 `.docx`;文件已存在时停止,只有用户明确同意覆盖后才传入 `--force`。
|
||||||
- 用户提供 `.docx` 模板时可传入 `--template`,转换器沿用模板样式并在文档末尾追加内容,不替换模板中的占位符。
|
- 用户提供 `.docx` 模板时可传入 `--template`,转换器沿用模板样式并在文档末尾追加内容,不替换模板中的占位符。
|
||||||
- 不自动下载图片、字体或依赖;远程图片保留为文字提示,本地缺失图片产生警告。
|
- 转换依赖 Python 包 `python-docx`。优先使用 Codex 工作区依赖运行时;环境缺失时停止并给出提示,不自动安装依赖。
|
||||||
|
- 不自动下载图片或字体;远程图片保留为文字提示,本地缺失图片产生警告。
|
||||||
- 不伪造修订记录、批注或目录。需要人工审阅留痕时应使用独立的文档修订流程。
|
- 不伪造修订记录、批注或目录。需要人工审阅留痕时应使用独立的文档修订流程。
|
||||||
|
|
||||||
## 转换流程
|
## 转换流程
|
||||||
|
|||||||
@@ -9,10 +9,6 @@ import sys
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from docx import Document
|
|
||||||
from docx.shared import Inches, Pt
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Result:
|
class Result:
|
||||||
"""记录生成物与转换统计。"""
|
"""记录生成物与转换统计。"""
|
||||||
@@ -43,6 +39,10 @@ class Converter:
|
|||||||
"""使用可预测的小型解析器转换常见 Markdown。"""
|
"""使用可预测的小型解析器转换常见 Markdown。"""
|
||||||
|
|
||||||
def __init__(self, source: Path, output: Path, template: Path | None) -> None:
|
def __init__(self, source: Path, output: Path, template: Path | None) -> None:
|
||||||
|
# 第三方库在真正转换时才加载,使 --help、参数错误和能力探测不依赖本机预装包。
|
||||||
|
from docx import Document
|
||||||
|
|
||||||
|
self.document_type = Document
|
||||||
self.source = source
|
self.source = source
|
||||||
self.doc = Document(template) if template else Document()
|
self.doc = Document(template) if template else Document()
|
||||||
self.result = Result(output)
|
self.result = Result(output)
|
||||||
@@ -61,12 +61,14 @@ class Converter:
|
|||||||
self.add_line(lines[index])
|
self.add_line(lines[index])
|
||||||
index += 1
|
index += 1
|
||||||
self.doc.save(self.result.output)
|
self.doc.save(self.result.output)
|
||||||
Document(self.result.output)
|
self.document_type(self.result.output)
|
||||||
return self.result
|
return self.result
|
||||||
|
|
||||||
def add_code(self, lines: list[str], start: int) -> int:
|
def add_code(self, lines: list[str], start: int) -> int:
|
||||||
"""读取围栏代码块;未闭合时输出其余内容并记录警告。"""
|
"""读取围栏代码块;未闭合时输出其余内容并记录警告。"""
|
||||||
|
|
||||||
|
from docx.shared import Pt
|
||||||
|
|
||||||
index = start + 1
|
index = start + 1
|
||||||
content: list[str] = []
|
content: list[str] = []
|
||||||
while index < len(lines) and not lines[index].lstrip().startswith("```"):
|
while index < len(lines) and not lines[index].lstrip().startswith("```"):
|
||||||
@@ -101,6 +103,8 @@ class Converter:
|
|||||||
def add_line(self, line: str) -> None:
|
def add_line(self, line: str) -> None:
|
||||||
"""识别标题、列表、引用、分隔线和普通段落。"""
|
"""识别标题、列表、引用、分隔线和普通段落。"""
|
||||||
|
|
||||||
|
from docx.shared import Inches
|
||||||
|
|
||||||
value = line.strip()
|
value = line.strip()
|
||||||
if not value:
|
if not value:
|
||||||
return
|
return
|
||||||
@@ -158,6 +162,8 @@ class Converter:
|
|||||||
def add_image(self, paragraph, alt: str, target: str) -> None:
|
def add_image(self, paragraph, alt: str, target: str) -> None:
|
||||||
"""只处理本地图片,防止转换过程产生隐式网络访问。"""
|
"""只处理本地图片,防止转换过程产生隐式网络访问。"""
|
||||||
|
|
||||||
|
from docx.shared import Inches
|
||||||
|
|
||||||
if re.match(r"^[a-z][a-z0-9+.-]*://", target, re.I):
|
if re.match(r"^[a-z][a-z0-9+.-]*://", target, re.I):
|
||||||
paragraph.add_run(f"[远程图片:{alt or target}]")
|
paragraph.add_run(f"[远程图片:{alt or target}]")
|
||||||
self.result.warnings.append(f"未下载远程图片:{target}")
|
self.result.warnings.append(f"未下载远程图片:{target}")
|
||||||
@@ -193,9 +199,22 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
print("错误:输出必须是可写的 .docx;覆盖需使用 --force", file=sys.stderr); return 1
|
print("错误:输出必须是可写的 .docx;覆盖需使用 --force", file=sys.stderr); return 1
|
||||||
if template and (not template.is_file() or template.suffix.lower() != ".docx"):
|
if template and (not template.is_file() or template.suffix.lower() != ".docx"):
|
||||||
print("错误:模板必须是存在的 .docx 文件", file=sys.stderr); return 2
|
print("错误:模板必须是存在的 .docx 文件", file=sys.stderr); return 2
|
||||||
|
try:
|
||||||
|
converter = Converter(source, output, template)
|
||||||
|
except ModuleNotFoundError as error:
|
||||||
|
if error.name == "docx":
|
||||||
|
print(
|
||||||
|
"错误:缺少 python-docx。请使用 Codex 工作区依赖运行时,"
|
||||||
|
"或在已获授权的本地 Python 环境中安装 python-docx 后重试。",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return 3
|
||||||
|
raise
|
||||||
|
except (OSError, ValueError) as error:
|
||||||
|
print(f"错误:{error}", file=sys.stderr); return 1
|
||||||
output.parent.mkdir(parents=True, exist_ok=True)
|
output.parent.mkdir(parents=True, exist_ok=True)
|
||||||
try:
|
try:
|
||||||
result = Converter(source, output, template).convert()
|
result = converter.convert()
|
||||||
except (OSError, ValueError) as error:
|
except (OSError, ValueError) as error:
|
||||||
print(f"错误:{error}", file=sys.stderr); return 1
|
print(f"错误:{error}", file=sys.stderr); return 1
|
||||||
print(f"DOCX:{result.output}")
|
print(f"DOCX:{result.output}")
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ description: 将 Excel .xlsx 工作簿转换为 Markdown,按工作表提取表
|
|||||||
- 默认保留公式文本;只有用户希望读取工作簿内已有缓存值时才使用 `--values`。转换器不会计算公式。
|
- 默认保留公式文本;只有用户希望读取工作簿内已有缓存值时才使用 `--values`。转换器不会计算公式。
|
||||||
- 合并单元格默认将锚点值填充到合并区域,可用 `--merged anchor` 仅保留左上角值。
|
- 合并单元格默认将锚点值填充到合并区域,可用 `--merged anchor` 仅保留左上角值。
|
||||||
- 默认在输入文件旁生成同名 `.md`,已存在时停止;覆盖必须获得用户确认并传入 `--force`。
|
- 默认在输入文件旁生成同名 `.md`,已存在时停止;覆盖必须获得用户确认并传入 `--force`。
|
||||||
- 不提取宏、图表、批注、数据验证、条件格式或图片,也不自动安装依赖。
|
- 转换依赖 Python 包 `openpyxl`。优先使用 Codex 工作区依赖运行时;环境缺失时停止并给出提示,不自动安装依赖。
|
||||||
|
- 不提取宏、图表、批注、数据验证、条件格式或图片。
|
||||||
|
|
||||||
## 工作流
|
## 工作流
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,10 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
import importlib.util
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from openpyxl import load_workbook
|
|
||||||
|
|
||||||
|
|
||||||
def display(value: object) -> str:
|
def display(value: object) -> str:
|
||||||
"""将单元格值转换为稳定、可读且适合表格的文本。"""
|
"""将单元格值转换为稳定、可读且适合表格的文本。"""
|
||||||
|
|
||||||
@@ -59,6 +57,9 @@ def render_sheet(title: str, rows: list[list[str]]) -> str:
|
|||||||
def convert(source: Path, output: Path, values_only: bool, merged_mode: str) -> tuple[int, int, int, list[str]]:
|
def convert(source: Path, output: Path, values_only: bool, merged_mode: str) -> tuple[int, int, int, list[str]]:
|
||||||
"""打开工作簿、转换全部工作表并写入 UTF-8 Markdown。"""
|
"""打开工作簿、转换全部工作表并写入 UTF-8 Markdown。"""
|
||||||
|
|
||||||
|
# 第三方库在真正读取工作簿时才加载,使 --help 和参数校验可在干净环境中执行。
|
||||||
|
from openpyxl import load_workbook
|
||||||
|
|
||||||
workbook = load_workbook(source, read_only=False, data_only=values_only)
|
workbook = load_workbook(source, read_only=False, data_only=values_only)
|
||||||
sections = [f"# {source.stem}"]
|
sections = [f"# {source.stem}"]
|
||||||
row_count = merged_count = 0
|
row_count = merged_count = 0
|
||||||
@@ -94,9 +95,26 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
print("错误:输出文件必须是 Markdown", file=sys.stderr); return 2
|
print("错误:输出文件必须是 Markdown", file=sys.stderr); return 2
|
||||||
if output.exists() and not args.force:
|
if output.exists() and not args.force:
|
||||||
print(f"错误:输出文件已存在:{output}", file=sys.stderr); return 1
|
print(f"错误:输出文件已存在:{output}", file=sys.stderr); return 1
|
||||||
|
# 在创建输出目录前完成依赖预检,保证缺少运行库时不会留下空目录或半成品。
|
||||||
|
if importlib.util.find_spec("openpyxl") is None:
|
||||||
|
print(
|
||||||
|
"错误:缺少 openpyxl。请使用 Codex 工作区依赖运行时,"
|
||||||
|
"或在已获授权的本地 Python 环境中安装 openpyxl 后重试。",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return 3
|
||||||
output.parent.mkdir(parents=True, exist_ok=True)
|
output.parent.mkdir(parents=True, exist_ok=True)
|
||||||
try:
|
try:
|
||||||
sheets, rows, merged, warnings = convert(source, output, args.values, args.merged)
|
sheets, rows, merged, warnings = convert(source, output, args.values, args.merged)
|
||||||
|
except ModuleNotFoundError as error:
|
||||||
|
if error.name == "openpyxl":
|
||||||
|
print(
|
||||||
|
"错误:缺少 openpyxl。请使用 Codex 工作区依赖运行时,"
|
||||||
|
"或在已获授权的本地 Python 环境中安装 openpyxl 后重试。",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return 3
|
||||||
|
raise
|
||||||
except (OSError, ValueError) as error:
|
except (OSError, ValueError) as error:
|
||||||
print(f"错误:{error}", file=sys.stderr); return 1
|
print(f"错误:{error}", file=sys.stderr); return 1
|
||||||
print(f"Markdown:{output}")
|
print(f"Markdown:{output}")
|
||||||
|
|||||||
Reference in New Issue
Block a user