@ksuchoi216/ahe 0.1.7 → 0.1.10
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/README.md +62 -19
- package/bin/ahe +34 -233
- package/package.json +7 -4
- package/packages/ahe-antigravity/bin/ahe-antigravity +325 -0
- package/packages/ahe-antigravity/package.json +7 -0
- package/packages/ahe-antigravity/skills/ahe-git/SKILL.md +35 -0
- package/packages/ahe-antigravity/skills/ahe-ship/SKILL.md +28 -0
- package/packages/ahe-antigravity/skills/ahe-ship/scripts/post-ship-hook.sh +12 -0
- package/packages/ahe-codex/.codex/agents/ahe-harness-manager.toml +19 -0
- package/{.codex → packages/ahe-codex/.codex}/ahe-shared/templates/AGENTS.md +11 -10
- package/{.codex/ahe-shared/templates/PRODUCT.md → packages/ahe-codex/.codex/ahe-shared/templates/product.md} +1 -1
- package/packages/ahe-codex/.codex/hooks/ahe-hook.js +296 -0
- package/packages/ahe-codex/.codex/skills/ahe/SKILL.md +34 -0
- package/{.codex/skills/ahe-compression → packages/ahe-codex/.codex/skills/ahe-compress}/SKILL.md +39 -16
- package/{.codex/skills/ahe-compression → packages/ahe-codex/.codex/skills/ahe-compress}/agents/openai.yaml +1 -1
- package/{.codex/skills/ahe-compression → packages/ahe-codex/.codex/skills/ahe-compress}/scripts/check-harness-size.sh +18 -6
- package/packages/ahe-codex/.codex/skills/ahe-compress/scripts/detect_stale_tests.py +30 -0
- package/{.codex/skills/ahe-conversator → packages/ahe-codex/.codex/skills/ahe-converse}/SKILL.md +6 -6
- package/packages/ahe-codex/.codex/skills/ahe-feature/SKILL.md +25 -0
- package/packages/ahe-codex/.codex/skills/ahe-fix/SKILL.md +59 -0
- package/packages/ahe-codex/.codex/skills/ahe-fix/scripts/write_fix_plan.py +108 -0
- package/packages/ahe-codex/.codex/skills/ahe-git/SKILL.md +34 -0
- package/packages/ahe-codex/.codex/skills/ahe-harness/SKILL.md +128 -0
- package/packages/ahe-codex/.codex/skills/ahe-harness-checker/SKILL.md +46 -0
- package/{.codex/skills/ahe-init → packages/ahe-codex/.codex/skills/ahe-new}/SKILL.md +32 -21
- package/packages/ahe-codex/.codex/skills/ahe-overview/SKILL.md +119 -0
- package/packages/ahe-codex/.codex/skills/ahe-review/SKILL.md +43 -0
- package/packages/ahe-codex/.codex/skills/ahe-ship/SKILL.md +58 -0
- package/packages/ahe-codex/.codex/skills/ahe-ship/agents/openai.yaml +4 -0
- package/packages/ahe-codex/.codex/skills/ahe-ship/scripts/write_plan.py +107 -0
- package/packages/ahe-codex/.codex/skills/ahe-solve/SKILL.md +30 -0
- package/packages/ahe-codex/.codex/skills/ahe-think/SKILL.md +114 -0
- package/packages/ahe-codex/bin/ahe-codex +398 -0
- package/packages/ahe-codex/package.json +7 -0
- package/.codex/hooks/ahe-hook.js +0 -195
- package/.codex/skills/ahe-harness/SKILL.md +0 -115
- package/.codex/skills/ahe-reviewer/SKILL.md +0 -35
- package/.codex/skills/ahe-solver/SKILL.md +0 -24
- package/.codex/skills/ahe-thinker/SKILL.md +0 -80
- /package/{.codex → packages/ahe-codex/.codex}/ahe-shared/config.yaml +0 -0
- /package/{.codex → packages/ahe-codex/.codex}/ahe-shared/schemas/feature-list-schema.json +0 -0
- /package/{.codex → packages/ahe-codex/.codex}/ahe-shared/schemas/process_status.schema.json +0 -0
- /package/{.codex → packages/ahe-codex/.codex}/ahe-shared/templates/INSTRUCTIONS.md +0 -0
- /package/{.codex → packages/ahe-codex/.codex}/ahe-shared/templates/feature-list.json +0 -0
- /package/{.codex → packages/ahe-codex/.codex}/ahe-shared/templates/init.sh +0 -0
- /package/{.codex/ahe-shared/templates/PROGRESS.md → packages/ahe-codex/.codex/ahe-shared/templates/progress.md} +0 -0
- /package/{.codex/ahe-shared/templates/SESSION-HANDOFF.md → packages/ahe-codex/.codex/ahe-shared/templates/session-handoff.md} +0 -0
- /package/{.codex → packages/ahe-codex/.codex}/hooks/hooks.json +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.11"
|
|
4
|
+
# dependencies = []
|
|
5
|
+
# ///
|
|
6
|
+
#
|
|
7
|
+
# How to run:
|
|
8
|
+
# 1. Install uv (if not installed):
|
|
9
|
+
# curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
10
|
+
# 2. Run directly:
|
|
11
|
+
# uv run write_plan.py --root "$PWD" --plan-name "Plan Title" < /tmp/ahe-plan.md
|
|
12
|
+
# 3. Or with Python when dependencies are already available:
|
|
13
|
+
# python write_plan.py --root "$PWD" --plan-name "Plan Title" < /tmp/ahe-plan.md
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
import sys
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True, slots=True)
|
|
24
|
+
class WritePlanArgs:
|
|
25
|
+
root: Path
|
|
26
|
+
plan_name: str
|
|
27
|
+
overwrite: bool
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ArgumentError(Exception):
|
|
31
|
+
def __init__(self, message: str) -> None:
|
|
32
|
+
self.message = message
|
|
33
|
+
super().__init__(message)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class PlanAlreadyExistsError(Exception):
|
|
37
|
+
def __init__(self, path: Path) -> None:
|
|
38
|
+
self.path = path
|
|
39
|
+
super().__init__(f"Plan file already exists: {path}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def sanitize_plan_name(plan_name: str) -> str:
|
|
43
|
+
normalized_name = re.sub(r"[^a-z0-9]+", "-", plan_name.strip().lower()).strip("-")
|
|
44
|
+
if normalized_name:
|
|
45
|
+
return normalized_name
|
|
46
|
+
raise ArgumentError("plan name must contain at least one letter or digit")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def parse_args(argv: list[str]) -> WritePlanArgs:
|
|
50
|
+
root: Path | None = None
|
|
51
|
+
plan_name: str | None = None
|
|
52
|
+
overwrite = False
|
|
53
|
+
index = 0
|
|
54
|
+
|
|
55
|
+
while index < len(argv):
|
|
56
|
+
argument = argv[index]
|
|
57
|
+
if argument == "--root":
|
|
58
|
+
index += 1
|
|
59
|
+
if index >= len(argv):
|
|
60
|
+
raise ArgumentError("--root requires a value")
|
|
61
|
+
root = Path(argv[index])
|
|
62
|
+
elif argument == "--plan-name":
|
|
63
|
+
index += 1
|
|
64
|
+
if index >= len(argv):
|
|
65
|
+
raise ArgumentError("--plan-name requires a value")
|
|
66
|
+
plan_name = argv[index]
|
|
67
|
+
elif argument == "--overwrite":
|
|
68
|
+
overwrite = True
|
|
69
|
+
else:
|
|
70
|
+
raise ArgumentError(f"unknown argument: {argument}")
|
|
71
|
+
index += 1
|
|
72
|
+
|
|
73
|
+
if root is None:
|
|
74
|
+
raise ArgumentError("--root is required")
|
|
75
|
+
if plan_name is None:
|
|
76
|
+
raise ArgumentError("--plan-name is required")
|
|
77
|
+
|
|
78
|
+
return WritePlanArgs(root=root, plan_name=plan_name, overwrite=overwrite)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def write_plan(args: WritePlanArgs, markdown: str) -> Path:
|
|
82
|
+
filename = f"{sanitize_plan_name(args.plan_name)}.md"
|
|
83
|
+
plans_dir = args.root / ".plans"
|
|
84
|
+
plan_path = plans_dir / filename
|
|
85
|
+
|
|
86
|
+
if plan_path.exists() and not args.overwrite:
|
|
87
|
+
raise PlanAlreadyExistsError(plan_path)
|
|
88
|
+
|
|
89
|
+
plans_dir.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
plan_path.write_text(markdown, encoding="utf-8")
|
|
91
|
+
return plan_path
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def main() -> int:
|
|
95
|
+
try:
|
|
96
|
+
args = parse_args(sys.argv[1:])
|
|
97
|
+
plan_path = write_plan(args, sys.stdin.read())
|
|
98
|
+
except (ArgumentError, PlanAlreadyExistsError) as error:
|
|
99
|
+
print(error, file=sys.stderr)
|
|
100
|
+
return 1
|
|
101
|
+
|
|
102
|
+
print(plan_path)
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if __name__ == "__main__":
|
|
107
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ahe-solve
|
|
3
|
+
description: Internal AHE feature-solving workflow for dividing, planning, and solving feature work after the active goal is clear enough.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AHE Solver
|
|
7
|
+
|
|
8
|
+
This is an internal AHE workflow skill, not a user-facing command.
|
|
9
|
+
|
|
10
|
+
Do not treat `$ahe-solve` as a user command.
|
|
11
|
+
Use it when `ahe-think` decides that the next job is building or planning a
|
|
12
|
+
feature.
|
|
13
|
+
|
|
14
|
+
## Command Workflow: solve
|
|
15
|
+
|
|
16
|
+
- Read the active feature context from `docs/product.md`, `feature-list.json`,
|
|
17
|
+
`progress.md`, and any relevant code files.
|
|
18
|
+
- Read all existing `docs/*.md` files as supporting context.
|
|
19
|
+
- When numbered product stages exist, solve against the active product stage:
|
|
20
|
+
start with `docs/product1.md`, advance to later numeric stages only after the
|
|
21
|
+
current stage's derived feature-list items are `done`, and keep future product stages as context rather than implementation scope.
|
|
22
|
+
- Keep `docs/product.md` as overview context while working from an active
|
|
23
|
+
product stage.
|
|
24
|
+
- Divide broad work into smaller problems when useful.
|
|
25
|
+
- Plan each smaller problem before implementation.
|
|
26
|
+
- Call `ahe-review` when repo or code understanding is needed.
|
|
27
|
+
- Call `ahe-converse` when feature requirements, scope, or success criteria
|
|
28
|
+
are still unclear.
|
|
29
|
+
- Report the solved step, remaining work, and recommended next agent back to
|
|
30
|
+
`ahe-think`.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ahe-think
|
|
3
|
+
description: Internal AHE orchestration protocol for routing exact `ahe`, `ahe <query>`, and `<query> ahe` requests through the AHE agent network.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AHE Thinker
|
|
7
|
+
|
|
8
|
+
This is an internal AHE workflow skill, not a user-facing command.
|
|
9
|
+
|
|
10
|
+
Do not treat `$ahe-think` as a user command.
|
|
11
|
+
Use it as the central decision layer for AHE work.
|
|
12
|
+
|
|
13
|
+
## Purpose
|
|
14
|
+
|
|
15
|
+
- Judge what is missing before another agent acts.
|
|
16
|
+
- Judge the active `project`, `feature`, or `sub-feature`.
|
|
17
|
+
- Decide which of `Why`, `What`, and `How` are still missing.
|
|
18
|
+
- Choose the next internal agent: `ahe-review`, `ahe-converse`,
|
|
19
|
+
`ahe-harness`, or `ahe-solve`.
|
|
20
|
+
- Receive each agent result, reassess the state, and decide the next step.
|
|
21
|
+
|
|
22
|
+
## Routing Inputs
|
|
23
|
+
|
|
24
|
+
- Exact `ahe` means continue existing harness work.
|
|
25
|
+
- Exact `ahe init`, exact `ahe-new`, and exact `$ahe-new` stay on the
|
|
26
|
+
`$ahe-new` path.
|
|
27
|
+
- `ahe <query>` and `<query> ahe` mean route the query through `ahe-think`.
|
|
28
|
+
- Broad non-prefixed prompts must not activate AHE.
|
|
29
|
+
|
|
30
|
+
## Size and Stale-Test Preflight
|
|
31
|
+
|
|
32
|
+
- Before reading full harness files, run
|
|
33
|
+
`sh .codex/skills/ahe-compress/scripts/check-harness-size.sh`.
|
|
34
|
+
- Run the test-overlap detector `python .codex/skills/ahe-compress/scripts/detect_stale_tests.py`
|
|
35
|
+
when the user's explicit AHE query is compression-oriented (e.g., `ahe compress`).
|
|
36
|
+
- Run both compression detectors before choosing the next compression step.
|
|
37
|
+
- If either detector exits with `COMPRESSION_REQUIRED` or code `2`, call
|
|
38
|
+
`ahe-compress` before normal routing. However, `ahe-compress` must not delete tests directly;
|
|
39
|
+
for test compression, `ahe-think` must decide whether the next step is `ahe-harness` for
|
|
40
|
+
harness-file compaction, `ahe-review` for stale-test confirmation and keeper selection,
|
|
41
|
+
or both in sequence.
|
|
42
|
+
- Do not read oversized harness files wholesale before compression routing is
|
|
43
|
+
settled.
|
|
44
|
+
|
|
45
|
+
## Startup Contract
|
|
46
|
+
|
|
47
|
+
- Check whether the copied harness files are correct before reading their contents.
|
|
48
|
+
- Read files strictly in this order:
|
|
49
|
+
`AGENTS.md` -> `docs/product.md` plus `docs/product{n}.md` -> `docs/architecture.md` -> `docs/instructions.md` -> `init.sh` -> `feature-list.json` -> `status.json` -> `progress.md` -> `session-handoff.md`
|
|
50
|
+
- `ahe-think` explicitly owns the "what to do next" decisions for Codex-side AHE work.
|
|
51
|
+
|
|
52
|
+
## Decision Rules
|
|
53
|
+
|
|
54
|
+
- For a `project`, require `Why`, `What`, and `How` by default.
|
|
55
|
+
- For a `feature` or `sub-feature`, require only the minimum of `Why`, `What`,
|
|
56
|
+
and `How` needed to proceed safely.
|
|
57
|
+
- Read all existing `docs/*.md` files before choosing product work.
|
|
58
|
+
- Treat `docs/product.md` as overview context.
|
|
59
|
+
- Treat only `docs/product{number}.md` files as ordered product stages, starting
|
|
60
|
+
with `docs/product1.md` and then `docs/product2.md`.
|
|
61
|
+
- Ignore non-numeric product docs such as `docs/product-alpha.md` for stage
|
|
62
|
+
ordering.
|
|
63
|
+
- Choose the active product source as the lowest-numbered stage whose derived
|
|
64
|
+
feature work is not complete, or `docs/product.md` when no numbered stage
|
|
65
|
+
exists.
|
|
66
|
+
- If the need is understanding repo code, harness drift, progress evidence, or
|
|
67
|
+
CodeGraph context, call `ahe-review`.
|
|
68
|
+
- If the need is user clarification, call `ahe-converse`.
|
|
69
|
+
- If the need is updating harness artifacts, product docs, feature tracking,
|
|
70
|
+
todo sync, or compression of completed history, call `ahe-harness`.
|
|
71
|
+
- If the need is solving or decomposing feature work, call `ahe-solve`.
|
|
72
|
+
|
|
73
|
+
## Interaction Model
|
|
74
|
+
|
|
75
|
+
- `ahe-think` is centered, but direct worker-to-worker calls are allowed when
|
|
76
|
+
they are the obvious next step.
|
|
77
|
+
- Typical loops:
|
|
78
|
+
- `ahe-think -> ahe-review -> ahe-think`
|
|
79
|
+
- `ahe-think -> ahe-harness -> ahe-think`
|
|
80
|
+
- `ahe-think -> ahe-converse -> ahe-think`
|
|
81
|
+
- `ahe-think -> ahe-solve -> ahe-think`
|
|
82
|
+
- `ahe-think -> ahe-harness-checker -> ahe-think`
|
|
83
|
+
- Allowed direct handoffs include:
|
|
84
|
+
- `ahe-harness -> ahe-converse`
|
|
85
|
+
- `ahe-solve -> ahe-review`
|
|
86
|
+
- `ahe-review -> ahe-harness`
|
|
87
|
+
- `ahe-harness -> ahe-harness-checker`
|
|
88
|
+
- `ahe-new -> ahe-harness -> ahe-harness-checker`
|
|
89
|
+
- Every handoff must state the goal, reason, relevant files or context, and the
|
|
90
|
+
expected result.
|
|
91
|
+
|
|
92
|
+
## Broad Intent Routing
|
|
93
|
+
|
|
94
|
+
- Use `ahe-review` for review-first requests.
|
|
95
|
+
- Use `ahe-harness` for product, instructions, progress, feature-list, todo, or
|
|
96
|
+
compression maintenance.
|
|
97
|
+
- Use `ahe-solve` for feature implementation planning or execution work.
|
|
98
|
+
- Use `ahe-converse` when no safe next step exists without user input.
|
|
99
|
+
|
|
100
|
+
## Hard Editing Rule
|
|
101
|
+
|
|
102
|
+
- `AGENTS.md` and any `instructions.md` file may only be modified inside an explicitly changeable section.
|
|
103
|
+
- If no changeable section exists, AHE must ask via `ahe-converse` instead of editing.
|
|
104
|
+
- Reference and template folders must be strictly excluded from reads and edits per project rules.
|
|
105
|
+
|
|
106
|
+
## Completion
|
|
107
|
+
|
|
108
|
+
- Continue to the next skill or next unfinished feature until the active unit is
|
|
109
|
+
resolved.
|
|
110
|
+
- Keep `docs/product.md` as overview context and `feature-list.json` as a
|
|
111
|
+
derived tracker.
|
|
112
|
+
- Derive features from only the active product stage.
|
|
113
|
+
- Advance from one numbered product stage to the next only after all
|
|
114
|
+
feature-list items derived from the current active stage are `done`.
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
readonly VERSION="0.1.0"
|
|
5
|
+
SCRIPT_PATH="${BASH_SOURCE[0]}"
|
|
6
|
+
|
|
7
|
+
while [ -L "${SCRIPT_PATH}" ]; do
|
|
8
|
+
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "${SCRIPT_PATH}")" && pwd)"
|
|
9
|
+
SCRIPT_PATH="$(readlink "${SCRIPT_PATH}")"
|
|
10
|
+
|
|
11
|
+
case "${SCRIPT_PATH}" in
|
|
12
|
+
/*) ;;
|
|
13
|
+
*) SCRIPT_PATH="${SCRIPT_DIR}/${SCRIPT_PATH}" ;;
|
|
14
|
+
esac
|
|
15
|
+
done
|
|
16
|
+
|
|
17
|
+
readonly SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "${SCRIPT_PATH}")" && pwd)"
|
|
18
|
+
readonly PACKAGE_ROOT="$(CDPATH= cd -- "${SCRIPT_DIR}/.." && pwd)"
|
|
19
|
+
readonly SOURCE_SKILLS_DIR="${PACKAGE_ROOT}/.codex/skills"
|
|
20
|
+
readonly SOURCE_SHARED_DIR="${PACKAGE_ROOT}/.codex/ahe-shared"
|
|
21
|
+
readonly SOURCE_HOOKS_DIR="${PACKAGE_ROOT}/.codex/hooks"
|
|
22
|
+
readonly SOURCE_AGENTS_DIR="${PACKAGE_ROOT}/.codex/agents"
|
|
23
|
+
readonly AHE_CONFIG_BLOCK_START="# BEGIN AHE MANAGED CONFIG"
|
|
24
|
+
readonly AHE_CONFIG_BLOCK_END="# END AHE MANAGED CONFIG"
|
|
25
|
+
readonly MANAGED_SKILLS=(
|
|
26
|
+
"ahe"
|
|
27
|
+
"ahe-compress"
|
|
28
|
+
"ahe-converse"
|
|
29
|
+
"ahe-feature"
|
|
30
|
+
"ahe-fix"
|
|
31
|
+
"ahe-git"
|
|
32
|
+
"ahe-harness"
|
|
33
|
+
"ahe-harness-checker"
|
|
34
|
+
"ahe-new"
|
|
35
|
+
"ahe-overview"
|
|
36
|
+
"ahe-review"
|
|
37
|
+
"ahe-ship"
|
|
38
|
+
"ahe-solve"
|
|
39
|
+
"ahe-think"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
codex_home() {
|
|
43
|
+
if [ -n "${CODEX_HOME:-}" ]; then
|
|
44
|
+
printf '%s\n' "${CODEX_HOME}"
|
|
45
|
+
return 0
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
printf '%s\n' "${HOME}/.codex"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
usage() {
|
|
52
|
+
cat <<'EOF'
|
|
53
|
+
Usage:
|
|
54
|
+
ahe install [--force] [--backup]
|
|
55
|
+
ahe uninstall
|
|
56
|
+
ahe doctor
|
|
57
|
+
ahe version
|
|
58
|
+
EOF
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
backup_existing_installation() {
|
|
62
|
+
local target_dir="$1"
|
|
63
|
+
local backup_root="$2"
|
|
64
|
+
local backup_name="$3"
|
|
65
|
+
local timestamp
|
|
66
|
+
|
|
67
|
+
timestamp="$(date '+%Y%m%d-%H%M%S')"
|
|
68
|
+
mkdir -p "${backup_root}"
|
|
69
|
+
mv "${target_dir}" "${backup_root}/${backup_name}-${timestamp}"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
cleanup_ahe_config_entries() {
|
|
73
|
+
local config_path="$1"
|
|
74
|
+
local temp_path=""
|
|
75
|
+
|
|
76
|
+
if [ ! -f "${config_path}" ]; then
|
|
77
|
+
return 0
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
temp_path="${config_path}.ahe-cleanup"
|
|
81
|
+
awk -v block_start="${AHE_CONFIG_BLOCK_START}" -v block_end="${AHE_CONFIG_BLOCK_END}" '
|
|
82
|
+
function is_ahe_header(line) {
|
|
83
|
+
return line ~ /^\[agents\."?ahe[-_][^]]*"?\]$/ ||
|
|
84
|
+
line ~ /^\[hooks\.state\."ahe[^"]*"\]$/ ||
|
|
85
|
+
line ~ /^\[plugins\."?ahe[^]]*"?\]$/ ||
|
|
86
|
+
line ~ /^\[plugins\."@ksuchoi216\/ahe[^"]*"\]$/
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
$0 == block_start {
|
|
90
|
+
skip = 1
|
|
91
|
+
next
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
$0 == block_end {
|
|
95
|
+
skip = 0
|
|
96
|
+
next
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/^\[/ {
|
|
100
|
+
if (is_ahe_header($0)) {
|
|
101
|
+
skip = 1
|
|
102
|
+
next
|
|
103
|
+
}
|
|
104
|
+
skip = 0
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
skip != 1 {
|
|
108
|
+
print
|
|
109
|
+
}
|
|
110
|
+
' "${config_path}" > "${temp_path}"
|
|
111
|
+
mv "${temp_path}" "${config_path}"
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
install_skill() {
|
|
115
|
+
local force="false"
|
|
116
|
+
local backup="false"
|
|
117
|
+
|
|
118
|
+
while [ "$#" -gt 0 ]; do
|
|
119
|
+
case "$1" in
|
|
120
|
+
--force)
|
|
121
|
+
force="true"
|
|
122
|
+
;;
|
|
123
|
+
--backup)
|
|
124
|
+
backup="true"
|
|
125
|
+
;;
|
|
126
|
+
*)
|
|
127
|
+
printf 'Unknown install option: %s\n' "$1" >&2
|
|
128
|
+
usage >&2
|
|
129
|
+
exit 1
|
|
130
|
+
;;
|
|
131
|
+
esac
|
|
132
|
+
shift
|
|
133
|
+
done
|
|
134
|
+
|
|
135
|
+
if [ ! -d "${SOURCE_SKILLS_DIR}" ]; then
|
|
136
|
+
printf 'Packaged skill files are missing: %s\n' "${SOURCE_SKILLS_DIR}" >&2
|
|
137
|
+
exit 1
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
if [ ! -d "${SOURCE_SHARED_DIR}" ]; then
|
|
141
|
+
printf 'Packaged shared files are missing: %s\n' "${SOURCE_SHARED_DIR}" >&2
|
|
142
|
+
exit 1
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
local target_codex_home
|
|
146
|
+
target_codex_home="$(codex_home)"
|
|
147
|
+
|
|
148
|
+
local target_skills_dir="${target_codex_home}/skills"
|
|
149
|
+
local target_shared_dir="${target_codex_home}/ahe-shared"
|
|
150
|
+
local target_hooks_dir="${target_codex_home}/hooks"
|
|
151
|
+
local target_agents_dir="${target_codex_home}/agents"
|
|
152
|
+
local target_config_path="${target_codex_home}/config.toml"
|
|
153
|
+
local backup_dir="${target_codex_home}/_backups"
|
|
154
|
+
local skill_name=""
|
|
155
|
+
local skill_target=""
|
|
156
|
+
|
|
157
|
+
cleanup_ahe_config_entries "${target_config_path}"
|
|
158
|
+
|
|
159
|
+
for skill_name in "${MANAGED_SKILLS[@]}"; do
|
|
160
|
+
skill_target="${target_skills_dir}/${skill_name}"
|
|
161
|
+
|
|
162
|
+
if [ -e "${skill_target}" ]; then
|
|
163
|
+
if [ "${backup}" = "true" ]; then
|
|
164
|
+
backup_existing_installation "${skill_target}" "${backup_dir}" "${skill_name}"
|
|
165
|
+
elif [ "${force}" != "true" ]; then
|
|
166
|
+
cat <<EOF >&2
|
|
167
|
+
AHE skill is already installed at:
|
|
168
|
+
${skill_target}
|
|
169
|
+
|
|
170
|
+
Re-run with --force to overwrite or --backup to move the existing install aside first.
|
|
171
|
+
EOF
|
|
172
|
+
exit 1
|
|
173
|
+
else
|
|
174
|
+
rm -rf "${skill_target}"
|
|
175
|
+
fi
|
|
176
|
+
fi
|
|
177
|
+
done
|
|
178
|
+
|
|
179
|
+
if [ -e "${target_shared_dir}" ]; then
|
|
180
|
+
if [ "${backup}" = "true" ]; then
|
|
181
|
+
backup_existing_installation "${target_shared_dir}" "${backup_dir}" "ahe-shared"
|
|
182
|
+
elif [ "${force}" = "true" ]; then
|
|
183
|
+
rm -rf "${target_shared_dir}"
|
|
184
|
+
else
|
|
185
|
+
cat <<EOF >&2
|
|
186
|
+
AHE shared assets are already installed at:
|
|
187
|
+
${target_shared_dir}
|
|
188
|
+
|
|
189
|
+
Re-run with --force to overwrite or --backup to move the existing install aside first.
|
|
190
|
+
EOF
|
|
191
|
+
exit 1
|
|
192
|
+
fi
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
if [ -e "${target_hooks_dir}" ]; then
|
|
196
|
+
if [ "${backup}" = "true" ]; then
|
|
197
|
+
backup_existing_installation "${target_hooks_dir}" "${backup_dir}" "hooks"
|
|
198
|
+
elif [ "${force}" = "true" ]; then
|
|
199
|
+
rm -rf "${target_hooks_dir}"
|
|
200
|
+
else
|
|
201
|
+
cat <<EOF >&2
|
|
202
|
+
AHE hooks are already installed at:
|
|
203
|
+
${target_hooks_dir}
|
|
204
|
+
|
|
205
|
+
Re-run with --force to overwrite or --backup to move the existing install aside first.
|
|
206
|
+
EOF
|
|
207
|
+
exit 1
|
|
208
|
+
fi
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
mkdir -p "${target_skills_dir}"
|
|
212
|
+
for skill_name in "${MANAGED_SKILLS[@]}"; do
|
|
213
|
+
cp -R "${SOURCE_SKILLS_DIR}/${skill_name}" "${target_skills_dir}/${skill_name}"
|
|
214
|
+
done
|
|
215
|
+
cp -R "${SOURCE_SHARED_DIR}" "${target_shared_dir}"
|
|
216
|
+
cp -R "${SOURCE_HOOKS_DIR}" "${target_hooks_dir}"
|
|
217
|
+
mkdir -p "${target_agents_dir}"
|
|
218
|
+
cp -R "${SOURCE_AGENTS_DIR}"/* "${target_agents_dir}/"
|
|
219
|
+
|
|
220
|
+
cat <<EOF >> "${target_config_path}"
|
|
221
|
+
${AHE_CONFIG_BLOCK_START}
|
|
222
|
+
[agents.ahe-harness-manager]
|
|
223
|
+
config_file = "${target_agents_dir}/ahe-harness-manager.toml"
|
|
224
|
+
${AHE_CONFIG_BLOCK_END}
|
|
225
|
+
EOF
|
|
226
|
+
cat > "${target_hooks_dir}/hooks.json" <<EOF
|
|
227
|
+
{
|
|
228
|
+
"hooks": {
|
|
229
|
+
"UserPromptSubmit": [
|
|
230
|
+
{
|
|
231
|
+
"hooks": [
|
|
232
|
+
{
|
|
233
|
+
"type": "command",
|
|
234
|
+
"command": "node \"${target_hooks_dir}/ahe-hook.js\"",
|
|
235
|
+
"timeout": 5,
|
|
236
|
+
"statusMessage": "AHE: Checking Keyword Trigger"
|
|
237
|
+
}
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
EOF
|
|
244
|
+
|
|
245
|
+
cat <<EOF
|
|
246
|
+
AHE Codex skill installed.
|
|
247
|
+
Global Codex home:
|
|
248
|
+
${target_codex_home}
|
|
249
|
+
|
|
250
|
+
Next:
|
|
251
|
+
1. Open Codex chat in any workspace.
|
|
252
|
+
2. Use \`ahe new\` for a new start.
|
|
253
|
+
3. Use exact \`ahe\` to continue existing harness work.
|
|
254
|
+
4. Use \`ahe ship\` after Plan Mode to write the latest plan to .plans.
|
|
255
|
+
5. Use \`ahe fix\` to create a .plans fix plan for errors or changed intent.
|
|
256
|
+
6. Use \`ahe <query>\` for explicit AHE requests such as \`ahe compress\`.
|
|
257
|
+
EOF
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
doctor() {
|
|
261
|
+
printf '=== codex ===\n'
|
|
262
|
+
|
|
263
|
+
local target_codex_home
|
|
264
|
+
target_codex_home="$(codex_home)"
|
|
265
|
+
|
|
266
|
+
local target_skills_dir="${target_codex_home}/skills"
|
|
267
|
+
local target_shared_dir="${target_codex_home}/ahe-shared"
|
|
268
|
+
local target_hooks_dir="${target_codex_home}/hooks"
|
|
269
|
+
local target_agents_dir="${target_codex_home}/agents"
|
|
270
|
+
local target_config_path="${target_codex_home}/config.toml"
|
|
271
|
+
local skill_name=""
|
|
272
|
+
local errors=0
|
|
273
|
+
|
|
274
|
+
for skill_name in "${MANAGED_SKILLS[@]}"; do
|
|
275
|
+
if [ ! -f "${target_skills_dir}/${skill_name}/SKILL.md" ]; then
|
|
276
|
+
printf 'Missing: %s/%s/SKILL.md\n' "${target_skills_dir}" "${skill_name}" >&2
|
|
277
|
+
errors=$((errors + 1))
|
|
278
|
+
fi
|
|
279
|
+
done
|
|
280
|
+
|
|
281
|
+
if [ ! -f "${target_shared_dir}/config.yaml" ]; then
|
|
282
|
+
printf 'Missing: %s/config.yaml\n' "${target_shared_dir}" >&2
|
|
283
|
+
errors=$((errors + 1))
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
if [ ! -d "${target_shared_dir}/templates" ]; then
|
|
287
|
+
printf 'Missing: %s/templates\n' "${target_shared_dir}" >&2
|
|
288
|
+
errors=$((errors + 1))
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
if [ ! -d "${target_shared_dir}/schemas" ]; then
|
|
292
|
+
printf 'Missing: %s/schemas\n' "${target_shared_dir}" >&2
|
|
293
|
+
errors=$((errors + 1))
|
|
294
|
+
fi
|
|
295
|
+
|
|
296
|
+
if [ ! -f "${target_hooks_dir}/hooks.json" ]; then
|
|
297
|
+
printf 'Missing: %s/hooks.json\n' "${target_hooks_dir}" >&2
|
|
298
|
+
errors=$((errors + 1))
|
|
299
|
+
else
|
|
300
|
+
if ! TARGET_HOOKS_DIR="${target_hooks_dir}" node -e "
|
|
301
|
+
try {
|
|
302
|
+
const h = require(process.env.TARGET_HOOKS_DIR + '/hooks.json');
|
|
303
|
+
const cmd = h.hooks.UserPromptSubmit[0].hooks[0].command;
|
|
304
|
+
if (!cmd.includes(process.env.TARGET_HOOKS_DIR + '/ahe-hook.js')) {
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
} catch (e) {
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
" 2>/dev/null; then
|
|
311
|
+
printf 'Invalid hook command: %s/hooks.json does not point to %s/ahe-hook.js\n' "${target_hooks_dir}" "${target_hooks_dir}" >&2
|
|
312
|
+
errors=$((errors + 1))
|
|
313
|
+
fi
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
if [ ! -f "${target_hooks_dir}/ahe-hook.js" ]; then
|
|
317
|
+
printf 'Missing: %s/ahe-hook.js\n' "${target_hooks_dir}" >&2
|
|
318
|
+
errors=$((errors + 1))
|
|
319
|
+
fi
|
|
320
|
+
|
|
321
|
+
if [ ! -f "${target_agents_dir}/ahe-harness-manager.toml" ]; then
|
|
322
|
+
printf 'Missing: %s/ahe-harness-manager.toml\n' "${target_agents_dir}" >&2
|
|
323
|
+
errors=$((errors + 1))
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
if ! grep -q '\[agents\.ahe-harness-manager\]' "${target_config_path}" 2>/dev/null; then
|
|
327
|
+
printf 'Missing: [agents.ahe-harness-manager] in %s\n' "${target_config_path}" >&2
|
|
328
|
+
errors=$((errors + 1))
|
|
329
|
+
fi
|
|
330
|
+
|
|
331
|
+
if [ "${errors}" -gt 0 ]; then
|
|
332
|
+
printf 'AHE Codex skill installation is unhealthy (%d errors).\n' "${errors}" >&2
|
|
333
|
+
exit 1
|
|
334
|
+
fi
|
|
335
|
+
|
|
336
|
+
printf 'AHE Codex skill installation looks healthy.\n'
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
uninstall_skill() {
|
|
340
|
+
local target_codex_home
|
|
341
|
+
target_codex_home="$(codex_home)"
|
|
342
|
+
|
|
343
|
+
local target_skills_dir="${target_codex_home}/skills"
|
|
344
|
+
local target_shared_dir="${target_codex_home}/ahe-shared"
|
|
345
|
+
local target_hooks_dir="${target_codex_home}/hooks"
|
|
346
|
+
local target_agents_dir="${target_codex_home}/agents"
|
|
347
|
+
local target_config_path="${target_codex_home}/config.toml"
|
|
348
|
+
local skill_name=""
|
|
349
|
+
|
|
350
|
+
echo "Uninstalling AHE skills from ${target_codex_home}..."
|
|
351
|
+
|
|
352
|
+
for skill_name in "${MANAGED_SKILLS[@]}"; do
|
|
353
|
+
rm -rf "${target_skills_dir}/${skill_name}"
|
|
354
|
+
done
|
|
355
|
+
|
|
356
|
+
rm -rf "${target_shared_dir}"
|
|
357
|
+
rm -rf "${target_hooks_dir}"
|
|
358
|
+
rm -f "${target_agents_dir}/ahe-harness-manager.toml"
|
|
359
|
+
cleanup_ahe_config_entries "${target_config_path}"
|
|
360
|
+
|
|
361
|
+
echo "AHE skills uninstalled successfully."
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
main() {
|
|
365
|
+
if [ "$#" -eq 0 ]; then
|
|
366
|
+
usage
|
|
367
|
+
exit 1
|
|
368
|
+
fi
|
|
369
|
+
|
|
370
|
+
local command="$1"
|
|
371
|
+
shift
|
|
372
|
+
|
|
373
|
+
case "${command}" in
|
|
374
|
+
install)
|
|
375
|
+
install_skill "$@"
|
|
376
|
+
;;
|
|
377
|
+
uninstall)
|
|
378
|
+
uninstall_skill
|
|
379
|
+
;;
|
|
380
|
+
doctor)
|
|
381
|
+
doctor
|
|
382
|
+
;;
|
|
383
|
+
version)
|
|
384
|
+
printf '%s\n' "${VERSION}"
|
|
385
|
+
;;
|
|
386
|
+
-h|--help|help)
|
|
387
|
+
usage
|
|
388
|
+
exit 0
|
|
389
|
+
;;
|
|
390
|
+
*)
|
|
391
|
+
printf 'Unknown command: %s\n' "${command}" >&2
|
|
392
|
+
usage >&2
|
|
393
|
+
exit 1
|
|
394
|
+
;;
|
|
395
|
+
esac
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
main "$@"
|