devloop 0.5.1__tar.gz → 0.6.0__tar.gz
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.
- {devloop-0.5.1 → devloop-0.6.0}/PKG-INFO +1 -1
- {devloop-0.5.1 → devloop-0.6.0}/pyproject.toml +1 -1
- devloop-0.6.0/src/devloop/cli/agent_rules.py +274 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/main.py +18 -0
- devloop-0.6.0/src/devloop/cli/templates/devloop_agents_template.md +908 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/metrics/__init__.py +10 -0
- devloop-0.6.0/src/devloop/metrics/value_metrics.py +362 -0
- devloop-0.6.0/src/devloop/providers/artifactory_registry.py +267 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/providers/provider_manager.py +2 -0
- devloop-0.6.0/src/devloop/telemetry/__init__.py +5 -0
- devloop-0.6.0/src/devloop/telemetry/telemetry_manager.py +281 -0
- devloop-0.5.1/src/devloop/cli/templates/devloop_agents_template.md +0 -317
- {devloop-0.5.1 → devloop-0.6.0}/LICENSE +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/README.md +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/agent_health_monitor.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/ci_monitor.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/code_rabbit.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/doc_lifecycle.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/echo.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/file_logger.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/formatter.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/git_commit_assistant.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/linter.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/performance_profiler.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/sandbox_helper.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/security_scanner.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/snyk.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/test_runner.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/agents/type_checker.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/agent_publish.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/audit.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/custom_agents.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/feedback.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/marketplace.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/marketplace_server.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/metrics.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/release.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/summary.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/telemetry.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/commands/tools.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/main_v1.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/pre_push_check.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/prerequisites.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/pyodide_installer.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/.devloop/tools-registry.json +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/claude_commands/README.md +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/claude_commands/agent-summary.md +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/claude_commands/devloop-findings.md +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/claude_commands/devloop-status.md +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/claude_commands/extract-findings.md +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/claude_commands/verify-work.md +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/git_hooks/pre-commit +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/git_hooks/pre-commit-checks +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/git_hooks/pre-push +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/supervisor/devloop.conf +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/cli/templates/systemd/devloop.service +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/collectors/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/collectors/base.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/collectors/filesystem.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/collectors/git.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/collectors/manager.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/collectors/process.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/collectors/system.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/action_logger.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/agent.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/agent_audit_logger.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/agent_template.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/amp_integration.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/amp_thread_mapper.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/auto_fix.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/backup_manager.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/claude_adapter.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/config.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/config_schema.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/context.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/context_store.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/contextual_feedback.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/custom_agent.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/daemon_health.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/debug_trace.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/error_handler.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/error_notifier.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/event.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/event_replayer.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/event_store.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/feedback.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/file_lock_manager.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/learning.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/manager.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/operational_health.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/pattern_analyzer.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/pattern_detector.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/performance.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/proactive_feedback.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/summary_formatter.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/summary_generator.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/telemetry.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/tool_dependencies.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/tool_registry.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/tool_runner.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/core/transactional_io.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/integrations/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/integrations/beads_integration.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/lsp/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/lsp/__main__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/lsp/mapper.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/lsp/server.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/api.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/cache.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/http_server.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/installer.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/metadata.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/publisher.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/registry.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/registry_client.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/reviews.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/search.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/marketplace/signing.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/metrics/dora.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/providers/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/providers/ci_provider.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/providers/circleci_provider.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/providers/github_actions_provider.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/providers/gitlab_ci_provider.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/providers/jenkins_provider.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/providers/pypi_registry.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/providers/registry_provider.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/release/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/release/release_manager.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/__init__.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/audit_logger.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/bubblewrap_sandbox.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/cgroups_helper.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/factory.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/no_sandbox.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/package.json +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/path_validator.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/pyodide_runner.js +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/pyodide_sandbox.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/sandbox.py +0 -0
- {devloop-0.5.1 → devloop-0.6.0}/src/devloop/security/token_manager.py +0 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""Agent rules configuration management and merging."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AgentRules:
|
|
10
|
+
"""Manages per-agent rules and configuration files."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, agents_dir: str = ".agents/agents"):
|
|
13
|
+
"""Initialize agent rules manager.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
agents_dir: Directory containing agent subdirectories with rules.yaml files
|
|
17
|
+
"""
|
|
18
|
+
self.agents_dir = agents_dir
|
|
19
|
+
self.agents_path = Path(agents_dir)
|
|
20
|
+
|
|
21
|
+
def discover_agents(self) -> List[str]:
|
|
22
|
+
"""Discover all agents with rules.yaml files.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
List of agent names
|
|
26
|
+
"""
|
|
27
|
+
agents = []
|
|
28
|
+
if self.agents_path.exists():
|
|
29
|
+
for agent_dir in self.agents_path.iterdir():
|
|
30
|
+
if agent_dir.is_dir():
|
|
31
|
+
rules_file = agent_dir / "rules.yaml"
|
|
32
|
+
if rules_file.exists():
|
|
33
|
+
agents.append(agent_dir.name)
|
|
34
|
+
return sorted(agents)
|
|
35
|
+
|
|
36
|
+
def load_agent_rules(self, agent_name: str) -> Optional[Dict[str, Any]]:
|
|
37
|
+
"""Load rules for a specific agent.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
agent_name: Name of the agent
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Parsed rules dictionary or None if not found
|
|
44
|
+
"""
|
|
45
|
+
rules_file = self.agents_path / agent_name / "rules.yaml"
|
|
46
|
+
if not rules_file.exists():
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
with open(rules_file) as f:
|
|
51
|
+
return yaml.safe_load(f) or {}
|
|
52
|
+
except (IOError, yaml.YAMLError):
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
def load_all_rules(self) -> Dict[str, Dict[str, Any]]:
|
|
56
|
+
"""Load rules for all discovered agents.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Dictionary mapping agent names to their rules
|
|
60
|
+
"""
|
|
61
|
+
rules = {}
|
|
62
|
+
for agent_name in self.discover_agents():
|
|
63
|
+
agent_rules = self.load_agent_rules(agent_name)
|
|
64
|
+
if agent_rules:
|
|
65
|
+
rules[agent_name] = agent_rules
|
|
66
|
+
return rules
|
|
67
|
+
|
|
68
|
+
def generate_template(self) -> str:
|
|
69
|
+
"""Generate AGENTS.md template from all agent rules.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Markdown template ready for merging with existing AGENTS.md
|
|
73
|
+
"""
|
|
74
|
+
all_rules = self.load_all_rules()
|
|
75
|
+
if not all_rules:
|
|
76
|
+
return ""
|
|
77
|
+
|
|
78
|
+
template_parts = []
|
|
79
|
+
template_parts.append("# Auto-Generated Agent Configuration\n")
|
|
80
|
+
template_parts.append(
|
|
81
|
+
"This section was generated from agent rules. "
|
|
82
|
+
"Do not edit manually - update `.agents/agents/*/rules.yaml` instead.\n\n"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Section: Preflight Tasks
|
|
86
|
+
preflight_commands = []
|
|
87
|
+
for agent_name, rules in sorted(all_rules.items()):
|
|
88
|
+
if "preflight" in rules and rules["preflight"]:
|
|
89
|
+
preflight_commands.extend(rules["preflight"])
|
|
90
|
+
|
|
91
|
+
if preflight_commands:
|
|
92
|
+
template_parts.append("## Preflight Checklist\n\n")
|
|
93
|
+
template_parts.append(
|
|
94
|
+
"Run these commands at the start of each session:\n\n"
|
|
95
|
+
)
|
|
96
|
+
for cmd in preflight_commands:
|
|
97
|
+
template_parts.append(f"```bash\n{cmd}\n```\n\n")
|
|
98
|
+
|
|
99
|
+
# Section: Dependencies
|
|
100
|
+
all_dependencies = []
|
|
101
|
+
for agent_name, rules in sorted(all_rules.items()):
|
|
102
|
+
if "dependencies" in rules and rules["dependencies"]:
|
|
103
|
+
for dep in rules["dependencies"]:
|
|
104
|
+
all_dependencies.append(dep)
|
|
105
|
+
|
|
106
|
+
if all_dependencies:
|
|
107
|
+
template_parts.append("## Dependencies\n\n")
|
|
108
|
+
for dep in all_dependencies:
|
|
109
|
+
requires = dep.get("requires", "unknown")
|
|
110
|
+
version = dep.get("version", "any")
|
|
111
|
+
template_parts.append(f"- **{requires}**: {version}\n")
|
|
112
|
+
template_parts.append("\n")
|
|
113
|
+
|
|
114
|
+
# Section: DevLoop Hints
|
|
115
|
+
all_hints = []
|
|
116
|
+
for agent_name, rules in sorted(all_rules.items()):
|
|
117
|
+
if "devloop_hints" in rules and rules["devloop_hints"]:
|
|
118
|
+
for hint in rules["devloop_hints"]:
|
|
119
|
+
all_hints.append(hint)
|
|
120
|
+
|
|
121
|
+
if all_hints:
|
|
122
|
+
template_parts.append("## Development Hints\n\n")
|
|
123
|
+
for hint in all_hints:
|
|
124
|
+
title = hint.get("title", "Tip")
|
|
125
|
+
description = hint.get("description", "")
|
|
126
|
+
workaround = hint.get("workaround")
|
|
127
|
+
|
|
128
|
+
template_parts.append(f"### {title}\n\n")
|
|
129
|
+
template_parts.append(f"{description}\n\n")
|
|
130
|
+
if workaround:
|
|
131
|
+
template_parts.append("**Workaround**:\n\n")
|
|
132
|
+
template_parts.append(f"```bash\n{workaround}\n```\n\n")
|
|
133
|
+
|
|
134
|
+
return "".join(template_parts)
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def merge_templates(existing_md: str, generated_template: str) -> str:
|
|
138
|
+
"""Intelligently merge generated template with existing AGENTS.md.
|
|
139
|
+
|
|
140
|
+
This function:
|
|
141
|
+
1. Detects and preserves existing custom content
|
|
142
|
+
2. Skips duplicate sections
|
|
143
|
+
3. Merges related content by topic
|
|
144
|
+
4. Maintains semantic structure
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
existing_md: Current AGENTS.md content
|
|
148
|
+
generated_template: Generated template from agent rules
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Merged AGENTS.md content
|
|
152
|
+
"""
|
|
153
|
+
# If no existing content, just return generated
|
|
154
|
+
if not existing_md.strip():
|
|
155
|
+
return generated_template
|
|
156
|
+
|
|
157
|
+
# Parse both documents into sections
|
|
158
|
+
existing_sections = _parse_markdown_sections(existing_md)
|
|
159
|
+
|
|
160
|
+
# Start with generated content (it's the "source of truth" from rules)
|
|
161
|
+
merged_parts = []
|
|
162
|
+
|
|
163
|
+
# Detect auto-generated marker
|
|
164
|
+
if "Auto-Generated Agent Configuration" in existing_md:
|
|
165
|
+
# Remove old auto-generated section
|
|
166
|
+
existing_md = _remove_section(
|
|
167
|
+
existing_md, "Auto-Generated Agent Configuration"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Add generated content first
|
|
171
|
+
merged_parts.append(generated_template)
|
|
172
|
+
|
|
173
|
+
# Then add custom content from existing file (preserve user modifications)
|
|
174
|
+
custom_sections = [
|
|
175
|
+
sec
|
|
176
|
+
for sec in existing_sections
|
|
177
|
+
if not _is_auto_generated_section(sec["title"])
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
if custom_sections:
|
|
181
|
+
merged_parts.append("\n# Custom Configuration\n\n")
|
|
182
|
+
for section in custom_sections:
|
|
183
|
+
merged_parts.append(f"## {section['title']}\n\n")
|
|
184
|
+
merged_parts.append(section["content"])
|
|
185
|
+
merged_parts.append("\n")
|
|
186
|
+
|
|
187
|
+
return "".join(merged_parts)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _parse_markdown_sections(content: str) -> List[Dict[str, str]]:
|
|
191
|
+
"""Parse markdown into sections.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
content: Markdown content
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
List of sections with title and content
|
|
198
|
+
"""
|
|
199
|
+
sections = []
|
|
200
|
+
current_title = "Preamble"
|
|
201
|
+
current_content = []
|
|
202
|
+
|
|
203
|
+
for line in content.split("\n"):
|
|
204
|
+
if line.startswith("## "):
|
|
205
|
+
# Save previous section
|
|
206
|
+
if current_content or current_title != "Preamble":
|
|
207
|
+
sections.append(
|
|
208
|
+
{
|
|
209
|
+
"title": current_title,
|
|
210
|
+
"content": "\n".join(current_content),
|
|
211
|
+
}
|
|
212
|
+
)
|
|
213
|
+
# Start new section
|
|
214
|
+
current_title = line[3:].strip()
|
|
215
|
+
current_content = []
|
|
216
|
+
else:
|
|
217
|
+
current_content.append(line)
|
|
218
|
+
|
|
219
|
+
# Save last section
|
|
220
|
+
if current_content or current_title != "Preamble":
|
|
221
|
+
sections.append(
|
|
222
|
+
{
|
|
223
|
+
"title": current_title,
|
|
224
|
+
"content": "\n".join(current_content),
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
return sections
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _is_auto_generated_section(title: str) -> bool:
|
|
232
|
+
"""Check if a section is auto-generated.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
title: Section title
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
True if section is auto-generated
|
|
239
|
+
"""
|
|
240
|
+
auto_sections = {
|
|
241
|
+
"Preflight Checklist",
|
|
242
|
+
"Dependencies",
|
|
243
|
+
"Development Hints",
|
|
244
|
+
"Agent Configuration",
|
|
245
|
+
"Auto-Generated Agent Configuration",
|
|
246
|
+
}
|
|
247
|
+
return title in auto_sections
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _remove_section(content: str, section_title: str) -> str:
|
|
251
|
+
"""Remove a section from markdown content.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
content: Markdown content
|
|
255
|
+
section_title: Title of section to remove
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Content with section removed
|
|
259
|
+
"""
|
|
260
|
+
sections = _parse_markdown_sections(content)
|
|
261
|
+
remaining = [
|
|
262
|
+
sec for sec in sections if sec["title"].lower() != section_title.lower()
|
|
263
|
+
]
|
|
264
|
+
|
|
265
|
+
# Reconstruct
|
|
266
|
+
parts = []
|
|
267
|
+
for section in remaining:
|
|
268
|
+
if section["title"] != "Preamble":
|
|
269
|
+
parts.append(f"## {section['title']}\n")
|
|
270
|
+
parts.append(section["content"])
|
|
271
|
+
if section != remaining[-1]:
|
|
272
|
+
parts.append("\n")
|
|
273
|
+
|
|
274
|
+
return "\n".join(parts)
|
|
@@ -685,6 +685,24 @@ def init(
|
|
|
685
685
|
missing_sections.append("Token security")
|
|
686
686
|
if "Development Discipline" not in content:
|
|
687
687
|
missing_sections.append("Development discipline")
|
|
688
|
+
if "Pre-Flight Development Checklist" not in content:
|
|
689
|
+
missing_sections.append("Pre-flight checklist")
|
|
690
|
+
if "CI Verification" not in content and "Pre-Push Hook" not in content:
|
|
691
|
+
missing_sections.append("CI verification (pre-push hook)")
|
|
692
|
+
if "Documentation Practices" not in content:
|
|
693
|
+
missing_sections.append("Documentation practices")
|
|
694
|
+
if "Publishing & Security Considerations" not in content:
|
|
695
|
+
missing_sections.append("Publishing & security considerations")
|
|
696
|
+
if "Release Process" not in content:
|
|
697
|
+
missing_sections.append("Release process")
|
|
698
|
+
if "Configuration" not in content:
|
|
699
|
+
missing_sections.append("Configuration (logging, agents)")
|
|
700
|
+
if "Security & Privacy" not in content:
|
|
701
|
+
missing_sections.append("Security & privacy")
|
|
702
|
+
if "Success Metrics" not in content:
|
|
703
|
+
missing_sections.append("Success metrics")
|
|
704
|
+
if "Future Considerations" not in content:
|
|
705
|
+
missing_sections.append("Future considerations")
|
|
688
706
|
|
|
689
707
|
needs_devloop_content = len(missing_sections) > 0
|
|
690
708
|
|