ace-handbook 0.31.0 → 0.32.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 837b9e5d5edbfab28596d94f6e53d3b0a4641451cf43da81899591736a3352ed
4
- data.tar.gz: d99a5b04b59b095d60575aac6adc4a7e7bf8009071b0fe42ba7bdb6d342bee3c
3
+ metadata.gz: 1e70a71fff6e61cbaa6724591eb965481b3b92153aed554c5fd6ad8b51f3e01e
4
+ data.tar.gz: 99db5b08f74300bfbc87b71f2621a0bcee70934d92a89d7a142884adb8a2e434
5
5
  SHA512:
6
- metadata.gz: 90654bdde3d8faec5d009c9991091db20aa2f7033615959f82aeec5d7fdaab8a3a38865468f4b5d16c5f3331bbc582ab1fd9ead74f97fba798589cef9ab9a1d1
7
- data.tar.gz: fc9dad5f9987207a3ff1c00782df8a4a663d8cba5296d06c3125ce8e0ab17d83506ba06186f91ae1d34f0033c3ef3362cfb80f927d67153edf6b01a5ca9d55b2
6
+ metadata.gz: 34c7119d2d01e4ad2ff58a6b91bc5b4b092fb42a575abc33319d3ee6f4c7e237058f1135c19ab560565ff3fa91a696e39a9b92e27b6b839f855de0bf20de0c64
7
+ data.tar.gz: c1b28858004960a5ef381e50bde467910b6694bfb29179e7cdd7014d45d25a2eb47ece9d75996cef92e35c30892705ca7687a6340f169b86e07781a287f8e499
data/CHANGELOG.md CHANGED
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.32.0] - 2026-09-27
11
+
12
+ ### Added
13
+ - Provider manifests may declare an optional `prompts_dir`; `ace-handbook sync` now projects canonical prompt templates from `ace-handbook-integration-*` packages (`handbook/prompts/*.md`) into that directory (idempotent writes; prunes only stale files carrying an `ace-handbook-integration-*` provenance marker, leaving user-authored templates untouched). Sync summary output gains a `; prompts <dir> (N prompts, U updated, R removed)` segment when a provider projects prompts.
14
+ - Shipped `wfi://handbook/manage-overrides` (with paired skill `as-handbook-manage-overrides`): canonical override documentation covering all overridable surfaces (workflows, cookbooks, guides), the source-priority resolution rule, skills' non-overridability ("override the workflow the skill calls"), the `.agents/` sync-projection status, and planted-competitor verification probes.
15
+
16
+ ### Changed
17
+ - Tightened `wfi://release/rubygems-publish` baseline guidance with an OTP-burst publication contract (build all artifacts before OTP; push in ≤5 concurrent waves; defer metadata verification) for project overlays.
18
+
19
+
20
+ ## [0.31.1] - 2026-09-02
21
+
22
+ ### Technical
23
+ - Included in the coordinated all-package patch release preparation for ACE monorepo review.
10
24
  ## [0.31.0] - 2026-08-12
11
25
 
12
26
  ### Added
data/README.md CHANGED
@@ -36,7 +36,7 @@
36
36
 
37
37
  **Integrate with resource discovery and context loading** - pair with [ace-nav](../ace-support-nav) for workflow and resource discovery, [ace-bundle](../ace-bundle) for loading complete workflow instructions, and provider integrations (`ace-handbook-integration-*`) that can project canonical handbook skills into harness-native folders on request.
38
38
 
39
- **Sync and inspect skill projections** - run `ace-handbook sync` to project canonical skills into `.agents/skills/` by default, or use `ace-handbook sync --provider PROVIDER` for harness-native folders such as `.codex/skills/`.
39
+ **Sync and inspect skill projections** - run `ace-handbook sync` to project canonical skills into `.agents/skills/` by default, or use `ace-handbook sync --provider PROVIDER` for harness-native folders such as `.codex/skills/`. Providers whose manifest also declares `prompts_dir` additionally receive canonical prompt templates from the matching integration package's `handbook/prompts/*.md` (projected flat into that directory; user-authored templates without an `ace-handbook-integration-*` provenance marker are never pruned).
40
40
 
