@delorenj/pjangler 1.2.18 → 1.2.21

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.
Files changed (57) hide show
  1. package/dist/index.js +884 -295
  2. package/dist/mcp-server.js +892 -303
  3. package/package.json +8 -2
  4. package/templates/commonproject/AGENTS.md +3 -3
  5. package/templates/commonproject/README.md +11 -12
  6. package/templates/commonproject/copier.yml +11 -31
  7. package/templates/commonproject/template/.agents/hooks/README.md +13 -26
  8. package/templates/commonproject/template/.agents/hooks/lib/local-config.sh +4 -14
  9. package/templates/commonproject/template/.agents/hooks/sync.py +5 -6
  10. package/templates/commonproject/template/.agents/local.example.json +2 -8
  11. package/templates/commonproject/template/.agents/skills.json +6 -0
  12. package/templates/commonproject/template/.project.json.jinja +9 -13
  13. package/templates/commonproject/template/mise.toml.jinja +9 -16
  14. package/templates/hermes-agent/README.md +9 -9
  15. package/templates/hermes-agent/config.example.toml +1 -66
  16. package/templates/hermes-agent/copier.yml +4 -3
  17. package/templates/hermes-agent/docs/architecture.md +9 -12
  18. package/templates/hermes-agent/docs/fleet-control-plane/README.md +1 -1
  19. package/templates/hermes-agent/docs/operations.md +6 -5
  20. package/templates/hermes-agent/docs/sentinel/README.md +9 -7
  21. package/templates/hermes-agent/docs/sentinel/architecture.md +1 -2
  22. package/templates/hermes-agent/docs/sentinel/development.md +12 -13
  23. package/templates/hermes-agent/docs/sentinel/providers.md +34 -15
  24. package/templates/hermes-agent/install-local.sh +15 -14
  25. package/templates/hermes-agent/runtime-scaffold/README.md +1 -1
  26. package/templates/hermes-agent/runtime-scaffold/bloodbank-consumer.py +46 -14
  27. package/templates/hermes-agent/runtime-scaffold/memories/MEMORY.md +2 -2
  28. package/templates/hermes-agent/scripts/fleet-sync.sh +1 -64
  29. package/templates/hermes-agent/template/.gitignore.jinja +0 -2
  30. package/templates/hermes-agent/template/.runtime-scaffold/README.md +1 -1
  31. package/templates/hermes-agent/template/.runtime-scaffold/bloodbank-consumer.py +43 -9
  32. package/templates/hermes-agent/template/.runtime-scaffold/memories/MEMORY.md +2 -2
  33. package/templates/hermes-agent/template/.scripts/01-config.sh +0 -1
  34. package/templates/hermes-agent/template/.scripts/05-fleet-env.sh +0 -9
  35. package/templates/hermes-agent/template/.scripts/10-hermes-profile.sh +3 -22
  36. package/templates/hermes-agent/template/.scripts/20-runtime-repo.sh +0 -22
  37. package/templates/hermes-agent/template/.scripts/40-plane.sh +51 -0
  38. package/templates/hermes-agent/template/.scripts/42-ticket-provider.sh +59 -21
  39. package/templates/hermes-agent/template/.scripts/60-bloodbank.sh +2 -1
  40. package/templates/hermes-agent/template/.scripts/70-systemd.sh +1 -10
  41. package/templates/hermes-agent/template/.scripts/_lib.sh +3 -61
  42. package/templates/hermes-agent/template/.scripts/config.example.toml +0 -5
  43. package/templates/hermes-agent/template/.scripts/heartbeat.sh +33 -66
  44. package/templates/hermes-agent/template/.scripts/lib/ticket-provider.sh +5 -9
  45. package/templates/hermes-agent/template/.scripts/momo-wip-lock.py +137 -0
  46. package/templates/hermes-agent/template/.scripts/providers/linear.sh +176 -0
  47. package/templates/hermes-agent/template/.scripts/providers/plane.sh +9 -29
  48. package/templates/hermes-agent/template/.scripts/sentinel/docs/autonomous-delegated-review.md +2 -2
  49. package/templates/hermes-agent/template/.scripts/sentinel/docs/continuous-ticket-orchestration.md +1 -33
  50. package/templates/hermes-agent/template/.scripts/sentinel.prompt.md.jinja +22 -27
  51. package/templates/hermes-agent/template/SOUL.md.jinja +49 -30
  52. package/templates/hermes-agent/template/role.yaml.jinja +35 -4
  53. package/templates/hermes-agent/tests/test_bloodbank_consumer_contract.py +138 -0
  54. package/templates/commonproject/template/.mise/scripts/link-project-skills-to-clis.sh +0 -110
  55. package/templates/commonproject/template/.mise/scripts/unlink-project-skills-from-clis.sh +0 -45
  56. package/templates/hermes-agent/docs/bloodbank-gateway.md +0 -57
  57. package/templates/hermes-agent/docs/fleet-control-plane/n8n-service-hub.md +0 -60
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delorenj/pjangler",
3
- "version": "1.2.18",
3
+ "version": "1.2.21",
4
4
  "description": "Project subsystem bootstrapper CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,7 +23,10 @@
23
23
  "start": "node dist/index.js",
24
24
  "mcp": "node dist/mcp-server.js",
25
25
  "typecheck": "tsc --noEmit",
