Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/contracts/review.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,5 +149,5 @@ python3 .github/scripts/validate_review_json.py pr_diff.txt review.json
Local skill validation may use:

```bash
python3 .agents/skills/review-pr/scripts/validate_review_json.py pr_diff.txt review.json
python3 .github/skills/review-pr/scripts/validate_review_json.py pr_diff.txt review.json
```
8 changes: 4 additions & 4 deletions .agents/skills/bootstrap-issue-config/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ This skill produces two files:

The reusable agent roles that support a repo-specific companion are:

- `.agents/skills/review-pr-repo/SKILL.md`
- `.agents/skills/review-spec-repo/SKILL.md`
- `.agents/skills/triage-issue-repo/SKILL.md`
- `.agents/skills/dedupe-issue-repo/SKILL.md`
- `.github/skills/review-pr-repo/SKILL.md`
- `.github/skills/review-spec-repo/SKILL.md`
- `.github/skills/triage-issue-repo/SKILL.md`
- `.github/skills/dedupe-issue-repo/SKILL.md`

Do **not** create these files during bootstrap. The prompt-construction layer treats a missing companion file and a body-only frontmatter stub the same way, so there is no value in materializing an empty file during bootstrap. Each file gets created on-demand by the matching `update-<agent>` self-improvement loop (or by a maintainer) the first time there is evidence-backed content to add. Bootstrap only needs to ensure the directory convention is documented; the files themselves stay absent until a real rule lands.

Expand Down
2 changes: 1 addition & 1 deletion .github/agents/product-wiki-query.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ description: Answer AICodingFlow product questions by querying docs/product/wiki

## 入口

1. 读取 `.agents/skills/product-wiki/SKILL.md`。
1. 读取 `.github/skills/product-wiki/SKILL.md`。
2. 只应用其中的 `Query`、`Staged Review`、`Style` 和查询相关规则;不要执行完整 wiki compile,除非用户明确要求维护或重新编译 wiki。
3. 从 `docs/product/wiki/index.md` 开始,按链接打开最相关的 concept、summary 和 raw source。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@


def script_path() -> str:
target = Path(".agents/skills/update-dedupe/scripts/aggregate_dedupe_feedback.py")
target = Path(".github/skills/update-dedupe/scripts/aggregate_dedupe_feedback.py")
if target.exists():
return str(target)
return "implementation-output/.agents/skills/update-dedupe/scripts/aggregate_dedupe_feedback.py"
return "implementation-output/.github/skills/update-dedupe/scripts/aggregate_dedupe_feedback.py"


aggregate = import_script(script_path(), "aggregate_dedupe_feedback")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


aggregate = import_script(
".agents/skills/update-pr-review/scripts/aggregate_review_feedback.py",
".github/skills/update-pr-review/scripts/aggregate_review_feedback.py",
"aggregate_review_feedback",
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@


def script_path() -> str:
target = Path(".agents/skills/update-triage/scripts/aggregate_triage_feedback.py")
target = Path(".github/skills/update-triage/scripts/aggregate_triage_feedback.py")
if target.exists():
return str(target)
return ".codex-runtime/handoff/implementation-output/.agents/skills/update-triage/scripts/aggregate_triage_feedback.py"
return ".codex-runtime/handoff/implementation-output/.github/skills/update-triage/scripts/aggregate_triage_feedback.py"


aggregate = import_script(script_path(), "aggregate_triage_feedback")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@


def script_path() -> str:
target = Path(".agents/skills/update-dedupe/scripts/apply_guidance_output.py")
target = Path(".github/skills/update-dedupe/scripts/apply_guidance_output.py")
if target.exists():
return str(target)
return "implementation-output/.agents/skills/update-dedupe/scripts/apply_guidance_output.py"
return "implementation-output/.github/skills/update-dedupe/scripts/apply_guidance_output.py"


apply_guidance = import_script(script_path(), "apply_dedupe_guidance_output")
Expand All @@ -24,15 +24,15 @@ def test_applies_allowed_changed_file_and_creates_parent(self) -> None:
root = Path(tmp)
output = root / "out"
proposed = output / "dedupe-issue-repo"
target = root / ".agents/skills/dedupe-issue-repo/SKILL.md"
target = root / ".github/skills/dedupe-issue-repo/SKILL.md"
proposed.mkdir(parents=True)
(proposed / "SKILL.md").write_text("new\n", encoding="utf-8")
(output / "status.json").write_text(
json.dumps(
{
"status": "changed",
"reason": "test",
"updated_files": [".agents/skills/dedupe-issue-repo/SKILL.md"],
"updated_files": [".github/skills/dedupe-issue-repo/SKILL.md"],
}
),
encoding="utf-8",
Expand Down Expand Up @@ -78,7 +78,7 @@ def test_blocks_core_dedupe_skill(self) -> None:
{
"status": "changed",
"reason": "test",
"updated_files": [".agents/skills/dedupe-issue/SKILL.md"],
"updated_files": [".github/skills/dedupe-issue/SKILL.md"],
}
),
encoding="utf-8",
Expand All @@ -96,7 +96,7 @@ def test_changed_status_requires_proposed_file(self) -> None:
{
"status": "changed",
"reason": "test",
"updated_files": [".agents/skills/dedupe-issue-repo/SKILL.md"],
"updated_files": [".github/skills/dedupe-issue-repo/SKILL.md"],
}
),
encoding="utf-8",
Expand All @@ -119,7 +119,7 @@ def test_changed_status_rejects_symlink_proposed_file(self) -> None:
{
"status": "changed",
"reason": "test",
"updated_files": [".agents/skills/dedupe-issue-repo/SKILL.md"],
"updated_files": [".github/skills/dedupe-issue-repo/SKILL.md"],
}
),
encoding="utf-8",
Expand Down
10 changes: 5 additions & 5 deletions .github/aicodingflow-tests/test_apply_guidance_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