41
41
  **Extend handbook content in normal projects** - put project-specific workflows, guides, cookbooks, templates, and skills under `.ace-handbook/` and discover them with protocol URLs (`wfi://`, `guide://`, `cookbook://`, `tmpl://`, `skill://`). See [Usage Guide](docs/usage.md) for path conventions.
42
42
 
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: as-handbook-manage-overrides
3
+ description: Manage project-local overrides of ace-managed docs (workflows, cookbooks, guides) and verify resolution precedence
4
+ # bundle: wfi://handbook/manage-overrides
5
+ # context: no-fork
6
+ # agent: general-purpose
7
+ user-invocable: true
8
+ allowed-tools:
9
+ - Bash(ace-nav:*)
10
+ - Bash(ace-handbook:*)
11
+ - Bash(ace-bundle:*)
12
+ - Read
13
+ - Write
14
+ - Edit
15
+ - Glob
16
+ - LS
17
+ argument-hint: "[surface|uri] [action: plan|verify]"
18
+ last_modified: 2026-09-20
19
+ source: ace-handbook
20
+ integration:
21
+ targets:
22
+ - claude
23
+ - codex
24
+ - gemini
25
+ - opencode
26
+ - pi
27
+ providers: {}
28
+ skill:
29
+ kind: workflow
30
+ execution:
31
+ workflow: wfi://handbook/manage-overrides
32
+ ---
33
+
34
+ Load and run `ace-bundle wfi://handbook/manage-overrides` in the current project, then follow the loaded workflow as the source of truth and execute it end-to-end instead of only summarizing it.
@@ -10,7 +10,7 @@ allowed-tools:
10
10
  - Bash(ace-bundle:*)
11
11
  - Read
12
12
  argument-hint: "[gem-name...] [--dry-run]"
13
- last_modified: 2026-04-10
13
+ last_modified: 2026-08-12
14
14
  source: ace-handbook
15
15
  integration:
16
16
  targets:
@@ -46,5 +46,6 @@ None
46
46
  - Follow the workflow as the source of truth.
47
47
  - Do the work described by the workflow instead of only summarizing it.
48
48
  - When the workflow requires edits, tests, or commits, perform them in this project.
49
+ - OTP timing: build every pending `.gem` first, then ask for one OTP (~30–45s), then push in ≤5-concurrent dependency waves aiming to finish within ~30s; verify metadata only after the burst.
49
50
 
50
51
  - After live publishing, recommend running `ace-test-e2e ace-monorepo-e2e TS-MONO-001` for installation verification.
@@ -60,6 +60,7 @@ Brief description of what this cookbook accomplishes.
60
60
 
61
61
  - Package-owned cookbooks (inside `ace-handbook`): `ace-handbook/handbook/cookbooks/`
62
62
  - Project-local cookbooks (outside package scope): `.ace-handbook/cookbooks/`
63
+ - Project-local document overrides (workflows, cookbooks, guides) and resolution precedence are governed canonically by `wfi://handbook/manage-overrides`
63
64
 
64
65
  ## Process Steps
65
66
 