26
- "test": "node tests/parity-migrate-regressions.mjs && node tests/mcp-catalog-regressions.mjs && node tests/mcp-server-regressions.mjs && node tests/project-registry-regressions.mjs",
26
+ "test": "node tests/parity-migrate-regressions.mjs && node tests/mcp-catalog-regressions.mjs && node tests/mcp-server-regressions.mjs && node tests/project-registry-regressions.mjs && node tests/pg-registry-regressions.mjs",
27
+ "migrate:up": "node-pg-migrate --migrations-dir migrations up",
28
+ "migrate:down": "node-pg-migrate --migrations-dir migrations down",
29
+ "migrate:create": "node-pg-migrate --migrations-dir migrations create",
27
30
  "prepublishOnly": "npm run build"
28
31
  },
29
32
  "keywords": [
@@ -39,12 +42,15 @@
39
42
  "@clack/prompts": "^1.4.0",
40
43
  "@modelcontextprotocol/sdk": "^1.29.0",
41
44
  "commander": "^14.0.2",
45
+ "pg": "^8.16.0",
42
46
  "yaml": "^2.9.0",
43
47
  "zod": "^4.4.3"
44
48
  },
45
49
  "devDependencies": {
46
50
  "@types/node": "^25.9.1",
51
+ "@types/pg": "^8.15.0",
47
52
  "esbuild": "^0.25.0",
53
+ "node-pg-migrate": "^8.0.0",
48
54
  "typescript": "^5"
49
55
  }
50
56
  }
@@ -20,7 +20,7 @@ CommonProject/
20
20
  │ ├── .agentvibes/ # AgentVibes config
21
21
  │ ├── .mise/tasks/ # File-based mise tasks
22
22
  │ ├── .scripts/ # Post-generation utilities
23
- │ │ └── setup-plane.py # Creates/links ticket provider board + ticket_provider block in .project.json
23
+ │ │ └── setup-plane.py # Creates Plane project + ticket_provider block in .project.json
24
24
  │ ├── AGENTS.md.jinja # Generated project's agent SSOT
25
25
  │ ├── CLAUDE.md # Symlink → AGENTS.md
26
26
  │ ├── GEMINI.md # Symlink → AGENTS.md
@@ -46,7 +46,7 @@ Root-level files describe the template itself. Files in `template/` are what Cop
46
46
  Only two questions asked: `project_name` and `project_description`. Everything else is derived or automated:
47
47
  - `project_slug` derived from project_name
48
48
  - `user_name` / `user_skill_level` hardcoded for BMAD config
49
- - Ticket board created or linked via post-generation task
49
+ - Plane project created via API in post-generation task
50
50
  - .gitignore copied from ~/.config/git/ignore
51
51
  - git init + initial commit run automatically
52
52
 
@@ -55,7 +55,7 @@ Only two questions asked: `project_name` and `project_description`. Everything e
55
55
  After rendering, Copier automatically:
56
56
  1. Copies .gitignore from ~/.config/git/ignore
57
57
  2. Makes scripts executable
58
- 3. Runs setup-plane.py (creates/links the ticket board, writes the ticket_provider block in .project.json)
58
+ 3. Runs setup-plane.py (creates Plane project, writes the ticket_provider block in .project.json)
59
59
  4. Runs git init + git add -A + git commit
60
60
 
61
61
  ### BMAD System
@@ -1,13 +1,13 @@
1
1
  # CommonProject
2
2
 
