From fa61d469332aeb3f8f6069f168cc9f566d336b06 Mon Sep 17 00:00:00 2001 From: wantsong Date: Sat, 13 Jun 2026 23:59:58 +0800 Subject: [PATCH] feat: migrate fix-title skill --- .gitignore | 4 + docs/installation.md | 107 +++++ environment.yml | 6 + registry/skills-index.md | 15 +- scripts/README.md | 16 +- scripts/install-skill.ps1 | 184 +++++++++ scripts/install-skill.sh | 220 ++++++++++ skills/fix-title/INSTALL.md | 59 +++ skills/fix-title/MIGRATION.md | 40 ++ skills/fix-title/SKILL.md | 52 +++ skills/fix-title/agents/openai.yaml | 3 + skills/fix-title/manual-test/README.md | 13 + skills/fix-title/ref/erro-example.md | 371 +++++++++++++++++ skills/fix-title/ref/recorder-example.md | 375 ++++++++++++++++++ .../fix-title/scripts/fix_markdown_titles.py | 116 ++++++ .../tests/test_fix_markdown_titles.py | 100 +++++ 16 files changed, 1676 insertions(+), 5 deletions(-) create mode 100644 docs/installation.md create mode 100644 environment.yml create mode 100644 scripts/install-skill.ps1 create mode 100644 scripts/install-skill.sh create mode 100644 skills/fix-title/INSTALL.md create mode 100644 skills/fix-title/MIGRATION.md create mode 100644 skills/fix-title/SKILL.md create mode 100644 skills/fix-title/agents/openai.yaml create mode 100644 skills/fix-title/manual-test/README.md create mode 100644 skills/fix-title/ref/erro-example.md create mode 100644 skills/fix-title/ref/recorder-example.md create mode 100644 skills/fix-title/scripts/fix_markdown_titles.py create mode 100644 skills/fix-title/tests/test_fix_markdown_titles.py diff --git a/.gitignore b/.gitignore index 0e85895..e500314 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,10 @@ tmp/ temp/ *.log +# Manual Skill test artifacts +skills/*/manual-test/*.md +!skills/*/manual-test/README.md + # Secrets .env .env.* diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..ccab009 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,107 @@ +# Installation + +This repository separates Skill development source from the local public installation surface. + +## Paths + +Development source: + +```text +C:\Users\wangq\Documents\Codex\skills-vault +``` + +Public local Skill installation surface: + +```text +C:\Users\wangq\.agents\skills +``` + +Agent-specific private Skill folders, such as `C:\Users\wangq\.claude\skills`, should link to the public installation surface instead of linking directly to the development source. + +## Conda Environment + +Use the shared repository environment by default: + +```powershell +conda env create -f environment.yml +conda activate skills-vault +``` + +If the environment already exists: + +```powershell +conda activate skills-vault +``` + +Most lightweight Skills should use this shared environment. Create a dedicated environment only when a Skill has conflicting or heavy dependencies. + +## Install One Skill + +PowerShell: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\install-skill.ps1 -Skill fix-title +``` + +Git Bash / Linux / macOS: + +```bash +bash scripts/install-skill.sh fix-title +``` + +## Install All Default Skills + +PowerShell: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\install-skill.ps1 -All +``` + +Git Bash / Linux / macOS: + +```bash +bash scripts/install-skill.sh --all +``` + +Full installation uses `registry/skills-index.md` and installs only Skills with: + +```text +Status: tested, installable, or installed +Install Policy: default +``` + +Conditional, optional, disabled, candidate, deprecated, and archived Skills are skipped during full installation. + +## Updating An Existing Installed Skill + +By default, install scripts refuse to overwrite an existing Skill. + +Use `-Force` or `--force` to back up the current installed directory and copy the repository version: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\install-skill.ps1 -Skill fix-title -Force +``` + +```bash +bash scripts/install-skill.sh fix-title --force +``` + +Backups are created next to the installed Skill: + +```text +C:\Users\wangq\.agents\skills\fix-title.backup-YYYYMMDDHHMMSS +``` + +## Claude Link + +To create or update the Claude private Skill entry as a link to the public installation surface: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\install-skill.ps1 -Skill fix-title -InstallClaudeLink +``` + +```bash +bash scripts/install-skill.sh fix-title --install-claude-link +``` + +Codex is not currently treated as a separate private install target for these shared automation Skills. diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..f96cee2 --- /dev/null +++ b/environment.yml @@ -0,0 +1,6 @@ +name: skills-vault +channels: + - conda-forge +dependencies: + - python=3.11 + - pip diff --git a/registry/skills-index.md b/registry/skills-index.md index 0dd57e7..e3222bf 100644 --- a/registry/skills-index.md +++ b/registry/skills-index.md @@ -11,9 +11,9 @@ Existing CCPE content is not migrated here. ## Skills -| Skill | Purpose | Source | Status | Installed Path | CCPE Registration | -| --- | --- | --- | --- | --- | --- | -| _none yet_ | | | | | | +| Skill | Purpose | Source | Status | Install Policy | Installed Path | CCPE Registration | +| --- | --- | --- | --- | --- | --- | --- | +| `fix-title` | Shift Markdown ATX heading depth for pasted LLM replies before insertion under parent sections. | `C:\Users\wangq\Documents\Codex\knowledge-vault\skills\fix-title` | `installed` | `default` | `C:\Users\wangq\.agents\skills\fix-title` | none | ## Status Values @@ -25,3 +25,12 @@ installed deprecated archived ``` + +## Install Policy Values + +```text +default +optional +conditional +disabled +``` diff --git a/scripts/README.md b/scripts/README.md index d8f42c6..4cf7892 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -6,8 +6,8 @@ Examples of future scripts: - validate Skill directory structure - list registered Skills -- install selected Skills to `C:\Users\wangq\.agents\skills` -- sync selected Skills +- install selected or default Skills to `C:\Users\wangq\.agents\skills` +- create Agentic private Skill links that point to `.agents\skills` - check registry consistency Skill-specific scripts should live under: @@ -15,3 +15,15 @@ Skill-specific scripts should live under: ```text skills//scripts/ ``` + +Current install scripts: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\install-skill.ps1 -Skill fix-title +powershell -ExecutionPolicy Bypass -File .\scripts\install-skill.ps1 -All +``` + +```bash +bash scripts/install-skill.sh fix-title +bash scripts/install-skill.sh --all +``` diff --git a/scripts/install-skill.ps1 b/scripts/install-skill.ps1 new file mode 100644 index 0000000..c268d0c --- /dev/null +++ b/scripts/install-skill.ps1 @@ -0,0 +1,184 @@ +param( + [string]$Skill, + [switch]$All, + [switch]$Force, + [switch]$InstallClaudeLink, + [string]$InstallRoot = "$HOME\.agents\skills", + [string]$ClaudeSkillsRoot = "$HOME\.claude\skills" +) + +$ErrorActionPreference = "Stop" + +function Show-Usage { + Write-Host "Usage:" + Write-Host " powershell -ExecutionPolicy Bypass -File .\scripts\install-skill.ps1 -Skill fix-title" + Write-Host " powershell -ExecutionPolicy Bypass -File .\scripts\install-skill.ps1 -All" + Write-Host "" + Write-Host "Options:" + Write-Host " -Force Back up and replace existing installed Skill" + Write-Host " -InstallClaudeLink Create/update .claude\skills link to .agents\skills" +} + +if (($Skill -and $All) -or (-not $Skill -and -not $All)) { + Show-Usage + exit 2 +} + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$RepoRoot = Resolve-Path (Join-Path $ScriptDir "..") +$SkillsRoot = Join-Path $RepoRoot "skills" +$RegistryPath = Join-Path $RepoRoot "registry\skills-index.md" + +function Get-RegistryMeta { + param([string]$Name) + + if (-not (Test-Path -LiteralPath $RegistryPath)) { + return $null + } + + $escaped = [regex]::Escape("`$Name") + $line = Get-Content -LiteralPath $RegistryPath | Where-Object { $_ -match "^\|\s*``$([regex]::Escape($Name))``\s*\|" } | Select-Object -First 1 + if (-not $line) { + return $null + } + + $parts = $line.Trim() -split "\|" + $cells = @() + foreach ($part in $parts) { + $cell = $part.Trim() + if ($cell.Length -gt 0) { + $cells += $cell + } + } + + if ($cells.Count -lt 7) { + return $null + } + + return [PSCustomObject]@{ + Skill = $cells[0].Trim("``") + Purpose = $cells[1] + Source = $cells[2] + Status = $cells[3].Trim("``") + InstallPolicy = $cells[4].Trim("``") + InstalledPath = $cells[5] + CcpeRegistration = $cells[6] + } +} + +function Get-AllDefaultSkills { + $allowedStatuses = @("tested", "installable", "installed") + $skills = @() + + Get-ChildItem -LiteralPath $SkillsRoot -Directory | ForEach-Object { + $skillName = $_.Name + $skillFile = Join-Path $_.FullName "SKILL.md" + if (-not (Test-Path -LiteralPath $skillFile)) { + return + } + + $meta = Get-RegistryMeta -Name $skillName + if (-not $meta) { + Write-Host "Skipping ${skillName}: no registry row" + return + } + + if ($allowedStatuses -notcontains $meta.Status) { + Write-Host "Skipping ${skillName}: status is $($meta.Status)" + return + } + + if ($meta.InstallPolicy -ne "default") { + Write-Host "Skipping ${skillName}: install policy is $($meta.InstallPolicy)" + return + } + + $skills += $skillName + } + + return $skills +} + +function Copy-SkillDirectory { + param( + [string]$Name, + [string]$Source, + [string]$Target + ) + + New-Item -ItemType Directory -Force -Path (Split-Path -Parent $Target) | Out-Null + New-Item -ItemType Directory -Force -Path $Target | Out-Null + + Get-ChildItem -Force -LiteralPath $Source | Where-Object { + $_.Name -notin @("manual-test", "__pycache__", ".pytest_cache") + } | ForEach-Object { + Copy-Item -LiteralPath $_.FullName -Destination $Target -Recurse -Force + } +} + +function Backup-ExistingPath { + param([string]$Path) + + $stamp = Get-Date -Format "yyyyMMddHHmmss" + $backup = "$Path.backup-$stamp" + Move-Item -LiteralPath $Path -Destination $backup + return $backup +} + +function Install-OneSkill { + param([string]$Name) + + if ($Name -notmatch "^[a-z0-9][a-z0-9-]*$") { + throw "Invalid Skill name: $Name" + } + + $source = Join-Path $SkillsRoot $Name + $skillFile = Join-Path $source "SKILL.md" + if (-not (Test-Path -LiteralPath $skillFile)) { + throw "Skill source not found: $skillFile" + } + + $meta = Get-RegistryMeta -Name $Name + if ($meta -and $meta.InstallPolicy -eq "disabled" -and -not $Force) { + throw "Skill $Name has install policy disabled. Use -Force only if you intentionally want to install it." + } + + $target = Join-Path $InstallRoot $Name + if (Test-Path -LiteralPath $target) { + if (-not $Force) { + throw "Installed Skill already exists: $target. Re-run with -Force to back it up and replace it." + } + $backup = Backup-ExistingPath -Path $target + Write-Host "Backed up existing Skill to $backup" + } + + Copy-SkillDirectory -Name $Name -Source $source -Target $target + Write-Host "Installed $Name to $target" + + if ($InstallClaudeLink) { + New-Item -ItemType Directory -Force -Path $ClaudeSkillsRoot | Out-Null + $claudeTarget = Join-Path $ClaudeSkillsRoot $Name + if (Test-Path -LiteralPath $claudeTarget) { + if (-not $Force) { + throw "Claude Skill link/path already exists: $claudeTarget. Re-run with -Force to back it up and replace it." + } + $backup = Backup-ExistingPath -Path $claudeTarget + Write-Host "Backed up existing Claude path to $backup" + } + New-Item -ItemType Junction -Path $claudeTarget -Target $target | Out-Null + Write-Host "Linked Claude Skill $claudeTarget -> $target" + } +} + +if ($All) { + $skillNames = Get-AllDefaultSkills + if ($skillNames.Count -eq 0) { + Write-Host "No default installable Skills found." + exit 0 + } + foreach ($name in $skillNames) { + Install-OneSkill -Name $name + } +} else { + Install-OneSkill -Name $Skill +} diff --git a/scripts/install-skill.sh b/scripts/install-skill.sh new file mode 100644 index 0000000..48c0b36 --- /dev/null +++ b/scripts/install-skill.sh @@ -0,0 +1,220 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: + bash scripts/install-skill.sh fix-title + bash scripts/install-skill.sh --all + +Options: + --force Back up and replace existing installed Skill + --install-claude-link Create/update .claude/skills link to .agents/skills +EOF +} + +skill="" +install_all=0 +force=0 +install_claude_link=0 + +while [[ $# -gt 0 ]]; do + case "$1" in + --all) + install_all=1 + shift + ;; + --force) + force=1 + shift + ;; + --install-claude-link) + install_claude_link=1 + shift + ;; + -h|--help) + usage + exit 0 + ;; + -*) + echo "Unknown option: $1" >&2 + usage + exit 2 + ;; + *) + if [[ -n "$skill" ]]; then + echo "Only one Skill name may be provided." >&2 + usage + exit 2 + fi + skill="$1" + shift + ;; + esac +done + +if [[ "$install_all" -eq 1 && -n "$skill" ]] || [[ "$install_all" -eq 0 && -z "$skill" ]]; then + usage + exit 2 +fi + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$script_dir/.." && pwd)" +skills_root="$repo_root/skills" +registry_path="$repo_root/registry/skills-index.md" + +if [[ -n "${USERPROFILE:-}" ]]; then + home_dir="$USERPROFILE" +else + home_dir="$HOME" +fi + +install_root="${SKILLS_VAULT_INSTALL_ROOT:-$home_dir/.agents/skills}" +claude_skills_root="${SKILLS_VAULT_CLAUDE_SKILLS_ROOT:-$home_dir/.claude/skills}" + +registry_meta() { + local name="$1" + [[ -f "$registry_path" ]] || return 1 + grep -E "^\|[[:space:]]*\`$name\`[[:space:]]*\|" "$registry_path" | head -n 1 || true +} + +registry_cell() { + local line="$1" + local index="$2" + printf '%s\n' "$line" | awk -F'|' -v i="$index" '{gsub(/^ +| +$/, "", $(i + 1)); gsub(/`/, "", $(i + 1)); print $(i + 1)}' +} + +all_default_skills() { + local allowed_statuses=" tested installable installed " + for dir in "$skills_root"/*; do + [[ -d "$dir" ]] || continue + [[ -f "$dir/SKILL.md" ]] || continue + local name + name="$(basename "$dir")" + local line + line="$(registry_meta "$name")" + if [[ -z "$line" ]]; then + echo "Skipping $name: no registry row" >&2 + continue + fi + local status policy + status="$(registry_cell "$line" 4)" + policy="$(registry_cell "$line" 5)" + if [[ "$allowed_statuses" != *" $status "* ]]; then + echo "Skipping $name: status is $status" >&2 + continue + fi + if [[ "$policy" != "default" ]]; then + echo "Skipping $name: install policy is $policy" >&2 + continue + fi + printf '%s\n' "$name" + done +} + +backup_existing_path() { + local path="$1" + local stamp + stamp="$(date +%Y%m%d%H%M%S)" + local backup="$path.backup-$stamp" + mv "$path" "$backup" + printf '%s\n' "$backup" +} + +copy_skill_directory() { + local source="$1" + local target="$2" + mkdir -p "$target" + shopt -s dotglob nullglob + for item in "$source"/*; do + local base + base="$(basename "$item")" + case "$base" in + manual-test|__pycache__|.pytest_cache) + continue + ;; + esac + cp -a "$item" "$target/" + done + shopt -u dotglob nullglob +} + +create_claude_link() { + local name="$1" + local target="$2" + local link="$claude_skills_root/$name" + mkdir -p "$claude_skills_root" + if [[ -e "$link" || -L "$link" ]]; then + if [[ "$force" -ne 1 ]]; then + echo "Claude Skill link/path already exists: $link. Re-run with --force to back it up and replace it." >&2 + exit 1 + fi + local backup + backup="$(backup_existing_path "$link")" + echo "Backed up existing Claude path to $backup" + fi + + if [[ "${OS:-}" == "Windows_NT" ]]; then + cmd.exe /c mklink /J "$(cygpath -w "$link")" "$(cygpath -w "$target")" >/dev/null + else + ln -s "$target" "$link" + fi + echo "Linked Claude Skill $link -> $target" +} + +install_one_skill() { + local name="$1" + if [[ ! "$name" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then + echo "Invalid Skill name: $name" >&2 + exit 1 + fi + + local source="$skills_root/$name" + if [[ ! -f "$source/SKILL.md" ]]; then + echo "Skill source not found: $source/SKILL.md" >&2 + exit 1 + fi + + local line + line="$(registry_meta "$name")" + if [[ -n "$line" ]]; then + local policy + policy="$(registry_cell "$line" 5)" + if [[ "$policy" == "disabled" && "$force" -ne 1 ]]; then + echo "Skill $name has install policy disabled. Use --force only if you intentionally want to install it." >&2 + exit 1 + fi + fi + + local target="$install_root/$name" + mkdir -p "$install_root" + if [[ -e "$target" || -L "$target" ]]; then + if [[ "$force" -ne 1 ]]; then + echo "Installed Skill already exists: $target. Re-run with --force to back it up and replace it." >&2 + exit 1 + fi + local backup + backup="$(backup_existing_path "$target")" + echo "Backed up existing Skill to $backup" + fi + + copy_skill_directory "$source" "$target" + echo "Installed $name to $target" + + if [[ "$install_claude_link" -eq 1 ]]; then + create_claude_link "$name" "$target" + fi +} + +if [[ "$install_all" -eq 1 ]]; then + mapfile -t skills < <(all_default_skills) + if [[ "${#skills[@]}" -eq 0 ]]; then + echo "No default installable Skills found." + exit 0 + fi + for name in "${skills[@]}"; do + install_one_skill "$name" + done +else + install_one_skill "$skill" +fi diff --git a/skills/fix-title/INSTALL.md b/skills/fix-title/INSTALL.md new file mode 100644 index 0000000..c9ebe80 --- /dev/null +++ b/skills/fix-title/INSTALL.md @@ -0,0 +1,59 @@ +# Install fix-title + +`fix-title` uses the repository-level `skills-vault` Conda environment. + +## Prepare Environment + +From the repository root: + +```powershell +conda env create -f environment.yml +conda activate skills-vault +``` + +If the environment already exists: + +```powershell +conda activate skills-vault +``` + +## Test + +```powershell +python -m unittest discover -s skills\fix-title\tests -v +``` + +## Install To Public Skill Surface + +PowerShell: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\install-skill.ps1 -Skill fix-title +``` + +Replace an existing installed copy after backing it up: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\install-skill.ps1 -Skill fix-title -Force +``` + +Git Bash / Linux / macOS: + +```bash +bash scripts/install-skill.sh fix-title +bash scripts/install-skill.sh fix-title --force +``` + +## Optional Claude Link + +PowerShell: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\install-skill.ps1 -Skill fix-title -InstallClaudeLink +``` + +Git Bash / Linux / macOS: + +```bash +bash scripts/install-skill.sh fix-title --install-claude-link +``` diff --git a/skills/fix-title/MIGRATION.md b/skills/fix-title/MIGRATION.md new file mode 100644 index 0000000..fff199c --- /dev/null +++ b/skills/fix-title/MIGRATION.md @@ -0,0 +1,40 @@ +# fix-title Migration + +## Source + +```text +C:\Users\wangq\Documents\Codex\knowledge-vault\skills\fix-title +``` + +## Target + +```text +C:\Users\wangq\Documents\Codex\skills-vault\skills\fix-title +``` + +## Policy + +This migration copies the automation Skill source into `skills-vault`. + +The original `knowledge-vault` directory is intentionally left in place. Deleting the old source is a manual user action. + +## Install Model + +`skills-vault` is the development source repository. + +The local public installation surface remains: + +```text +C:\Users\wangq\.agents\skills\fix-title +``` + +Claude, OpenClaw, and other Agentic private Skill folders may link to the public installation surface instead of linking directly to this development source. + +## Status + +```text +migration status: migrated-source-and-installed +installed version updated: yes +installed backup: C:\Users\wangq\.agents\skills\fix-title.backup-20260613235817 +manual test: passed +``` diff --git a/skills/fix-title/SKILL.md b/skills/fix-title/SKILL.md new file mode 100644 index 0000000..f233fdf --- /dev/null +++ b/skills/fix-title/SKILL.md @@ -0,0 +1,52 @@ +--- +name: fix-title +description: Use when a pasted ChatGPT or LLM Markdown reply has heading levels that need to be shifted down before insertion under a parent section such as "## GPT"; also use for Markdown title repair, heading nesting fixes, ATX header adjustment, and adding one or more # levels to headings in a file. +--- + +# Fix Title + +## Overview + +Fix Markdown heading depth for copied LLM replies before inserting them into a discussion record. Use the bundled script for deterministic file edits instead of asking the model to rewrite the document. + +The default operation adds two heading levels, turning `# Title` into `### Title`, which fits content placed under a parent heading like `## GPT`. + +## Workflow + +1. Confirm the input is a Markdown file containing the raw reply to adjust. +2. Choose the number of levels to add. Default to `2` unless the user specifies another value. +3. Run `scripts/fix_markdown_titles.py` on the file. +4. If the target is important or the requested level shift is unusual, use `--dry-run` or `--output` first and inspect the result. +5. Report the path modified and the level shift used. + +## Commands + +```bash +python scripts/fix_markdown_titles.py path/to/reply.md +python scripts/fix_markdown_titles.py path/to/reply.md --levels 1 +python scripts/fix_markdown_titles.py path/to/reply.md --levels 3 --dry-run +python scripts/fix_markdown_titles.py path/to/reply.md --levels 2 --output path/to/fixed.md +``` + +On Windows PowerShell, quote paths that contain spaces: + +```powershell +python .\scripts\fix_markdown_titles.py "C:\path with spaces\reply.md" --levels 2 +``` + +## Behavior + +| Input | `--levels 2` output | +| --- | --- | +| `# A` | `### A` | +| `## B` | `#### B` | +| `###### C` | `###### C` | + +The script only changes ATX headings: lines beginning with up to three spaces followed by `#` through `######` and then whitespace. It does not change headings inside fenced code blocks, quoted lines such as `> # text`, or indented code. + +## Common Mistakes + +- Do not use a broad replace like `#` to `###`; it will corrupt code blocks, quoted content, tags, and inline text. +- Do not increase headings past level six; Markdown ATX headings stop at `######`. +- Do not change the recorder's outer headings such as `# 1` or `## GPT` unless the user explicitly points the skill at the recorder file and requests that behavior. +- Do not use prompt rewriting for this task when the script can perform the edit directly. diff --git a/skills/fix-title/agents/openai.yaml b/skills/fix-title/agents/openai.yaml new file mode 100644 index 0000000..f67e912 --- /dev/null +++ b/skills/fix-title/agents/openai.yaml @@ -0,0 +1,3 @@ +display_name: Fix Markdown Titles +short_description: Shift Markdown headings down before inserting pasted chat replies. +default_prompt: Fix the Markdown heading levels in this file so it can be pasted under a parent section. diff --git a/skills/fix-title/manual-test/README.md b/skills/fix-title/manual-test/README.md new file mode 100644 index 0000000..32e6e69 --- /dev/null +++ b/skills/fix-title/manual-test/README.md @@ -0,0 +1,13 @@ +# Manual Test Input + +Place one raw Markdown file here for manual validation. + +Recommended filename: + +```text +raw-reply.md +``` + +The file should be an original pasted LLM reply before heading repair. + +After the file is added, run the Skill against a copy or with `--output` first, then compare the result before updating the installed version. diff --git a/skills/fix-title/ref/erro-example.md b/skills/fix-title/ref/erro-example.md new file mode 100644 index 0000000..71bcfc7 --- /dev/null +++ b/skills/fix-title/ref/erro-example.md @@ -0,0 +1,371 @@ +结论:**V4.1 已经基本补齐了前面讨论里最关键的纲领级缺口**,尤其是这五个补得很准: + +1. **生产工坊**补上了 `video-workbench / PPT / 多模态素材生产` 这类场景的位置。 +2. **QPI Drift Guard**补上了“P 域任务滑入 I 域治理项目”的防线。 +3. **Whole-source Gestalt First**吸收了你素材提炼测试里的关键教训。 +4. **Execution Authenticity**吸收了第一次多 Agent 评审测试里的“伪执行”教训。 +5. **Cost Ledger + Stop Right**吸收了 3000 万 tokens 的 ROI 教训。 + +这版已经不是“有没有骨架”的问题,而是**有几处术语、边界和强度需要校准**。我会把它分成三类:必须改的小 bug、建议补入纲领的遗漏、建议下放到 Agentic 守则的内容。 + +--- + +# 一、必须改的几个小 bug / 强冲突 + +## 1. “两种封装策略”要改成“三种封装策略” + +你第 2 节现在写: + +> 根据业务场景的熵值与秩序,我们采用两种不同的封装策略。 + +但表格已经是三类: + +```text +逻辑轮机 +生产工坊 +战略透镜 +``` + +应改为: + +```md +根据业务场景的熵值、秩序与产物形态,我们采用三种不同的封装策略。 +``` + +同时,下面这句也要改: + +> 针对 B 类(战略透镜)场景…… + +因为加了“生产工坊”之后,如果继续按 A/B/C 排列,战略透镜已经不是 B 类,而是 C 类。最稳妥的写法是不要写字母: + +```md +针对“战略透镜”场景,当面临试错成本极高、失败即引发法理追责的“单向门”商业决策时…… +``` + +这是小 bug,但很重要。分类体系一旦留下歧义,后面守则会被带歪。 + +--- + +## 2. “Agent 输出必须包含 CoT 摘要”建议改掉 + +你现在写: + +```md +Agent 输出结果时,必须包含 CoT(思维链)摘要,即“我为什么这么判/这么想” +``` + +这里建议改成: + +```md +Agent 输出结果时,必须包含可审计的理由摘要(Decision Rationale),说明其关键依据、适用规则、不确定性、排除选项与可推翻条件。 +``` + +原因不是咬文嚼字。**你真正需要的不是 CoT,而是可校准、可审计、可反驳的理由结构。** + +CoT 这个词容易把系统引向“暴露内部推理过程”。但真实产品里更稳的形态是: + +```text +结论 +依据 +规则 +不确定性 +反例 +人工确认点 +可推翻条件 +``` + +这和你的“钢尺与皮尺”“不仅做对,还要好改”更一致。 + +--- + +## 3. QPI 的 I 域响应里,“强制启动双离合榨取”太重 + +你现在写: + +```md +【I】系统响应:生态学干预。强制启动后道的“思想考古”与“双离合榨取”…… +``` + +但后面 3.6 又明确说“双离合榨取”是 Beta / 理论沙盘,工程实现成本极高。这两个地方有冲突。 + +建议改成: + +```md +【I】系统响应:生态学干预。优先启动思想考古、战略透镜与人机回环;仅在高危、非遍历、明确授权的深水区,才考虑进入“双离合榨取”或摩擦蒸馏等 Beta 机制。 +``` + +这样层级就清楚了: + +```text +I 域默认:思想考古 + 战略透镜 + 人机回环 +I 域高危:预设委员会 +I 域极高危/实验:双离合榨取 +``` + +否则会把一个 Beta 沙盘机制误升成 I 域默认动作,容易再度重工业化。 + +--- + +## 4. “全源完形前置”需要加适用条件 + +这个原则非常重要,但现在写得有点绝对: + +```md +严禁对连贯的长篇语料直接采用基于分块的并发提取机制…… +必须首先调用高上下文承载力的主节点…… +``` + +核心判断对,但建议补上适用边界: + +```md +当源材料是连贯长篇语料,且仍处于高上下文参与者可承载范围内时,应优先进行 Whole-source Gestalt。若源材料是混合型,应先做宏主题拆分;若源材料是碎片型,应采用平铺发现,不强行制造层级。 +``` + +这个边界其实你原来的 runbook 已经有:Step 0 会先判断 source 是 coherent、mixed 还是 fragmented,并据此选择 structure-first、macro-topic 或 flat-discovery 模式;它还要求在没有真实参与者输出时停止。 + +所以我建议纲领别写成“所有长材料都必须全源完形”,而是写成: + +> **连贯长材料禁止 chunk-first;混合材料先宏拆;碎片材料不强行升层。** + +这更精准。 + +--- + +## 5. “确定性的专家能力”建议改成“可校准的专家能力” + +第 0 节你写: + +```md +通过架构工程封装为确定性的专家能力 +``` + +这句话对逻辑轮机很对,但对战略透镜不完全对。I 域本来就不是确定性求解,而是动态权衡、反馈控制、人类裁决。 + +建议改成: + +```md +通过架构工程封装为高保真、可校准、可追溯的专家能力。 +``` + +或者: + +```md +在 Q/P 域追求确定性执行,在 I 域追求高保真辅助、责任锚定与动态校准。 +``` + +这样不会和你自己的 QPI / 战略透镜冲突。 + +--- + +## 6. “执行真实性”要和“绿野仙踪”显式解耦 + +你现在同时有: + +* 绿野仙踪:人类专家幕后扮演 Agent; +* 执行真实性:禁止主控节点代写模拟。 + +这两条都对,但需要一条桥接规则,否则读者可能觉得矛盾。 + +建议在第 11 条下面加一句: + +```md +绿野仙踪阶段允许人类或主控节点模拟 Agent 以验证价值流,但所有此类产物必须标记为 simulation-only,不得作为正式 Agentic 输出进入生产决策链。正式运行阶段必须具备真实 Invocation Record。 +``` + +这很关键。 + +因为你的第一次流程测试失败,不是因为“模拟”本身不允许,而是因为**模拟产物被当成正式多 Agent 产物**。你原来的 distillation runbook 已经有类似底线:不能把 packet-only 或 prompt-only 当成已执行,主会话不得做 primary source analysis / routing / topic-doc drafting / material extraction,carrier 不可用时要停在 blocked 状态。 + +所以这里要明确: + +```text +模拟可以用于价值验证; +模拟不能冒充正式执行。 +``` + +--- + +## 7. “绝对停止权”的“代码级”可以稍微下放 + +你现在写: + +```md +系统必须具备代码级的绝对停止权…… +``` + +作为工程守则可以;作为方法论纲领稍微细了点。 + +建议纲领里写成: + +```md +系统必须具备可执行、可审计、可由人类覆盖的停止机制。 +``` + +然后把“代码级熔断、状态锁定、预算上限、自动停止条件”放到 Agentic Engineering Handbook。 + +更稳的版本: + +```md +一旦达到最小充分产物或触发成本 / 范围 / 风险阈值,系统必须停止后续自我扩展,除非人类显式授权升级模式。 +``` + +这样保留原则,又不把实现细节塞进宪法。 + +--- + +# 二、我认为还缺的纲领级内容 + +整体看,V4.1 已经很完整。但如果问“是否还有遗漏”,我认为还有 **4 个纲领级补丁** 值得加入。 + +--- + +## 1. 增加“方法论与 Agentic 守则的边界” + +你前面已经决定拆分: + +```text +HiFi Agent Studio = 纲领 +Agentic Handbook = 守则 +CCPE System = 工坊 +Project Runbook = 项目规程 +``` + +但 V4.1 本文里还没有显式声明这个边界。 + +建议加在开头 Profile 后,或者最后加一节: + +```md +## 文档边界 + +HiFi Agent Studio 是 AI 开发与专家型 Agentic 系统建设的方法论纲领,只规定世界观、产品观、架构底线与最高航行纪律。 + +具体的 Project Intake、Mode Selector、Invocation Record、Cost Ledger 模板、Authority Matrix、Evaluation Stack、Runtime Maturity Model、平台适配规则,应下放至 HiFi Agentic Engineering Handbook 与各项目 Runbook。 + +当具体工程守则与本纲领冲突时,以本纲领的核心原则为准;当本纲领没有规定时,以守则和项目 Runbook 执行。 +``` + +这个要加。否则 V4.1 会继续向“宪法 + 施工手册混合体”膨胀。 + +--- + +## 2. 增加“责任不可外包 / Liability Boundary” + +你在 Captain、非遍历性、控制棒里都提到了责任,但我建议把它升成航行纪律之一。 + +可以作为第 14 条: + +```md +14. **责任不可外包 (Liability Boundary)** + * *定义*:在 I 域与高风险 P 域,AI 可以扩展人类专家的认知半径,但不能替代人类承担最终责任。 + * *红线*:任何涉及法理追责、客户重大利益、不可逆决策或高风险判断的系统,必须明确 AI 建议、人类裁决、组织责任与客户责任的边界。禁止用“模型判断”掩盖人的责任坐标。 +``` + +这条应该进纲领,不应只放守则。 + +因为这是 HiFi Agent Studio 区别于“全自动幻觉派”的价值底线。 + +--- + +## 3. 增加“最小权限与副作用隔离” + +你在产品定义里提到了 Reactor 里的 Authority,但航行纪律里还没有把它作为底线写出来。 + +Agentic 系统和普通 prompt 最大区别之一是:它能调用工具、改文件、发请求、写数据库、触发外部动作。这里必须有一条纲领级红线。 + +建议加: + +```md +15. **最小权限与副作用隔离 (Least Privilege & Side-effect Isolation)** + * *定义*:Agent 的工具权限必须小于其语言能力。能说不代表能做,能建议不代表能执行。 + * *红线*:任何具备文件修改、外部 API、数据库写入、消息发送、支付、发布、删除或客户可见输出能力的 Agent,必须采用最小权限、沙箱隔离、人工确认与可回滚机制。禁止让通用推理能力直接获得不可逆执行权。 +``` + +这条非常适合你的“物理反应堆”隐喻。 +反应堆不是光靠控制棒,还要有阀门、屏蔽层和紧急停堆。 + +--- + +## 4. 增加“过程数据的授权与主权” + +你有“过程即数据”,也有“双离合榨取 / 隐性遥测”。但这里最好补一条伦理和数据边界。 + +尤其你未来服务客户,专家修改轨迹、光标悬停、编辑距离、反驳意见,这些都可能是高价值、高敏感数据。 + +建议在第 3 条“过程即数据”后补一句,或者单独作为第 16 条: + +```md +16. **过程数据主权与授权 (Process Data Sovereignty)** + * *定义*:专家的修改痕迹、反馈、犹豫、反驳、编辑轨迹与隐性遥测,是高价值认知资产,也是高敏感数据。 + * *红线*:任何过程数据采集必须具备明确授权、用途边界、最小采集、可撤回机制与隔离存储策略。禁止以“系统学习”为名,默认吞并专家经验或客户场景数据。 +``` + +这条会让你的“双离合榨取”更稳,不会显得像认知压榨机。 + +--- + +# 三、已有内容中建议弱化或移动的部分 + +## 1. “双离合榨取”可以保留,但建议再降半档 + +这部分很有你自己的特色,也确实是战略透镜高危区的未来方向。但它现在语言太硬,比如: + +```md +在数学底层上绝对不奖励多数派的平庸共识,精准重奖反共识真理 +``` + +建议改柔一点: + +```md +尝试提高少数高质量反共识信号的可见性,避免系统被多数派平庸共识吞没。 +``` + +“反共识”不天然等于“真理”。BTS 是一种机制,不是神谕。这里稍微降调,会让方法论更稳。 + +--- + +## 2. “全源完形前置”应该从思想考古里稍微移出来 + +现在它在 3.2 思想考古下面。我理解你的意图:它来自知识加工和长讨论蒸馏。 + +但严格说,它更像: + +> 上下文工程 / 材料工程原则 + +而不是思想考古本身。 + +可以保留在 3.2,但标题可以改成: + +```md +3.2 思想考古与上下文完形 +``` + +或者在 3.2 前加一个小节: + +```md +3.2 上下文工程:Whole-source Gestalt First +3.3 思想考古 +``` + +如果你想保持纲领简洁,当前放法也可以,但建议在表述中明确它是“长材料处理原则”,不是所有思想考古任务的默认动作。 + +--- + +## 3. “实测去魅”的例子不要只写专家相似度 + +你现在写: + +```md +构建自动化评测集(如:100 道真题),用数据(如:85% 专家相似度)说话。 +``` + +这对逻辑轮机很好,但对战略透镜不够。 + +建议改成: + +```md +构建分层评测:对逻辑轮机使用标准答案、专家相似度与自动化测试;对生产工坊使用可用性、转化效率与返工率;对战略透镜使用决策效用、可校准性、反例处理与治理可追踪性。 +``` + +这样不会把所有 HiFi 系统都压成“准确率系统”。 + +--- diff --git a/skills/fix-title/ref/recorder-example.md b/skills/fix-title/ref/recorder-example.md new file mode 100644 index 0000000..be5d96a --- /dev/null +++ b/skills/fix-title/ref/recorder-example.md @@ -0,0 +1,375 @@ +# 1 + +I said ..... + +## GPT + +结论:**V4.1 已经基本补齐了前面讨论里最关键的纲领级缺口**,尤其是这五个补得很准: + +1. **生产工坊**补上了 `video-workbench / PPT / 多模态素材生产` 这类场景的位置。 +2. **QPI Drift Guard**补上了“P 域任务滑入 I 域治理项目”的防线。 +3. **Whole-source Gestalt First**吸收了你素材提炼测试里的关键教训。 +4. **Execution Authenticity**吸收了第一次多 Agent 评审测试里的“伪执行”教训。 +5. **Cost Ledger + Stop Right**吸收了 3000 万 tokens 的 ROI 教训。 + +这版已经不是“有没有骨架”的问题,而是**有几处术语、边界和强度需要校准**。我会把它分成三类:必须改的小 bug、建议补入纲领的遗漏、建议下放到 Agentic 守则的内容。 + +--- + +### 一、必须改的几个小 bug / 强冲突 + +#### 1. “两种封装策略”要改成“三种封装策略” + +你第 2 节现在写: + +> 根据业务场景的熵值与秩序,我们采用两种不同的封装策略。 + +但表格已经是三类: + +```text +逻辑轮机 +生产工坊 +战略透镜 +``` + +应改为: + +```md +根据业务场景的熵值、秩序与产物形态,我们采用三种不同的封装策略。 +``` + +同时,下面这句也要改: + +> 针对 B 类(战略透镜)场景…… + +因为加了“生产工坊”之后,如果继续按 A/B/C 排列,战略透镜已经不是 B 类,而是 C 类。最稳妥的写法是不要写字母: + +```md +针对“战略透镜”场景,当面临试错成本极高、失败即引发法理追责的“单向门”商业决策时…… +``` + +这是小 bug,但很重要。分类体系一旦留下歧义,后面守则会被带歪。 + +--- + +#### 2. “Agent 输出必须包含 CoT 摘要”建议改掉 + +你现在写: + +```md +Agent 输出结果时,必须包含 CoT(思维链)摘要,即“我为什么这么判/这么想” +``` + +这里建议改成: + +```md +Agent 输出结果时,必须包含可审计的理由摘要(Decision Rationale),说明其关键依据、适用规则、不确定性、排除选项与可推翻条件。 +``` + +原因不是咬文嚼字。**你真正需要的不是 CoT,而是可校准、可审计、可反驳的理由结构。** + +CoT 这个词容易把系统引向“暴露内部推理过程”。但真实产品里更稳的形态是: + +```text +结论 +依据 +规则 +不确定性 +反例 +人工确认点 +可推翻条件 +``` + +这和你的“钢尺与皮尺”“不仅做对,还要好改”更一致。 + +--- + +#### 3. QPI 的 I 域响应里,“强制启动双离合榨取”太重 + +你现在写: + +```md +【I】系统响应:生态学干预。强制启动后道的“思想考古”与“双离合榨取”…… +``` + +但后面 3.6 又明确说“双离合榨取”是 Beta / 理论沙盘,工程实现成本极高。这两个地方有冲突。 + +建议改成: + +```md +【I】系统响应:生态学干预。优先启动思想考古、战略透镜与人机回环;仅在高危、非遍历、明确授权的深水区,才考虑进入“双离合榨取”或摩擦蒸馏等 Beta 机制。 +``` + +这样层级就清楚了: + +```text +I 域默认:思想考古 + 战略透镜 + 人机回环 +I 域高危:预设委员会 +I 域极高危/实验:双离合榨取 +``` + +否则会把一个 Beta 沙盘机制误升成 I 域默认动作,容易再度重工业化。 + +--- + +#### 4. “全源完形前置”需要加适用条件 + +这个原则非常重要,但现在写得有点绝对: + +```md +严禁对连贯的长篇语料直接采用基于分块的并发提取机制…… +必须首先调用高上下文承载力的主节点…… +``` + +核心判断对,但建议补上适用边界: + +```md +当源材料是连贯长篇语料,且仍处于高上下文参与者可承载范围内时,应优先进行 Whole-source Gestalt。若源材料是混合型,应先做宏主题拆分;若源材料是碎片型,应采用平铺发现,不强行制造层级。 +``` + +这个边界其实你原来的 runbook 已经有:Step 0 会先判断 source 是 coherent、mixed 还是 fragmented,并据此选择 structure-first、macro-topic 或 flat-discovery 模式;它还要求在没有真实参与者输出时停止。 + +所以我建议纲领别写成“所有长材料都必须全源完形”,而是写成: + +> **连贯长材料禁止 chunk-first;混合材料先宏拆;碎片材料不强行升层。** + +这更精准。 + +--- + +#### 5. “确定性的专家能力”建议改成“可校准的专家能力” + +第 0 节你写: + +```md +通过架构工程封装为确定性的专家能力 +``` + +这句话对逻辑轮机很对,但对战略透镜不完全对。I 域本来就不是确定性求解,而是动态权衡、反馈控制、人类裁决。 + +建议改成: + +```md +通过架构工程封装为高保真、可校准、可追溯的专家能力。 +``` + +或者: + +```md +在 Q/P 域追求确定性执行,在 I 域追求高保真辅助、责任锚定与动态校准。 +``` + +这样不会和你自己的 QPI / 战略透镜冲突。 + +--- + +#### 6. “执行真实性”要和“绿野仙踪”显式解耦 + +你现在同时有: + +* 绿野仙踪:人类专家幕后扮演 Agent; +* 执行真实性:禁止主控节点代写模拟。 + +这两条都对,但需要一条桥接规则,否则读者可能觉得矛盾。 + +建议在第 11 条下面加一句: + +```md +绿野仙踪阶段允许人类或主控节点模拟 Agent 以验证价值流,但所有此类产物必须标记为 simulation-only,不得作为正式 Agentic 输出进入生产决策链。正式运行阶段必须具备真实 Invocation Record。 +``` + +这很关键。 + +因为你的第一次流程测试失败,不是因为“模拟”本身不允许,而是因为**模拟产物被当成正式多 Agent 产物**。你原来的 distillation runbook 已经有类似底线:不能把 packet-only 或 prompt-only 当成已执行,主会话不得做 primary source analysis / routing / topic-doc drafting / material extraction,carrier 不可用时要停在 blocked 状态。 + +所以这里要明确: + +```text +模拟可以用于价值验证; +模拟不能冒充正式执行。 +``` + +--- + +#### 7. “绝对停止权”的“代码级”可以稍微下放 + +你现在写: + +```md +系统必须具备代码级的绝对停止权…… +``` + +作为工程守则可以;作为方法论纲领稍微细了点。 + +建议纲领里写成: + +```md +系统必须具备可执行、可审计、可由人类覆盖的停止机制。 +``` + +然后把“代码级熔断、状态锁定、预算上限、自动停止条件”放到 Agentic Engineering Handbook。 + +更稳的版本: + +```md +一旦达到最小充分产物或触发成本 / 范围 / 风险阈值,系统必须停止后续自我扩展,除非人类显式授权升级模式。 +``` + +这样保留原则,又不把实现细节塞进宪法。 + +--- + +### 二、我认为还缺的纲领级内容 + +整体看,V4.1 已经很完整。但如果问“是否还有遗漏”,我认为还有 **4 个纲领级补丁** 值得加入。 + +--- + +#### 1. 增加“方法论与 Agentic 守则的边界” + +你前面已经决定拆分: + +```text +HiFi Agent Studio = 纲领 +Agentic Handbook = 守则 +CCPE System = 工坊 +Project Runbook = 项目规程 +``` + +但 V4.1 本文里还没有显式声明这个边界。 + +建议加在开头 Profile 后,或者最后加一节: + +```md +## 文档边界 + +HiFi Agent Studio 是 AI 开发与专家型 Agentic 系统建设的方法论纲领,只规定世界观、产品观、架构底线与最高航行纪律。 + +具体的 Project Intake、Mode Selector、Invocation Record、Cost Ledger 模板、Authority Matrix、Evaluation Stack、Runtime Maturity Model、平台适配规则,应下放至 HiFi Agentic Engineering Handbook 与各项目 Runbook。 + +当具体工程守则与本纲领冲突时,以本纲领的核心原则为准;当本纲领没有规定时,以守则和项目 Runbook 执行。 +``` + +这个要加。否则 V4.1 会继续向“宪法 + 施工手册混合体”膨胀。 + +--- + +#### 2. 增加“责任不可外包 / Liability Boundary” + +你在 Captain、非遍历性、控制棒里都提到了责任,但我建议把它升成航行纪律之一。 + +可以作为第 14 条: + +```md +14. **责任不可外包 (Liability Boundary)** + * *定义*:在 I 域与高风险 P 域,AI 可以扩展人类专家的认知半径,但不能替代人类承担最终责任。 + * *红线*:任何涉及法理追责、客户重大利益、不可逆决策或高风险判断的系统,必须明确 AI 建议、人类裁决、组织责任与客户责任的边界。禁止用“模型判断”掩盖人的责任坐标。 +``` + +这条应该进纲领,不应只放守则。 + +因为这是 HiFi Agent Studio 区别于“全自动幻觉派”的价值底线。 + +--- + +#### 3. 增加“最小权限与副作用隔离” + +你在产品定义里提到了 Reactor 里的 Authority,但航行纪律里还没有把它作为底线写出来。 + +Agentic 系统和普通 prompt 最大区别之一是:它能调用工具、改文件、发请求、写数据库、触发外部动作。这里必须有一条纲领级红线。 + +建议加: + +```md +15. **最小权限与副作用隔离 (Least Privilege & Side-effect Isolation)** + * *定义*:Agent 的工具权限必须小于其语言能力。能说不代表能做,能建议不代表能执行。 + * *红线*:任何具备文件修改、外部 API、数据库写入、消息发送、支付、发布、删除或客户可见输出能力的 Agent,必须采用最小权限、沙箱隔离、人工确认与可回滚机制。禁止让通用推理能力直接获得不可逆执行权。 +``` + +这条非常适合你的“物理反应堆”隐喻。 +反应堆不是光靠控制棒,还要有阀门、屏蔽层和紧急停堆。 + +--- + +#### 4. 增加“过程数据的授权与主权” + +你有“过程即数据”,也有“双离合榨取 / 隐性遥测”。但这里最好补一条伦理和数据边界。 + +尤其你未来服务客户,专家修改轨迹、光标悬停、编辑距离、反驳意见,这些都可能是高价值、高敏感数据。 + +建议在第 3 条“过程即数据”后补一句,或者单独作为第 16 条: + +```md +16. **过程数据主权与授权 (Process Data Sovereignty)** + * *定义*:专家的修改痕迹、反馈、犹豫、反驳、编辑轨迹与隐性遥测,是高价值认知资产,也是高敏感数据。 + * *红线*:任何过程数据采集必须具备明确授权、用途边界、最小采集、可撤回机制与隔离存储策略。禁止以“系统学习”为名,默认吞并专家经验或客户场景数据。 +``` + +这条会让你的“双离合榨取”更稳,不会显得像认知压榨机。 + +--- + +### 三、已有内容中建议弱化或移动的部分 + +#### 1. “双离合榨取”可以保留,但建议再降半档 + +这部分很有你自己的特色,也确实是战略透镜高危区的未来方向。但它现在语言太硬,比如: + +```md +在数学底层上绝对不奖励多数派的平庸共识,精准重奖反共识真理 +``` + +建议改柔一点: + +```md +尝试提高少数高质量反共识信号的可见性,避免系统被多数派平庸共识吞没。 +``` + +“反共识”不天然等于“真理”。BTS 是一种机制,不是神谕。这里稍微降调,会让方法论更稳。 + +--- + +#### 2. “全源完形前置”应该从思想考古里稍微移出来 + +现在它在 3.2 思想考古下面。我理解你的意图:它来自知识加工和长讨论蒸馏。 + +但严格说,它更像: + +> 上下文工程 / 材料工程原则 + +而不是思想考古本身。 + +可以保留在 3.2,但标题可以改成: + +```md +3.2 思想考古与上下文完形 +``` + +或者在 3.2 前加一个小节: + +```md +3.2 上下文工程:Whole-source Gestalt First +3.3 思想考古 +``` + +如果你想保持纲领简洁,当前放法也可以,但建议在表述中明确它是“长材料处理原则”,不是所有思想考古任务的默认动作。 + +--- + +#### 3. “实测去魅”的例子不要只写专家相似度 + +你现在写: + +```md +构建自动化评测集(如:100 道真题),用数据(如:85% 专家相似度)说话。 +``` + +这对逻辑轮机很好,但对战略透镜不够。 + +建议改成: + +```md +构建分层评测:对逻辑轮机使用标准答案、专家相似度与自动化测试;对生产工坊使用可用性、转化效率与返工率;对战略透镜使用决策效用、可校准性、反例处理与治理可追踪性。 +``` + +这样不会把所有 HiFi 系统都压成“准确率系统”。 diff --git a/skills/fix-title/scripts/fix_markdown_titles.py b/skills/fix-title/scripts/fix_markdown_titles.py new file mode 100644 index 0000000..f75fe92 --- /dev/null +++ b/skills/fix-title/scripts/fix_markdown_titles.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""Shift Markdown ATX headings down by a fixed number of levels.""" + +from __future__ import annotations + +import argparse +import pathlib +import re +import sys + + +HEADING_RE = re.compile(r"^( {0,3})(#{1,6})([ \t]+.*)$") +FENCE_RE = re.compile(r"^( {0,3})(`{3,}|~{3,})") + + +def shift_headings(text: str, levels: int) -> str: + if levels <= 0: + raise ValueError("levels must be a positive integer") + + lines = text.splitlines(keepends=True) + in_fence = False + fence_marker = "" + output: list[str] = [] + + for line in lines: + body = line[:-1] if line.endswith("\n") else line + newline = "\n" if line.endswith("\n") else "" + if body.endswith("\r"): + body = body[:-1] + newline = "\r" + newline + + fence_match = FENCE_RE.match(body) + if fence_match: + marker = fence_match.group(2) + marker_char = marker[0] + if not in_fence: + in_fence = True + fence_marker = marker_char * len(marker) + elif marker_char == fence_marker[0] and len(marker) >= len(fence_marker): + in_fence = False + fence_marker = "" + output.append(line) + continue + + if in_fence: + output.append(line) + continue + + heading_match = HEADING_RE.match(body) + if heading_match: + indent, hashes, rest = heading_match.groups() + new_level = min(6, len(hashes) + levels) + output.append(f"{indent}{'#' * new_level}{rest}{newline}") + else: + output.append(line) + + return "".join(output) + + +def parse_args(argv: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Shift Markdown ATX headings down by adding # characters." + ) + parser.add_argument("path", type=pathlib.Path, help="Markdown file to rewrite") + parser.add_argument( + "-l", + "--levels", + type=int, + default=2, + help="Number of heading levels to add. Default: 2", + ) + parser.add_argument( + "-o", + "--output", + type=pathlib.Path, + help="Write to this file instead of modifying the source file", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Print converted content to stdout without writing files", + ) + return parser.parse_args(argv) + + +def main(argv: list[str]) -> int: + args = parse_args(argv) + + if args.levels <= 0: + print("error: --levels must be a positive integer", file=sys.stderr) + return 2 + + try: + source = args.path.read_text(encoding="utf-8") + except OSError as exc: + print(f"error: cannot read {args.path}: {exc}", file=sys.stderr) + return 1 + + fixed = shift_headings(source, args.levels) + + if args.dry_run: + sys.stdout.write(fixed) + return 0 + + destination = args.output or args.path + try: + destination.write_text(fixed, encoding="utf-8") + except OSError as exc: + print(f"error: cannot write {destination}: {exc}", file=sys.stderr) + return 1 + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/skills/fix-title/tests/test_fix_markdown_titles.py b/skills/fix-title/tests/test_fix_markdown_titles.py new file mode 100644 index 0000000..340e12f --- /dev/null +++ b/skills/fix-title/tests/test_fix_markdown_titles.py @@ -0,0 +1,100 @@ +import pathlib +import subprocess +import sys +import tempfile +import unittest + + +SCRIPT = pathlib.Path(__file__).resolve().parents[1] / "scripts" / "fix_markdown_titles.py" + + +def run_cli(*args): + return subprocess.run( + [sys.executable, str(SCRIPT), *args], + text=True, + capture_output=True, + check=False, + ) + + +class FixMarkdownTitlesTest(unittest.TestCase): + def test_shifts_atx_headings_and_leaves_code_blocks(self): + with tempfile.TemporaryDirectory() as tmp: + target = pathlib.Path(tmp) / "reply.md" + target.write_text( + "\n".join( + [ + "Intro", + "", + "# One", + "## Two ##", + "```md", + "# Not a heading", + "```", + "> # quoted content", + " # indented code", + "###### Six", + "", + ] + ), + encoding="utf-8", + ) + + result = run_cli(str(target), "--levels", "2") + + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual( + target.read_text(encoding="utf-8"), + "\n".join( + [ + "Intro", + "", + "### One", + "#### Two ##", + "```md", + "# Not a heading", + "```", + "> # quoted content", + " # indented code", + "###### Six", + "", + ] + ), + ) + + def test_dry_run_prints_without_writing(self): + with tempfile.TemporaryDirectory() as tmp: + target = pathlib.Path(tmp) / "reply.md" + target.write_text("# One\n", encoding="utf-8") + + result = run_cli(str(target), "--levels", "1", "--dry-run") + + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(result.stdout, "## One\n") + self.assertEqual(target.read_text(encoding="utf-8"), "# One\n") + + def test_output_writes_to_separate_file(self): + with tempfile.TemporaryDirectory() as tmp: + source = pathlib.Path(tmp) / "reply.md" + output = pathlib.Path(tmp) / "fixed.md" + source.write_text("# One\n", encoding="utf-8") + + result = run_cli(str(source), "--levels", "3", "--output", str(output)) + + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(source.read_text(encoding="utf-8"), "# One\n") + self.assertEqual(output.read_text(encoding="utf-8"), "#### One\n") + + def test_rejects_non_positive_levels(self): + with tempfile.TemporaryDirectory() as tmp: + target = pathlib.Path(tmp) / "reply.md" + target.write_text("# One\n", encoding="utf-8") + + result = run_cli(str(target), "--levels", "0") + + self.assertNotEqual(result.returncode, 0) + self.assertIn("positive integer", result.stderr) + + +if __name__ == "__main__": + unittest.main()