@@ -0,0 +1,142 @@
1
+ ---
2
+ doc-type: workflow
3
+ name: manage-overrides
4
+ title: Manage Project-Local Overrides
5
+ description: Canonical reference for ace-managed document override surfaces, resolution precedence, and verification probes
6
+ purpose: Documentation for ace-handbook/handbook/workflow-instructions/handbook/manage-overrides.wf.md
7
+ allowed-tools:
8
+ - Bash(ace-nav:*)
9
+ - Bash(ace-bundle:*)
10
+ - Bash(ace-handbook:*)
11
+ - Read
12
+ - Write
13
+ - Edit
14
+ ace-docs:
15
+ last-updated: 2026-09-20
16
+ last-checked: 2026-09-20
17
+ ---
18
+
19
+ # Manage Project-Local Overrides
20
+
21
+ ## Goal
22
+
23
+ Answer, from shipped documentation and tooling, the three override questions: which ace-managed documents can this
24
+ project override, where do the project-local files live, and who wins when a project-local file and a gem file share
25
+ the same name. This workflow is the canonical reference; `ace-nav resolve --why` makes the same precedence observable
26
+ per URI.
27
+
28
+ ## Prerequisites
29
+
30
+ - Understanding of handbook asset conventions (workflows, cookbooks, guides, skills)
31
+ - `ace-nav` and `ace-handbook` available in the project bundle
32
+
33
+ ## Override Surfaces
34
+
35
+ | Surface | Protocol | Project-local location | Overridable? | Winner of duplicates (shipped defaults) |
36
+ |---------|----------|------------------------|--------------|------------------------------------------|
37
+ | Workflows | `wfi://` | `.ace-handbook/workflow-instructions/<ns>/<name>.wf.md` | Yes | Project-local, when its source priority is lower than the gem source's (e.g. the shipped `local` source at priority 5 vs gem at 10) |
38
+ | Cookbooks | `cookbook://` | `.ace-handbook/cookbooks/<name>.cookbook.md` | Yes | The candidate from the source with the lower priority number. The shipped project-local default registers priority 20 and the gem source 10, so **the gem wins by default**; register a project cookbook source with priority < 10 to make project-local win |
39
+ | Guides | `guide://` | `handbook/guides/<name>.g.md` | Yes, with registration | Requires a project source registration (`.ace/nav/protocols/guide-sources/local.yml` → `handbook/guides`); with priority < 10 the project-local guide wins. Without registration the gem wins |
40
+ | Skills | `skill://` | none | **No** | Always the gem. Skills are thin layers; override the workflow the skill calls instead |
41
+ | `.agents/skills/` projection | — | written by `ace-handbook sync` | Never edit | Not a resolution source. Neutral `agents`-provider mirror regenerated (and stale entries pruned) on every sync; it does not ship |
42
+
43
+ ## Resolution Rule
44
+
45
+ Sources for a protocol are scanned in ascending priority order (lower number = higher precedence); the first source
46
+ containing the resource wins and remaining duplicates are ignored. Registration defaults: project sources 10, user
47
+ sources 50, gem defaults 100 — an individual source registration may pin another value (this is why the shipped
48
+ `local` workflow source at priority 5 beats the gem at 10, while the shipped project-local cookbook source at 20 loses
49
+ to the gem at 10).
50
+
51
+ To answer "why did this candidate win?" for a concrete URI without planting probe files:
52
+
53
+ ```bash
54
+ ace-nav resolve wfi://release/publish --why
55
+ # uri: wfi://release/publish
56
+ # rule: Candidates are tried in ascending source-priority order ...
57
+ # candidates (resolution order):
58
+ # 1. [priority 5] @local (project) → ... ← winner
59
+ # 2. [priority 10] @ace-handbook (gem-default) → ...
60
+ ```
61
+
62
+ ## Skills Are Not Overridable
63
+
64
+ Skills (`skill://`) are thin launch layers that load and execute a workflow; they carry no logic worth overriding.
65
+ Consequently no project-local skill source exists: competitors planted in `.ace-handbook/skills/` or `.agents/skills/`
66
+ are ignored and resolution always returns the gem file. The design rule is: **override the workflow the skill calls**
67
+ (under `.ace-handbook/workflow-instructions/`), not the skill. To change a skill's own wording, change the canonical
68
+ `handbook/skills/<name>/SKILL.md` in the owning gem and run `ace-handbook sync`.
69
+
70
+ ## Process Steps
71
+
72
+ 1. **Identify the surface** you need to override (workflow, cookbook, or guide) and its canonical gem path.
73
+ 2. **Create the project-local file** at the surface's project-local location (see table), keeping the canonical
74
+ frontmatter and structure so tools keep resolving it.
75
+ 3. **Ensure the source registration makes your file win:**
76
+ - workflows: keep `.ace/nav/protocols/wfi-sources/local.yml` priority below the gem source (shipped default: 5).
77
+ - cookbooks: register `.ace/nav/protocols/cookbook-sources/local.yml` with priority < 10 (the shipped
78
+ project-local default of 20 loses to the gem's 10).
79
+ - guides: register `.ace/nav/protocols/guide-sources/local.yml` pointing at `handbook/guides` with priority < 10;
80
+ without a registration the project-local file is never the winner.
81
+ 4. **Verify precedence with `ace-nav resolve <uri> --why`:** your file must be listed first and marked
82
+ `← winner`. For a list of all duplicate candidates use `ace-nav list '<protocol>://<name>'`.
83
+ 5. **Never override skills or the `.agents/` projection.** Point the skill at an overridden workflow, or edit the
84
+ canonical skill in its gem.
85
+ 6. **Document the override** in the project's own agent guidance so maintainers know the file shadows a gem asset and
86
+ should be re-checked on gem upgrades.
87
+
88
+ ## Success Criteria
89
+
90
+ - [ ] Each project-local override file lives at the canonical project-local path for its surface.
91
+ - [ ] `ace-nav resolve <uri> --why` shows the project-local candidate first, marked `← winner` (or, for skills, shows
92
+ the gem candidate only).
93
+ - [ ] No files were planted in `.ace-handbook/skills/` or `.agents/skills/` expecting resolution changes.
94
+ - [ ] `ace-handbook sync` runs clean after any canonical skill change.
95
+
96
+ ## Validation Commands
97
+
98
+ ```bash
99
+ ace-bundle wfi://handbook/manage-overrides
100
+ ace-nav resolve wfi://release/publish --why
101
+ ace-nav list 'cookbook://setup-starting-a-multi-ruby-gem-monorepo-with-ace'
102
+ ace-handbook sync
103
+ ```
104
+
105
+ ## Planted-Competitor Verification Probes
106
+
107
+ Executed against ace-handbook 0.31.1 / ace-support-nav 0.28.6 (2026-09-20). Plant a project-local competitor file,
108
+ run the resolve, inspect the first line of the winner, then remove the plant:
109
+
110
+ ```bash
111
+ # Workflows: project-local WINS
112
+ mkdir -p .ace-handbook/workflow-instructions/handbook
113
+ printf '# OVERRIDE\n' > .ace-handbook/workflow-instructions/handbook/manage-cookbooks.wf.md
114
+ ace-nav resolve wfi://handbook/manage-cookbooks # → .ace-handbook/... (project-local)
115
+ rm .ace-handbook/workflow-instructions/handbook/manage-cookbooks.wf.md
116
+
117
+ # Cookbooks: with shipped defaults the GEM wins (project-local source priority 20 > gem 10)
118
+ mkdir -p .ace-handbook/cookbooks
119
+ printf '# OVERRIDE\n' > .ace-handbook/cookbooks/setup-starting-a-multi-ruby-gem-monorepo-with-ace.cookbook.md
120
+ ace-nav resolve cookbook://setup-starting-a-multi-ruby-gem-monorepo-with-ace # → ace-handbook/handbook/... (gem)
121
+ ace-nav list 'cookbook://setup-starting-a-multi-ruby-gem-monorepo-with-ace' # both candidates listed
122
+ rm .ace-handbook/cookbooks/setup-starting-a-multi-ruby-gem-monorepo-with-ace.cookbook.md
123
+
124
+ # Skills: gem WINS even with competitors in both overlay locations
125
+ mkdir -p .ace-handbook/skills/as-review-pr .agents/skills/as-review-pr
126
+ printf '# OVERRIDE\n' > .ace-handbook/skills/as-review-pr/SKILL.md
127
+ printf '# OVERRIDE\n' > .agents/skills/as-review-pr/SKILL.md
128
+ ace-nav resolve skill://as-review-pr # → ace-review/handbook/skills/... (gem)
129
+ rm .ace-handbook/skills/as-review-pr/SKILL.md
130
+ rmdir .ace-handbook/skills/as-review-pr .ace-handbook/skills
131
+ ace-handbook sync # regenerates .agents/skills/ and prunes the plant
132
+ ```
133
+
134
+ ## Error Handling
135
+
136
+ - **Override silently loses:** inspect `ace-nav resolve <uri> --why`; the losing candidate's source priority is too
137
+ high — lower it in the source registration.
138
+ - **Guide override not discovered at all:** no project guide source is registered; add
139
+ `.ace/nav/protocols/guide-sources/local.yml` (see Process Step 3).
140
+ - **Skill edit appears to do nothing:** skills are not overridable; override the workflow the skill calls.
141
+ - **`.agents/skills/` edits keep disappearing:** expected — `ace-handbook sync` regenerates the projection and prunes
142
+ stale entries; edit canonical skills in the owning gem.
@@ -72,6 +72,7 @@ Publish plan (dry):
72
72
  echo "${GEM_HOST_API_KEY:+GEM_HOST_API_KEY is set}"
73
73
  ```
74
74
 
75
+ If unset, try loading tooling env (for example `mise env`) without printing secrets, then re-check.
75
76
  Do not print secret values.
76
77
 
77
78
  ### 4. Publication gate
@@ -87,11 +88,23 @@ Only if a higher-priority project workflow authorizes pushes **and** the operato
87
88
  real publish (not merely prep/dry-run) may `gem build` + `gem push` proceed under that
88
89
  project contract's steps and verification gates.
89
90
 
90
- Suggested placeholders for a project overlay (not executed by this baseline):
91
+ ### 5. OTP-burst contract for project overlays (do not run from this baseline)
92
+
93
+ When a project overlay authorizes live `gem push`, use this timing contract so one OTP can cover a large queue:
94
+
95
+ 1. Resolve credentials and the pending queue first
96
+ 2. **Build every pending `.gem` artifact** before asking for OTP
97
+ 3. Show the final queue (order, versions, artifact paths, count)
98
+ 4. Prompt once for OTP — codes are short-lived (**~30–45s**)
99
+ 5. Push immediately in dependency-respecting waves of **up to 5 concurrent** `gem push --otp` calls; aim to finish the burst in **≤30s**
100
+ 6. Verify RubyGems metadata **after** the burst (not between pushes)
101
+ 7. On expired/incorrect OTP: stop, list remaining gems, request a fresh OTP, resume without rebuilding unless an artifact is missing
102
+
103
+ Suggested overlay commands (executed only under a project publication contract):
91
104
 
92
105
  ```bash
93
- # gem build <gemspec>
94
- # gem push <built-gem> # only under project publication contract
106
+ # gem build <gemspec> # all pending artifacts first
107
+ # gem push <built-gem> --otp <OTP> # waves of ≤5 concurrent
95
108
  ```
96
109
 
97
110
  ## Override
@@ -105,8 +118,10 @@ WFI source). Keep URI `wfi://release/rubygems-publish` stable for the distribute
105
118
  - [ ] Ordered publish plan produced for requested gems
106
119
  - [ ] Credential presence reported without leaking secrets
107
120
  - [ ] No `gem push` / registry mutation from this baseline alone
121
+ - [ ] Overlay guidance documents build-all → OTP → ≤5 concurrent wave publish → deferred verify
108
122
 
109
123
  ## Common Issues
110
124
 
111
125
  - ACE monorepo operators may use a specialized overlay; plain projects must not depend on it.
112
126
  - Missing credentials should block publish contracts, not local planning.
127
+ - Asking for OTP before builds complete wastes the short OTP window on slow work.
@@ -34,6 +34,10 @@ module Ace
34
34
  manifest(provider).fetch("output_dir")
35
35
  end
36
36
 
37
+ def prompts_dir(provider)
38
+ manifest(provider)["prompts_dir"]
39
+ end
40
+
37
41
  def manifests
38
42
  @manifests ||= begin
39
43
  all_paths = local_manifest_paths + installed_manifest_paths
@@ -22,9 +22,11 @@ module Ace
22
22
  return if quiet
23
23
 
24
24
  results.each do |result|
25
- puts "synced #{result[:provider]} -> #{result[:relative_output_dir]} " \
26
- "(#{result[:projected_skills]} skills, #{result[:updated_files]} updated, " \
27
- "#{result[:removed_entries]} removed)"
25
+ line = "synced #{result[:provider]} -> #{result[:relative_output_dir]} " \
26
+ "(#{result[:projected_skills]} skills, #{result[:updated_files]} updated, " \
27
+ "#{result[:removed_entries]} removed)"
28
+ line += format_prompts_summary(result) if result.key?(:relative_prompts_dir)
29
+ puts line
28
30
  end
29
31
  print_inventory_summary(results)
30
32
  rescue => e
@@ -33,6 +35,12 @@ module Ace
33
35
 
34
36
  private
35
37
 
38
+ def format_prompts_summary(result)
39
+ "; prompts #{result[:relative_prompts_dir]} " \
40
+ "(#{result[:projected_prompts]} prompts, #{result[:updated_prompt_files]} updated, " \
41
+ "#{result[:removed_prompt_entries]} removed)"
42
+ end
43
+
36
44
  def print_inventory_summary(results)
37
45
  source_breakdown = results.first&.fetch(:source_breakdown, {})
38
46
  return if source_breakdown.nil? || source_breakdown.empty?
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ace
4
+ module Handbook
5
+ module Models
6
+ class PromptTemplate
7
+ attr_reader :provider, :source_path, :frontmatter, :content
8
+
9
+ def initialize(provider:, source_path:, frontmatter:, content:)
10
+ @provider = provider
11
+ @source_path = source_path
12
+ @frontmatter = frontmatter
13
+ @content = content
14
+ end
15
+
16
+ def name
17
+ File.basename(source_path, ".md")
18
+ end
19
+
20
+ def source
21
+ frontmatter["source"].to_s
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Ace
6
+ module Handbook
7
+ module Organisms
8
+ # Discovers canonical provider prompt templates shipped by integration packages.
9
+ #
10
+ # Each integration package owns its provider's prompt templates in
11
+ # handbook/prompts/*.md; they project into the provider manifest's prompts_dir.
12
+ class PromptTemplateInventory
13
+ FRONTMATTER_PATTERN = /\A---\s*\n(.*?)\n---\s*\n?(.*)\z/m
14
+ PACKAGE_GLOB = "ace-handbook-integration-*"
15
+ GEM_NAME_PREFIX = "ace-handbook-integration-"
16
+
17
+ attr_reader :project_root
18
+
19
+ def initialize(project_root: Ace::Handbook.project_root, gem_roots: nil)
20
+ @project_root = project_root
21
+ @gem_roots = gem_roots
22
+ end
23
+
24
+ def all
25
+ package_roots.filter_map { |root, provider| parse_templates(root, provider) }
26
+ .each_with_object({}) do |(provider, templates), index|
27
+ index[provider] = (index[provider] || []).concat(templates)
28
+ end
29
+ end
30
+
31
+ def for_provider(provider)
32
+ all.fetch(provider.to_s, [])
33
+ end
34
+
35
+ private
36
+
37
+ def package_roots
38
+ (local_package_roots + installed_package_roots).sort.uniq.filter_map do |root|
39
+ provider = package_provider(root)
40
+ next if provider.nil? || provider.empty?
41
+
42
+ [root, provider]
43
+ end
44
+ end
45
+
46
+ def local_package_roots
47
+ Dir.glob(File.join(project_root, PACKAGE_GLOB)).select { |path| File.directory?(path) }
48
+ end
49
+
50
+ def installed_package_roots
51
+ return @gem_roots unless @gem_roots.nil?
52
+
53
+ Gem::Specification.find_all
54
+ .select { |spec| spec.name.start_with?(GEM_NAME_PREFIX) }
55
+ .map(&:full_gem_path)
56
+ end
57
+
58
+ def package_provider(root)
59
+ manifests = Dir.glob(File.join(root, ".ace-defaults", "handbook", "providers", "*.yml")).sort
60
+ return nil if manifests.empty?
61
+
62
+ data = YAML.safe_load_file(manifests.first, permitted_classes: [Date, Time], aliases: true) || {}
63
+ data["provider"].to_s
64
+ rescue StandardError
65
+ nil
66
+ end
67
+
68
+ def parse_templates(root, provider)
69
+ templates = Dir.glob(File.join(root, "handbook", "prompts", "*.md")).sort.filter_map do |path|
70
+ match = File.read(path).match(FRONTMATTER_PATTERN)
71
+ frontmatter = match ? parse_frontmatter(match[1]) : {}
72
+ Models::PromptTemplate.new(provider: provider, source_path: path, frontmatter: frontmatter, content: File.read(path))
73
+ end
74
+ [provider, templates]
75
+ end
76
+
77
+ def parse_frontmatter(raw)
78
+ YAML.safe_load(raw, permitted_classes: [Date, Time], aliases: true) || {}
79
+ rescue StandardError
80
+ {}
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,22 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
+ require "yaml"
4
5
 
5
6
  module Ace
6
7
  module Handbook
7
8
  module Organisms
8
9
  class ProviderSyncer
9
- attr_reader :project_root, :registry, :inventory, :config
10
+ PROJECTION_SOURCE_PREFIX = "ace-handbook-integration-"
11
+
12
+ attr_reader :project_root, :registry, :inventory, :prompt_inventory, :config
10
13
 
11
14
  def initialize(
12
15
  project_root: Ace::Handbook.project_root,
13
16
  registry: nil,
14
17
  inventory: nil,
18
+ prompt_inventory: nil,
15
19
  config: nil
16
20
  )
17
21
  @project_root = project_root
18
22
  @registry = registry || Atoms::ProviderRegistry.new(project_root: project_root)
19
23
  @inventory = inventory || SkillInventory.new(project_root: project_root)
24
+ @prompt_inventory = prompt_inventory || PromptTemplateInventory.new(project_root: project_root)
20
25
  @config = config || Ace::Handbook.config.resolve_namespace("handbook").to_h
21
26
  end
22
27
 
@@ -31,6 +36,14 @@ module Ace
31
36
  private
32
37
 
33
38
  def sync_provider(provider, skills:, source_breakdown:)
39
+ skills_result = sync_skills(provider, skills: skills, source_breakdown: source_breakdown)
40
+ prompts_result = sync_prompts(provider)
41
+ return skills_result if prompts_result.nil?
42
+
43
+ skills_result.merge(prompts_result)
44
+ end
45
+
46
+ def sync_skills(provider, skills:, source_breakdown:)
34
47
  output_dir = File.join(project_root, registry.output_dir(provider))
35
48
  prepare_output_dir(output_dir)
36
49
 
@@ -64,6 +77,35 @@ module Ace
64
77
  }