3
- Copier template for creating new 33GOD ecosystem components with BMAD methodology, ticket-provider enforcement, and multi-agent development tooling.
3
+ Copier template for creating new 33GOD ecosystem components with BMAD methodology, Plane ticket enforcement, and multi-agent development tooling.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - **Language Support**: Python, TypeScript, Rust
8
8
  - **Event-Driven Architecture**: Optional [Bloodbank](https://github.com/delorenj/Bloodbank) integration
9
- - **Ticket Provider Integration**: `.project.json` records Plane or Trello board bindings
10
- - **Ticket Enforcement**: Git hooks enforce configured ticket-provider requirements
9
+ - **Plane Integration**: `.project.json` for ticket management and project config
10
+ - **Ticket Enforcement**: Git hooks enforce Plane ticket requirements
11
11
  - [TODO] **BMAD Integration**: Full BMAD methodology structure by auto running `npx bmad-method@latest install`
12
12
  - [TODO] Multiple-choice selection for different workflows: BMAD, GSD, GoogleAgentSkills, None.
13
13
  - **Containerization**: Optional Docker + Docker Compose
@@ -25,7 +25,7 @@ uv tool install copier
25
25
  # Or with pip
26
26
  pip install copier
27
27
 
28
- # Set up a provider key if you want automated board creation/linking
28
+ # Set up Plane API key (for automated project creation)
29
29
  export PLANE_API_KEY="your-plane-api-key"
30
30
  OR
31
31
  [TODO] export PLANE_API_KEY=op://VaultName/Key/Field
@@ -36,14 +36,14 @@ OR
36
36
 
37
37
  ### Option 1: Automated Init (Recommended)
38
38
 
39
- [TODO] One command handles everything: ticket board creation/linking + Copier template:
39
+ [TODO] One command handles everything: Plane project creation + Copier template:
40
40
 
41
41
  > TASK - Implement the following:
42
42
  > Currently, I have to run `copier copy --trust ~/code CommonProject .` globally, or `mise run init-project` while in the CommonProject source dir - but this is not ideal.
43
43
  > I would rather run `commonProject .` globally.
44
44
 
45
45
  ```bash
46
- # Interactive wizard - creates/links the configured ticket board automatically
46
+ # Interactive wizard - creates Plane project automatically
47
47
  mise run init-project
48
48
 
49
49
  # Or non-interactive (uses defaults)
@@ -56,7 +56,7 @@ mise run init-project-non-interactive
56
56
  The wizard will:
57
57
 
58
58
  1. Ask for project details (name, description, type)
59
- 2. Create or link the configured ticket board
59
+ 2. Create the Plane project automatically
60
60
  3. Run Copier with all answers pre-filled
61
61
  4. Output next steps
62
62
 
@@ -83,10 +83,9 @@ The template asks for:
83
83
  - **Project Slug**: Directory/package name (auto-generated from name)
84
84
  - **Description**: One-sentence project description
85
85
 
86
- ### Ticket Provider Integration
86
+ ### Plane Integration
87
87
 
88
- - **Provider**: Plane or Trello
89
- - **Workspace**: Provider workspace slug when applicable (Plane default: 33god)
88
+ - **Workspace**: Plane workspace slug (default: 33god)
90
89
  - **Project Name**
91
90
  - **Project Identifier**: 2+ character ticket prefix (e.g., HOLO, VERN)
92
91
 
@@ -111,10 +110,10 @@ The template asks for:
111
110
  git commit -m "Initial commit from template"
112
111
  ```
113
112
 
114
- 3. **Create or Claim Ticket**
113
+ 3. **Create Plane Ticket**
115
114
 
116
115
  ```bash
117
- # Create or claim a ticket in the configured provider
116
+ # Create ticket in Plane (or it was already created for you!)
118
117
  # Move to "In Progress"
119
118
  git checkout -b <IDENTIFIER>-123-initial-setup
120
119
  ```
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # Generic base project template. Creates a project skeleton with:
5
5
  # - BMAD methodology (pre-initialized)
6
- # - Ticket-provider metadata (Plane or Trello)
6
+ # - Plane project (auto-created in 33god workspace)
7
7
  # - mise environment management
8
8
  # - CLI coder configs (Claude, Codex, Gemini, etc.)
9
9
 
@@ -30,15 +30,16 @@ project_description:
30
30
  help: One-line description
31
31
  default: ""
32
32
 
33
- # Ticket board provider for this project. Plane boards can be created before
34
- # Copier runs; Trello boards are linked by id/url. The board identity is
35
- # persisted into .project.json so it is the single source of truth shared with
36
- # the hermes-agent template's agents.
33
+ # Ticket board provider for this project. The board itself is created by
34
+ # init-project.sh (create-plane-project.sh) BEFORE copier runs; its identity is
35
+ # threaded back in via plane_* below and persisted into .project.json so it is
36
+ # the single source of truth shared with the hermes-agent template's agents.
37
37
  ticket_provider:
38
38
  type: str
39
39
  help: Ticket board provider this project's board lives in
40
40
  default: plane
41
41
  choices:
42
+ Linear: linear
42
43
  Plane: plane
43
44
  Trello: trello
44
45
 
@@ -49,7 +50,8 @@ project_slug:
49
50
  default: "{{ project_name | lower | replace(' ', '-') | replace('_', '-') }}"
50
51
  when: false
51
52
 
52
- # Back-compat aliases kept for existing pjangler/CommonProject callers.
53
+ # Board binding — supplied by init-project.sh's --data-file after the board is
54
+ # created. Defaults keep a bare `copier copy` rendering valid (empty binding).
53
55
  plane_workspace:
54
56
  type: str
55
57
  default: "33god"
@@ -70,27 +72,6 @@ plane_base:
70
72
  default: "https://plane.delo.sh"
71
73
  when: false
72
74
 
73
- # Provider-agnostic board binding — supplied by pjangler's --data values after
74
- # the board is created or linked. Defaults keep a bare `copier copy` rendering
75
- # valid, and fall back to the legacy plane_* aliases above. Board URLs are
76
- # derived from provider/workspace/board_id and are intentionally not persisted.
77
- board_id:
78
- type: str
79
- default: "{{ plane_project_id }}"
80
- when: false
81
-
82
- # Deprecated no-op accepted so older pjangler/CommonProject callers do not fail
83
- # when they still pass `--data board_url=...`.
84
- board_url:
85
- type: str
86
- default: ""
87
- when: false
88
-
89
- ticket_workspace:
90
- type: str
91
- default: "{{ plane_workspace if ticket_provider == 'plane' else '' }}"
92
- when: false
93
-
94
75
  # Drives conditional blocks in rendered files (e.g. _.python.venv in mise.toml).
95
76
  # Not asked interactively; init-project.sh can override via --data-file.
96
77
  primary_language:
@@ -109,8 +90,9 @@ agent_hooks_layer:
109
90
  default: true
110
91
  when: false
111
92
 
112
- # When agent_hooks_layer is false, omit the project-scoped hook tree + skill-link
113
- # scripts entirely (the mise.toml wiring is already gated inside the template).
93
+ # When agent_hooks_layer is false, omit only the project-scoped hook tree. The
94
+ # skills manifest stays — sync-skills.py writes project-local CLI dirs and is safe
95
+ # even on machines that already run a global ~/.agents/hooks install.
114
96
  # The `.copier-noop-never-matches` sentinel keeps every pattern non-empty when the
115
97
  # layer is enabled, which copier requires.
116
98
  _exclude:
@@ -124,8 +106,6 @@ _exclude:
124
106
  - "{% if agent_hooks_layer %}.copier-noop-never-matches{% else %}.agents/hooks{% endif %}"
125
107
  - "{% if agent_hooks_layer %}.copier-noop-never-matches{% else %}.agents/hooks/**{% endif %}"
126
108
  - "{% if agent_hooks_layer %}.copier-noop-never-matches{% else %}.agents/local.example.json{% endif %}"
127
- - "{% if agent_hooks_layer %}.copier-noop-never-matches{% else %}.mise/scripts/link-project-skills-to-clis.sh{% endif %}"
128
- - "{% if agent_hooks_layer %}.copier-noop-never-matches{% else %}.mise/scripts/unlink-project-skills-from-clis.sh{% endif %}"
129
109
 
130
110
  # --- Post-generation tasks ---
131
111
  #
@@ -15,6 +15,7 @@ master writes zero bytes).
15
15
  mise run hooks-sync # fan out the master -> claude + codex + hermes
