aes-cli 0.7.0__tar.gz → 0.12.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.
- {aes_cli-0.7.0 → aes_cli-0.12.0}/PKG-INFO +1 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/__init__.py +1 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/__main__.py +9 -2
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/commands/init.py +104 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/commands/inspect.py +44 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/commands/sync.py +88 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/config.py +12 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/domains.py +39 -12
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/global_config.py +17 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/i18n/_messages.py +1 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/i18n/ja.py +1 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/registry.py +4 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/agent.yaml.jinja +2 -2
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/bom.yaml.jinja +1 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/instructions.md.jinja +6 -4
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/ja/instructions.md.jinja +2 -0
- aes_cli-0.12.0/aes/scaffold/ja/memory_command.md.jinja +195 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/ja/operations.md.jinja +5 -4
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/ja/orchestrator.md.jinja +4 -3
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/ja/setup.md.jinja +20 -2
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/ja/workflow_command.md.jinja +8 -5
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/local.example.yaml.jinja +2 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/local.yaml.jinja +2 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/memory_command.md.jinja +98 -2
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/operations.md.jinja +5 -4
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/orchestrator.md.jinja +4 -3
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/permissions.yaml.jinja +2 -2
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/setup.md.jinja +71 -9
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/skill.yaml.jinja +2 -2
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/workflow.yaml.jinja +1 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/workflow_command.md.jinja +8 -5
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/schemas/agent.schema.json +20 -0
- aes_cli-0.12.0/aes/schemas/instinct.schema.json +137 -0
- aes_cli-0.12.0/aes/schemas/learning-config.schema.json +129 -0
- aes_cli-0.12.0/aes/schemas/lifecycle.schema.json +196 -0
- aes_cli-0.12.0/aes/schemas/rules-config.schema.json +55 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/targets/__init__.py +2 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/targets/_base.py +5 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/targets/_composer.py +246 -2
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/targets/claude.py +51 -13
- aes_cli-0.12.0/aes/targets/codex.py +138 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/targets/copilot.py +30 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/targets/cursor.py +30 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/targets/openclaw.py +64 -5
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/targets/windsurf.py +30 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/validator.py +83 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes_cli.egg-info/PKG-INFO +1 -1
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes_cli.egg-info/SOURCES.txt +10 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/pyproject.toml +1 -1
- aes_cli-0.12.0/tests/test_codex_target.py +304 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_init.py +1 -1
- aes_cli-0.12.0/tests/test_learning_validation.py +344 -0
- aes_cli-0.12.0/tests/test_lifecycle_validation.py +234 -0
- aes_cli-0.12.0/tests/test_project_locale.py +94 -0
- aes_cli-0.12.0/tests/test_rules_validation.py +146 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_sync.py +6 -6
- aes_cli-0.7.0/aes/scaffold/ja/memory_command.md.jinja +0 -99
- {aes_cli-0.7.0 → aes_cli-0.12.0}/README.md +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/analyzer.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/commands/__init__.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/commands/bom.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/commands/install.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/commands/publish.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/commands/search.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/commands/status.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/commands/upgrade.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/commands/validate.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/frameworks.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/i18n/__init__.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/i18n/domains_ja.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/mcp_server.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/migrations.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/agentignore.jinja +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/ja/skill.md.jinja +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/scaffold/skill.md.jinja +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/schemas/bom.schema.json +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/schemas/decision-record.schema.json +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/schemas/permissions.schema.json +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/schemas/registry.schema.json +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/schemas/skill.schema.json +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes/schemas/workflow.schema.json +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes_cli.egg-info/dependency_links.txt +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes_cli.egg-info/entry_points.txt +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes_cli.egg-info/requires.txt +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/aes_cli.egg-info/top_level.txt +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/setup.cfg +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_analyzer.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_bom.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_frameworks.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_inspect.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_install.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_mcp_server.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_openclaw_target.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_publish.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_registry.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_search.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_status.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_upgrade.py +0 -0
- {aes_cli-0.7.0 → aes_cli-0.12.0}/tests/test_validate.py +0 -0
|
@@ -53,7 +53,7 @@ def cli(lang: Optional[str] = None) -> None:
|
|
|
53
53
|
"""
|
|
54
54
|
from aes.i18n import init_locale
|
|
55
55
|
|
|
56
|
-
# Priority: --lang flag > AES_LANG env > ~/.aes/config.yaml > first-run prompt
|
|
56
|
+
# Priority: --lang flag > AES_LANG env > .agent/local.yaml > ~/.aes/config.yaml > first-run prompt
|
|
57
57
|
if lang:
|
|
58
58
|
init_locale(lang)
|
|
59
59
|
return
|
|
@@ -63,7 +63,14 @@ def cli(lang: Optional[str] = None) -> None:
|
|
|
63
63
|
init_locale(env_lang)
|
|
64
64
|
return
|
|
65
65
|
|
|
66
|
-
from
|
|
66
|
+
from pathlib import Path
|
|
67
|
+
from aes.global_config import get_locale, get_project_locale
|
|
68
|
+
|
|
69
|
+
project_locale = get_project_locale(Path.cwd())
|
|
70
|
+
if project_locale:
|
|
71
|
+
init_locale(project_locale)
|
|
72
|
+
return
|
|
73
|
+
|
|
67
74
|
saved = get_locale()
|
|
68
75
|
if saved is None and sys.stdin.isatty():
|
|
69
76
|
_prompt_language()
|
|
@@ -22,9 +22,17 @@ from aes.config import (
|
|
|
22
22
|
AGENTIGNORE_FILE,
|
|
23
23
|
BOM_FILE,
|
|
24
24
|
DECISIONS_DIR,
|
|
25
|
+
INSTINCTS_DIR,
|
|
26
|
+
LEARNING_CONFIG_FILE,
|
|
27
|
+
LEARNING_DIR,
|
|
28
|
+
LIFECYCLE_FILE,
|
|
25
29
|
LOCAL_EXAMPLE_FILE,
|
|
26
30
|
LOCAL_FILE,
|
|
31
|
+
LOGS_DIR,
|
|
32
|
+
RULES_CONFIG_FILE,
|
|
33
|
+
RULES_DIR,
|
|
27
34
|
SCAFFOLD_DIR,
|
|
35
|
+
SCRIPTS_DIR,
|
|
28
36
|
SKILLS_DIR,
|
|
29
37
|
REGISTRY_DIR,
|
|
30
38
|
WORKFLOWS_DIR,
|
|
@@ -451,6 +459,8 @@ def _print_post_init_summary(
|
|
|
451
459
|
("Cursor", ".cursorrules"),
|
|
452
460
|
("Copilot", ".github/copilot-instructions.md"),
|
|
453
461
|
("Windsurf", ".windsurfrules"),
|
|
462
|
+
("Codex", "AGENTS.md"),
|
|
463
|
+
("OpenClaw", ".openclaw/openclaw.json"),
|
|
454
464
|
]
|
|
455
465
|
for tool_name, file_name in sync_targets:
|
|
456
466
|
if (project_root / file_name).exists():
|
|
@@ -735,6 +745,100 @@ def init_cmd(
|
|
|
735
745
|
content = _render_template(env, "operations.md.jinja", ops_context)
|
|
736
746
|
(agent_dir / MEMORY_DIR / "operations.md").write_text(content)
|
|
737
747
|
|
|
748
|
+
# Lifecycle hooks (all domains)
|
|
749
|
+
if domain_config.scaffold_lifecycle:
|
|
750
|
+
(agent_dir / SCRIPTS_DIR).mkdir(exist_ok=True)
|
|
751
|
+
(agent_dir / LOGS_DIR).mkdir(exist_ok=True)
|
|
752
|
+
lifecycle_content = (
|
|
753
|
+
"# .agent/lifecycle.yaml — Lifecycle Hooks\n"
|
|
754
|
+
"apiVersion: aes/v1\n"
|
|
755
|
+
"kind: Lifecycle\n"
|
|
756
|
+
f"\nprofile: {domain_config.lifecycle_profile}\n"
|
|
757
|
+
"\nhooks:\n"
|
|
758
|
+
" on_session_start:\n"
|
|
759
|
+
" - name: restore-context\n"
|
|
760
|
+
" description: Load previous session summary into context\n"
|
|
761
|
+
" profile: minimal\n"
|
|
762
|
+
" action: script\n"
|
|
763
|
+
" command: node .agent/scripts/restore-context.js\n"
|
|
764
|
+
" timeout_seconds: 10\n"
|
|
765
|
+
" async: false\n"
|
|
766
|
+
" fail_strategy: warn\n"
|
|
767
|
+
"\n"
|
|
768
|
+
" on_session_end:\n"
|
|
769
|
+
" - name: persist-summary\n"
|
|
770
|
+
" description: Save session summary for next session\n"
|
|
771
|
+
" profile: minimal\n"
|
|
772
|
+
" action: script\n"
|
|
773
|
+
" command: node .agent/scripts/persist-summary.js\n"
|
|
774
|
+
" timeout_seconds: 15\n"
|
|
775
|
+
" async: true\n"
|
|
776
|
+
" fail_strategy: warn\n"
|
|
777
|
+
)
|
|
778
|
+
(agent_dir / LIFECYCLE_FILE).write_text(lifecycle_content)
|
|
779
|
+
|
|
780
|
+
# Continuous learning (agent-integrated domains only)
|
|
781
|
+
if domain_config.scaffold_learning:
|
|
782
|
+
(agent_dir / LEARNING_DIR).mkdir(exist_ok=True)
|
|
783
|
+
(agent_dir / INSTINCTS_DIR / "active").mkdir(parents=True, exist_ok=True)
|
|
784
|
+
(agent_dir / INSTINCTS_DIR / "candidates").mkdir(exist_ok=True)
|
|
785
|
+
(agent_dir / INSTINCTS_DIR / "archived").mkdir(exist_ok=True)
|
|
786
|
+
learning_content = (
|
|
787
|
+
"# .agent/learning/config.yaml\n"
|
|
788
|
+
"apiVersion: aes/v1\n"
|
|
789
|
+
"kind: LearningConfig\n"
|
|
790
|
+
"\nextraction:\n"
|
|
791
|
+
" enabled: true\n"
|
|
792
|
+
" auto_extract: true\n"
|
|
793
|
+
" min_session_length: 5\n"
|
|
794
|
+
" max_candidates_per_session: 3\n"
|
|
795
|
+
"\nconfidence:\n"
|
|
796
|
+
" initial_score: 0.4\n"
|
|
797
|
+
" promotion_threshold: 0.6\n"
|
|
798
|
+
" promotion_min_validations: 3\n"
|
|
799
|
+
" decay_rate_per_week: 0.01\n"
|
|
800
|
+
" min_score: 0.3\n"
|
|
801
|
+
"\ncontext_loading:\n"
|
|
802
|
+
" max_instincts_in_context: 10\n"
|
|
803
|
+
" sort_by: confidence_score\n"
|
|
804
|
+
" token_budget: 2000\n"
|
|
805
|
+
" format: compact\n"
|
|
806
|
+
)
|
|
807
|
+
(agent_dir / LEARNING_CONFIG_FILE).write_text(learning_content)
|
|
808
|
+
|
|
809
|
+
# Rules & conventions (all domains)
|
|
810
|
+
if domain_config.scaffold_rules:
|
|
811
|
+
(agent_dir / RULES_DIR).mkdir(exist_ok=True)
|
|
812
|
+
(agent_dir / RULES_DIR / "common").mkdir(exist_ok=True)
|
|
813
|
+
rules_content = (
|
|
814
|
+
"# .agent/rules/rules.yaml\n"
|
|
815
|
+
"apiVersion: aes/v1\n"
|
|
816
|
+
"kind: RulesConfig\n"
|
|
817
|
+
"\nloading:\n"
|
|
818
|
+
" always: [common]\n"
|
|
819
|
+
)
|
|
820
|
+
(agent_dir / RULES_CONFIG_FILE).write_text(rules_content)
|
|
821
|
+
|
|
822
|
+
# Starter rule
|
|
823
|
+
starter_rule = (
|
|
824
|
+
"---\n"
|
|
825
|
+
"name: testing\n"
|
|
826
|
+
"scope: common\n"
|
|
827
|
+
"priority: high\n"
|
|
828
|
+
"---\n\n"
|
|
829
|
+
"# Testing Standards\n\n"
|
|
830
|
+
"## Requirements\n"
|
|
831
|
+
"- All public functions must have unit tests\n"
|
|
832
|
+
"- No test should take longer than 5 seconds\n\n"
|
|
833
|
+
"## Patterns\n"
|
|
834
|
+
"- Use Arrange-Act-Assert structure\n"
|
|
835
|
+
"- Mock external dependencies\n\n"
|
|
836
|
+
"## Anti-patterns\n"
|
|
837
|
+
"- Never test implementation details\n"
|
|
838
|
+
"- Never share state between tests\n"
|
|
839
|
+
)
|
|
840
|
+
(agent_dir / RULES_DIR / "common" / "testing.md").write_text(starter_rule)
|
|
841
|
+
|
|
738
842
|
# Auto-sync: generate tool-specific config files
|
|
739
843
|
synced_files = run_sync(project_root, force=True, quiet=True)
|
|
740
844
|
_write_mcp_config(project_root)
|
|
@@ -12,7 +12,11 @@ import yaml
|
|
|
12
12
|
from rich.console import Console
|
|
13
13
|
from rich.table import Table
|
|
14
14
|
|
|
15
|
-
from aes.config import
|
|
15
|
+
from aes.config import (
|
|
16
|
+
AGENT_DIR, BOM_FILE, DECISIONS_DIR,
|
|
17
|
+
INSTINCTS_DIR, LEARNING_CONFIG_FILE, LIFECYCLE_FILE,
|
|
18
|
+
RULES_CONFIG_FILE, RULES_DIR,
|
|
19
|
+
)
|
|
16
20
|
from aes.i18n import t
|
|
17
21
|
from aes.registry import (
|
|
18
22
|
fetch_index,
|
|
@@ -246,6 +250,45 @@ def _inspect_local(path: str) -> None:
|
|
|
246
250
|
console.print(f" {t('inspect.decisions_count', count=dr_count)}")
|
|
247
251
|
console.print()
|
|
248
252
|
|
|
253
|
+
# Lifecycle hooks
|
|
254
|
+
lifecycle_path = agent_dir / LIFECYCLE_FILE
|
|
255
|
+
if lifecycle_path.exists():
|
|
256
|
+
lc = _load_yaml(lifecycle_path)
|
|
257
|
+
hooks = lc.get("hooks", {}) or {}
|
|
258
|
+
hook_count = sum(
|
|
259
|
+
len(v) if isinstance(v, list) else (1 if isinstance(v, dict) else 0)
|
|
260
|
+
for v in hooks.values()
|
|
261
|
+
)
|
|
262
|
+
profile = lc.get("profile", "standard")
|
|
263
|
+
console.print(f"[bold]Lifecycle[/]")
|
|
264
|
+
console.print(f" Profile: {profile} | Hooks: {hook_count}")
|
|
265
|
+
console.print()
|
|
266
|
+
|
|
267
|
+
# Learning / instincts
|
|
268
|
+
learning_config_path = agent_dir / LEARNING_CONFIG_FILE
|
|
269
|
+
if learning_config_path.exists():
|
|
270
|
+
lconf = _load_yaml(learning_config_path)
|
|
271
|
+
extraction = lconf.get("extraction", {}).get("enabled", True)
|
|
272
|
+
instincts_base = agent_dir / INSTINCTS_DIR
|
|
273
|
+
active = len(list((instincts_base / "active").glob("*.instinct.yaml"))) if (instincts_base / "active").exists() else 0
|
|
274
|
+
candidates = len(list((instincts_base / "candidates").glob("*.instinct.yaml"))) if (instincts_base / "candidates").exists() else 0
|
|
275
|
+
archived = len(list((instincts_base / "archived").glob("*.instinct.yaml"))) if (instincts_base / "archived").exists() else 0
|
|
276
|
+
console.print(f"[bold]Learning[/]")
|
|
277
|
+
console.print(f" Extraction: {'enabled' if extraction else 'disabled'} | Active: {active} | Candidates: {candidates} | Archived: {archived}")
|
|
278
|
+
console.print()
|
|
279
|
+
|
|
280
|
+
# Rules
|
|
281
|
+
rules_config_path = agent_dir / RULES_CONFIG_FILE
|
|
282
|
+
if rules_config_path.exists():
|
|
283
|
+
rconf = _load_yaml(rules_config_path)
|
|
284
|
+
languages = rconf.get("languages", [])
|
|
285
|
+
rules_base = agent_dir / RULES_DIR
|
|
286
|
+
rule_count = len(list(rules_base.glob("**/*.md"))) if rules_base.exists() else 0
|
|
287
|
+
lang_str = ", ".join(languages) if languages else "auto-detect"
|
|
288
|
+
console.print(f"[bold]Rules[/]")
|
|
289
|
+
console.print(f" Languages: {lang_str} | Rule files: {rule_count}")
|
|
290
|
+
console.print()
|
|
291
|
+
|
|
249
292
|
# Summary
|
|
250
293
|
console.print(f"[bold]{t('inspect.summary')}[/]")
|
|
251
294
|
console.print(f" {t('inspect.skills_count', count=len(skills))}")
|
|
@@ -16,10 +16,15 @@ from rich.console import Console
|
|
|
16
16
|
from aes.config import (
|
|
17
17
|
AGENT_DIR,
|
|
18
18
|
COMMANDS_DIR,
|
|
19
|
+
INSTINCTS_DIR,
|
|
20
|
+
LEARNING_CONFIG_FILE,
|
|
21
|
+
LIFECYCLE_FILE,
|
|
19
22
|
LOCAL_FILE,
|
|
20
23
|
MANIFEST_FILE,
|
|
21
24
|
MEMORY_DIR,
|
|
22
25
|
PERMISSIONS_FILE,
|
|
26
|
+
RULES_CONFIG_FILE,
|
|
27
|
+
RULES_DIR,
|
|
23
28
|
SKILLS_DIR,
|
|
24
29
|
)
|
|
25
30
|
from aes.i18n import t
|
|
@@ -224,6 +229,84 @@ def _load_agent_context(project_root: Path) -> AgentContext:
|
|
|
224
229
|
if local_perms and permissions:
|
|
225
230
|
permissions = _deep_merge(permissions, local_perms)
|
|
226
231
|
|
|
232
|
+
# Load lifecycle.yaml
|
|
233
|
+
lifecycle: Optional[dict] = None
|
|
234
|
+
lifecycle_path = agent_dir / LIFECYCLE_FILE
|
|
235
|
+
if lifecycle_path.exists():
|
|
236
|
+
with open(lifecycle_path) as f:
|
|
237
|
+
lifecycle = yaml.safe_load(f) or {}
|
|
238
|
+
|
|
239
|
+
# Load learning config and active instincts
|
|
240
|
+
learning_config: Optional[dict] = None
|
|
241
|
+
active_instincts: List[dict] = []
|
|
242
|
+
learning_config_path = agent_dir / LEARNING_CONFIG_FILE
|
|
243
|
+
if learning_config_path.exists():
|
|
244
|
+
with open(learning_config_path) as f:
|
|
245
|
+
learning_config = yaml.safe_load(f) or {}
|
|
246
|
+
|
|
247
|
+
instincts_active_dir = agent_dir / INSTINCTS_DIR / "active"
|
|
248
|
+
if instincts_active_dir.exists():
|
|
249
|
+
max_instincts = 10
|
|
250
|
+
if learning_config:
|
|
251
|
+
max_instincts = (
|
|
252
|
+
learning_config.get("context_loading", {})
|
|
253
|
+
.get("max_instincts_in_context", 10)
|
|
254
|
+
)
|
|
255
|
+
raw_instincts = []
|
|
256
|
+
for inst_file in sorted(instincts_active_dir.glob("*.instinct.yaml")):
|
|
257
|
+
with open(inst_file) as f:
|
|
258
|
+
inst_data = yaml.safe_load(f)
|
|
259
|
+
if inst_data:
|
|
260
|
+
raw_instincts.append(inst_data)
|
|
261
|
+
# Sort by confidence score descending
|
|
262
|
+
raw_instincts.sort(
|
|
263
|
+
key=lambda i: i.get("confidence", {}).get("score", 0),
|
|
264
|
+
reverse=True,
|
|
265
|
+
)
|
|
266
|
+
active_instincts = raw_instincts[:max_instincts]
|
|
267
|
+
|
|
268
|
+
# Load rules config and rule files
|
|
269
|
+
rules_config: Optional[dict] = None
|
|
270
|
+
rules_files: Dict[str, str] = {}
|
|
271
|
+
rules_config_path = agent_dir / RULES_CONFIG_FILE
|
|
272
|
+
if rules_config_path.exists():
|
|
273
|
+
with open(rules_config_path) as f:
|
|
274
|
+
rules_config = yaml.safe_load(f) or {}
|
|
275
|
+
|
|
276
|
+
rules_base = agent_dir / RULES_DIR
|
|
277
|
+
overrides = rules_config.get("overrides", {})
|
|
278
|
+
|
|
279
|
+
# Determine which language directories to load
|
|
280
|
+
languages = rules_config.get("languages", [])
|
|
281
|
+
if not languages:
|
|
282
|
+
# Auto-detect from project root
|
|
283
|
+
detection = rules_config.get("detection", {})
|
|
284
|
+
for lang, patterns in detection.items():
|
|
285
|
+
for pattern in patterns:
|
|
286
|
+
if list(project_root.glob(pattern)):
|
|
287
|
+
languages.append(lang)
|
|
288
|
+
break
|
|
289
|
+
|
|
290
|
+
# Always-load directories (default: common)
|
|
291
|
+
always = rules_config.get("loading", {}).get("always", ["common"])
|
|
292
|
+
dirs_to_load = list(always) + languages
|
|
293
|
+
|
|
294
|
+
for dir_name in dirs_to_load:
|
|
295
|
+
rule_dir = rules_base / dir_name
|
|
296
|
+
if not rule_dir.exists():
|
|
297
|
+
continue
|
|
298
|
+
for md_file in sorted(rule_dir.glob("*.md")):
|
|
299
|
+
content = md_file.read_text()
|
|
300
|
+
# Resolve ${variable} placeholders from overrides
|
|
301
|
+
rule_name = md_file.stem
|
|
302
|
+
rule_overrides = overrides.get(rule_name, {})
|
|
303
|
+
for var_name, var_value in rule_overrides.items():
|
|
304
|
+
content = content.replace(
|
|
305
|
+
f"${{{var_name}}}", str(var_value)
|
|
306
|
+
)
|
|
307
|
+
key = f"{dir_name}/{md_file.name}"
|
|
308
|
+
rules_files[key] = content
|
|
309
|
+
|
|
227
310
|
return AgentContext(
|
|
228
311
|
project_root=project_root,
|
|
229
312
|
agent_dir=agent_dir,
|
|
@@ -236,6 +319,11 @@ def _load_agent_context(project_root: Path) -> AgentContext:
|
|
|
236
319
|
memory_project=memory_project,
|
|
237
320
|
skill_metadata=skill_metadata,
|
|
238
321
|
local_config=local_config,
|
|
322
|
+
lifecycle=lifecycle,
|
|
323
|
+
learning_config=learning_config,
|
|
324
|
+
active_instincts=active_instincts,
|
|
325
|
+
rules_config=rules_config,
|
|
326
|
+
rules_files=rules_files,
|
|
239
327
|
)
|
|
240
328
|
|
|
241
329
|
|
|
@@ -34,6 +34,14 @@ OVERRIDES_DIR = "overrides"
|
|
|
34
34
|
LOCAL_FILE = "local.yaml"
|
|
35
35
|
LOCAL_EXAMPLE_FILE = "local.example.yaml"
|
|
36
36
|
BOM_FILE = "bom.yaml"
|
|
37
|
+
LIFECYCLE_FILE = "lifecycle.yaml"
|
|
38
|
+
LEARNING_DIR = "learning"
|
|
39
|
+
LEARNING_CONFIG_FILE = "learning/config.yaml"
|
|
40
|
+
INSTINCTS_DIR = "learning/instincts"
|
|
41
|
+
RULES_DIR = "rules"
|
|
42
|
+
RULES_CONFIG_FILE = "rules/rules.yaml"
|
|
43
|
+
SCRIPTS_DIR = "scripts"
|
|
44
|
+
LOGS_DIR = "logs"
|
|
37
45
|
|
|
38
46
|
# Schema file mapping
|
|
39
47
|
SCHEMA_MAP = {
|
|
@@ -44,4 +52,8 @@ SCHEMA_MAP = {
|
|
|
44
52
|
"permissions": "permissions.schema.json",
|
|
45
53
|
"bom": "bom.schema.json",
|
|
46
54
|
"decision-record": "decision-record.schema.json",
|
|
55
|
+
"lifecycle": "lifecycle.schema.json",
|
|
56
|
+
"instinct": "instinct.schema.json",
|
|
57
|
+
"learning-config": "learning-config.schema.json",
|
|
58
|
+
"rules-config": "rules-config.schema.json",
|
|
47
59
|
}
|
|
@@ -154,6 +154,12 @@ class DomainConfig:
|
|
|
154
154
|
heartbeat_checklist: str = ""
|
|
155
155
|
channels: Dict[str, Dict[str, str]] = field(default_factory=dict)
|
|
156
156
|
|
|
157
|
+
# Lifecycle / Learning / Rules scaffolding
|
|
158
|
+
scaffold_lifecycle: bool = True
|
|
159
|
+
scaffold_learning: bool = False # True for agent-integrated domains
|
|
160
|
+
scaffold_rules: bool = True
|
|
161
|
+
lifecycle_profile: str = "standard"
|
|
162
|
+
|
|
157
163
|
|
|
158
164
|
# ---------------------------------------------------------------------------
|
|
159
165
|
# ML domain config — drawn from examples/ml-pipeline
|
|
@@ -529,6 +535,7 @@ ML_CONFIG = DomainConfig(
|
|
|
529
535
|
{"name": "OPTUNA_TIMEOUT", "default": "300", "description": "Seconds per model for HPO"},
|
|
530
536
|
{"name": "OPTUNA_N_TRIALS", "default": "50", "description": "Max trials per model"},
|
|
531
537
|
],
|
|
538
|
+
scaffold_learning=True,
|
|
532
539
|
)
|
|
533
540
|
|
|
534
541
|
|
|
@@ -1338,6 +1345,7 @@ RESEARCH_CONFIG = DomainConfig(
|
|
|
1338
1345
|
{"name": "SEMANTIC_SCHOLAR_API_KEY", "default": "", "description": "Semantic Scholar API key (optional, increases rate limits)"},
|
|
1339
1346
|
{"name": "MAX_ITEMS_PER_RUN", "default": "50", "description": "Maximum items to process per pipeline run"},
|
|
1340
1347
|
],
|
|
1348
|
+
scaffold_learning=True,
|
|
1341
1349
|
)
|
|
1342
1350
|
|
|
1343
1351
|
|
|
@@ -1387,6 +1395,7 @@ AGENT_INTEGRATED_BASE_CONFIG = DomainConfig(
|
|
|
1387
1395
|
),
|
|
1388
1396
|
],
|
|
1389
1397
|
instructions_description="", # empty -> falls through to TODO scaffolding in template
|
|
1398
|
+
scaffold_learning=True,
|
|
1390
1399
|
)
|
|
1391
1400
|
|
|
1392
1401
|
|
|
@@ -1476,23 +1485,40 @@ _ASSISTANT_SKILLS = [
|
|
|
1476
1485
|
),
|
|
1477
1486
|
]
|
|
1478
1487
|
|
|
1479
|
-
|
|
1480
|
-
id="
|
|
1481
|
-
trigger="/
|
|
1482
|
-
description="
|
|
1483
|
-
runbook_purpose="
|
|
1484
|
-
worker_specialty="
|
|
1488
|
+
_ASSISTANT_BUILD_COMMAND = CommandDef(
|
|
1489
|
+
id="build",
|
|
1490
|
+
trigger="/build",
|
|
1491
|
+
description="Build the assistant project from scratch: identity, channels, skills, integrations, permissions, tests",
|
|
1492
|
+
runbook_purpose="Construct the complete assistant project. The agent sets up project structure, defines identity and model config, wires channel integrations, implements skills, configures security and permissions, and verifies with tests.",
|
|
1493
|
+
worker_specialty="Constructing assistant projects — identity, channels, skills, integrations",
|
|
1494
|
+
runbook_phases=[
|
|
1495
|
+
{"title": "Project Structure", "content": "Create directory layout matching the AES assistant convention: skills/, config/, scripts/, tests/. Set up pyproject.toml, environment files, and dependency management."},
|
|
1496
|
+
{"title": "Identity & Model", "content": "Configure identity (persona, name, emoji) in agent.yaml. Set up model provider, API key environment variables, and sandbox settings. Create USER.md template for user profile."},
|
|
1497
|
+
{"title": "Channel Integration", "content": "Wire messaging channels (Telegram, Discord, etc.). Set up bot token environment variables, configure channel-specific settings, and implement message routing stubs."},
|
|
1498
|
+
{"title": "Skill Wiring", "content": "Implement skill manifests and runbooks for each configured skill. Wire MCP servers if needed. Set up auto-activation rules and skill dependencies."},
|
|
1499
|
+
{"title": "Security & Permissions", "content": "Configure permissions.yaml with shell confirmation rules, action confirmations, and channel-specific security policies. Set up credential management via environment variables."},
|
|
1500
|
+
{"title": "Tests & Verification", "content": "Write unit tests for skill execution, channel routing, and permission checks. Add integration test that validates the full config. Verify all environment variables are documented."},
|
|
1501
|
+
],
|
|
1502
|
+
)
|
|
1503
|
+
|
|
1504
|
+
_ASSISTANT_RUN_COMMAND = CommandDef(
|
|
1505
|
+
id="run",
|
|
1506
|
+
trigger="/run",
|
|
1507
|
+
description="Start the autonomous assistant loop: initialize, monitor channels, execute skills, maintain heartbeat",
|
|
1508
|
+
runbook_purpose="Run the assistant's autonomous agent loop. The agent initializes from stored state, monitors connected channels for messages, executes skills on demand or automatically, persists learnings to memory, and maintains the heartbeat cycle for proactive tasks.",
|
|
1509
|
+
worker_specialty="Running autonomous assistant agents — channel monitoring, skill execution, heartbeat maintenance",
|
|
1485
1510
|
runbook_phases=[
|
|
1486
|
-
{"title": "
|
|
1487
|
-
{"title": "
|
|
1488
|
-
{"title": "Skill Execution", "content": "When a skill is triggered (explicitly via command or automatically via context match), execute it
|
|
1489
|
-
{"title": "Memory &
|
|
1511
|
+
{"title": "Initialization", "content": "Load user profile from USER.md. Restore session state from MEMORY.md. Check HEARTBEAT.md for pending tasks. Review recent activity log for context continuity."},
|
|
1512
|
+
{"title": "Channel Monitoring", "content": "Connect to configured channels (Telegram, Discord, etc.). Listen for incoming messages. Determine intent — greeting, skill invocation, general conversation, or system command. Route accordingly."},
|
|
1513
|
+
{"title": "Skill Execution", "content": "When a skill is triggered (explicitly via command or automatically via context match), execute it within permission boundaries. Return results to the originating channel. Handle errors gracefully with user-facing messages."},
|
|
1514
|
+
{"title": "Memory & Learning", "content": "Persist important learnings to MEMORY.md. Update active instincts based on interaction patterns. Record conversation context for future sessions."},
|
|
1515
|
+
{"title": "Heartbeat & Maintenance", "content": "Execute heartbeat checklist at configured intervals. Proactively surface important updates (calendar, PRs, unread messages). Respect quiet hours. Log heartbeat results to activity log."},
|
|
1490
1516
|
],
|
|
1491
1517
|
)
|
|
1492
1518
|
|
|
1493
1519
|
ASSISTANT_CONFIG = DomainConfig(
|
|
1494
1520
|
mode="agent-integrated",
|
|
1495
|
-
workflow_commands=[
|
|
1521
|
+
workflow_commands=[_ASSISTANT_BUILD_COMMAND, _ASSISTANT_RUN_COMMAND],
|
|
1496
1522
|
|
|
1497
1523
|
# Identity defaults for scaffold
|
|
1498
1524
|
identity_persona=(
|
|
@@ -1518,7 +1544,7 @@ ASSISTANT_CONFIG = DomainConfig(
|
|
|
1518
1544
|
"discord": {"enabled": "true", "bot_token_env": "DISCORD_BOT_TOKEN"},
|
|
1519
1545
|
},
|
|
1520
1546
|
|
|
1521
|
-
instructions_description="
|
|
1547
|
+
instructions_description="Autonomous AI assistant powered by OpenClaw. Runs 24/7, monitors channels, executes skills, and maintains proactive heartbeat tasks.",
|
|
1522
1548
|
instructions_quick_ref=(
|
|
1523
1549
|
"```bash\n"
|
|
1524
1550
|
"aes sync -t openclaw # generate .openclaw/ config\n"
|
|
@@ -1571,6 +1597,7 @@ ASSISTANT_CONFIG = DomainConfig(
|
|
|
1571
1597
|
{"name": "TELEGRAM_BOT_TOKEN", "description": "Bot token for Telegram integration"},
|
|
1572
1598
|
{"name": "DISCORD_BOT_TOKEN", "description": "Bot token for Discord integration"},
|
|
1573
1599
|
],
|
|
1600
|
+
scaffold_learning=True,
|
|
1574
1601
|
)
|
|
1575
1602
|
|
|
1576
1603
|
|
|
@@ -27,6 +27,23 @@ def save_global_config(config: dict) -> None:
|
|
|
27
27
|
yaml.dump(config, f, default_flow_style=False, allow_unicode=True)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
def get_project_locale(project_path: Path) -> Optional[str]:
|
|
31
|
+
"""Return locale from .agent/local.yaml in project, or None."""
|
|
32
|
+
local_yaml = project_path / ".agent" / "local.yaml"
|
|
33
|
+
if not local_yaml.exists():
|
|
34
|
+
return None
|
|
35
|
+
try:
|
|
36
|
+
with open(local_yaml) as f:
|
|
37
|
+
data = yaml.safe_load(f)
|
|
38
|
+
if isinstance(data, dict):
|
|
39
|
+
locale = data.get("locale")
|
|
40
|
+
if isinstance(locale, str) and locale:
|
|
41
|
+
return locale
|
|
42
|
+
except (OSError, yaml.YAMLError):
|
|
43
|
+
pass
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
30
47
|
def get_locale() -> Optional[str]:
|
|
31
48
|
"""Return configured locale or None."""
|
|
32
49
|
return load_global_config().get("locale")
|
|
@@ -178,7 +178,7 @@ MESSAGES: dict = {
|
|
|
178
178
|
"init.type_skip": "Skip",
|
|
179
179
|
"init.type_ml": "ML pipeline",
|
|
180
180
|
"init.type_research": "Research / Content pipeline",
|
|
181
|
-
"init.type_assistant": "Assistant (24/7
|
|
181
|
+
"init.type_assistant": "Assistant (24/7 autonomous agent)",
|
|
182
182
|
"init.type_custom": "Custom",
|
|
183
183
|
"init.overwrite_warning": "{agent_dir}/ already exists at {path}",
|
|
184
184
|
"init.overwrite_confirm": "Overwrite existing files?",
|
|
@@ -178,7 +178,7 @@ MESSAGES: dict = {
|
|
|
178
178
|
"init.type_skip": "スキップ",
|
|
179
179
|
"init.type_ml": "ML パイプライン",
|
|
180
180
|
"init.type_research": "研究 / コンテンツパイプライン",
|
|
181
|
-
"init.type_assistant": "アシスタント(24
|
|
181
|
+
"init.type_assistant": "アシスタント(24時間自律エージェント)",
|
|
182
182
|
"init.type_custom": "カスタム",
|
|
183
183
|
"init.overwrite_warning": "{path} に {agent_dir}/ が既に存在します",
|
|
184
184
|
"init.overwrite_confirm": "既存のファイルを上書きしますか?",
|
|
@@ -15,6 +15,7 @@ from typing import Dict, List, Optional, Tuple
|
|
|
15
15
|
REGISTRY_URL = os.environ.get("AES_REGISTRY_URL", "https://registry.aes-official.com")
|
|
16
16
|
INDEX_PATH = "index.json"
|
|
17
17
|
PACKAGES_PATH = "packages"
|
|
18
|
+
_USER_AGENT = "aes-cli/0.11.0"
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def _validate_registry_url(url: str) -> str:
|
|
@@ -131,6 +132,7 @@ def fetch_index(registry_url: Optional[str] = None) -> dict:
|
|
|
131
132
|
url = f"{base.rstrip('/')}/{INDEX_PATH}"
|
|
132
133
|
|
|
133
134
|
req = urllib.request.Request(url)
|
|
135
|
+
req.add_header("User-Agent", _USER_AGENT)
|
|
134
136
|
token = os.environ.get("AES_REGISTRY_KEY")
|
|
135
137
|
if token:
|
|
136
138
|
req.add_header("Authorization", f"Bearer {token}")
|
|
@@ -158,6 +160,7 @@ def download_package(
|
|
|
158
160
|
tarball_path = dest / f"{name}-{version}.tar.gz"
|
|
159
161
|
|
|
160
162
|
req = urllib.request.Request(url)
|
|
163
|
+
req.add_header("User-Agent", _USER_AGENT)
|
|
161
164
|
token = os.environ.get("AES_REGISTRY_KEY")
|
|
162
165
|
if token:
|
|
163
166
|
req.add_header("Authorization", f"Bearer {token}")
|
|
@@ -206,6 +209,7 @@ def upload_package(
|
|
|
206
209
|
# Upload tarball — server auto-updates index from X-AES-* headers
|
|
207
210
|
upload_url = f"{base.rstrip('/')}/{PACKAGES_PATH}/{name}/{version}.tar.gz"
|
|
208
211
|
req = urllib.request.Request(upload_url, data=tarball_data, method="PUT")
|
|
212
|
+
req.add_header("User-Agent", _USER_AGENT)
|
|
209
213
|
req.add_header("Authorization", f"Bearer {token}")
|
|
210
214
|
req.add_header("Content-Type", "application/gzip")
|
|
211
215
|
req.add_header("X-AES-Description", description)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{% if domain_config %}
|
|
2
2
|
# .agent/agent.yaml — AES Manifest
|
|
3
|
-
aes: "1.
|
|
3
|
+
aes: "1.4"
|
|
4
4
|
|
|
5
5
|
name: "{{ name }}"
|
|
6
6
|
version: "0.1.0"
|
|
@@ -121,7 +121,7 @@ environment:
|
|
|
121
121
|
{% endif %}
|
|
122
122
|
{% else %}
|
|
123
123
|
# .agent/agent.yaml — AES Manifest
|
|
124
|
-
aes: "1.
|
|
124
|
+
aes: "1.4"
|
|
125
125
|
|
|
126
126
|
name: "{{ name }}"
|
|
127
127
|
version: "0.1.0"
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
{% for rule in domain_config.instructions_rules %}
|
|
21
21
|
{{ loop.index }}. {{ rule }}
|
|
22
22
|
{% endfor %}
|
|
23
|
+
{{ domain_config.instructions_rules|length + 1 }}. **Memory discipline** — update `.agent/memory/operations.md` after every significant action. Run `/memory` at session end. Next session starts blind without this.
|
|
23
24
|
|
|
24
25
|
## Primary Workflow
|
|
25
26
|
|
|
@@ -51,7 +52,7 @@ When creating or editing `.agent/` files, follow these formats exactly.
|
|
|
51
52
|
### Skill manifest (`.skill.yaml`)
|
|
52
53
|
|
|
53
54
|
```yaml
|
|
54
|
-
aes_skill: "1.
|
|
55
|
+
aes_skill: "1.4"
|
|
55
56
|
|
|
56
57
|
id: "my-skill"
|
|
57
58
|
name: "My Skill"
|
|
@@ -100,7 +101,7 @@ tags:
|
|
|
100
101
|
### Workflow (`.yaml`)
|
|
101
102
|
|
|
102
103
|
```yaml
|
|
103
|
-
aes_workflow: "1.
|
|
104
|
+
aes_workflow: "1.4"
|
|
104
105
|
|
|
105
106
|
id: "my-workflow"
|
|
106
107
|
entity: "item"
|
|
@@ -165,6 +166,7 @@ Key rules: `aes_skill`/`aes_workflow` version key is **required**. `inputs` is a
|
|
|
165
166
|
Example: "Python 3.9+ — use `from __future__ import annotations` everywhere." -->
|
|
166
167
|
|
|
167
168
|
1. **Fail graceful** — Each item wrapped in try/except, log error, continue.
|
|
169
|
+
2. **Memory discipline** — update `.agent/memory/operations.md` after every significant action. Run `/memory` at session end.
|
|
168
170
|
|
|
169
171
|
## Domain Model
|
|
170
172
|
|
|
@@ -226,7 +228,7 @@ When creating or editing `.agent/` files, follow these formats exactly.
|
|
|
226
228
|
### Skill manifest (`.skill.yaml`)
|
|
227
229
|
|
|
228
230
|
```yaml
|
|
229
|
-
aes_skill: "1.
|
|
231
|
+
aes_skill: "1.4"
|
|
230
232
|
|
|
231
233
|
id: "my-skill"
|
|
232
234
|
name: "My Skill"
|
|
@@ -275,7 +277,7 @@ tags:
|
|
|
275
277
|
### Workflow (`.yaml`)
|
|
276
278
|
|
|
277
279
|
```yaml
|
|
278
|
-
aes_workflow: "1.
|
|
280
|
+
aes_workflow: "1.4"
|
|
279
281
|
|
|
280
282
|
id: "my-workflow"
|
|
281
283
|
entity: "item"
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
{% for rule in domain_config.instructions_rules %}
|
|
21
21
|
{{ loop.index }}. {{ rule }}
|
|
22
22
|
{% endfor %}
|
|
23
|
+
{{ domain_config.instructions_rules|length + 1 }}. **メモリ規律** — 重要なアクションの後に `.agent/memory/operations.md` を必ず更新してください。セッション終了時に `/memory` を実行してください。これを怠ると次のセッションが盲目的に開始されます。
|
|
23
24
|
|
|
24
25
|
## 主要ワークフロー
|
|
25
26
|
|
|
@@ -165,6 +166,7 @@ idempotency:
|
|
|
165
166
|
例: "Python 3.9+ — `from __future__ import annotations` を全ファイルで使用。" -->
|
|
166
167
|
|
|
167
168
|
1. **グレースフルフェイル** — 各アイテムをtry/exceptでラップし、エラーをログして続行。
|
|
169
|
+
2. **メモリ規律** — 重要なアクションの後に `.agent/memory/operations.md` を必ず更新。セッション終了時に `/memory` を実行。
|
|
168
170
|
|
|
169
171
|
## ドメインモデル
|
|
170
172
|
|