apply_guidance = import_script(
".agents/skills/update-pr-review/scripts/apply_guidance_output.py",
".github/skills/update-pr-review/scripts/apply_guidance_output.py",
"apply_guidance_output",
)

Expand All @@ -20,7 +20,7 @@ def test_applies_allowed_changed_file(self) -> None:
root = Path(tmp)
output = root / "out"
proposed = output / "review-pr-repo"
target = root / ".agents/skills/review-pr-repo/SKILL.md"
target = root / ".github/skills/review-pr-repo/SKILL.md"
proposed.mkdir(parents=True)
target.parent.mkdir(parents=True)
target.write_text("old\n", encoding="utf-8")
Expand All @@ -30,7 +30,7 @@ def test_applies_allowed_changed_file(self) -> None:
{
"status": "changed",
"reason": "test",
"updated_files": [".agents/skills/review-pr-repo/SKILL.md"],
"updated_files": [".github/skills/review-pr-repo/SKILL.md"],
}
),
encoding="utf-8",
Expand Down Expand Up @@ -76,7 +76,7 @@ def test_blocks_disallowed_output_path(self) -> None:
{
"status": "changed",
"reason": "test",
"updated_files": [".agents/skills/review-pr/SKILL.md"],
"updated_files": [".github/skills/review-pr/SKILL.md"],
}
),
encoding="utf-8",
Expand All @@ -94,7 +94,7 @@ def test_changed_status_requires_proposed_file(self) -> None:
{
"status": "changed",
"reason": "test",
"updated_files": [".agents/skills/review-spec-repo/SKILL.md"],
"updated_files": [".github/skills/review-spec-repo/SKILL.md"],
}
),
encoding="utf-8",
Expand Down
14 changes: 7 additions & 7 deletions .github/aicodingflow-tests/test_apply_implementation_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ def test_applies_agents_output_file(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
output = root / "implementation-output"
source = output / ".agents/skills/update-dedupe/SKILL.md"
source = output / ".github/skills/update-dedupe/SKILL.md"
source.parent.mkdir(parents=True)
source.write_text("new skill\n", encoding="utf-8")
metadata = self.write_metadata(root, [".agents/skills/update-dedupe/SKILL.md"])
metadata = self.write_metadata(root, [".github/skills/update-dedupe/SKILL.md"])

applied = apply_impl.apply_output(output, root, metadata)

self.assertEqual(applied, [".agents/skills/update-dedupe/SKILL.md"])
self.assertEqual(applied, [".github/skills/update-dedupe/SKILL.md"])
self.assertEqual(
(root / ".agents/skills/update-dedupe/SKILL.md").read_text(encoding="utf-8"),
(root / ".github/skills/update-dedupe/SKILL.md").read_text(encoding="utf-8"),
"new skill\n",
)

Expand All @@ -66,17 +66,17 @@ def test_rejects_non_agents_output_file(self) -> None:
source.write_text("name: test\n", encoding="utf-8")
metadata = self.write_metadata(root, [".github/workflows/new.yml"])

with self.assertRaisesRegex(SystemExit, "only contain .agents/"):
with self.assertRaisesRegex(SystemExit, "only contain .agents/, .github/skills/"):
apply_impl.apply_output(output, root, metadata)

def test_rejects_output_not_listed_in_intended_files(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
output = root / "implementation-output"
source = output / ".agents/skills/update-dedupe/SKILL.md"
source = output / ".github/skills/update-dedupe/SKILL.md"
source.parent.mkdir(parents=True)
source.write_text("new skill\n", encoding="utf-8")
metadata = self.write_metadata(root, [".agents/skills/other/SKILL.md"])
metadata = self.write_metadata(root, [".github/skills/other/SKILL.md"])

with self.assertRaisesRegex(SystemExit, "not listed"):
apply_impl.apply_output(output, root, metadata)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@


def script_path() -> str:
target = Path(".agents/skills/update-triage/scripts/apply_guidance_output.py")
target = Path(".github/skills/update-triage/scripts/apply_guidance_output.py")
if target.exists():
return str(target)
return ".codex-runtime/handoff/implementation-output/.agents/skills/update-triage/scripts/apply_guidance_output.py"
return ".codex-runtime/handoff/implementation-output/.github/skills/update-triage/scripts/apply_guidance_output.py"


apply_guidance = import_script(script_path(), "apply_triage_guidance_output")
Expand All @@ -30,10 +30,10 @@ def test_applies_allowed_companion_skill_and_creates_parent(self) -> None:
root = Path(tmp)
output = root / "out"
proposed = output / "triage-issue-repo"
target = root / ".agents/skills/triage-issue-repo/SKILL.md"
target = root / ".github/skills/triage-issue-repo/SKILL.md"
proposed.mkdir(parents=True)
(proposed / "SKILL.md").write_text("new\n", encoding="utf-8")
self.write_status(output, [".agents/skills/triage-issue-repo/SKILL.md"])
self.write_status(output, [".github/skills/triage-issue-repo/SKILL.md"])

status = apply_guidance.apply_output(output, root)

Expand Down Expand Up @@ -79,7 +79,7 @@ def test_blocks_core_triage_skill(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
output = Path(tmp) / "out"
output.mkdir()
self.write_status(output, [".agents/skills/triage-issue/SKILL.md"])
self.write_status(output, [".github/skills/triage-issue/SKILL.md"])

with self.assertRaises(SystemExit):
apply_guidance.apply_output(output, Path(tmp))
Expand All @@ -93,7 +93,7 @@ def test_changed_status_rejects_symlink_proposed_file(self) -> None:
outside = root / "outside.md"
outside.write_text("outside\n", encoding="utf-8")
(proposed / "SKILL.md").symlink_to(outside)
self.write_status(output, [".agents/skills/triage-issue-repo/SKILL.md"])
self.write_status(output, [".github/skills/triage-issue-repo/SKILL.md"])

with self.assertRaisesRegex(SystemExit, "refusing to apply symlink output"):
apply_guidance.apply_output(output, root)
Expand All @@ -107,7 +107,7 @@ def test_changed_status_rejects_symlink_parent_escape(self) -> None:
outside.mkdir()
(outside / "SKILL.md").write_text("outside\n", encoding="utf-8")
(output / "triage-issue-repo").symlink_to(outside, target_is_directory=True)
self.write_status(output, [".agents/skills/triage-issue-repo/SKILL.md"])
self.write_status(output, [".github/skills/triage-issue-repo/SKILL.md"])

with self.assertRaisesRegex(SystemExit, "outside output dir"):
apply_guidance.apply_output(output, root)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@ def test_implementation_paths_excludes_workflow_temp_files(self) -> None:
"resolved_review_comments.json",
".local_review_baseline.status",
".codex-runtime/skills/implement-specs/SKILL.md",
"implementation-output/.agents/skills/update-dedupe/SKILL.md",
"implementation-output/.github/skills/update-dedupe/SKILL.md",
".github/scripts/post_pr_review.py",
".github/scripts/__pycache__/post_pr_review.cpython-312.pyc",
".agents/skills/review-pr-repo/SKILL.md",
".github/skills/review-pr-repo/SKILL.md",
"tests/test_post_pr_review.py",
"tests/__pycache__/test_post_pr_review.cpython-312.pyc",
]
),
[
".agents/skills/review-pr-repo/SKILL.md",
".github/scripts/post_pr_review.py",
".github/skills/review-pr-repo/SKILL.md",
"tests/test_post_pr_review.py",
],
)
Expand Down Expand Up @@ -124,12 +124,12 @@ def test_stage_implementation_changes_stages_intended_paths_and_unstages_workflo
mock.patch.object(commit_impl, "existing_temp_workflow_paths", return_value=["pr-metadata.json"]),
mock.patch.object(commit_impl, "run", side_effect=lambda args, **_: calls.append(args) or ""),
):
commit_impl.stage_implementation_changes([".agents/skills/review-pr-repo/SKILL.md", "tests/test_file.py"])
commit_impl.stage_implementation_changes([".github/skills/review-pr-repo/SKILL.md", "tests/test_file.py"])