16
16
  mise run hooks-check # drift gate (read-only; used in CI)
17
17
  mise run hooks-uninstall # remove codex + hermes injections
18
+ mise run skills-sync # sync .agents/skills.json -> local CLI skill dirs
18
19
  mise run hindsight-setup # one-time: pull the shared CAF Hindsight key into .env
19
20
  ```
20
21
 
@@ -70,8 +71,7 @@ Copy [`.agents/local.example.json`](../local.example.json) → `.agents/local.js
70
71
  "hooks": {
71
72
  "disabled": ["hindsight-recall"], // hook ids — skipped at runtime, ALL agents
72
73
  "disabled_agents": ["codex"] // claude|codex|hermes — codex/hermes injection skipped/removed
73
- },
74
- "skills": { "disabled": ["bmad-help"] } // skill dir names — not symlinked for you
74
+ }
75
75
  }
76
76
  ```
77
77
 
@@ -83,32 +83,18 @@ Copy [`.agents/local.example.json`](../local.example.json) → `.agents/local.js
83
83
  - **Disabled agents** are enforced at **install** time (codex/hermes injection is
84
84
  skipped, and removed if previously installed). `CAF_HOOKS_SKIP_CODEX=1` is an
85
85
  env shortcut for `disabled_agents:["codex"]`.
86
- - **Disabled skills** are honored by the skill fan-out (below).
86
+ ## Skill fan-out `.agents/skills.json` each CLI
87
87
 