65
78
  end
66
79
 
80
+ def sync_prompts(provider)
81
+ prompts_dir = registry.prompts_dir(provider)
82
+ return nil if prompts_dir.nil? || prompts_dir.to_s.empty?
83
+
84
+ output_dir = File.join(project_root, prompts_dir)
85
+ FileUtils.mkdir_p(output_dir)
86
+
87
+ expected = {}
88
+ updated_files = 0
89
+
90
+ prompt_inventory.for_provider(provider).each do |template|
91
+ output_path = File.join(output_dir, "#{template.name}.md")
92
+ expected[template.name] = output_path
93
+ next if File.exist?(output_path) && File.read(output_path) == template.content
94
+
95
+ File.write(output_path, template.content)
96
+ updated_files += 1
97
+ end
98
+
99
+ removed_entries = prune_stale_prompt_files(output_dir, expected.keys)
100
+
101
+ {
102
+ relative_prompts_dir: prompts_dir,
103
+ projected_prompts: expected.size,
104
+ updated_prompt_files: updated_files,
105
+ removed_prompt_entries: removed_entries
106
+ }
107
+ end
108
+
67
109
  def summarize_sources(skills)
68
110
  skills.each_with_object(Hash.new(0)) do |skill, memo|
69
111
  source = skill.source.to_s
