@asm-agent/coding-agent 0.8.4 → 0.8.6

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/bundle/{anthropic-CWQBVVHC.js → anthropic-CILMWJ23.js} +1 -1
  3. package/dist/bundle/{azure-openai-responses-7JP64QDL.js → azure-openai-responses-TPNZ54LG.js} +2 -2
  4. package/dist/bundle/{bundled-modules-DZAECPSN.js → bundled-modules-SKYRKITN.js} +3 -3
  5. package/dist/bundle/{chunk-7HN2VZJJ.js → chunk-7QIBGY2R.js} +74 -74
  6. package/dist/bundle/{chunk-O7SB7LML.js → chunk-EFDSSG52.js} +69 -18
  7. package/dist/bundle/{chunk-FCQTXDPX.js → chunk-IYXHHLR6.js} +1 -1
  8. package/dist/bundle/{chunk-4KQVBOJB.js → chunk-YYVQTKC6.js} +2 -2
  9. package/dist/bundle/{cli-main-YXP4FEPB.js → cli-main-O3HK7MNQ.js} +3 -3
  10. package/dist/bundle/cli.js +1 -1
  11. package/dist/bundle/{google-DPYNVS7L.js → google-3TL5FKLN.js} +1 -1
  12. package/dist/bundle/{google-vertex-SKMI6LMM.js → google-vertex-WVFMK6QO.js} +1 -1
  13. package/dist/bundle/{main-DOLQ46ZM.js → main-4QYKME2K.js} +3 -3
  14. package/dist/bundle/{mistral-DUU7KDZ3.js → mistral-QV2OSBWW.js} +1 -1
  15. package/dist/bundle/{openai-codex-responses-D7FCP7DO.js → openai-codex-responses-J2GJ2FAO.js} +2 -2
  16. package/dist/bundle/{openai-completions-AH6TQF5C.js → openai-completions-W622H2EL.js} +1 -1
  17. package/dist/bundle/{openai-responses-DDC3EYTG.js → openai-responses-V75QG5T7.js} +2 -2
  18. package/dist/core/always-applied-skills.d.ts +3 -0
  19. package/dist/core/always-applied-skills.d.ts.map +1 -0
  20. package/dist/core/always-applied-skills.js +47 -0
  21. package/dist/core/always-applied-skills.js.map +1 -0
  22. package/dist/core/system-prompt.d.ts.map +1 -1
  23. package/dist/core/system-prompt.js +9 -0
  24. package/dist/core/system-prompt.js.map +1 -1
  25. package/dist/skills/final-response-summary/SKILL.md +39 -0
  26. package/dist/skills/request-report/SKILL.md +46 -0
  27. package/dist/skills/request-report/pyproject.toml +16 -0
  28. package/dist/skills/request-report/src/request_report/__init__.py +160 -0
  29. package/dist/skills/skill-creator/SKILL.md +2 -1
  30. package/dist/skills/solid-source-modularity/SKILL.md +48 -0
  31. package/dist/skills/solid-source-modularity/pyproject.toml +16 -0
  32. package/dist/skills/solid-source-modularity/src/solid_source_modularity/__init__.py +154 -0
  33. package/docs/skills.md +8 -3
  34. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  35. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  36. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  37. package/examples/extensions/sandbox/package-lock.json +2 -2
  38. package/examples/extensions/sandbox/package.json +1 -1
  39. package/examples/extensions/with-deps/package-lock.json +2 -2
  40. package/examples/extensions/with-deps/package.json +1 -1
  41. package/package.json +5 -5
  42. package/skills/final-response-summary/SKILL.md +39 -0
  43. package/skills/request-report/SKILL.md +46 -0
  44. package/skills/request-report/pyproject.toml +16 -0
  45. package/skills/request-report/src/request_report/__init__.py +160 -0
  46. package/skills/skill-creator/SKILL.md +2 -1
  47. package/skills/solid-source-modularity/SKILL.md +48 -0
  48. package/skills/solid-source-modularity/pyproject.toml +16 -0
  49. package/skills/solid-source-modularity/src/solid_source_modularity/__init__.py +154 -0
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: solid-source-modularity
3
+ always-apply: true
4
+ description: Enforce SOLID design, cohesive source modules, staged refactoring, and source-file size limits. MUST be used whenever the agent creates, modifies, reviews, or refactors authored source code in any project.
5
+ ---
6
+
7
+ # SOLID Source Modularity
8
+
9
+ Apply this policy to all authored source-code work.
10
+
11
+ ## Required policy
12
+
13
+ - Follow SOLID and give each module one cohesive responsibility.
14
+ - Separate orchestration, domain logic, I/O, rendering, and platform adapters.
15
+ - Prefer small contracts and explicit dependencies. Avoid generic catch-all modules.
16
+ - Target at most 300 physical lines per authored source file.
17
+ - Allow 301–500 lines only as a documented exception when splitting would reduce
18
+ cohesion or create artificial coupling.
19
+ - More than 500 lines is always a violation and requires modularization.
20
+ - Generated files and lockfiles are exempt.
21
+
22
+ ## Required refactoring sequence
23
+
24
+ 1. Inventory files and responsibilities.
25
+ 2. Define boundaries, contracts, and dependency direction.
26
+ 3. Create all destination modules before replacing the original implementation.
27
+ 4. Validate new modules in isolation when possible.
28
+ 5. Replace the original code with imports, delegation, and orchestration only
29
+ after the destination modules exist.
30
+ 6. Organize tests by responsibility and run the project's required checks.
31
+ 7. Run the source audit and document each 301–500-line exception.
32
+
33
+ Keep structural refactoring separate from new behavior. Preserve behavior during
34
+ structural changes. Never create a commit without explicit user authorization.
35
+
36
+ ## Audit
37
+
38
+ Call the prepared Python skill from the repository root:
39
+
40
+ ```python
41
+ report = await solid_source_modularity(
42
+ root=".",
43
+ extensions=[".ts", ".tsx", ".py"],
44
+ )
45
+ ```
46
+
47
+ `report` separates `compliant`, `exceptions`, `violations`, and `skipped` files.
48
+ Do not raise `target_lines` above 300 or `hard_limit` above 500.
@@ -0,0 +1,16 @@
1
+ [project]
2
+ name = "solid-source-modularity"
3
+ version = "0.1.0"
4
+ description = "Audit authored source files against ASM Agent modularity limits."
5
+ requires-python = ">=3.10"
6
+ dependencies = []
7
+
8
+ [project.scripts]
9
+ solid_source_modularity = "rlm.skill:cli"
10
+
11
+ [build-system]
12
+ requires = ["hatchling"]
13
+ build-backend = "hatchling.build"
14
+
15
+ [tool.hatch.build.targets.wheel]
16
+ packages = ["src/solid_source_modularity"]
@@ -0,0 +1,154 @@
1
+ """Audit authored source files against ASM Agent modularity limits."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from pathlib import Path
7
+ from typing import Iterable
8
+
9
+ _DEFAULT_EXTENSIONS = (
10
+ ".c",
11
+ ".cpp",
12
+ ".cs",
13
+ ".go",
14
+ ".h",
15
+ ".hpp",
16
+ ".java",
17
+ ".js",
18
+ ".jsx",
19
+ ".py",
20
+ ".rs",
21
+ ".ts",
22
+ ".tsx",
23
+ )
24
+ _DEFAULT_EXCLUDED_NAMES = (
25
+ "Cargo.lock",
26
+ "package-lock.json",
27
+ "pnpm-lock.yaml",
28
+ "yarn.lock",
29
+ )
30
+ _EXCLUDED_DIRECTORIES = frozenset(
31
+ {
32
+ ".git",
33
+ ".mypy_cache",
34
+ ".next",
35
+ ".pytest_cache",
36
+ ".tox",
37
+ ".venv",
38
+ "build",
39
+ "coverage",
40
+ "dist",
41
+ "node_modules",
42
+ "target",
43
+ "vendor",
44
+ }
45
+ )
46
+ _GENERATED_NAME_MARKERS = (".generated.", ".gen.")
47
+
48
+
49
+ def _normalize_extensions(extensions: Iterable[str]) -> frozenset[str]:
50
+ normalized = set()
51
+ for extension in extensions:
52
+ if not isinstance(extension, str) or not extension.strip():
53
+ raise TypeError("extensions must contain non-empty strings")
54
+ value = extension.strip().lower()
55
+ normalized.add(value if value.startswith(".") else f".{value}")
56
+ if not normalized:
57
+ raise ValueError("extensions must not be empty")
58
+ return frozenset(normalized)
59
+
60
+
61
+ def _validate_limits(target_lines: int, hard_limit: int) -> None:
62
+ if not isinstance(target_lines, int) or isinstance(target_lines, bool):
63
+ raise TypeError("target_lines must be an integer")
64
+ if not isinstance(hard_limit, int) or isinstance(hard_limit, bool):
65
+ raise TypeError("hard_limit must be an integer")
66
+ if target_lines <= 0 or target_lines > 300:
67
+ raise ValueError("target_lines must be between 1 and 300")
68
+ if hard_limit <= target_lines or hard_limit > 500:
69
+ raise ValueError("hard_limit must be greater than target_lines and at most 500")
70
+
71
+
72
+ def _is_generated_name(name: str) -> bool:
73
+ lowered = name.lower()
74
+ return any(marker in lowered for marker in _GENERATED_NAME_MARKERS)
75
+
76
+
77
+ def _entry(path: Path, root: Path, lines: int) -> dict[str, object]:
78
+ return {"path": path.relative_to(root).as_posix(), "lines": lines}
79
+
80
+
81
+ async def audit_source_files(
82
+ root: str,
83
+ extensions: list[str] | None = None,
84
+ target_lines: int = 300,
85
+ hard_limit: int = 500,
86
+ excluded_names: list[str] | None = None,
87
+ ) -> dict[str, object]:
88
+ """Audit authored source files and classify their physical line counts."""
89
+ _validate_limits(target_lines, hard_limit)
90
+ root_path = Path(root).expanduser().resolve()
91
+ if not root_path.is_dir():
92
+ raise NotADirectoryError(f"{root} is not an existing directory")
93
+
94
+ included_extensions = _normalize_extensions(extensions or list(_DEFAULT_EXTENSIONS))
95
+ excluded = set(excluded_names or list(_DEFAULT_EXCLUDED_NAMES))
96
+ compliant: list[dict[str, object]] = []
97
+ exceptions: list[dict[str, object]] = []
98
+ violations: list[dict[str, object]] = []
99
+ skipped: list[dict[str, str]] = []
100
+
101
+ for current_root, directories, filenames in os.walk(root_path):
102
+ directories[:] = sorted(name for name in directories if name not in _EXCLUDED_DIRECTORIES)
103
+ current_path = Path(current_root)
104
+ for filename in sorted(filenames):
105
+ path = current_path / filename
106
+ if path.suffix.lower() not in included_extensions:
107
+ continue
108
+ relative = path.relative_to(root_path).as_posix()
109
+ if filename in excluded or _is_generated_name(filename):
110
+ skipped.append({"path": relative, "reason": "excluded or generated"})
111
+ continue
112
+ try:
113
+ line_count = len(path.read_text(encoding="utf-8").splitlines())
114
+ except (OSError, UnicodeError) as error:
115
+ skipped.append({"path": relative, "reason": str(error)})
116
+ continue
117
+ entry = _entry(path, root_path, line_count)
118
+ if line_count <= target_lines:
119
+ compliant.append(entry)
120
+ elif line_count <= hard_limit:
121
+ exceptions.append(entry)
122
+ else:
123
+ violations.append(entry)
124
+
125
+ return {
126
+ "root": str(root_path),
127
+ "target_lines": target_lines,
128
+ "hard_limit": hard_limit,
129
+ "scanned_files": len(compliant) + len(exceptions) + len(violations),
130
+ "compliant": compliant,
131
+ "exceptions": exceptions,
132
+ "violations": violations,
133
+ "skipped": skipped,
134
+ }
135
+
136
+
137
+ async def run(
138
+ root: str,
139
+ extensions: list[str] | None = None,
140
+ target_lines: int = 300,
141
+ hard_limit: int = 500,
142
+ excluded_names: list[str] | None = None,
143
+ ) -> dict[str, object]:
144
+ """Audit source files under ``root`` against the 300/500-line policy."""
145
+ return await audit_source_files(
146
+ root,
147
+ extensions=extensions,
148
+ target_lines=target_lines,
149
+ hard_limit=hard_limit,
150
+ excluded_names=excluded_names,
151
+ )
152
+
153
+
154
+ __all__ = ["audit_source_files", "run"]