ace-idea 0.18.5 → 0.21.3
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 +4 -4
- data/CHANGELOG.md +85 -0
- data/docs/usage.md +11 -3
- data/handbook/workflow-instructions/idea/capture.wf.md +2 -2
- data/handbook/workflow-instructions/idea/prioritize.wf.md +1 -1
- data/lib/ace/idea/cli/commands/create.rb +12 -8
- data/lib/ace/idea/molecules/idea_clipboard_reader.rb +3 -3
- data/lib/ace/idea/molecules/idea_creator.rb +82 -44
- data/lib/ace/idea/organisms/idea_doctor.rb +21 -0
- data/lib/ace/idea/organisms/idea_manager.rb +27 -2
- data/lib/ace/idea/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0669d1f1ea0f1d9e894bf29551ae7575a81983bffe85fb0de6225edca8a3b6de'
|
|
4
|
+
data.tar.gz: 2cfa1bd44ac20a02f462d35f8bafd27374bb4533a2719e0192a783707437fcaa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ebdfaaf5c40128b9cc02c0aad4afee4428fca994bf5c859ca0d61ecfeb9c81921e8450cab9ff9b6a91f326c509b9ccc44922d247c7e5298c6d36bd5bacd73342
|
|
7
|
+
data.tar.gz: 1e448f583770a87ee3169173bf23b9dbffdfd27541e66fcbb35b1a0d61bdb5f5340e423f1ab2fbf06f9a886e4c4e9d93b52ca0dd91477ba31955df828cabd92e
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,91 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.21.3] - 2026-04-20
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Updated stale idea workflow and root quick-start docs so `next` is consistently documented as the root-scope queue alias rather than a physical `_next` folder.
|
|
14
|
+
|
|
15
|
+
### Technical
|
|
16
|
+
- Tightened `TS-IDEA-001` move-to-next E2E contracts so Goal 3 requires raw update/list artifacts and verifies the `next` transition before the later archive step mutates final state.
|
|
17
|
+
|
|
18
|
+
## [0.21.2] - 2026-04-18
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Pinned `TS-IDEA-001` to a sandbox-local `.ace-ideas` root so archive-listing lifecycle coverage no longer depends on ambient project-root discovery.
|
|
22
|
+
|
|
23
|
+
### Technical
|
|
24
|
+
- Added a CLI regression proving `ace-idea list --in archive` surfaces an idea immediately after `update --move-to archive`.
|
|
25
|
+
|
|
26
|
+
## [0.21.1] - 2026-04-16
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- Updated `TS-IDEA-001` sandbox setup to source `mise.toml` from `${ACE_E2E_SOURCE_ROOT:-$PROJECT_ROOT_PATH}`, keeping idea lifecycle runs compatible with read-only source-root mounts.
|
|
30
|
+
|
|
31
|
+
## [0.21.0] - 2026-04-14
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
- Reworked `TS-IDEA-001` lifecycle runner instructions around public command-output ID flow (`create` output to `update`/archive transitions) instead of hidden frontmatter ID recipes.
|
|
35
|
+
|
|
36
|
+
### Technical
|
|
37
|
+
- Reduced duplicated list assertions across lifecycle scenario goals and added lightweight scenario freshness metadata (`last-verified` + freshness policy) for E2E maintenance.
|
|
38
|
+
|
|
39
|
+
## [0.20.2] - 2026-04-13
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
- Made create-time collision retries deterministic by reusing one precomputed payload across retries so clipboard capture and LLM enhancement are not rerun with divergent inputs.
|
|
43
|
+
|
|
44
|
+
### Technical
|
|
45
|
+
- Added manager-level regression coverage to enforce payload reuse semantics across collision retries.
|
|
46
|
+
|
|
47
|
+
## [0.20.1] - 2026-04-13
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
- Added atomic idea-ID reservation during create so concurrent creates fail fast with retryable collision semantics instead of racing through non-atomic prechecks.
|
|
51
|
+
|
|
52
|
+
### Technical
|
|
53
|
+
- Narrowed idea ID-existence scans to ID-prefixed glob matches and added regression coverage for reservation-lock collision handling.
|
|
54
|
+
|
|
55
|
+
## [0.20.0] - 2026-04-13
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
- Added duplicate persisted idea-ID detection to doctor health checks and made frontmatter-only doctor validation fail when duplicates are present.
|
|
59
|
+
- Added bounded retry handling for `ace-idea create` ID collisions, including retry-exhaustion failures and enforcement that same-ID/different-slug creates are not treated as success.
|
|
60
|
+
|
|
61
|
+
### Technical
|
|
62
|
+
- Added regression coverage for duplicate-ID doctor failures, create-time retry semantics, and orphan-artifact cleanup guarantees.
|
|
63
|
+
|
|
64
|
+
## [0.19.3] - 2026-04-13
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
- Added bounded retry handling for `ace-idea create` ID collisions so success is emitted only after a unique persisted idea ID exists.
|
|
68
|
+
- Enforced create-time collision semantics where "same ID, different folder slug" is no longer treated as successful creation.
|
|
69
|
+
- Added clear retry-exhaustion failure messaging for create-time ID collisions.
|
|
70
|
+
|
|
71
|
+
### Technical
|
|
72
|
+
- Updated molecule, command, and feature regression coverage to validate ID-retry semantics and no-orphan cleanup behavior.
|
|
73
|
+
|
|
74
|
+
## [0.19.2] - 2026-04-13
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
- Report duplicate persisted idea IDs as explicit doctor health-check errors with file-path context.
|
|
78
|
+
- Include duplicate ID detection in `ace-idea doctor --check frontmatter` and fail health status when duplicates are present.
|
|
79
|
+
|
|
80
|
+
### Technical
|
|
81
|
+
- Added organism and CLI regression coverage for duplicate ID failures and frontmatter-only doctor checks.
|
|
82
|
+
|
|
83
|
+
## [0.19.1] - 2026-04-13
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
- Completed the batch i05 migration follow-through for this package and aligned it with the restarted `fast` / `feat` / `e2e` verification model.
|
|
87
|
+
|
|
88
|
+
### Technical
|
|
89
|
+
- Included in the coordinated assignment-driven patch release for batch i05 package updates.
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
### Changed
|
|
93
|
+
- Migrated deterministic package coverage to `test/fast` and `test/feat`, retained lifecycle workflow coverage in `test/e2e`, and updated E2E scenario metadata to the restarted `fast`/`feat`/`e2e` contract.
|
|
94
|
+
|
|
10
95
|
## [0.18.5] - 2026-03-31
|
|
11
96
|
|
|
12
97
|
### Changed
|
data/docs/usage.md
CHANGED
|
@@ -3,8 +3,8 @@ doc-type: user
|
|
|
3
3
|
title: ace-idea CLI Usage Reference
|
|
4
4
|
purpose: Command reference for ace-idea
|
|
5
5
|
ace-docs:
|
|
6
|
-
last-updated: 2026-
|
|
7
|
-
last-checked: 2026-
|
|
6
|
+
last-updated: '2026-04-13'
|
|
7
|
+
last-checked: '2026-04-13'
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# ace-idea CLI Usage Reference
|
|
@@ -92,6 +92,11 @@ ace-idea status --up-next-limit 5
|
|
|
92
92
|
|
|
93
93
|
Create a new idea from direct text or clipboard input.
|
|
94
94
|
|
|
95
|
+
If the generated idea ID collides with an existing persisted idea, `ace-idea create`
|
|
96
|
+
automatically retries with a new ID. If retries are exhausted, the command fails
|
|
97
|
+
without leaving behind a partial idea artifact. A same-ID create under a different
|
|
98
|
+
folder slug is treated as a collision, not a successful create.
|
|
99
|
+
|
|
95
100
|
**Syntax:**
|
|
96
101
|
|
|
97
102
|
```bash
|
|
@@ -210,6 +215,9 @@ ace-idea update q7w --move-to archive
|
|
|
210
215
|
|
|
211
216
|
Run health checks across the idea store.
|
|
212
217
|
|
|
218
|
+
Duplicate persisted idea IDs are reported as errors. `ace-idea doctor --check frontmatter`
|
|
219
|
+
also fails when duplicate IDs are present in idea frontmatter.
|
|
220
|
+
|
|
213
221
|
**Syntax:**
|
|
214
222
|
|
|
215
223
|
```bash
|
|
@@ -317,4 +325,4 @@ ID if needed.
|
|
|
317
325
|
|
|
318
326
|
**Symptom:** The configured ideas root does not exist yet.
|
|
319
327
|
|
|
320
|
-
**Solution:** Create an idea first or set `idea.root_dir` in config before running `doctor`.
|
|
328
|
+
**Solution:** Create an idea first or set `idea.root_dir` in config before running `doctor`.
|
|
@@ -178,8 +178,8 @@ ace-idea update q7w --move-to archive
|
|
|
178
178
|
* **Note**: Clipboard functionality requires `ace-support-mac-clipboard` gem
|
|
179
179
|
|
|
180
180
|
**"Invalid target folder" Error:**
|
|
181
|
-
* **Cause**: `--move-to` was given
|
|
182
|
-
* **Solution**: Use a physical folder
|
|
181
|
+
* **Cause**: `--move-to` was given an unsupported or invalid folder value
|
|
182
|
+
* **Solution**: Use a physical folder such as `maybe`, `anytime`, `archive`, or any custom name. Use `--move-to next` or `--move-to root` to place ideas in the root-scope next queue.
|
|
183
183
|
|
|
184
184
|
**LLM Enhancement Failures:**
|
|
185
185
|
* **Cause**: API issues, model unavailability, or `--no-llm-enhance` flag
|
|
@@ -94,7 +94,7 @@ Systematically organize, prioritize, and align queued ideas with current project
|
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
**How it works:**
|
|
97
|
-
* Updates move idea files between folders (`
|
|
97
|
+
* Updates move idea files between physical folders (`_maybe`, `_anytime`) or back to the root-scope `next` queue
|
|
98
98
|
* Ideas are listed per folder when using `ace-idea list --in <folder>`
|
|
99
99
|
* The same command can also update metadata such as `status` when needed
|
|
100
100
|
|
|
@@ -71,14 +71,18 @@ module Ace
|
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
manager = Ace::Idea::Organisms::IdeaManager.new
|
|
74
|
-
idea =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
idea = begin
|
|
75
|
+
manager.create(
|
|
76
|
+
content,
|
|
77
|
+
title: title,
|
|
78
|
+
tags: tags,
|
|
79
|
+
move_to: move_to,
|
|
80
|
+
clipboard: clipboard || false,
|
|
81
|
+
llm_enhance: llm_enhance || false
|
|
82
|
+
)
|
|
83
|
+
rescue Ace::Idea::Organisms::IdeaManager::CreateRetriesExhaustedError => e
|
|
84
|
+
raise Ace::Support::Cli::Error, e.message
|
|
85
|
+
end
|
|
82
86
|
|
|
83
87
|
folder_info = idea.special_folder ? " (#{idea.special_folder})" : ""
|
|
84
88
|
puts "Idea created: #{idea.id} #{idea.title}#{folder_info}"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Clipboard reader adapted from ace-
|
|
4
|
-
# Kept independent (no ace-
|
|
5
|
-
# "Shared utilities from ace-
|
|
3
|
+
# Clipboard reader adapted from ace-task's ClipboardReader.
|
|
4
|
+
# Kept independent (no ace-task dependency) as per task spec:
|
|
5
|
+
# "Shared utilities from ace-task are duplicated rather than centralized."
|
|
6
6
|
|
|
7
7
|
begin
|
|
8
8
|
require "clipboard"
|
|
@@ -16,6 +16,8 @@ module Ace
|
|
|
16
16
|
# Creates new ideas with b36ts IDs, folder/file creation.
|
|
17
17
|
# Supports --clipboard, --llm-enhance, and --move-to options.
|
|
18
18
|
class IdeaCreator
|
|
19
|
+
class IdCollisionError < StandardError; end
|
|
20
|
+
|
|
19
21
|
# @param root_dir [String] Root directory for ideas
|
|
20
22
|
# @param config [Hash] Configuration hash
|
|
21
23
|
def initialize(root_dir:, config: {})
|
|
@@ -33,66 +35,102 @@ module Ace
|
|
|
33
35
|
# @param time [Time] Creation time (default: now)
|
|
34
36
|
# @return [Idea] Created idea object
|
|
35
37
|
def create(content = nil, title: nil, tags: [], move_to: nil,
|
|
36
|
-
clipboard: false, llm_enhance: false, time: Time.now.utc)
|
|
37
|
-
|
|
38
|
+
clipboard: false, llm_enhance: false, time: Time.now.utc, prepared_payload: nil)
|
|
39
|
+
payload = prepared_payload || prepare_create_payload(content, clipboard: clipboard, llm_enhance: llm_enhance)
|
|
40
|
+
enhanced_body = payload.fetch(:enhanced_body)
|
|
41
|
+
attachments_to_save = payload.fetch(:attachments_to_save)
|
|
42
|
+
|
|
43
|
+
# Step 3: Generate ID and slugs
|
|
44
|
+
id = Atoms::IdeaIdFormatter.generate(time)
|
|
45
|
+
with_id_reservation(id) do
|
|
46
|
+
raise IdCollisionError, "Idea ID collision detected for #{id}" if idea_id_exists?(id)
|
|
47
|
+
|
|
48
|
+
slug_title = title || extract_title(enhanced_body)
|
|
49
|
+
folder_slug = generate_folder_slug(slug_title)
|
|
50
|
+
file_slug = generate_file_slug(slug_title)
|
|
51
|
+
|
|
52
|
+
# Step 4: Determine target directory
|
|
53
|
+
target_dir = determine_target_dir(move_to)
|
|
54
|
+
FileUtils.mkdir_p(target_dir)
|
|
55
|
+
|
|
56
|
+
# Step 5: Create idea folder
|
|
57
|
+
folder_name, _ = unique_folder_name(id, folder_slug, target_dir)
|
|
58
|
+
idea_dir = File.join(target_dir, folder_name)
|
|
59
|
+
FileUtils.mkdir_p(idea_dir)
|
|
60
|
+
|
|
61
|
+
begin
|
|
62
|
+
# Step 6: Handle attachments
|
|
63
|
+
if attachments_to_save.any?
|
|
64
|
+
enhanced_body = save_attachments_and_inject_refs(attachments_to_save, idea_dir, enhanced_body)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Step 7: Write spec file
|
|
68
|
+
effective_title = title || extract_title(enhanced_body) || "Untitled Idea"
|
|
69
|
+
frontmatter = Atoms::IdeaFrontmatterDefaults.build(
|
|
70
|
+
id: id,
|
|
71
|
+
title: effective_title,
|
|
72
|
+
tags: tags,
|
|
73
|
+
status: "pending",
|
|
74
|
+
created_at: time
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
file_content = build_file_content(frontmatter, enhanced_body, effective_title)
|
|
78
|
+
spec_filename = Atoms::IdeaFilePattern.spec_filename(id, file_slug)
|
|
79
|
+
spec_file = File.join(idea_dir, spec_filename)
|
|
80
|
+
File.write(spec_file, file_content)
|
|
81
|
+
|
|
82
|
+
# Step 8: Load and return the created idea
|
|
83
|
+
loader = IdeaLoader.new
|
|
84
|
+
special_folder = Ace::Support::Items::Atoms::SpecialFolderDetector.detect_in_path(
|
|
85
|
+
idea_dir, root: @root_dir
|
|
86
|
+
)
|
|
87
|
+
loader.load(idea_dir, id: id, special_folder: special_folder)
|
|
88
|
+
rescue StandardError
|
|
89
|
+
FileUtils.rm_rf(idea_dir) if Dir.exist?(idea_dir)
|
|
90
|
+
raise
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def prepare_create_payload(content, clipboard: false, llm_enhance: false)
|
|
38
96
|
body, attachments_to_save = gather_content(content, clipboard: clipboard)
|
|
39
97
|
|
|
40
98
|
if body.nil? || body.strip.empty?
|
|
41
99
|
raise ArgumentError, "No content provided. Provide text or use --clipboard."
|
|
42
100
|
end
|
|
43
101
|
|
|
44
|
-
# Step 2: Optionally enhance with LLM
|
|
45
102
|
enhanced_body = if llm_enhance
|
|
46
103
|
enhance_with_llm(body, config: @config)
|
|
47
104
|
else
|
|
48
105
|
body
|
|
49
106
|
end
|
|
50
107
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
file_slug = generate_file_slug(slug_title)
|
|
56
|
-
|
|
57
|
-
# Step 4: Determine target directory
|
|
58
|
-
target_dir = determine_target_dir(move_to)
|
|
59
|
-
FileUtils.mkdir_p(target_dir)
|
|
60
|
-
|
|
61
|
-
# Step 5: Create idea folder (ensure unique name if ID collision occurs)
|
|
62
|
-
folder_name, _ = unique_folder_name(id, folder_slug, target_dir)
|
|
63
|
-
idea_dir = File.join(target_dir, folder_name)
|
|
64
|
-
FileUtils.mkdir_p(idea_dir)
|
|
65
|
-
|
|
66
|
-
# Step 6: Handle attachments
|
|
67
|
-
if attachments_to_save.any?
|
|
68
|
-
enhanced_body = save_attachments_and_inject_refs(attachments_to_save, idea_dir, enhanced_body)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Step 7: Write spec file
|
|
72
|
-
effective_title = title || extract_title(enhanced_body) || "Untitled Idea"
|
|
73
|
-
frontmatter = Atoms::IdeaFrontmatterDefaults.build(
|
|
74
|
-
id: id,
|
|
75
|
-
title: effective_title,
|
|
76
|
-
tags: tags,
|
|
77
|
-
status: "pending",
|
|
78
|
-
created_at: time
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
file_content = build_file_content(frontmatter, enhanced_body, effective_title)
|
|
82
|
-
spec_filename = Atoms::IdeaFilePattern.spec_filename(id, file_slug)
|
|
83
|
-
spec_file = File.join(idea_dir, spec_filename)
|
|
84
|
-
File.write(spec_file, file_content)
|
|
85
|
-
|
|
86
|
-
# Step 8: Load and return the created idea
|
|
87
|
-
loader = IdeaLoader.new
|
|
88
|
-
special_folder = Ace::Support::Items::Atoms::SpecialFolderDetector.detect_in_path(
|
|
89
|
-
idea_dir, root: @root_dir
|
|
90
|
-
)
|
|
91
|
-
loader.load(idea_dir, id: id, special_folder: special_folder)
|
|
108
|
+
{
|
|
109
|
+
enhanced_body: enhanced_body,
|
|
110
|
+
attachments_to_save: attachments_to_save
|
|
111
|
+
}
|
|
92
112
|
end
|
|
93
113
|
|
|
94
114
|
private
|
|
95
115
|
|
|
116
|
+
def idea_id_exists?(id)
|
|
117
|
+
Dir.glob(File.join(@root_dir, "**", "#{id}-*")).any? do |path|
|
|
118
|
+
File.directory?(path)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def with_id_reservation(id)
|
|
123
|
+
reservation_path = File.join(@root_dir, ".ace-idea-id-lock-#{id}")
|
|
124
|
+
acquired = false
|
|
125
|
+
Dir.mkdir(reservation_path)
|
|
126
|
+
acquired = true
|
|
127
|
+
yield
|
|
128
|
+
rescue Errno::EEXIST
|
|
129
|
+
raise IdCollisionError, "Idea ID collision detected for #{id}"
|
|
130
|
+
ensure
|
|
131
|
+
FileUtils.rm_rf(reservation_path) if acquired && reservation_path && Dir.exist?(reservation_path)
|
|
132
|
+
end
|
|
133
|
+
|
|
96
134
|
def gather_content(content, clipboard: false)
|
|
97
135
|
attachments = []
|
|
98
136
|
|
|
@@ -117,11 +117,18 @@ module Ace
|
|
|
117
117
|
|
|
118
118
|
scan_results = scanner.scan
|
|
119
119
|
@stats[:ideas_scanned] = scan_results.size
|
|
120
|
+
id_locations = Hash.new { |hash, key| hash[key] = [] }
|
|
120
121
|
|
|
121
122
|
scan_results.each do |scan_result|
|
|
122
123
|
spec_file = scan_result.file_path
|
|
123
124
|
next unless spec_file && File.exist?(spec_file)
|
|
124
125
|
|
|
126
|
+
content = File.read(spec_file)
|
|
127
|
+
frontmatter, _body = Ace::Support::Items::Atoms::FrontmatterParser.parse(content)
|
|
128
|
+
if frontmatter.is_a?(Hash) && frontmatter["id"] && !frontmatter["id"].to_s.strip.empty?
|
|
129
|
+
id_locations[frontmatter["id"]] << spec_file
|
|
130
|
+
end
|
|
131
|
+
|
|
125
132
|
issues = Molecules::IdeaFrontmatterValidator.validate(
|
|
126
133
|
spec_file,
|
|
127
134
|
special_folder: scan_result.special_folder
|
|
@@ -134,6 +141,8 @@ module Ace
|
|
|
134
141
|
add_issue(issue[:type], issue[:message], issue[:location])
|
|
135
142
|
end
|
|
136
143
|
end
|
|
144
|
+
|
|
145
|
+
add_duplicate_id_issues(id_locations)
|
|
137
146
|
end
|
|
138
147
|
|
|
139
148
|
def run_scope_check
|
|
@@ -183,6 +192,18 @@ module Ace
|
|
|
183
192
|
count
|
|
184
193
|
end
|
|
185
194
|
|
|
195
|
+
def add_duplicate_id_issues(id_locations)
|
|
196
|
+
id_locations.each do |id, locations|
|
|
197
|
+
next unless locations.size > 1
|
|
198
|
+
|
|
199
|
+
add_issue(
|
|
200
|
+
:error,
|
|
201
|
+
"Duplicate idea ID '#{id}' found in: #{locations.sort.join(', ')}",
|
|
202
|
+
locations.first
|
|
203
|
+
)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
186
207
|
def add_issue(type, message, location = nil)
|
|
187
208
|
issue = {type: type, message: message}
|
|
188
209
|
issue[:location] = location if location
|
|
@@ -15,6 +15,9 @@ module Ace
|
|
|
15
15
|
# Orchestrates all idea CRUD operations.
|
|
16
16
|
# Entry point for idea management with config-driven root directory.
|
|
17
17
|
class IdeaManager
|
|
18
|
+
CREATE_RETRY_LIMIT = 3
|
|
19
|
+
class CreateRetriesExhaustedError < StandardError; end
|
|
20
|
+
|
|
18
21
|
attr_reader :last_list_total, :last_folder_counts
|
|
19
22
|
|
|
20
23
|
# @param root_dir [String, nil] Override root directory for ideas
|
|
@@ -36,8 +39,30 @@ module Ace
|
|
|
36
39
|
clipboard: false, llm_enhance: false)
|
|
37
40
|
ensure_root_dir
|
|
38
41
|
creator = Molecules::IdeaCreator.new(root_dir: @root_dir, config: @config)
|
|
39
|
-
creator.
|
|
40
|
-
|
|
42
|
+
prepared_payload = creator.prepare_create_payload(
|
|
43
|
+
content,
|
|
44
|
+
clipboard: clipboard,
|
|
45
|
+
llm_enhance: llm_enhance
|
|
46
|
+
)
|
|
47
|
+
attempts = 0
|
|
48
|
+
|
|
49
|
+
begin
|
|
50
|
+
attempts += 1
|
|
51
|
+
creator.create(
|
|
52
|
+
nil,
|
|
53
|
+
title: title,
|
|
54
|
+
tags: tags,
|
|
55
|
+
move_to: move_to,
|
|
56
|
+
clipboard: false,
|
|
57
|
+
llm_enhance: false,
|
|
58
|
+
time: Time.now.utc + ((attempts - 1) * 2),
|
|
59
|
+
prepared_payload: prepared_payload
|
|
60
|
+
)
|
|
61
|
+
rescue Molecules::IdeaCreator::IdCollisionError
|
|
62
|
+
retry if attempts < CREATE_RETRY_LIMIT
|
|
63
|
+
raise CreateRetriesExhaustedError,
|
|
64
|
+
"Failed to create idea: unable to generate a unique ID after #{CREATE_RETRY_LIMIT} attempts"
|
|
65
|
+
end
|
|
41
66
|
end
|
|
42
67
|
|
|
43
68
|
# Create an idea from clipboard
|
data/lib/ace/idea/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-idea
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.21.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-04-
|
|
10
|
+
date: 2026-04-20 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-support-core
|