@@ -128,6 +170,28 @@ module Ace
128
170
  stale.each { |path| FileUtils.rm_rf(path) }
129
171
  stale.size
130
172
  end
173
+
174
+ # The prompts dir is shared with user-authored templates, so only files
175
+ # carrying an ACE integration provenance marker are ever pruned.
176
+ def prune_stale_prompt_files(output_dir, expected_template_names)
177
+ stale = Dir.glob(File.join(output_dir, "*.md")).reject do |path|
178
+ expected_template_names.include?(File.basename(path, ".md"))
179
+ end.select do |path|
180
+ template_source(path).start_with?(PROJECTION_SOURCE_PREFIX)
181
+ end
182
+ stale.each { |path| FileUtils.rm(path) }
183
+ stale.size
184
+ end
185
+
186
+ def template_source(path)
187
+ match = File.read(path).match(/\A---\s*\n(.*?)\n---/m)
188
+ return "" unless match
189
+
190
+ frontmatter = YAML.safe_load(match[1], permitted_classes: [Date, Time], aliases: true)
191
+ frontmatter.is_a?(Hash) ? frontmatter["source"].to_s : ""
192
+ rescue StandardError
193
+ ""
194
+ end
131
195
  end
132
196
  end
133
197
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ace
4
4
  module Handbook
