diff --git a/.gitea/ci/thirdparty-skill-overlays/codebase-recon.patch b/.gitea/ci/thirdparty-skill-overlays/codebase-recon.patch index beafdb7b..58f51f79 100644 --- a/.gitea/ci/thirdparty-skill-overlays/codebase-recon.patch +++ b/.gitea/ci/thirdparty-skill-overlays/codebase-recon.patch @@ -1,11 +1,23 @@ diff --git a/skills/thirdparty/codebase-recon/SKILL.md b/skills/thirdparty/codebase-recon/SKILL.md --- a/skills/thirdparty/codebase-recon/SKILL.md +++ b/skills/thirdparty/codebase-recon/SKILL.md -@@ -252 +252 @@ Core methodology: +@@ -251,4 +251,4 @@ + Core methodology: -- [confidence.md](../pathfinding/references/confidence.md) — confidence calibration (shared with pathfinding) +- +- [confidence-calibration.md](references/confidence-calibration.md) — evidence-based confidence calibration -@@ -263 +263,0 @@ Related skills: ++ + Micro-skills (load as needed): +@@ -260,7 +260,6 @@ + - [architecture-analysis.md](references/architecture-analysis.md) — system structure mapping +- ++ + Related skills: -- `outfitter:pathfinding` — clarifying requirements before analysis + - `outfitter:debugging` — structured bug investigation +- ++ + diff --git a/skills/thirdparty/codebase-recon/references/confidence-calibration.md b/skills/thirdparty/codebase-recon/references/confidence-calibration.md new file mode 100644 --- /dev/null diff --git a/skills/tsl-syntax-reference/references/02_core_model.md b/skills/tsl-syntax-reference/references/02_core_model.md index 75b33b6e..d10f7134 100644 --- a/skills/tsl-syntax-reference/references/02_core_model.md +++ b/skills/tsl-syntax-reference/references/02_core_model.md @@ -17,11 +17,17 @@ - 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据;未给后缀时,再按交付目标判断。 + + - 未给后缀时,入口流程、脚本任务或一次性执行逻辑对应 `.tsl`;可复用交付物(函数、过程、类、模块或扩展文件)对应 `.tsf`;只是脚本内部封装函数或类时,仍按 `.tsl` 处理;仍不明确时向用户确认,不要把脚本入口和可复用模块合并成一个猜测文件。 + + - `.tsl` 脚本按两段理解:语句区在前并按顺序执行;声明区在后,可放 `function / procedure` 或 `type Name = class`。写 `.tsl` 时先写语句区,需要函数、过程或类时把声明区放在语句区之后。 + + - 写 `.tsf` 时只写顶层函数 / 过程 / 类声明,或 `unit`;不要写成会直接顺序执行的脚本入口。 - `.tsf` 里的非 `unit` 顶层函数 / 过程可按函数扩展理解:部署到解释器 `funcext` 后,`.tsl` 可以直接调用;顶层类声明只按可复用声明理解;`unit` 按模块组织理解。 - `uses` 可以出现在顶层,但这里只把它当成辅助语句,不把它当成主体声明;函数体和类定义体里的位置限制见 [09_units_and_scope.md](09_units_and_scope.md)。 @@ -30,7 +36,9 @@ - `unit` 默认先按完整形态理解;它也可以省略 `interface` / `implementation` 写成简写形态,见 [09_units_and_scope.md](09_units_and_scope.md)。 - 不要把 `.tsl` 写成只有顶层函数的模块;如果用户要通用可复用函数,优先写 `.tsf`。 - 不要把 `.tsf` 写成会直接执行脚本语句的入口;如果用户要顺序执行入口,优先写 `.tsl`。 + + - `.tsf` 文件名(不含扩展名)必须与第一个顶层声明同名;第一个声明可以是同名 `function`、`type Name = class` 或 `unit`。 - TSL 语言大小写无关,因此 `userAccount.tsf` 和 `UserAccount.tsf` 在语法层面都合法。 diff --git a/skills/tsl-syntax-reference/references/03_values_and_literals.md b/skills/tsl-syntax-reference/references/03_values_and_literals.md index 4cf29a97..3f2ef8b0 100644 --- a/skills/tsl-syntax-reference/references/03_values_and_literals.md +++ b/skills/tsl-syntax-reference/references/03_values_and_literals.md @@ -20,7 +20,9 @@ - `\\`、`\"`、`\n` 这类基础转义可用。 - `\t` 和 `\xNN` 这类转义也可用。 - `\r`、`\r\n`、`\a`、`\b`、`\f`、`\v` 这些经典转义也可用。 + + - `array(...)` 既可以写顺序数组,也可以写字符串键表;顺序数组和 `binary(...)` 二进制缓冲区下标从 `0` 开始,字符串下标从 `1` 开始。 - `s[0]` 在运行时会越界,不要把字符串当成 0 基下标。 - 字符串取子串用 `s[start:end]`,并且 `end` 是包含在结果里的。 diff --git a/skills/tsl-syntax-reference/references/05_functions_and_calls.md b/skills/tsl-syntax-reference/references/05_functions_and_calls.md index 6cedeec4..6488366b 100644 --- a/skills/tsl-syntax-reference/references/05_functions_and_calls.md +++ b/skills/tsl-syntax-reference/references/05_functions_and_calls.md @@ -13,9 +13,13 @@ - 最稳妥的函数骨架仍然是 `function Name(...); begin ... end;`。 + + - 用户提示词里的“函数”默认对应 `function`,不要自动改写成 `procedure`。 + + - `procedure Name(...); begin ... end;` 只在用户明确要求 `procedure` / 过程时生成;不要因为没有返回值就自动改用 `procedure`。 - `procedure` 头后不允许写返回类型;返回类型注解只用于 `function`。 - 在 `.tsl` 文件模型层,脚本语句后可以接函数声明;语句区在前顺序执行,声明区在后提供函数/过程定义。见 [02_core_model.md](02_core_model.md)。 @@ -35,10 +39,14 @@ - 如果不确定任务是否需要写回语义,优先显式用 `const` 形参,或先切到 `{$varByRef-}`;不要依赖未修饰参数的运行时默认行为。 - `return expr;` 会直接返回当前函数结果。 - `exit;` 会立即结束当前函数;在本页最小样例里,如果此前没有写入返回结果,调用方观察到的是默认值 `0`。 + + - 调用时支持命名参数,写法是 `name: value`。 - 命名参数也支持 `call(...)` 这类按函数名或函数指针转调的模型。 + + - 一旦某次调用里开始使用命名参数,后面的参数就不能再退回位置参数。 - 对二进制函数 / 系统函数直接使用命名参数,会报 `named parameter mode can't support here`;这类函数要先用 TSL 再封一层。 - 函数参数支持默认值。 diff --git a/skills/tsl-syntax-reference/references/08_objects_and_classes.md b/skills/tsl-syntax-reference/references/08_objects_and_classes.md index d844bccf..0a97f929 100644 --- a/skills/tsl-syntax-reference/references/08_objects_and_classes.md +++ b/skills/tsl-syntax-reference/references/08_objects_and_classes.md @@ -42,7 +42,9 @@ - 基础覆盖写法是:父类方法声明为 `virtual`,子类对应方法声明为 `override`。 - 基础祖先类调用:可以用 `Inherited;`、`Inherited MethodName(...)` 或 `class(BaseClass, ObjectName).MethodName()`。 - 创建对象有两种方式:`new ClassName()` 最常用,`createObject(...)` 作为次选。 + + - 普通本地类实例化默认生成 `new ClassName()`;`createObject("ClassName")`、`createObject(ClassType)` 只在字符串类名、类类型变量或跨 `unit` 路径场景生成。 - 如果类里定义了 `function create(...)`,`new`、`createObject("ClassName", ...)` 和 `createObject(ClassType, ...)` 都可以透传构造参数,也都支持默认参数和命名参数。 - 析构写法是无参 `function destroy();`;对象的最后一个引用被清空(如设为 `nil`)时会触发它。存在别名引用时,只清空其中一个引用不会触发。 diff --git a/skills/tsl-syntax-reference/references/09_units_and_scope.md b/skills/tsl-syntax-reference/references/09_units_and_scope.md index fdb659cb..d2f8ef99 100644 --- a/skills/tsl-syntax-reference/references/09_units_and_scope.md +++ b/skills/tsl-syntax-reference/references/09_units_and_scope.md @@ -22,7 +22,9 @@ - `unit` 是完整的顶层主体;常见完整形态是 `unit Name; interface ... implementation ... end.`。 - `unit` 也可以省略 `interface` / `implementation` 写成简写形态;这种简写里定义的函数对外可调用。 + + - 如果没有特殊需求,默认优先用完整形态;简写形态只在不需要显式区分 `interface` / `implementation` 时再用。 - 完整 `unit` 示例中,`implementation` 前保留空行,避免接口段和实现段挤在一起。 - `unit` 允许接口声明与实现段分离:`interface` 段可以只声明函数签名或类方法签名,函数体和类方法体放到 `implementation` 段。 diff --git a/test/test_thirdparty_skills_pipeline.py b/test/test_thirdparty_skills_pipeline.py index b2aef7e2..e72e1ed3 100644 --- a/test/test_thirdparty_skills_pipeline.py +++ b/test/test_thirdparty_skills_pipeline.py @@ -16,14 +16,6 @@ LEGACY_WORKFLOW = ROOT / ".gitea" / "workflows" / "update-thirdparty-superpowers UPDATE_SCRIPT = ROOT / ".gitea" / "ci" / "update_thirdparty_skills.sh" SYNC_SCRIPT = ROOT / ".gitea" / "ci" / "sync_thirdparty_skills.sh" SKILLS_MD = ROOT / "SKILLS.md" -MATT_POCOCK_LIST = ( - ROOT / "skills" / "thirdparty" / ".sources" / "matt-pocock-skills.list" -) -UI_UX_PRO_MAX_LIST = ROOT / "skills" / "thirdparty" / ".sources" / "ui-ux-pro-max.list" -UI_UX_PRO_MAX_DIR = ROOT / "skills" / "thirdparty" / "ui-ux-pro-max" -BROOKS_LINT_LIST = ROOT / "skills" / "thirdparty" / ".sources" / "brooks-lint.list" -CODEBASE_RECON_LIST = ROOT / "skills" / "thirdparty" / ".sources" / "codebase-recon.list" -PATHFINDING_DIR = ROOT / "skills" / "thirdparty" / "pathfinding" def load_manifest() -> dict: @@ -59,6 +51,7 @@ def extract_workflow_region(name: str) -> str: class ThirdpartySkillsPipelineTests(unittest.TestCase): + def test_manifest_declares_all_thirdparty_sources(self): data = load_manifest() self.assertEqual( @@ -66,11 +59,10 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): [ "matt-pocock-skills", "ui-ux-pro-max", - "andrej-karpathy-skills", "brooks-lint", "codebase-recon", - "codebase-migrate", - "uncle-bob-craft", + "cangjie-skill", + "darwin-skill", ], ) @@ -93,10 +85,7 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): self.assertIn("grilling", matt["include_skill_dirs"]) self.assertIn("to-tickets", matt["include_skill_dirs"]) - def test_matt_pocock_source_and_required_workflow_skills_are_materialized(self): - self.assertTrue(MATT_POCOCK_LIST.is_file()) - - synced = set(MATT_POCOCK_LIST.read_text(encoding="utf-8").splitlines()) + def test_matt_pocock_manifest_includes_required_workflow_skills(self): manifest_skills = set( next( item @@ -111,36 +100,43 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): "domain-modeling", "to-spec", "to-tickets", - "implement", "tdd", "codebase-design", "code-review", "handoff", } - self.assertEqual(synced, manifest_skills) - self.assertTrue(required <= synced) + self.assertTrue(required <= manifest_skills) legacy_main_chain = { "using-superpowers", "brainstorming", "writing-plans", "executing-plans", } - self.assertTrue(legacy_main_chain.isdisjoint(synced)) - for name in required: - self.assertTrue((ROOT / "skills" / "thirdparty" / name / "SKILL.md").is_file()) + self.assertTrue(legacy_main_chain.isdisjoint(manifest_skills)) - def test_karpathy_manifest_uses_copy_skill_dirs_sync_mode(self): + def test_copy_skill_root_sources_declare_curated_paths(self): data = load_manifest() - karpathy = next( - item for item in data["sources"] if item["id"] == "andrej-karpathy-skills" - ) - self.assertEqual(karpathy["sync_mode"], "copy_skill_dirs") - self.assertEqual(karpathy["snapshot_dir"], "andrej-karpathy-skills") - self.assertEqual(karpathy["skills_subdir"], "skills") - self.assertEqual( - karpathy["source_list"], "skills/thirdparty/.sources/andrej-karpathy-skills.list" - ) + sources = {item["id"]: item for item in data["sources"]} + + expected = { + "cangjie-skill": { + "snapshot_dir": "cangjie-skill", + "output_name": "cangjie-skill", + "required_paths": {"SKILL.md", "methodology", "extractors", "templates"}, + }, + "darwin-skill": { + "snapshot_dir": "darwin-skill", + "output_name": "darwin-skill", + "required_paths": {"SKILL.md", "references", "scripts", "templates"}, + }, + } + for source_id, contract in expected.items(): + source = sources[source_id] + self.assertEqual(source["sync_mode"], "copy_skill_root") + self.assertEqual(source["snapshot_dir"], contract["snapshot_dir"]) + self.assertEqual(source["output_name"], contract["output_name"]) + self.assertTrue(contract["required_paths"] <= set(source["include_paths"])) def test_ui_ux_pro_max_uses_render_skill_sync_mode(self): data = load_manifest() @@ -168,39 +164,11 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): self.assertEqual( recon["source_list"], "skills/thirdparty/.sources/codebase-recon.list" ) - self.assertEqual(recon["include_skill_dirs"], ["codebase-recon", "pathfinding"]) - - migrate = next( - item for item in data["sources"] if item["id"] == "codebase-migrate" - ) + self.assertEqual(recon["include_skill_dirs"], ["codebase-recon"]) self.assertEqual( - migrate["upstream_repo"], - "https://github.com/ComposioHQ/awesome-codex-skills.git", + recon["overlay_patch"], + ".gitea/ci/thirdparty-skill-overlays/codebase-recon.patch", ) - self.assertEqual(migrate["upstream_ref"], "master") - self.assertEqual(migrate["sync_mode"], "copy_skill_dirs") - self.assertEqual(migrate["snapshot_dir"], "awesome-codex-skills") - self.assertEqual(migrate["skills_subdir"], ".") - self.assertEqual( - migrate["source_list"], "skills/thirdparty/.sources/codebase-migrate.list" - ) - self.assertEqual(migrate["include_skill_dirs"], ["codebase-migrate"]) - - craft = next( - item for item in data["sources"] if item["id"] == "uncle-bob-craft" - ) - self.assertEqual( - craft["upstream_repo"], - "https://github.com/sickn33/antigravity-awesome-skills.git", - ) - self.assertEqual(craft["upstream_ref"], "main") - self.assertEqual(craft["sync_mode"], "copy_skill_dirs") - self.assertEqual(craft["snapshot_dir"], "antigravity-awesome-skills") - self.assertEqual(craft["skills_subdir"], "skills") - self.assertEqual( - craft["source_list"], "skills/thirdparty/.sources/uncle-bob-craft.list" - ) - self.assertEqual(craft["include_skill_dirs"], ["uncle-bob-craft"]) def test_workflow_inlines_update_and_sync_in_single_serial_job(self): text = WORKFLOW.read_text(encoding="utf-8") @@ -254,11 +222,12 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): self.assertIn("skills/thirdparty/", text) self.assertNotIn("Third-party Skills (superpowers)", text) - def test_non_legacy_thirdparty_source_lists_exist(self): - self.assertTrue(UI_UX_PRO_MAX_LIST.is_file()) - self.assertTrue(CODEBASE_RECON_LIST.is_file()) - self.assertIn("ui-ux-pro-max", UI_UX_PRO_MAX_LIST.read_text(encoding="utf-8")) - self.assertIn("pathfinding", CODEBASE_RECON_LIST.read_text(encoding="utf-8")) + def test_manifest_declares_unique_thirdparty_source_lists(self): + source_lists = [entry["source_list"] for entry in load_manifest()["sources"]] + self.assertEqual(len(source_lists), len(set(source_lists))) + self.assertTrue( + all(path.startswith("skills/thirdparty/.sources/") for path in source_lists) + ) def test_superpowers_source_and_vendored_skills_are_absent(self): data = load_manifest() @@ -289,17 +258,6 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): set(), ) - def test_codebase_recon_pathfinding_dependency_is_synced(self): - self.assertTrue((PATHFINDING_DIR / "SKILL.md").is_file()) - self.assertTrue( - (PATHFINDING_DIR / "references" / "confidence.md").is_file() - ) - - def test_ui_ux_pro_max_output_exists_with_data_and_scripts(self): - self.assertTrue((UI_UX_PRO_MAX_DIR / "SKILL.md").is_file()) - self.assertTrue((UI_UX_PRO_MAX_DIR / "data").is_dir()) - self.assertTrue((UI_UX_PRO_MAX_DIR / "scripts").is_dir()) - def test_inline_update_materializes_manifest_before_target_checkout(self): text = WORKFLOW.read_text(encoding="utf-8") self.assertIn('manifest_copy="$tmp_dir/thirdparty_skills.json"', text) @@ -325,7 +283,7 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): self.assertNotIn("exclude_skill_dirs", text) self.assertNotIn("is_excluded_skill_dir", text) - def test_inline_sync_generates_karpathy_outputs_in_temp_repo(self): + def test_inline_sync_applies_codebase_recon_overlay_in_temp_repo(self): with tempfile.TemporaryDirectory() as tmp_dir: tmp_root = Path(tmp_dir) mirror = tmp_root / "origin.git" @@ -394,12 +352,40 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): manifest_data["sources"] = [ entry for entry in manifest_data["sources"] - if entry["id"] == "andrej-karpathy-skills" + if entry["id"] == "codebase-recon" ] (work / ".gitea" / "ci" / "thirdparty_skills.json").write_text( json.dumps(manifest_data, indent=2) + "\n", encoding="utf-8", ) + overlay_path = Path(manifest_data["sources"][0]["overlay_patch"]) + shutil.copy2(ROOT / overlay_path, work / overlay_path) + fixture_commit = run_command( + "git", + "-C", + str(work), + "add", + ".gitea/ci/thirdparty_skills.json", + overlay_path.as_posix(), + ) + self.assertEqual(fixture_commit.returncode, 0, msg=fixture_commit.stderr) + fixture_commit = run_command( + "git", + "-C", + str(work), + "-c", + "user.name=test", + "-c", + "user.email=test@example.invalid", + "commit", + "-m", + "test: configure thirdparty sync fixture", + ) + self.assertEqual(fixture_commit.returncode, 0, msg=fixture_commit.stderr) + fixture_push = run_command( + "git", "-C", str(work), "push", "origin", "HEAD:main" + ) + self.assertEqual(fixture_push.returncode, 0, msg=fixture_push.stderr) sync_script = extract_workflow_region("sync_thirdparty_skills") script_path = work / ".sync-thirdparty-test.sh" script_path.write_text( @@ -431,15 +417,28 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase): ) generated_list = ( - work / "skills" / "thirdparty" / ".sources" / "andrej-karpathy-skills.list" + work / "skills" / "thirdparty" / ".sources" / "codebase-recon.list" ) generated_skill = ( - work / "skills" / "thirdparty" / "karpathy-guidelines" / "SKILL.md" + work / "skills" / "thirdparty" / "codebase-recon" / "SKILL.md" + ) + generated_reference = ( + work + / "skills" + / "thirdparty" + / "codebase-recon" + / "references" + / "confidence-calibration.md" ) self.assertTrue(generated_list.is_file()) self.assertTrue(generated_skill.is_file()) + self.assertTrue(generated_reference.is_file()) self.assertIn( - "karpathy-guidelines", generated_list.read_text(encoding="utf-8") + "codebase-recon", generated_list.read_text(encoding="utf-8") + ) + self.assertIn( + "[confidence-calibration.md](references/confidence-calibration.md)", + generated_skill.read_text(encoding="utf-8"), )