88
- ## Skill fan-out — `.agents/skills` each CLI
88
+ `.agents/skills.json` is the project's skill manifest. `sync-skills.py --scope
89
+ project` resolves that manifest into project-local CLI dirs (for example
90
+ `./.codex/skills`, `./.hermes/skills`, `./.kimi-code/skills`) and honors
91
+ `inherit_global: true` so a dev's global `~/.agents/skills.json` loadout is
92
+ merged in automatically.
89
93
 
90
- `.agents/skills/` is the project's enabled skill set (committed inherited skills +
91
- `./skills/*` app skills symlinked in on enter). `link-project-skills-to-clis.sh`
92
- fans it out as per-skill symlinks to every agent CLI that doesn't read `.agents/`
93
- natively a small `SKILL_TARGETS` table:
94
-
95
- | CLI | target dir | scope |
96
- |---|---|---|
97
- | Codex | `~/.codex/skills` | **global** — link ours in, never clobber foreign entries, removed on `leave` |
98
- | Kimi | `./.kimi-code/skills` | **local** — a project-scoped mirror we fully manage (stale real copies replaced with symlinks) |
99
-
100
- Add a CLI = one `dir|scope` line. Per-dev control via `.agents/local.json`
101
- `skills`:
102
-
103
- - `disabled: [...]` — never link these; pruned from managed mirrors.
104
- - `defer_to_global: true` — **only if you run a global `~/.agents/skills` system.**
105
- Skips any project skill whose name also exists there, so your global copy wins
106
- and you get **zero duplicates** (it even yields the slot in `~/.codex/skills`).
107
- Teammates with no global layer leave it `false` (default) and **inherit the full
108
- set**. Override the global dir with `AGENTS_GLOBAL_SKILLS_DIR`.
109
-
110
- Tasks: `mise run skills-relink` (re-fan), `mise run link-project-skills-to-clis`,
111
- `mise run unlink-project-skills-from-clis`.
94
+ - Project-local skills with the same name shadow the global ones.
95
+ - No `defer_to_global` flag or bash cleanup scripts are needed anymore.
96
+ - The manifest is the only hand-edited skill SSOT; run `mise run skills-sync`
97
+ after changing it, or just re-enter the repo.
112
98
 
113
99
  ## Hindsight credentials — `mise run hindsight-setup`
114
100
 
@@ -137,6 +123,7 @@ Without a key the hooks **no-op gracefully** — recall/retain just do nothing.
137
123
  ```
138
124
  .agents/
139
125
  local.example.json # per-dev override template (copy -> local.json, gitignored)
126
+ skills.json # project skill manifest (SSOT for sync-skills.py)
140
127
  hooks/
141
128
  hooks.master.json # SSOT — the only hand-edited config
142
129
  sync.py # fan-out engine (--install / --uninstall / --check)
@@ -4,10 +4,9 @@
4
4
  # Source this, then call:
5
5
  # hook_disabled <hook-id> # true if .hooks.disabled[] contains the id
6
6
  # agent_disabled <agent> # true if .hooks.disabled_agents[] contains the agent
7
- # skill_disabled <skill-name> # true if .skills.disabled[] contains the name
8
7
  #
9
8
  # All FAIL OPEN (return 1 = "not disabled") when `.agents/local.json` or `jq` is
10
- # absent, so a missing/garbled local config can never silently kill hooks/skills.
9
+ # absent, so a missing/garbled local config can never silently kill hooks.
11
10
  #
12
11
  # `.agents/local.json` is gitignored — each dev owns their own. See
13
12
  # `.agents/local.example.json` for the schema.
@@ -30,22 +29,13 @@ _caf_listed() {
30
29
 
31
30
  hook_disabled() { _caf_listed '.hooks.disabled' "$1"; }
32
31
  agent_disabled() { _caf_listed '.hooks.disabled_agents' "$1"; }
33
- skill_disabled() { _caf_listed '.skills.disabled' "$1"; }
34
32
 
35
- # True if the dev opted to let their GLOBAL agent system (~/.agents/skills) provide
36
- # any overlapping skill, so the project linker should skip it (no duplicates).
37
- # Teammates with no global layer omit this and inherit the full project set.
33
+ # True if the dev opted to let their GLOBAL agent system (~/.agents/hooks) provide
34
+ # the hook layer, so project-scoped per-user injections should be suppressed.
38
35
  skills_defer_to_global() {
39
36
  local f
40
37
  f="$(_caf_local_json)" || return 1
41
38
  [[ -f "$f" ]] || return 1
42
39
  command -v jq >/dev/null 2>&1 || return 1
43
- jq -e '.skills.defer_to_global == true' "$f" >/dev/null 2>&1
44
- }
45
-
46
- # True if <skill-name> is provided by the global agent SSOT. `-e` follows the
47
- # ~/.agents/skills symlink (-> skillex/skill-sets/global) and its entries.
48
- skill_is_global() {
49
- local name="$1" gdir="${AGENTS_GLOBAL_SKILLS_DIR:-$HOME/.agents/skills}"
50
- [[ -e "${gdir}/${name}" ]]
40
+ jq -e '.hooks.defer_to_global == true' "$f" >/dev/null 2>&1
51
41
  }
@@ -72,10 +72,10 @@ def load_local() -> dict:
72
72
  (so even Claude's committed hooks honor them); this only needs disabled
73
73
  AGENTS, which gate install. CAF_HOOKS_SKIP_CODEX=1 is an env shortcut.
74
74
 
75
- `defer_to_global` (under either `hooks` or `skills`) means "I already run
76
- these hooks from a global agent system" — so the shared per-user injections
77
- (codex/kimi/hermes) are suppressed and actively removed. Claude's committed
78
- repo settings are harmless and left in place.
75
+ `hooks.defer_to_global` means "I already run these hooks from a global agent
76
+ system" — so the shared per-user injections (codex/kimi/hermes) are
77
+ suppressed and actively removed. Claude's committed repo settings are
78
+ harmless and left in place.
79
79
  """
80
80
  disabled_agents: set[str] = set()
81
81
  defer_to_global = False
@@ -84,9 +84,8 @@ def load_local() -> dict:
84
84
  try:
85
85
  data = json.loads(p.read_text() or "{}")
86
86
  hooks_cfg = data.get("hooks") or {}
87
- skills_cfg = data.get("skills") or {}
88
87
  disabled_agents = set(hooks_cfg.get("disabled_agents") or [])
89
- defer_to_global = bool(hooks_cfg.get("defer_to_global")) or bool(skills_cfg.get("defer_to_global"))
88
+ defer_to_global = bool(hooks_cfg.get("defer_to_global"))
90
89
  except (json.JSONDecodeError, OSError) as exc:
91
90
  warn(f"ignoring malformed .agents/local.json: {exc}")
92
91
  if os.environ.get("CAF_HOOKS_SKIP_CODEX") == "1":
@@ -1,17 +1,11 @@
1
1
  {
2
- "$comment": "PER-DEV LOCAL OVERRIDES — copy this file to .agents/local.json (gitignored) and edit. Lets you opt out of individual hooks, whole agents, or specific auto-linked skills, and tune how project skills fan out to your agent CLIs — without touching committed config. Hooks self-skip at RUNTIME via .agents/hooks/lib/hook-guard.sh (so even Claude's committed hooks honor it); codex/hermes injections additionally skip at INSTALL time. Re-run `mise run hooks-sync` / `mise run skills-relink` (or re-cd into the repo) after editing.",
2
+ "$comment": "PER-DEV LOCAL OVERRIDES — copy this file to .agents/local.json (gitignored) and edit. Lets you opt out of individual hooks or whole injected agents without touching committed config. Hooks self-skip at RUNTIME via .agents/hooks/lib/hook-guard.sh (so even Claude's committed hooks honor it); codex/hermes injections additionally skip at INSTALL time. Re-run `mise run hooks-sync` (or re-cd into the repo) after editing.",
3
3
  "hooks": {
4
4
  "$disabled_help": "Hook ids from .agents/hooks/hooks.master.json: skill-check-reminder, hindsight-recall, hindsight-retain, hindsight-session-end. Listed ids are skipped at runtime across ALL agents.",
5
5
  "disabled": [],
6
6
  "$disabled_agents_help": "Agent keys: claude, codex, kimi, hermes. Listed agents are NOT installed (codex/kimi/hermes injections are skipped/removed). Claude's hooks are committed and can't be fully removed per-dev — disable them individually via `disabled` instead.",
7
7
  "disabled_agents": [],
8
- "$defer_to_global_help": "Set true if you already run these hooks from a GLOBAL agent system (~/.agents/hooks). When true, sync.py suppresses AND removes the shared per-user injections (codex/kimi/hermes) so a project never double-injects into ~/.codex / ~/.kimi-code; Claude's committed repo settings are left in place. Implied by skills.defer_to_global. On a machine with a global install, pjangler skips scaffolding this whole layer, so you rarely need to set this by hand.",
9
- "defer_to_global": false
10
- },
11
- "skills": {
12
- "$disabled_help": "Skill directory names under .agents/skills/. Listed skills are NOT symlinked into any agent CLI dir for you, and are pruned from your managed mirrors.",
13
- "disabled": [],
14
- "$defer_to_global_help": "Set true ONLY if you run a global agent system at ~/.agents/skills. When true, the linker skips any project skill whose name also exists in your global SSOT, so your global copy wins and you get ZERO duplicates/conflicts (and yields the slot in shared dirs like ~/.codex/skills). Teammates with no global layer leave this false (the default) and inherit the FULL project skill set. Override the global dir with the AGENTS_GLOBAL_SKILLS_DIR env var.",
8
+ "$defer_to_global_help": "Set true if you already run these hooks from a GLOBAL agent system (~/.agents/hooks). When true, sync.py suppresses AND removes the shared per-user injections (codex/kimi/hermes) so a project never double-injects into ~/.codex / ~/.kimi-code; Claude's committed repo settings are left in place. On a machine with a global install, pjangler skips scaffolding this whole layer, so you rarely need to set this by hand.",
15
9
  "defer_to_global": false
16
10
  }
17
11
  }
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/skillex/schemas/main/skills.schema.json",
3
+ "inherit_global": true,
4
+ "registry": "https://github.com/delorenj/skillex.git",
5
+ "skills": []
6
+ }
@@ -1,5 +1,8 @@
1
- {% set ticket_board_id = board_id | default(plane_project_id) -%}
2
- {% set workspace = ticket_workspace | default(plane_workspace if ticket_provider == 'plane' else '') -%}
1
+ {% if ticket_provider == 'plane' and plane_project_id -%}
2
+ {% set board_url = plane_base ~ '/' ~ plane_workspace ~ '/projects/' ~ plane_project_id ~ '/issues/' -%}
3
+ {% else -%}
4
+ {% set board_url = '' -%}
5
+ {% endif -%}
3
6
  {
4
7
  "project_name": {{ project_name | tojson }},
5
8
  "project_description": {{ project_description | tojson }},
@@ -7,17 +10,10 @@
7
10
  "repo_path": "",
8
11
  "ticket_provider": {
9
12
  "type": {{ ticket_provider | tojson }},
10
- "workspace": {{ workspace | tojson }},
13
+ "workspace": {{ plane_workspace | tojson }},
11
14
  "identifier": {{ project_identifier | tojson }},
12
- "board_id": {{ ticket_board_id | tojson }},
13
- "state": {{ ("linked" if ticket_board_id else "planned") | tojson }}
15
+ "board_id": {{ plane_project_id | tojson }},
16
+ "board_url": {{ board_url | tojson }}
14
17
  },
15
- "agents": {},
16
- "automation": {
17
- "reconcile": {
18
- "enabled": false,
19
- "grace_hours": 0,
20
- "auto_review": true
21
- }
22
- }
18
+ "agents": {}
23
19
  }
@@ -20,23 +20,24 @@ script = "'{% raw %}{{config_root}}{% endraw %}/.mise/scripts/link-agentfiles.sh
20
20
  [[hooks.enter]]
21
21
  script = "op inject -i .env.op > .env"
22
22
  [[hooks.enter]]
23
+ script = "sync-skills.py --scope project"
24
+ [[hooks.enter]]
23
25
  script = "[ -f '{% raw %}{{config_root}}{% endraw %}/.mise/scripts/codegraph.sh' ] && '{% raw %}{{config_root}}{% endraw %}/.mise/scripts/codegraph.sh' || true"
24
26
  {%- if agent_hooks_layer %}
25
27
  [[hooks.enter]]
26
- script = "'{% raw %}{{config_root}}{% endraw %}/.mise/scripts/link-project-skills-to-clis.sh'"
27
- [[hooks.enter]]
28
28
  script = "'{% raw %}{{config_root}}{% endraw %}/.agents/hooks/sync.py' --install --quiet"
29
29
  {%- endif %}
30
30
  {%- if agent_hooks_layer %}
31
31
  [[hooks.leave]]
32
- script = "'{% raw %}{{config_root}}{% endraw %}/.mise/scripts/unlink-project-skills-from-clis.sh'"
33
- [[hooks.leave]]
34
32
  script = "'{% raw %}{{config_root}}{% endraw %}/.agents/hooks/sync.py' --uninstall --quiet"
35
33
  {%- endif %}
36
34
 
37
35
  [[watch_files]]
38
36
  patterns = ["AGENTS.md"]
39
37
  task = "link-agentfiles"
38
+ [[watch_files]]
39
+ patterns = [".agents/skills.json"]
40
+ task = "skills-sync"
40
41
  {% if agent_hooks_layer %}
41
42
  # Re-fan-out the agent hooks whenever the single source of truth changes.
42
43
  [[watch_files]]
@@ -46,6 +47,10 @@ task = "hooks-sync"
46
47
  [tasks.link-agentfiles]
47
48
  description = "Symlink all agent files to AGENTS.md"
48
49
  run = "'{% raw %}{{config_root}}{% endraw %}/.mise/scripts/link-agentfiles.sh'"
50
+
51
+ [tasks.skills-sync]
52
+ description = "Sync skills from manifest to local CLI dirs"
53
+ run = "sync-skills.py --scope project"
49
54
  {% if agent_hooks_layer %}
50
55
  # --- Project-scoped agent hooks + skill fan-out (see .agents/hooks/README.md) ---
51
56
 
@@ -60,18 +65,6 @@ run = "'{% raw %}{{config_root}}{% endraw %}/.agents/hooks/sync.py' --check"
60
65
  [tasks.hooks-uninstall]
61
66
  description = "Remove per-user agent-hook injections (codex/kimi/hermes)"
62
67
  run = "'{% raw %}{{config_root}}{% endraw %}/.agents/hooks/sync.py' --uninstall"
63
-
64
- [tasks.link-project-skills-to-clis]
65
- description = "Fan .agents/skills out to each agent CLI; honors local.json defer_to_global + disabled"
66
- run = "'{% raw %}{{config_root}}{% endraw %}/.mise/scripts/link-project-skills-to-clis.sh'"
67
-
68
- [tasks.unlink-project-skills-from-clis]
69
- description = "Remove project skill symlinks from shared per-CLI dirs"
70
- run = "'{% raw %}{{config_root}}{% endraw %}/.mise/scripts/unlink-project-skills-from-clis.sh'"
71
-
72
- [tasks.skills-relink]
73
- description = "Re-fan the project skill set to all CLIs"
74
- run = "'{% raw %}{{config_root}}{% endraw %}/.mise/scripts/link-project-skills-to-clis.sh'"
75
68
  {% endif %}
76
69
  [tasks.hindsight-setup]
77
70
  description = "Provision this dev's shared project Hindsight key from 1Password into .env (op inject)"
@@ -11,7 +11,7 @@ memory/state checkpointing.
11
11
  | What it scaffolds | A new top-level project | An agent role inside an existing project |
12
12
  | Copier target | `./my-new-project/` | `./agents/hermes/<role>/` |
13
13
  | Asks | project name + description | role + purpose + tone |
14
- | Post-gen artifacts | Ticket board binding, bmad init, mise tasks | Ticket board binding, Telegram bot wiring, agent-hm-* runtime repo, systemd units |
14
+ | Post-gen artifacts | Plane project, bmad init, mise tasks | Plane project, Telegram bot wiring, agent-hm-* runtime repo, systemd units |
15
15
 
16
16
  CommonProject runs first to create the umbrella project. hermes-agent-template
17
17
  runs second (for each agent role you want) to drop agents into it.
@@ -20,8 +20,8 @@ runs second (for each agent role you want) to drop agents into it.
20
20
 
21
21
  The template provisions a single Hermes role per invocation. The `pm` role
22
22
  handles project management and triage, and also runs the continuous ticket
23
- sentinel out-of-band: a provider-agnostic board-reconciliation pass (Plane or
24
- Trello) with an autonomous adversarial review (act, do not wait). The
23
+ sentinel out-of-band: a provider-agnostic board-reconciliation pass (Linear,
24
+ Plane, or Trello) with an autonomous adversarial review (act, do not wait). The
25
25
  sentinel runs as the PM's **heartbeat** — a fused systemd timer tick that does
26
26
  the reconciliation pass and then a gated runtime checkpoint. (There is no
27
27
  separate `scrum-master` role; its duties folded into the PM heartbeat.)
@@ -47,7 +47,7 @@ The template will:
47
47
  5. Populate it with the runtime scaffold (config.yaml, SOUL.md, memories, consumer.py)
48
48
  6. Add it as a git submodule at `agents/hermes/pm/runtime/` (== HERMES_HOME)
49
49
  7. Prompt for a BotFather token, store it in `runtime/.env`
50
- 8. Create or bind the configured ticket board
50
+ 8. Create a Plane project in your configured workspace
51
51
  9. Install systemd `--user` units: gateway, consumer, heartbeat timer (reconcile + checkpoint)
52
52
  10. Append the agent to `~/.hermes/agents-registry.yaml`
53
53
 
@@ -68,7 +68,7 @@ you to review it. Keys:
68
68
  | `fleet` | `fleet_env`, `registry_file` | Fleet source-of-truth + registry locations |
69
69
  | `fleet` | `oauth_file`, `codex_home` | Shared Hermes OAuth store + Codex CLI/app-server auth home |
70
70
  | `fleet` | `runtime_scaffold_dir` | Fallback scaffold (if agent-local one is missing) |
71
- | `fleet` | `canonical_skills_dir`, `pm_external_skill_dirs`, `symlinked_runtime_skills` | Shared runtime skills plus PM external skill libraries |
71
+ | `fleet` | `canonical_skills_dir`, `symlinked_runtime_skills` | Skills mirrored into each profile |
72
72
  | `github` | `runtime_repo_owner` | Owner of the `agent-hm-*` runtime repos |
73
73
  | `plane` | `base`, `workspace` | Plane URL + workspace slug |
74
74
  | `bloodbank` | `nats_host`, `nats_port`, `compose_dir` | NATS endpoint + compose dir hint |
@@ -138,14 +138,14 @@ to force re-run:
138
138
 
139
139
  ```bash
140
140
  cd agents/hermes/pm
141
- rm .scripts/.done-42-ticket-provider
142
- SKIP_SYSTEMD=1 ./.scripts/42-ticket-provider.sh
141
+ rm .scripts/.done-40-plane
142
+ SKIP_TELEGRAM=1 SKIP_SYSTEMD=1 ./.scripts/40-plane.sh
143
143
  ```
144
144
 
145
145
  ## Retiring an agent
146
146
 
147
147
  (TODO: ship `retire.sh` in v1.1)
148
148
 
149
- Manual: stop systemd units, `hermes profile delete`, archive the ticket board
150
- if the provider supports archival, `/deletebot` in BotFather, archive the
149
+ Manual: stop systemd units, `hermes profile delete`, archive the Plane
150
+ project, `/deletebot` in BotFather, archive the
151
151
  `agent-hm-*` runtime repo, remove the registry entry.
@@ -1,66 +1 @@
1
- # hermes-agent-template — distributable configuration
2
- #
3
- # This file holds every environment-specific default the provisioning scripts
4
- # need. Copy it to ~/.config/hermes-agent-template/config.toml (the first
5
- # provisioning run does this for you) and edit the values for YOUR machine.
6
- #
7
- # Resolution precedence for each value (highest wins):
8
- # explicit env var > ~/.hermes/fleet.env > this file > built-in fallback
9
- #
10
- # Override the config location with $HERMES_TEMPLATE_CONFIG if you keep it
11
- # somewhere other than $XDG_CONFIG_HOME/hermes-agent-template/config.toml.
12
-
13
- [fleet]
14
- # Absolute path to the shared Hermes executable every agent launcher execs.
15
- hermes_bin = "~/.hermes/hermes-agent/.venv/bin/hermes"
16
- # Checkout of the hermes-agent repo. Its .venv is used to install nats-py, etc.
17
- hermes_repo = "~/.hermes/hermes-agent"
18
- # Fallback runtime scaffold dir — used only when an agent's vendored
19
- # ./.runtime-scaffold is missing.
20
- runtime_scaffold_dir = "~/code/hermes-agent-template/runtime-scaffold"
21
- # Shared fleet source-of-truth env file + fleet registry. ~ is expanded.
22
- fleet_env = "~/.hermes/fleet.env"
23
- registry_file = "~/.hermes/agents-registry.yaml"
24
- # Shared auth roots. HERMES_OAUTH_FILE is the Hermes provider OAuth store
25
- # (including openai-codex); CODEX_HOME is for Codex CLI/app-server auth.
26
- oauth_file = "~/.hermes/auth.json"
27
- codex_home = "~/.codex"
28
- # Canonical external skills dir mirrored into each agent profile.
29
- canonical_skills_dir = "/home/delorenj/.agents/skills"
30
- # PM-only external skill libraries surfaced alongside Hermes built-ins.
31
- pm_external_skill_dirs = [
32
- "~/code/skillex/skill-sets/global/.system",
33
- "~/code/skillex/packs/bmad/6.10.2",
34
- ]
35
- # Voxxy plugin + service defaults for Hermes PM agents.
36
- voxxy_plugin_dir = "~/code/voxxy/plugins/tts/voxxy"
37
- vox_url = "https://vox.delo.sh"
38
- # Canonical PM config.yaml the provisioner seeds each runtime's config.yaml from.
39
- # This is the fleet-wide Hermes inference/skill config (model, toolsets, etc.)
40
- # every agent runtime starts from. ~ is expanded.
41
- # canonical_pm_config = "~/.hermes/config.yaml"
42
- # Skills symlinked into every agent's runtime skills root.
43
- symlinked_runtime_skills = [
44
- "delonet-conventions",
45
- "delonet-dotenv",
46
- "hermes-pm-template-maintenance",
47
- "hindsight",
48
- "33god-projects",
49
- "subagent-driven-development",
50
- ]
51
-
52
- [github]
53
- # Owner of the per-agent runtime repos (creates <owner>/agent-hm-<repo>-<role>).
54
- runtime_repo_owner = "delorenj"
55
-
56
- [plane]
57
- # Plane instance base URL and workspace slug (one project per agent).
58
- base = "https://plane.delo.sh"
59
- workspace = "33god"
60
-
61
- [bloodbank]
62
- # NATS endpoint the consumer connects to.
63
- nats_host = "127.0.0.1"
64
- nats_port = 4222
65
- # Where the bloodbank docker compose lives (used only for a hint message). ~ ok.
66
- compose_dir = "~/code/33GOD/bloodbank"
1
+ template/.scripts/config.example.toml
@@ -12,13 +12,13 @@
12
12
  # 1. Renders agents/hermes/<role>/{role.yaml, SOUL.md, hermes, .scripts/}
13
13
  # 2. Creates a per-agent runtime repo gh:delorenj/agent-hm-<repo>-<role>
14
14
  # 3. Submodules it at agents/hermes/<role>/runtime/ (this is HERMES_HOME)
15
- # 4. Creates or binds the configured ticket board
15
+ # 4. Creates a Plane project in 33god workspace
16
16
  # 6. Prompts for a BotFather Telegram token, stores in runtime/.env
17
17
  # 7. Installs a Bloodbank consumer + heartbeat timer (reconcile + checkpoint)
18
18
  # 8. Appends to ~/.hermes/agents-registry.yaml
19
19
  #
20
- # Environment-specific defaults (Hermes binary path, runtime repo owner, ticket
21
- # provider defaults, NATS endpoint, skills dir, …) are NOT hardcoded — they live in
20
+ # Environment-specific defaults (Hermes binary path, runtime repo owner, Plane
21
+ # workspace/URL, NATS endpoint, skills dir, …) are NOT hardcoded — they live in
22
22
  # ~/.config/hermes-agent-template/config.toml, seeded from config.example.toml on
23
23
  # first run. Edit that file to retarget the template for a different machine/user.
24
24
 
@@ -59,6 +59,7 @@ ticket_provider:
59
59
  help: "Ticket system this agent's board lives in"
60
60
  default: plane
61
61
  choices:
62
+ "Linear": linear
62
63
  "Plane": plane
63
64
  "Trello": trello
64
65