5
- VERSION = "0.31.0"
5
+ VERSION = "0.32.0"
6
6
  end
7
7
  end
data/lib/ace/handbook.rb CHANGED
@@ -5,8 +5,10 @@ require "ace/support/config"
5
5
  require_relative "handbook/version"
6
6
  require_relative "handbook/atoms/provider_registry"
7
7
  require_relative "handbook/models/skill_document"
8
+ require_relative "handbook/models/prompt_template"
8
9
  require_relative "handbook/molecules/skill_projection"
9
10
  require_relative "handbook/organisms/skill_inventory"
11
+ require_relative "handbook/organisms/prompt_template_inventory"
10
12
  require_relative "handbook/organisms/provider_syncer"
11
13
  require_relative "handbook/organisms/status_collector"
12
14
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ace-handbook
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.0
4
+ version: 0.32.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-08-12 00:00:00.000000000 Z
10
+ date: 2026-09-27 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ace-support-config
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0.17'
18
+ version: '0.18'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0.17'
25
+ version: '0.18'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: ace-support-core
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -169,6 +169,7 @@ files:
169
169
  - handbook/skills/as-handbook-manage-agents/SKILL.md
170
170
  - handbook/skills/as-handbook-manage-cookbooks/SKILL.md
171
171
  - handbook/skills/as-handbook-manage-guides/SKILL.md
