aidp 0.17.1 โ 0.19.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 +4 -4
- data/README.md +69 -0
- data/lib/aidp/cli/terminal_io.rb +5 -2
- data/lib/aidp/cli.rb +43 -2
- data/lib/aidp/config.rb +9 -14
- data/lib/aidp/execute/agent_signal_parser.rb +20 -0
- data/lib/aidp/execute/persistent_tasklist.rb +220 -0
- data/lib/aidp/execute/prompt_manager.rb +128 -1
- data/lib/aidp/execute/repl_macros.rb +719 -0
- data/lib/aidp/execute/work_loop_runner.rb +162 -1
- data/lib/aidp/harness/ai_decision_engine.rb +376 -0
- data/lib/aidp/harness/capability_registry.rb +273 -0
- data/lib/aidp/harness/config_schema.rb +305 -1
- data/lib/aidp/harness/configuration.rb +452 -0
- data/lib/aidp/harness/enhanced_runner.rb +7 -1
- data/lib/aidp/harness/provider_factory.rb +0 -2
- data/lib/aidp/harness/runner.rb +7 -1
- data/lib/aidp/harness/thinking_depth_manager.rb +335 -0
- data/lib/aidp/harness/zfc_condition_detector.rb +395 -0
- data/lib/aidp/init/devcontainer_generator.rb +274 -0
- data/lib/aidp/init/runner.rb +37 -10
- data/lib/aidp/init.rb +1 -0
- data/lib/aidp/prompt_optimization/context_composer.rb +286 -0
- data/lib/aidp/prompt_optimization/optimizer.rb +335 -0
- data/lib/aidp/prompt_optimization/prompt_builder.rb +309 -0
- data/lib/aidp/prompt_optimization/relevance_scorer.rb +256 -0
- data/lib/aidp/prompt_optimization/source_code_fragmenter.rb +308 -0
- data/lib/aidp/prompt_optimization/style_guide_indexer.rb +240 -0
- data/lib/aidp/prompt_optimization/template_indexer.rb +250 -0
- data/lib/aidp/provider_manager.rb +0 -2
- data/lib/aidp/providers/anthropic.rb +19 -0
- data/lib/aidp/setup/wizard.rb +299 -4
- data/lib/aidp/utils/devcontainer_detector.rb +166 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/build_processor.rb +72 -6
- data/lib/aidp/watch/repository_client.rb +2 -1
- data/lib/aidp.rb +1 -1
- data/templates/aidp.yml.example +128 -0
- metadata +15 -2
- data/lib/aidp/providers/macos_ui.rb +0 -102
|
@@ -275,6 +275,30 @@ module Aidp
|
|
|
275
275
|
usage: "/skill <list|show|use> [args]",
|
|
276
276
|
example: "/skill list",
|
|
277
277
|
handler: method(:cmd_skill)
|
|
278
|
+
},
|
|
279
|
+
"/tools" => {
|
|
280
|
+
description: "Manage available tools (coverage, testing, etc.)",
|
|
281
|
+
usage: "/tools <show|coverage|test> [subcommand]",
|
|
282
|
+
example: "/tools show",
|
|
283
|
+
handler: method(:cmd_tools)
|
|
284
|
+
},
|
|
285
|
+
"/thinking" => {
|
|
286
|
+
description: "Manage thinking depth tier for model selection",
|
|
287
|
+
usage: "/thinking <show|set|max|reset> [tier]",
|
|
288
|
+
example: "/thinking show",
|
|
289
|
+
handler: method(:cmd_thinking)
|
|
290
|
+
},
|
|
291
|
+
"/prompt" => {
|
|
292
|
+
description: "Inspect and control prompt optimization",
|
|
293
|
+
usage: "/prompt <explain|stats|expand|reset>",
|
|
294
|
+
example: "/prompt explain",
|
|
295
|
+
handler: method(:cmd_prompt)
|
|
296
|
+
},
|
|
297
|
+
"/tasks" => {
|
|
298
|
+
description: "Manage persistent tasklist (cross-session task tracking)",
|
|
299
|
+
usage: "/tasks <list|show|done|abandon|stats> [args]",
|
|
300
|
+
example: "/tasks list pending",
|
|
301
|
+
handler: method(:cmd_tasks)
|
|
278
302
|
}
|
|
279
303
|
}
|
|
280
304
|
end
|
|
@@ -1489,6 +1513,701 @@ module Aidp
|
|
|
1489
1513
|
}
|
|
1490
1514
|
end
|
|
1491
1515
|
end
|
|
1516
|
+
|
|
1517
|
+
# /tools command - manage available tools
|
|
1518
|
+
def cmd_tools(args)
|
|
1519
|
+
Aidp.log_debug("repl_macros", "Executing /tools command", args: args)
|
|
1520
|
+
|
|
1521
|
+
subcommand = args[0]&.downcase
|
|
1522
|
+
|
|
1523
|
+
case subcommand
|
|
1524
|
+
when "show"
|
|
1525
|
+
cmd_tools_show
|
|
1526
|
+
when "coverage"
|
|
1527
|
+
cmd_tools_coverage(args[1..])
|
|
1528
|
+
when "test"
|
|
1529
|
+
cmd_tools_test(args[1..])
|
|
1530
|
+
else
|
|
1531
|
+
{
|
|
1532
|
+
success: false,
|
|
1533
|
+
message: "Usage: /tools <command> [args]\n\nCommands:\n show - Show configured tools and their status\n coverage - Run coverage analysis and show delta\n test <type> - Run interactive tests (web, cli, desktop)\n\nExamples:\n /tools show\n /tools coverage\n /tools test web",
|
|
1534
|
+
action: :none
|
|
1535
|
+
}
|
|
1536
|
+
end
|
|
1537
|
+
end
|
|
1538
|
+
|
|
1539
|
+
def cmd_tools_show
|
|
1540
|
+
require_relative "../harness/configuration"
|
|
1541
|
+
|
|
1542
|
+
begin
|
|
1543
|
+
config = Aidp::Harness::Configuration.new(@project_dir)
|
|
1544
|
+
|
|
1545
|
+
output = ["๐ Configured Tools\n", "=" * 50]
|
|
1546
|
+
|
|
1547
|
+
# Coverage tools
|
|
1548
|
+
if config.coverage_enabled?
|
|
1549
|
+
output << "\n๐ Coverage:"
|
|
1550
|
+
output << " Tool: #{config.coverage_tool || "not specified"}"
|
|
1551
|
+
output << " Command: #{config.coverage_run_command || "not specified"}"
|
|
1552
|
+
output << " Report paths: #{config.coverage_report_paths.join(", ")}" if config.coverage_report_paths.any?
|
|
1553
|
+
output << " Fail on drop: #{config.coverage_fail_on_drop? ? "yes" : "no"}"
|
|
1554
|
+
output << " Minimum coverage: #{config.coverage_minimum || "not set"}%" if config.coverage_minimum
|
|
1555
|
+
else
|
|
1556
|
+
output << "\n๐ Coverage: disabled"
|
|
1557
|
+
end
|
|
1558
|
+
|
|
1559
|
+
# VCS configuration
|
|
1560
|
+
output << "\n\n๐๏ธ Version Control:"
|
|
1561
|
+
output << " Tool: #{config.vcs_tool}"
|
|
1562
|
+
output << " Behavior: #{config.vcs_behavior}"
|
|
1563
|
+
output << " Conventional commits: #{config.conventional_commits? ? "yes" : "no"}"
|
|
1564
|
+
|
|
1565
|
+
# Interactive testing
|
|
1566
|
+
if config.interactive_testing_enabled?
|
|
1567
|
+
output << "\n\n๐ฏ Interactive Testing:"
|
|
1568
|
+
output << " App type: #{config.interactive_testing_app_type}"
|
|
1569
|
+
tools = config.interactive_testing_tools
|
|
1570
|
+
if tools.any?
|
|
1571
|
+
tools.each do |category, category_tools|
|
|
1572
|
+
output << " #{category.to_s.capitalize}:"
|
|
1573
|
+
category_tools.each do |tool_name, tool_config|
|
|
1574
|
+
next unless tool_config[:enabled]
|
|
1575
|
+
output << " โข #{tool_name}: enabled"
|
|
1576
|
+
output << " Run: #{tool_config[:run]}" if tool_config[:run]
|
|
1577
|
+
output << " Specs: #{tool_config[:specs_dir]}" if tool_config[:specs_dir]
|
|
1578
|
+
end
|
|
1579
|
+
end
|
|
1580
|
+
else
|
|
1581
|
+
output << " No tools configured"
|
|
1582
|
+
end
|
|
1583
|
+
else
|
|
1584
|
+
output << "\n\n๐ฏ Interactive Testing: disabled"
|
|
1585
|
+
end
|
|
1586
|
+
|
|
1587
|
+
# Model families
|
|
1588
|
+
output << "\n\n๐ค Model Families:"
|
|
1589
|
+
config.configured_providers.each do |provider|
|
|
1590
|
+
family = config.model_family(provider)
|
|
1591
|
+
output << " #{provider}: #{family}"
|
|
1592
|
+
end
|
|
1593
|
+
|
|
1594
|
+
{
|
|
1595
|
+
success: true,
|
|
1596
|
+
message: output.join("\n"),
|
|
1597
|
+
action: :none
|
|
1598
|
+
}
|
|
1599
|
+
rescue => e
|
|
1600
|
+
Aidp.log_error("repl_macros", "Failed to show tools", error: e.message)
|
|
1601
|
+
{
|
|
1602
|
+
success: false,
|
|
1603
|
+
message: "Failed to load tool configuration: #{e.message}",
|
|
1604
|
+
action: :none
|
|
1605
|
+
}
|
|
1606
|
+
end
|
|
1607
|
+
end
|
|
1608
|
+
|
|
1609
|
+
def cmd_tools_coverage(args)
|
|
1610
|
+
require_relative "../harness/configuration"
|
|
1611
|
+
|
|
1612
|
+
begin
|
|
1613
|
+
config = Aidp::Harness::Configuration.new(@project_dir)
|
|
1614
|
+
|
|
1615
|
+
unless config.coverage_enabled?
|
|
1616
|
+
return {
|
|
1617
|
+
success: false,
|
|
1618
|
+
message: "Coverage is not enabled. Run 'aidp config --interactive' to configure coverage.",
|
|
1619
|
+
action: :none
|
|
1620
|
+
}
|
|
1621
|
+
end
|
|
1622
|
+
|
|
1623
|
+
unless config.coverage_run_command
|
|
1624
|
+
return {
|
|
1625
|
+
success: false,
|
|
1626
|
+
message: "Coverage run command not configured. Run 'aidp config --interactive' to set it up.",
|
|
1627
|
+
action: :none
|
|
1628
|
+
}
|
|
1629
|
+
end
|
|
1630
|
+
|
|
1631
|
+
Aidp.log_debug("repl_macros", "Running coverage", command: config.coverage_run_command)
|
|
1632
|
+
|
|
1633
|
+
{
|
|
1634
|
+
success: true,
|
|
1635
|
+
message: "Running coverage with: #{config.coverage_run_command}\n(Coverage execution to be implemented in work loop)",
|
|
1636
|
+
action: :run_coverage,
|
|
1637
|
+
data: {
|
|
1638
|
+
command: config.coverage_run_command,
|
|
1639
|
+
tool: config.coverage_tool,
|
|
1640
|
+
report_paths: config.coverage_report_paths
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
rescue => e
|
|
1644
|
+
Aidp.log_error("repl_macros", "Failed to run coverage", error: e.message)
|
|
1645
|
+
{
|
|
1646
|
+
success: false,
|
|
1647
|
+
message: "Failed to run coverage: #{e.message}",
|
|
1648
|
+
action: :none
|
|
1649
|
+
}
|
|
1650
|
+
end
|
|
1651
|
+
end
|
|
1652
|
+
|
|
1653
|
+
def cmd_tools_test(args)
|
|
1654
|
+
require_relative "../harness/configuration"
|
|
1655
|
+
|
|
1656
|
+
test_type = args[0]&.downcase
|
|
1657
|
+
|
|
1658
|
+
begin
|
|
1659
|
+
config = Aidp::Harness::Configuration.new(@project_dir)
|
|
1660
|
+
|
|
1661
|
+
unless config.interactive_testing_enabled?
|
|
1662
|
+
return {
|
|
1663
|
+
success: false,
|
|
1664
|
+
message: "Interactive testing is not enabled. Run 'aidp config --interactive' to configure it.",
|
|
1665
|
+
action: :none
|
|
1666
|
+
}
|
|
1667
|
+
end
|
|
1668
|
+
|
|
1669
|
+
unless test_type
|
|
1670
|
+
return {
|
|
1671
|
+
success: false,
|
|
1672
|
+
message: "Usage: /tools test <type>\n\nTypes: web, cli, desktop\n\nExample: /tools test web",
|
|
1673
|
+
action: :none
|
|
1674
|
+
}
|
|
1675
|
+
end
|
|
1676
|
+
|
|
1677
|
+
unless %w[web cli desktop].include?(test_type)
|
|
1678
|
+
return {
|
|
1679
|
+
success: false,
|
|
1680
|
+
message: "Invalid test type: #{test_type}. Must be one of: web, cli, desktop",
|
|
1681
|
+
action: :none
|
|
1682
|
+
}
|
|
1683
|
+
end
|
|
1684
|
+
|
|
1685
|
+
tools = config.interactive_testing_tools.dig(test_type.to_sym)
|
|
1686
|
+
unless tools&.any? { |_, t| t[:enabled] }
|
|
1687
|
+
return {
|
|
1688
|
+
success: false,
|
|
1689
|
+
message: "No #{test_type} testing tools configured. Run 'aidp config --interactive' to set them up.",
|
|
1690
|
+
action: :none
|
|
1691
|
+
}
|
|
1692
|
+
end
|
|
1693
|
+
|
|
1694
|
+
enabled_tools = tools.select { |_, t| t[:enabled] }
|
|
1695
|
+
tool_list = enabled_tools.map { |name, cfg| " โข #{name}: #{cfg[:run] || "no command"}" }.join("\n")
|
|
1696
|
+
|
|
1697
|
+
Aidp.log_debug("repl_macros", "Running interactive tests", type: test_type, tools: enabled_tools.keys)
|
|
1698
|
+
|
|
1699
|
+
{
|
|
1700
|
+
success: true,
|
|
1701
|
+
message: "Running #{test_type} tests:\n#{tool_list}\n(Test execution to be implemented in work loop)",
|
|
1702
|
+
action: :run_interactive_tests,
|
|
1703
|
+
data: {
|
|
1704
|
+
test_type: test_type,
|
|
1705
|
+
tools: enabled_tools
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
rescue => e
|
|
1709
|
+
Aidp.log_error("repl_macros", "Failed to run interactive tests", error: e.message)
|
|
1710
|
+
{
|
|
1711
|
+
success: false,
|
|
1712
|
+
message: "Failed to run interactive tests: #{e.message}",
|
|
1713
|
+
action: :none
|
|
1714
|
+
}
|
|
1715
|
+
end
|
|
1716
|
+
end
|
|
1717
|
+
|
|
1718
|
+
# Command: /thinking
|
|
1719
|
+
def cmd_thinking(args)
|
|
1720
|
+
subcommand = args[0]
|
|
1721
|
+
|
|
1722
|
+
case subcommand
|
|
1723
|
+
when "show"
|
|
1724
|
+
cmd_thinking_show
|
|
1725
|
+
when "set"
|
|
1726
|
+
cmd_thinking_set(args[1])
|
|
1727
|
+
when "max"
|
|
1728
|
+
cmd_thinking_max(args[1])
|
|
1729
|
+
when "reset"
|
|
1730
|
+
cmd_thinking_reset
|
|
1731
|
+
else
|
|
1732
|
+
{
|
|
1733
|
+
success: false,
|
|
1734
|
+
message: "Unknown subcommand: #{subcommand}\nUsage: /thinking <show|set|max|reset> [tier]",
|
|
1735
|
+
action: :none
|
|
1736
|
+
}
|
|
1737
|
+
end
|
|
1738
|
+
rescue => e
|
|
1739
|
+
Aidp.log_error("repl_macros", "Failed to execute thinking command", error: e.message)
|
|
1740
|
+
{
|
|
1741
|
+
success: false,
|
|
1742
|
+
message: "Failed to execute thinking command: #{e.message}",
|
|
1743
|
+
action: :none
|
|
1744
|
+
}
|
|
1745
|
+
end
|
|
1746
|
+
|
|
1747
|
+
# Subcommand: /thinking show
|
|
1748
|
+
def cmd_thinking_show
|
|
1749
|
+
require_relative "../harness/configuration"
|
|
1750
|
+
require_relative "../harness/thinking_depth_manager"
|
|
1751
|
+
|
|
1752
|
+
config = Aidp::Harness::Configuration.new(@project_dir)
|
|
1753
|
+
manager = Aidp::Harness::ThinkingDepthManager.new(config, root_dir: @project_dir)
|
|
1754
|
+
|
|
1755
|
+
lines = []
|
|
1756
|
+
lines << "Thinking Depth Configuration:"
|
|
1757
|
+
lines << ""
|
|
1758
|
+
lines << "Current Tier: #{manager.current_tier}"
|
|
1759
|
+
lines << "Default Tier: #{manager.default_tier}"
|
|
1760
|
+
lines << "Max Tier: #{manager.max_tier}"
|
|
1761
|
+
lines << ""
|
|
1762
|
+
|
|
1763
|
+
# Show all available tiers
|
|
1764
|
+
require_relative "../harness/capability_registry"
|
|
1765
|
+
lines << "Available Tiers:"
|
|
1766
|
+
Aidp::Harness::CapabilityRegistry::VALID_TIERS.each do |tier|
|
|
1767
|
+
marker = if tier == manager.current_tier
|
|
1768
|
+
"โ"
|
|
1769
|
+
elsif tier == manager.max_tier
|
|
1770
|
+
"โ"
|
|
1771
|
+
else
|
|
1772
|
+
" "
|
|
1773
|
+
end
|
|
1774
|
+
lines << " #{marker} #{tier}"
|
|
1775
|
+
end
|
|
1776
|
+
lines << ""
|
|
1777
|
+
lines << "Legend: โ current, โ max allowed"
|
|
1778
|
+
lines << ""
|
|
1779
|
+
|
|
1780
|
+
# Show current model selection
|
|
1781
|
+
current_model = manager.select_model_for_tier
|
|
1782
|
+
if current_model
|
|
1783
|
+
provider, model_name, model_data = current_model
|
|
1784
|
+
lines << "Current Model: #{provider}/#{model_name}"
|
|
1785
|
+
lines << " Tier: #{model_data["tier"]}" if model_data["tier"]
|
|
1786
|
+
lines << " Context Window: #{model_data["context_window"]}" if model_data["context_window"]
|
|
1787
|
+
else
|
|
1788
|
+
lines << "Current Model: (none selected)"
|
|
1789
|
+
end
|
|
1790
|
+
|
|
1791
|
+
lines << ""
|
|
1792
|
+
lines << "Provider Switching: #{config.allow_provider_switch_for_tier? ? "enabled" : "disabled"}"
|
|
1793
|
+
|
|
1794
|
+
# Show escalation config
|
|
1795
|
+
escalation = config.escalation_config
|
|
1796
|
+
lines << ""
|
|
1797
|
+
lines << "Escalation Settings:"
|
|
1798
|
+
lines << " Fail Attempts Threshold: #{escalation[:on_fail_attempts]}"
|
|
1799
|
+
if escalation[:on_complexity_threshold]&.any?
|
|
1800
|
+
lines << " Complexity Thresholds:"
|
|
1801
|
+
escalation[:on_complexity_threshold].each do |key, value|
|
|
1802
|
+
lines << " #{key}: #{value}"
|
|
1803
|
+
end
|
|
1804
|
+
end
|
|
1805
|
+
|
|
1806
|
+
{
|
|
1807
|
+
success: true,
|
|
1808
|
+
message: lines.join("\n"),
|
|
1809
|
+
action: :display
|
|
1810
|
+
}
|
|
1811
|
+
end
|
|
1812
|
+
|
|
1813
|
+
# Subcommand: /thinking set <tier>
|
|
1814
|
+
def cmd_thinking_set(tier)
|
|
1815
|
+
unless tier
|
|
1816
|
+
return {
|
|
1817
|
+
success: false,
|
|
1818
|
+
message: "Usage: /thinking set <tier>\nTiers: mini, standard, thinking, pro, max",
|
|
1819
|
+
action: :none
|
|
1820
|
+
}
|
|
1821
|
+
end
|
|
1822
|
+
|
|
1823
|
+
require_relative "../harness/configuration"
|
|
1824
|
+
require_relative "../harness/thinking_depth_manager"
|
|
1825
|
+
|
|
1826
|
+
config = Aidp::Harness::Configuration.new(@project_dir)
|
|
1827
|
+
manager = Aidp::Harness::ThinkingDepthManager.new(config, root_dir: @project_dir)
|
|
1828
|
+
|
|
1829
|
+
old_tier = manager.current_tier
|
|
1830
|
+
manager.current_tier = tier
|
|
1831
|
+
|
|
1832
|
+
{
|
|
1833
|
+
success: true,
|
|
1834
|
+
message: "Thinking tier changed: #{old_tier} โ #{tier}\nMax tier: #{manager.max_tier}",
|
|
1835
|
+
action: :tier_changed
|
|
1836
|
+
}
|
|
1837
|
+
rescue ArgumentError => e
|
|
1838
|
+
{
|
|
1839
|
+
success: false,
|
|
1840
|
+
message: "Invalid tier: #{e.message}\nValid tiers: mini, standard, thinking, pro, max",
|
|
1841
|
+
action: :none
|
|
1842
|
+
}
|
|
1843
|
+
end
|
|
1844
|
+
|
|
1845
|
+
# Subcommand: /thinking max <tier>
|
|
1846
|
+
def cmd_thinking_max(tier)
|
|
1847
|
+
unless tier
|
|
1848
|
+
return {
|
|
1849
|
+
success: false,
|
|
1850
|
+
message: "Usage: /thinking max <tier>\nTiers: mini, standard, thinking, pro, max",
|
|
1851
|
+
action: :none
|
|
1852
|
+
}
|
|
1853
|
+
end
|
|
1854
|
+
|
|
1855
|
+
require_relative "../harness/configuration"
|
|
1856
|
+
require_relative "../harness/thinking_depth_manager"
|
|
1857
|
+
|
|
1858
|
+
config = Aidp::Harness::Configuration.new(@project_dir)
|
|
1859
|
+
manager = Aidp::Harness::ThinkingDepthManager.new(config, root_dir: @project_dir)
|
|
1860
|
+
|
|
1861
|
+
old_max = manager.max_tier
|
|
1862
|
+
manager.max_tier = tier
|
|
1863
|
+
|
|
1864
|
+
{
|
|
1865
|
+
success: true,
|
|
1866
|
+
message: "Max tier changed: #{old_max} โ #{tier}\nCurrent tier: #{manager.current_tier}",
|
|
1867
|
+
action: :max_tier_changed
|
|
1868
|
+
}
|
|
1869
|
+
rescue ArgumentError => e
|
|
1870
|
+
{
|
|
1871
|
+
success: false,
|
|
1872
|
+
message: "Invalid tier: #{e.message}\nValid tiers: mini, standard, thinking, pro, max",
|
|
1873
|
+
action: :none
|
|
1874
|
+
}
|
|
1875
|
+
end
|
|
1876
|
+
|
|
1877
|
+
# Subcommand: /thinking reset
|
|
1878
|
+
def cmd_thinking_reset
|
|
1879
|
+
require_relative "../harness/configuration"
|
|
1880
|
+
require_relative "../harness/thinking_depth_manager"
|
|
1881
|
+
|
|
1882
|
+
config = Aidp::Harness::Configuration.new(@project_dir)
|
|
1883
|
+
manager = Aidp::Harness::ThinkingDepthManager.new(config, root_dir: @project_dir)
|
|
1884
|
+
|
|
1885
|
+
old_tier = manager.current_tier
|
|
1886
|
+
manager.reset_to_default
|
|
1887
|
+
|
|
1888
|
+
{
|
|
1889
|
+
success: true,
|
|
1890
|
+
message: "Thinking tier reset: #{old_tier} โ #{manager.current_tier}\nEscalation count cleared",
|
|
1891
|
+
action: :tier_reset
|
|
1892
|
+
}
|
|
1893
|
+
end
|
|
1894
|
+
|
|
1895
|
+
# Command: /prompt - Inspect and control prompt optimization
|
|
1896
|
+
def cmd_prompt(args)
|
|
1897
|
+
subcommand = args[0]
|
|
1898
|
+
|
|
1899
|
+
case subcommand
|
|
1900
|
+
when "explain"
|
|
1901
|
+
cmd_prompt_explain
|
|
1902
|
+
when "stats"
|
|
1903
|
+
cmd_prompt_stats
|
|
1904
|
+
when "expand"
|
|
1905
|
+
cmd_prompt_expand(args[1])
|
|
1906
|
+
when "reset"
|
|
1907
|
+
cmd_prompt_reset
|
|
1908
|
+
else
|
|
1909
|
+
{
|
|
1910
|
+
success: false,
|
|
1911
|
+
message: "Unknown subcommand: #{subcommand}\nUsage: /prompt <explain|stats|expand|reset>",
|
|
1912
|
+
action: :none
|
|
1913
|
+
}
|
|
1914
|
+
end
|
|
1915
|
+
rescue => e
|
|
1916
|
+
Aidp.log_error("repl_macros", "Failed to execute prompt command", error: e.message)
|
|
1917
|
+
{
|
|
1918
|
+
success: false,
|
|
1919
|
+
message: "Failed to execute prompt command: #{e.message}",
|
|
1920
|
+
action: :none
|
|
1921
|
+
}
|
|
1922
|
+
end
|
|
1923
|
+
|
|
1924
|
+
# Subcommand: /prompt explain
|
|
1925
|
+
# Shows which fragments were selected for the current prompt and why
|
|
1926
|
+
def cmd_prompt_explain
|
|
1927
|
+
require_relative "prompt_manager"
|
|
1928
|
+
|
|
1929
|
+
prompt_manager = PromptManager.new(@project_dir, config: load_config)
|
|
1930
|
+
|
|
1931
|
+
unless prompt_manager.optimization_enabled?
|
|
1932
|
+
return {
|
|
1933
|
+
success: false,
|
|
1934
|
+
message: "Prompt optimization is not enabled. Check your .aidp/config.yml:\n" \
|
|
1935
|
+
"prompt_optimization:\n enabled: true",
|
|
1936
|
+
action: :none
|
|
1937
|
+
}
|
|
1938
|
+
end
|
|
1939
|
+
|
|
1940
|
+
unless prompt_manager.last_optimization_stats
|
|
1941
|
+
return {
|
|
1942
|
+
success: false,
|
|
1943
|
+
message: "No optimization performed yet. Prompt optimization will be used on the next work loop iteration.",
|
|
1944
|
+
action: :none
|
|
1945
|
+
}
|
|
1946
|
+
end
|
|
1947
|
+
|
|
1948
|
+
report = prompt_manager.optimization_report
|
|
1949
|
+
{
|
|
1950
|
+
success: true,
|
|
1951
|
+
message: report,
|
|
1952
|
+
action: :show_optimization_report
|
|
1953
|
+
}
|
|
1954
|
+
end
|
|
1955
|
+
|
|
1956
|
+
# Subcommand: /prompt stats
|
|
1957
|
+
# Shows overall optimizer statistics across all runs
|
|
1958
|
+
def cmd_prompt_stats
|
|
1959
|
+
require_relative "prompt_manager"
|
|
1960
|
+
|
|
1961
|
+
prompt_manager = PromptManager.new(@project_dir, config: load_config)
|
|
1962
|
+
|
|
1963
|
+
unless prompt_manager.optimization_enabled?
|
|
1964
|
+
return {
|
|
1965
|
+
success: false,
|
|
1966
|
+
message: "Prompt optimization is not enabled.",
|
|
1967
|
+
action: :none
|
|
1968
|
+
}
|
|
1969
|
+
end
|
|
1970
|
+
|
|
1971
|
+
stats = prompt_manager.optimizer_stats
|
|
1972
|
+
unless stats
|
|
1973
|
+
return {
|
|
1974
|
+
success: false,
|
|
1975
|
+
message: "No optimization statistics available.",
|
|
1976
|
+
action: :none
|
|
1977
|
+
}
|
|
1978
|
+
end
|
|
1979
|
+
|
|
1980
|
+
lines = []
|
|
1981
|
+
lines << "# Prompt Optimizer Statistics"
|
|
1982
|
+
lines << ""
|
|
1983
|
+
lines << "- **Total Runs**: #{stats[:runs_count]}"
|
|
1984
|
+
lines << "- **Total Fragments Indexed**: #{stats[:total_fragments_indexed]}"
|
|
1985
|
+
lines << "- **Total Fragments Selected**: #{stats[:total_fragments_selected]}"
|
|
1986
|
+
lines << "- **Total Fragments Excluded**: #{stats[:total_fragments_excluded]}"
|
|
1987
|
+
lines << "- **Total Tokens Used**: #{stats[:total_tokens_used]}"
|
|
1988
|
+
lines << "- **Average Fragments/Run**: #{stats[:average_fragments_selected]}"
|
|
1989
|
+
lines << "- **Average Budget Utilization**: #{stats[:average_budget_utilization]}%"
|
|
1990
|
+
lines << "- **Average Optimization Time**: #{stats[:average_optimization_time_ms]}ms"
|
|
1991
|
+
|
|
1992
|
+
{
|
|
1993
|
+
success: true,
|
|
1994
|
+
message: lines.join("\n"),
|
|
1995
|
+
action: :show_optimizer_stats
|
|
1996
|
+
}
|
|
1997
|
+
end
|
|
1998
|
+
|
|
1999
|
+
# Subcommand: /prompt expand <fragment_id>
|
|
2000
|
+
# Adds a specific omitted fragment to the next prompt
|
|
2001
|
+
def cmd_prompt_expand(fragment_id)
|
|
2002
|
+
unless fragment_id
|
|
2003
|
+
return {
|
|
2004
|
+
success: false,
|
|
2005
|
+
message: "Usage: /prompt expand <fragment_id>\nUse /prompt explain to see available fragments",
|
|
2006
|
+
action: :none
|
|
2007
|
+
}
|
|
2008
|
+
end
|
|
2009
|
+
|
|
2010
|
+
# For now, this is a placeholder - full implementation would:
|
|
2011
|
+
# 1. Look up the fragment by ID
|
|
2012
|
+
# 2. Add it to an override list
|
|
2013
|
+
# 3. Include it in the next prompt generation
|
|
2014
|
+
{
|
|
2015
|
+
success: true,
|
|
2016
|
+
message: "Fragment expansion not yet implemented.\n" \
|
|
2017
|
+
"This will be available in a future update to manually include excluded fragments.",
|
|
2018
|
+
action: :feature_not_implemented
|
|
2019
|
+
}
|
|
2020
|
+
end
|
|
2021
|
+
|
|
2022
|
+
# Subcommand: /prompt reset
|
|
2023
|
+
# Clears optimizer cache and resets to default behavior
|
|
2024
|
+
def cmd_prompt_reset
|
|
2025
|
+
require_relative "prompt_manager"
|
|
2026
|
+
|
|
2027
|
+
prompt_manager = PromptManager.new(@project_dir, config: load_config)
|
|
2028
|
+
|
|
2029
|
+
unless prompt_manager.optimization_enabled?
|
|
2030
|
+
return {
|
|
2031
|
+
success: false,
|
|
2032
|
+
message: "Prompt optimization is not enabled.",
|
|
2033
|
+
action: :none
|
|
2034
|
+
}
|
|
2035
|
+
end
|
|
2036
|
+
|
|
2037
|
+
prompt_manager.optimizer.clear_cache
|
|
2038
|
+
|
|
2039
|
+
{
|
|
2040
|
+
success: true,
|
|
2041
|
+
message: "Optimizer cache cleared. Next prompt will use fresh indexing.",
|
|
2042
|
+
action: :optimizer_reset
|
|
2043
|
+
}
|
|
2044
|
+
end
|
|
2045
|
+
|
|
2046
|
+
# Manage persistent tasklist
|
|
2047
|
+
def cmd_tasks(args)
|
|
2048
|
+
tasklist = PersistentTasklist.new(@project_dir)
|
|
2049
|
+
subcommand = args[0]
|
|
2050
|
+
|
|
2051
|
+
case subcommand
|
|
2052
|
+
when "list", nil
|
|
2053
|
+
cmd_tasks_list(tasklist, args[1])
|
|
2054
|
+
when "show"
|
|
2055
|
+
cmd_tasks_show(tasklist, args[1])
|
|
2056
|
+
when "done"
|
|
2057
|
+
cmd_tasks_done(tasklist, args[1])
|
|
2058
|
+
when "abandon"
|
|
2059
|
+
cmd_tasks_abandon(tasklist, args[1], args[2..]&.join(" "))
|
|
2060
|
+
when "stats"
|
|
2061
|
+
cmd_tasks_stats(tasklist)
|
|
2062
|
+
else
|
|
2063
|
+
{
|
|
2064
|
+
success: false,
|
|
2065
|
+
message: "Unknown subcommand: #{subcommand}. Use: list, show, done, abandon, stats",
|
|
2066
|
+
action: :none
|
|
2067
|
+
}
|
|
2068
|
+
end
|
|
2069
|
+
rescue => e
|
|
2070
|
+
Aidp.log_error("repl_macros", "Tasks command failed", error: e.message)
|
|
2071
|
+
{success: false, message: "Error: #{e.message}", action: :none}
|
|
2072
|
+
end
|
|
2073
|
+
|
|
2074
|
+
private
|
|
2075
|
+
|
|
2076
|
+
# List tasks with optional status filter
|
|
2077
|
+
def cmd_tasks_list(tasklist, status_filter = nil)
|
|
2078
|
+
status = status_filter&.to_sym
|
|
2079
|
+
tasks = status ? tasklist.all(status: status) : tasklist.all
|
|
2080
|
+
|
|
2081
|
+
if tasks.empty?
|
|
2082
|
+
message = status ? "No #{status} tasks found." : "No tasks found."
|
|
2083
|
+
return {success: true, message: message, action: :display}
|
|
2084
|
+
end
|
|
2085
|
+
|
|
2086
|
+
# Group by status
|
|
2087
|
+
by_status = tasks.group_by(&:status)
|
|
2088
|
+
output = []
|
|
2089
|
+
|
|
2090
|
+
[:pending, :in_progress, :done, :abandoned].each do |st|
|
|
2091
|
+
next unless by_status[st]
|
|
2092
|
+
next if status && st != status # Skip if filtering by specific status
|
|
2093
|
+
|
|
2094
|
+
output << ""
|
|
2095
|
+
output << "#{st.to_s.upcase.tr("_", " ")} (#{by_status[st].size})"
|
|
2096
|
+
output << "=" * 50
|
|
2097
|
+
|
|
2098
|
+
by_status[st].each do |task|
|
|
2099
|
+
priority_icon = case task.priority
|
|
2100
|
+
when :high then "โ ๏ธ "
|
|
2101
|
+
when :medium then "โ "
|
|
2102
|
+
when :low then "ยท "
|
|
2103
|
+
end
|
|
2104
|
+
|
|
2105
|
+
age = ((Time.now - task.created_at) / 86400).to_i
|
|
2106
|
+
age_str = (age > 0) ? " (#{age}d ago)" : " (today)"
|
|
2107
|
+
|
|
2108
|
+
output << " #{priority_icon}[#{task.id}] #{task.description}#{age_str}"
|
|
2109
|
+
end
|
|
2110
|
+
end
|
|
2111
|
+
|
|
2112
|
+
{
|
|
2113
|
+
success: true,
|
|
2114
|
+
message: output.join("\n"),
|
|
2115
|
+
action: :display
|
|
2116
|
+
}
|
|
2117
|
+
end
|
|
2118
|
+
|
|
2119
|
+
# Show detailed information about a specific task
|
|
2120
|
+
def cmd_tasks_show(tasklist, task_id)
|
|
2121
|
+
return {success: false, message: "Usage: /tasks show <task_id>", action: :none} unless task_id
|
|
2122
|
+
|
|
2123
|
+
task = tasklist.find(task_id)
|
|
2124
|
+
unless task
|
|
2125
|
+
return {success: false, message: "Task not found: #{task_id}", action: :none}
|
|
2126
|
+
end
|
|
2127
|
+
|
|
2128
|
+
output = []
|
|
2129
|
+
output << ""
|
|
2130
|
+
output << "Task Details:"
|
|
2131
|
+
output << "=" * 50
|
|
2132
|
+
output << "ID: #{task.id}"
|
|
2133
|
+
output << "Description: #{task.description}"
|
|
2134
|
+
output << "Status: #{task.status}"
|
|
2135
|
+
output << "Priority: #{task.priority}"
|
|
2136
|
+
output << "Created: #{task.created_at}"
|
|
2137
|
+
output << "Updated: #{task.updated_at}"
|
|
2138
|
+
output << "Session: #{task.session}" if task.session
|
|
2139
|
+
output << "Context: #{task.discovered_during}" if task.discovered_during
|
|
2140
|
+
output << "Started: #{task.started_at}" if task.started_at
|
|
2141
|
+
output << "Completed: #{task.completed_at}" if task.completed_at
|
|
2142
|
+
output << "Abandoned: #{task.abandoned_at} (#{task.abandoned_reason})" if task.abandoned_at
|
|
2143
|
+
output << "Tags: #{task.tags.join(", ")}" if task.tags&.any?
|
|
2144
|
+
|
|
2145
|
+
{
|
|
2146
|
+
success: true,
|
|
2147
|
+
message: output.join("\n"),
|
|
2148
|
+
action: :display
|
|
2149
|
+
}
|
|
2150
|
+
end
|
|
2151
|
+
|
|
2152
|
+
# Mark a task as done
|
|
2153
|
+
def cmd_tasks_done(tasklist, task_id)
|
|
2154
|
+
return {success: false, message: "Usage: /tasks done <task_id>", action: :none} unless task_id
|
|
2155
|
+
|
|
2156
|
+
task = tasklist.update_status(task_id, :done)
|
|
2157
|
+
{
|
|
2158
|
+
success: true,
|
|
2159
|
+
message: "โ Task marked as done: #{task.description}",
|
|
2160
|
+
action: :display
|
|
2161
|
+
}
|
|
2162
|
+
rescue PersistentTasklist::TaskNotFoundError
|
|
2163
|
+
{success: false, message: "Task not found: #{task_id}", action: :none}
|
|
2164
|
+
end
|
|
2165
|
+
|
|
2166
|
+
# Abandon a task with optional reason
|
|
2167
|
+
def cmd_tasks_abandon(tasklist, task_id, reason = nil)
|
|
2168
|
+
return {success: false, message: "Usage: /tasks abandon <task_id> [reason]", action: :none} unless task_id
|
|
2169
|
+
|
|
2170
|
+
task = tasklist.update_status(task_id, :abandoned, reason: reason)
|
|
2171
|
+
message = "โ Task abandoned: #{task.description}"
|
|
2172
|
+
message += " (Reason: #{reason})" if reason
|
|
2173
|
+
|
|
2174
|
+
{
|
|
2175
|
+
success: true,
|
|
2176
|
+
message: message,
|
|
2177
|
+
action: :display
|
|
2178
|
+
}
|
|
2179
|
+
rescue PersistentTasklist::TaskNotFoundError
|
|
2180
|
+
{success: false, message: "Task not found: #{task_id}", action: :none}
|
|
2181
|
+
end
|
|
2182
|
+
|
|
2183
|
+
# Show task statistics
|
|
2184
|
+
def cmd_tasks_stats(tasklist)
|
|
2185
|
+
counts = tasklist.counts
|
|
2186
|
+
|
|
2187
|
+
output = []
|
|
2188
|
+
output << ""
|
|
2189
|
+
output << "Task Statistics:"
|
|
2190
|
+
output << "=" * 50
|
|
2191
|
+
output << "Total: #{counts[:total]}"
|
|
2192
|
+
output << "Pending: #{counts[:pending]}"
|
|
2193
|
+
output << "In Progress: #{counts[:in_progress]}"
|
|
2194
|
+
output << "Done: #{counts[:done]}"
|
|
2195
|
+
output << "Abandoned: #{counts[:abandoned]}"
|
|
2196
|
+
|
|
2197
|
+
{
|
|
2198
|
+
success: true,
|
|
2199
|
+
message: output.join("\n"),
|
|
2200
|
+
action: :display
|
|
2201
|
+
}
|
|
2202
|
+
end
|
|
2203
|
+
|
|
2204
|
+
private
|
|
2205
|
+
|
|
2206
|
+
# Load configuration for prompt commands
|
|
2207
|
+
def load_config
|
|
2208
|
+
require_relative "../harness/configuration"
|
|
2209
|
+
Aidp::Harness::Configuration.new(@project_dir)
|
|
2210
|
+
end
|
|
1492
2211
|
end
|
|
1493
2212
|
end
|
|
1494
2213
|
end
|