self.assertEqual(
calls,
[
["git", "add", "-A", "--", ".agents/skills/review-pr-repo/SKILL.md", "tests/test_file.py"],
["git", "add", "-A", "--", ".github/skills/review-pr-repo/SKILL.md", "tests/test_file.py"],
["git", "reset", "--", "pr-metadata.json"],
],
)
Expand Down Expand Up @@ -170,9 +170,9 @@ def test_push_repo_prefers_agent_push_repo(self) -> None:
)

def test_validate_intended_files_rejects_missing_and_unexpected_files(self) -> None:
commit_impl.validate_intended_files([".agents/skills/review-pr-repo/SKILL.md", "app.py"], ["app.py", ".agents/skills/review-pr-repo/SKILL.md"])
commit_impl.validate_intended_files([".github/skills/review-pr-repo/SKILL.md", "app.py"], ["app.py", ".github/skills/review-pr-repo/SKILL.md"])
with self.assertRaisesRegex(SystemExit, "without implementation changes"):
commit_impl.validate_intended_files(["app.py"], ["app.py", ".agents/skills/review-pr-repo/SKILL.md"])
commit_impl.validate_intended_files(["app.py"], ["app.py", ".github/skills/review-pr-repo/SKILL.md"])
with self.assertRaisesRegex(SystemExit, "not listed in intended_files"):
commit_impl.validate_intended_files(["app.py", "tests/test_app.py"], ["app.py"])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def compact(text: str) -> str:

class ImplementationSkillGuidanceTest(unittest.TestCase):
def test_implement_specs_prefers_stable_workflow_context_over_fetching(self) -> None:
text = (ROOT / ".agents/skills/implement-specs/SKILL.md").read_text(encoding="utf-8")
text = (ROOT / ".github/skills/implement-specs/SKILL.md").read_text(encoding="utf-8")
compact_text = compact(text)

self.assertIn("workflow-provided files as the authoritative GitHub context snapshot", compact_text)
Expand All @@ -20,7 +20,7 @@ def test_implement_specs_prefers_stable_workflow_context_over_fetching(self) ->
self.assertNotIn("Fetch any additional GitHub issue or PR content on demand", text)

def test_implement_issue_does_not_require_fetching_in_workflows(self) -> None:
text = (ROOT / ".agents/skills/implement-issue/SKILL.md").read_text(encoding="utf-8")
text = (ROOT / ".github/skills/implement-issue/SKILL.md").read_text(encoding="utf-8")
compact_text = compact(text)

self.assertIn("Workflow-provided files are the authoritative context snapshot", compact_text)
Expand All @@ -30,10 +30,10 @@ def test_implement_issue_does_not_require_fetching_in_workflows(self) -> None:

def test_non_review_workflow_skills_do_not_require_root_handoff_paths(self) -> None:
skill_paths = [
".agents/skills/create-product-spec/SKILL.md",
".agents/skills/create-tech-spec/SKILL.md",
".agents/skills/implement-issue/SKILL.md",
".agents/skills/implement-specs/SKILL.md",
".github/skills/create-product-spec/SKILL.md",
".github/skills/create-tech-spec/SKILL.md",
".github/skills/implement-issue/SKILL.md",
".github/skills/implement-specs/SKILL.md",
]

for path in skill_paths:
Expand All @@ -46,7 +46,7 @@ def test_non_review_workflow_skills_do_not_require_root_handoff_paths(self) -> N
self.assertNotIn("at the repository root", compact_text)

def test_implement_issue_documents_resolved_review_comments_contract(self) -> None:
text = (ROOT / ".agents/skills/implement-issue/SKILL.md").read_text(encoding="utf-8")
text = (ROOT / ".github/skills/implement-issue/SKILL.md").read_text(encoding="utf-8")
compact_text = compact(text)

self.assertIn('"resolved_review_comments"', text)
Expand Down
2 changes: 1 addition & 1 deletion .github/aicodingflow-tests/test_product_change_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ def test_workflow_prompt_restricts_write_surface(self) -> None:
codex_step = next(step for step in steps if step.get("name") == "Generate product change report")
prompt = codex_step["with"]["prompt"]

self.assertIn(".agents/skills/product-change-report/SKILL.md", prompt)
self.assertIn(".github/skills/product-change-report/SKILL.md", prompt)
self.assertIn("Generate or update only:", prompt)
self.assertNotIn("automatic language selection rules", prompt)
self.assertIn("Do not modify .agents, .github, specs, product code, docs/product, or docs/product/wiki.", prompt)
Expand Down
2 changes: 1 addition & 1 deletion .github/aicodingflow-tests/test_product_docs_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ def test_workflow_prompt_restricts_write_surface(self) -> None:
codex_step = next(step for step in steps if step.get("name") == "Run product docs sync")
prompt = codex_step["with"]["prompt"]

self.assertIn(".agents/skills/product-docs-sync/SKILL.md", prompt)
self.assertIn(".github/skills/product-docs-sync/SKILL.md", prompt)
self.assertIn("product-docs-sync-result.json", prompt)
self.assertIn("modify only files under docs/product/", prompt)
self.assertIn("If docs_update is not-needed, do not modify docs/product/.", prompt)
Expand Down
Loading