172
+ - handbook/skills/as-handbook-manage-overrides/SKILL.md
172
173
  - handbook/skills/as-handbook-manage-workflows/SKILL.md
173
174
  - handbook/skills/as-handbook-parallel-research/SKILL.md
174
175
  - handbook/skills/as-handbook-perform-delivery/SKILL.md
@@ -188,6 +189,7 @@ files:
188
189
  - handbook/workflow-instructions/handbook/manage-agents.wf.md
189
190
  - handbook/workflow-instructions/handbook/manage-cookbooks.wf.md
190
191
  - handbook/workflow-instructions/handbook/manage-guides.wf.md
192
+ - handbook/workflow-instructions/handbook/manage-overrides.wf.md
191
193
  - handbook/workflow-instructions/handbook/manage-workflows.wf.md
192
194
  - handbook/workflow-instructions/handbook/parallel-research.wf.md
193
195
  - handbook/workflow-instructions/handbook/perform-delivery.wf.md
@@ -207,8 +209,10 @@ files:
207
209
  - lib/ace/handbook/cli.rb
208
210
  - lib/ace/handbook/cli/commands/status.rb
209
211
  - lib/ace/handbook/cli/commands/sync.rb
212
+ - lib/ace/handbook/models/prompt_template.rb
210
213
  - lib/ace/handbook/models/skill_document.rb
211
214
  - lib/ace/handbook/molecules/skill_projection.rb
215
+ - lib/ace/handbook/organisms/prompt_template_inventory.rb
212
216
  - lib/ace/handbook/organisms/provider_syncer.rb
213
217
  - lib/ace/handbook/organisms/skill_inventory.rb
214
218
  - lib/ace/handbook/organisms/status_collector.rb