simple-agent-extension 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +20 -12
- data/lib/simple_agent_extension/agent_directory_loader.rb +8 -3
- data/lib/simple_agent_extension/agents/opencode.rb +16 -0
- data/lib/simple_agent_extension/cli.rb +50 -11
- data/lib/simple_agent_extension/extensions/base.rb +9 -0
- data/lib/simple_agent_extension/frontmatter.rb +2 -0
- data/lib/simple_agent_extension/metadata_loader.rb +8 -0
- data/lib/simple_agent_extension/runner.rb +19 -0
- data/lib/simple_agent_extension/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 404e535b96de550ec739358155e62a38ec0d9a07a2ec2f78e7607eb3a98930d7
|
|
4
|
+
data.tar.gz: e17a63f61c78d8b9f30631d52ff212ba890e71ccc0ab17e8a9e1a9b56d919060
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b9d622f95994d3f6abcfa23cb3bce94b92316250ad9c3a24ca0b83092be5f05628fa73a10ca5093b60b86cb27753e903566c4a38e248c200518a2d7bb99ef3d7
|
|
7
|
+
data.tar.gz: 963497aacd95ec19efa3157202dc547dacccd2c71567be762d7a8c8ba347efd6a65894c3501b1c0f5ab5794622ba3ff26db682eda2d73fe68cf1c3dbe4f54e63
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [0.2.3] 2026-10-12
|
|
2
|
+
|
|
3
|
+
- feat: add version option
|
|
4
|
+
- feat: add clean subcommand
|
|
5
|
+
- feat: support `disable-model-invocation' field for very heavy skills
|
|
6
|
+
|
|
7
|
+
## [0.2.2] 2026-10-04
|
|
8
|
+
|
|
9
|
+
- fix: make the causes of errors easier to read
|
|
10
|
+
- chore: add gem package task
|
|
11
|
+
- docs: install from rubygems.org or run with rvx
|
|
12
|
+
|
|
1
13
|
## [0.2.1] 2026-10-02
|
|
2
14
|
|
|
3
15
|
- feat: support Claude Code
|
data/README.md
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
Requires Ruby 3.3 or later.
|
|
7
|
+
Requires Ruby 3.3 or later (not needed beforehand when running with `rvx`, which provides Ruby itself).
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Add the gem to your Gemfile:
|
|
10
10
|
|
|
11
11
|
```ruby
|
|
12
12
|
# Gemfile
|
|
13
13
|
group :development do
|
|
14
|
-
gem "simple-agent-extension"
|
|
14
|
+
gem "simple-agent-extension"
|
|
15
15
|
end
|
|
16
16
|
```
|
|
17
17
|
|
|
@@ -25,16 +25,12 @@ Then run the CLI through Bundler:
|
|
|
25
25
|
bundle exec simple-agent-extension packages
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Or run it without a Gemfile with [rv](https://github.com/spinel-coop/rv)'s `rvx`, which installs the gem on first use:
|
|
29
29
|
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
github: "colorfulcompany/simple-agent-extension",
|
|
33
|
-
tag: "v0.1.0"
|
|
30
|
+
```sh
|
|
31
|
+
rvx simple-agent-extension packages
|
|
34
32
|
```
|
|
35
33
|
|
|
36
|
-
`branch:` and `ref:` are also available; note that `branch:` follows new commits rather than pinning.
|
|
37
|
-
|
|
38
34
|
## Usage
|
|
39
35
|
|
|
40
36
|
### Defining a package
|
|
@@ -119,9 +115,16 @@ entrypoint frontmatter
|
|
|
119
115
|
< static.agents.<agent>
|
|
120
116
|
```
|
|
121
117
|
|
|
122
|
-
####
|
|
118
|
+
#### Supported adaptive fields
|
|
119
|
+
|
|
120
|
+
| Field | Applies to | Value | Meaning |
|
|
121
|
+
|---|---|---|---|
|
|
122
|
+
| `permissions` | agent, skill | map of tool name to `allow`, `ask`, or `deny` | Which tools the extension may use, and how. |
|
|
123
|
+
| `disable-model-invocation` | skill | boolean | `true` keeps the skill out of model auto-selection; `/name` still works. |
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
Each Agent decides how a field appears in its artifact; see the `MetadataTranslator` in `lib/simple_agent_extension/agents/`. A field an Agent has no rule for, listed here or not, passes through unchanged, so it takes effect only on an Agent that recognizes it.
|
|
126
|
+
|
|
127
|
+
The `permissions` fragment above (`read: allow`, `edit: ask`) compiles to:
|
|
125
128
|
|
|
126
129
|
| Agent | Artifact metadata |
|
|
127
130
|
|---|---|
|
|
@@ -131,6 +134,8 @@ One `adaptive` fragment becomes different artifact metadata per Agent. The `perm
|
|
|
131
134
|
|
|
132
135
|
`claude` maps known lowercase tool names such as `read` and `webfetch` to Claude Code's names (`Read`, `WebFetch`) and passes any other name through unchanged. `deny` becomes `disallowedTools` on an agent and `disallowed-tools` on a skill. An agent's `tools` lists both `allow` and `ask` tools, because Claude Code's `tools` limits which tools exist rather than pre-approving them.
|
|
133
136
|
|
|
137
|
+
`disable-model-invocation` is not part of the Agent Skills specification, but Claude Code, Copilot, and OpenCode v2 ( 2.0.23+ ) read it as written. Older OpenCode versions read only their own `metadata` keys, so `opencode` translates the field for them.
|
|
138
|
+
|
|
134
139
|
A skill whose metadata is left unchanged by adaptation and carries no Agent-specific static fragments is written once to the shared skill directory. Otherwise it is written into that Agent's own configuration tree ( written with Ruby ). An agent extension is always Agent-specific. `claude` does not read the shared skill directory, so its skills are always written to `~/.claude/skills`.
|
|
135
140
|
|
|
136
141
|
### Commands
|
|
@@ -141,11 +146,14 @@ snapshot as below:
|
|
|
141
146
|
|
|
142
147
|
```sh
|
|
143
148
|
simple-agent-extension packages
|
|
149
|
+
simple-agent-extension clean
|
|
144
150
|
simple-agent-extension build
|
|
145
151
|
simple-agent-extension deploy
|
|
146
152
|
simple-agent-extension agents
|
|
147
153
|
```
|
|
148
154
|
|
|
155
|
+
`build` (and therefore `deploy`) runs `clean` first, so an artifact whose source package was removed is not deployed again. `clean` removes `<build-root>/<agent>/` for each targeted Agent only. It does not remove anything already deployed.
|
|
156
|
+
|
|
149
157
|
All commands resolve `--source-root` from `./packages` and `--build-root` from `./build` by default. Either root may be outside the repository.
|
|
150
158
|
|
|
151
159
|
```sh
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SimpleAgentExtension
|
|
4
|
-
class
|
|
4
|
+
class AgentDirectoryNotFound < Error; end
|
|
5
|
+
class AgentDirectoryNotDirectory < Error; end
|
|
6
|
+
class AgentDirectoryNotReadable < Error; end
|
|
5
7
|
class MissingAgentRegistration < Error; end
|
|
6
8
|
class UnloadableAgentRegistration < Error; end
|
|
7
9
|
|
|
@@ -28,12 +30,15 @@ module SimpleAgentExtension
|
|
|
28
30
|
@directories.map { |directory| canonical_directory(directory) }.uniq
|
|
29
31
|
end
|
|
30
32
|
|
|
33
|
+
# The error class names the condition, so the message carries the path
|
|
34
|
+
# alone.
|
|
31
35
|
def canonical_directory(directory)
|
|
32
36
|
path = File.expand_path(directory)
|
|
33
|
-
raise
|
|
37
|
+
raise AgentDirectoryNotFound, path unless File.exist?(path)
|
|
38
|
+
raise AgentDirectoryNotDirectory, path unless File.directory?(path)
|
|
34
39
|
|
|
35
40
|
path = File.realpath(path)
|
|
36
|
-
raise
|
|
41
|
+
raise AgentDirectoryNotReadable, path unless File.readable?(path)
|
|
37
42
|
|
|
38
43
|
path
|
|
39
44
|
end
|
|
@@ -10,6 +10,22 @@ module SimpleAgentExtension
|
|
|
10
10
|
{"permission" => permissions}
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
# For OpenCode before 2.0.23, which reads only these `metadata` keys.
|
|
14
|
+
# 2.0.23+ reads top-level `disable-model-invocation` itself and ignores
|
|
15
|
+
# `opencode/slash`, so this rule can go once older versions are unsupported.
|
|
16
|
+
define_translation type: :skill, field: "disable-model-invocation" do |disabled|
|
|
17
|
+
if disabled == true
|
|
18
|
+
{
|
|
19
|
+
"metadata" => {
|
|
20
|
+
"opencode/autoinvoke" => "false",
|
|
21
|
+
"opencode/slash" => "true"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else # standard:disable Style/EmptyElse
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
13
29
|
define_translation type: :agent, field: "name" do |_name|
|
|
14
30
|
nil
|
|
15
31
|
end
|
|
@@ -6,9 +6,17 @@ require "simple_agent_extension"
|
|
|
6
6
|
module SimpleAgentExtension
|
|
7
7
|
# Command-line adapter for public package compilation and deployment.
|
|
8
8
|
class CLI
|
|
9
|
+
# The command line itself was written wrong. The message describes the
|
|
10
|
+
# mistake in full, so where it was raised tells the caller nothing.
|
|
11
|
+
class UsageError < Error; end
|
|
12
|
+
|
|
13
|
+
# No command was resolved, so the caller still needs the vocabulary.
|
|
14
|
+
class CommandError < UsageError; end
|
|
15
|
+
|
|
9
16
|
COMMANDS = {
|
|
10
17
|
"packages" => "List package names under the source root",
|
|
11
|
-
"
|
|
18
|
+
"clean" => "Remove artifacts from the build root",
|
|
19
|
+
"build" => "Clean, then compile artifacts into the build root",
|
|
12
20
|
"deploy" => "Build artifacts, then deploy them to Agents",
|
|
13
21
|
"agents" => "List Agent names that can be targeted"
|
|
14
22
|
}.freeze
|
|
@@ -22,6 +30,12 @@ module SimpleAgentExtension
|
|
|
22
30
|
@error = error
|
|
23
31
|
end
|
|
24
32
|
|
|
33
|
+
# Only errors this CLI defines, and the one OptionParser raises for a
|
|
34
|
+
# misspelled option, are reduced to a message. Everything else passes
|
|
35
|
+
# through with its class, origin, and backtrace intact.
|
|
36
|
+
#
|
|
37
|
+
# CommandError has to be listed first: it is a UsageError, so the wider
|
|
38
|
+
# clause would take it otherwise.
|
|
25
39
|
def run(arguments)
|
|
26
40
|
arguments = arguments.dup
|
|
27
41
|
options = default_options
|
|
@@ -29,15 +43,17 @@ module SimpleAgentExtension
|
|
|
29
43
|
parser.parse!(arguments)
|
|
30
44
|
|
|
31
45
|
return display_help(parser) if options[:help]
|
|
46
|
+
return display_version(parser) if options[:version]
|
|
32
47
|
|
|
33
48
|
command = command(arguments)
|
|
34
49
|
validate_options(command, options)
|
|
35
50
|
AgentDirectoryLoader.new(options[:agent_directories]).load
|
|
36
51
|
run_command(command, options)
|
|
37
52
|
0
|
|
38
|
-
rescue
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
rescue CommandError => error
|
|
54
|
+
report_failure(error, usage: parser)
|
|
55
|
+
rescue OptionParser::ParseError, UsageError => error
|
|
56
|
+
report_failure(error)
|
|
41
57
|
end
|
|
42
58
|
|
|
43
59
|
# Aligns the name column alone. Columns an Agent does not declare are
|
|
@@ -55,6 +71,16 @@ module SimpleAgentExtension
|
|
|
55
71
|
|
|
56
72
|
private
|
|
57
73
|
|
|
74
|
+
# @param [Exception] error
|
|
75
|
+
# @param [OptionParser, nil] usage printed when the caller still needs the
|
|
76
|
+
# command vocabulary
|
|
77
|
+
# @return [Integer] exit status the run reports as a failure
|
|
78
|
+
def report_failure(error, usage: nil)
|
|
79
|
+
@error.puts error.message
|
|
80
|
+
@error.puts usage if usage
|
|
81
|
+
1
|
|
82
|
+
end
|
|
83
|
+
|
|
58
84
|
def default_options
|
|
59
85
|
{
|
|
60
86
|
source_root: File.expand_path("packages"),
|
|
@@ -62,13 +88,16 @@ module SimpleAgentExtension
|
|
|
62
88
|
agents: [],
|
|
63
89
|
agent_directories: [],
|
|
64
90
|
force: false,
|
|
65
|
-
help: false
|
|
91
|
+
help: false,
|
|
92
|
+
version: false
|
|
66
93
|
}
|
|
67
94
|
end
|
|
68
95
|
|
|
69
96
|
def option_parser(options)
|
|
70
97
|
OptionParser.new do |parser|
|
|
71
98
|
parser.banner = banner
|
|
99
|
+
parser.program_name = "simple-agent-extension"
|
|
100
|
+
parser.version = VERSION
|
|
72
101
|
|
|
73
102
|
parser.on("--source-root DIRECTORY", "Source package root (default: ./packages)") do |directory|
|
|
74
103
|
options[:source_root] = File.expand_path(directory)
|
|
@@ -76,7 +105,7 @@ module SimpleAgentExtension
|
|
|
76
105
|
parser.on("--build-root DIRECTORY", "Build artifact root (default: ./build)") do |directory|
|
|
77
106
|
options[:build_root] = File.expand_path(directory)
|
|
78
107
|
end
|
|
79
|
-
parser.on("--agent NAME", "
|
|
108
|
+
parser.on("--agent NAME", "Clean, build, or deploy only NAME (repeatable)") do |name|
|
|
80
109
|
options[:agents] << name
|
|
81
110
|
end
|
|
82
111
|
parser.on("--agent-dir DIRECTORY", "Load direct *.rb Agent registrations from DIRECTORY") do |directory|
|
|
@@ -88,6 +117,9 @@ module SimpleAgentExtension
|
|
|
88
117
|
parser.on("-h", "--help", "Show this help") do
|
|
89
118
|
options[:help] = true
|
|
90
119
|
end
|
|
120
|
+
parser.on("-V", "--version", "Show version") do
|
|
121
|
+
options[:version] = true
|
|
122
|
+
end
|
|
91
123
|
end
|
|
92
124
|
end
|
|
93
125
|
|
|
@@ -99,15 +131,20 @@ module SimpleAgentExtension
|
|
|
99
131
|
end
|
|
100
132
|
|
|
101
133
|
def display_help(parser)
|
|
102
|
-
@output.puts parser
|
|
134
|
+
@output.puts parser.ver, "", parser
|
|
135
|
+
0
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def display_version(parser)
|
|
139
|
+
@output.puts parser.ver
|
|
103
140
|
0
|
|
104
141
|
end
|
|
105
142
|
|
|
106
143
|
def command(arguments)
|
|
107
144
|
command = arguments.shift
|
|
108
|
-
raise
|
|
109
|
-
raise
|
|
110
|
-
raise
|
|
145
|
+
raise CommandError, "missing command" unless command
|
|
146
|
+
raise CommandError, "unknown command: #{command}" unless COMMANDS.key?(command)
|
|
147
|
+
raise CommandError, "unexpected arguments: #{arguments.join(" ")}" unless arguments.empty?
|
|
111
148
|
|
|
112
149
|
command
|
|
113
150
|
end
|
|
@@ -115,7 +152,7 @@ module SimpleAgentExtension
|
|
|
115
152
|
def validate_options(command, options)
|
|
116
153
|
return unless options[:force] && command != "deploy"
|
|
117
154
|
|
|
118
|
-
raise
|
|
155
|
+
raise UsageError, "--force is only valid for deploy"
|
|
119
156
|
end
|
|
120
157
|
|
|
121
158
|
def run_command(command, options)
|
|
@@ -130,6 +167,8 @@ module SimpleAgentExtension
|
|
|
130
167
|
@output.puts runner.packages
|
|
131
168
|
when "agents"
|
|
132
169
|
@output.puts agent_lines(runner.agents)
|
|
170
|
+
when "clean"
|
|
171
|
+
@output.puts runner.clean(agents: options[:agents])
|
|
133
172
|
when "build"
|
|
134
173
|
@output.puts runner.build(agents: options[:agents])
|
|
135
174
|
when "deploy"
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
module SimpleAgentExtension
|
|
2
2
|
module Extensions
|
|
3
|
+
class EntrypointDocumentNotFound < Error; end
|
|
4
|
+
class InvalidEntrypointFrontmatter < Error; end
|
|
5
|
+
|
|
3
6
|
EntrypointDocument = Data.define(:metadata, :body)
|
|
4
7
|
|
|
5
8
|
# An extension that owns loading metadata from its directory.
|
|
@@ -48,6 +51,12 @@ module SimpleAgentExtension
|
|
|
48
51
|
metadata, body = Frontmatter.parse(File.read(entrypoint_path))
|
|
49
52
|
|
|
50
53
|
EntrypointDocument.new(metadata, body)
|
|
54
|
+
rescue Errno::ENOENT => e
|
|
55
|
+
not_found = EntrypointDocumentNotFound.new("entrypoint of package `#{@package}' / type `#{type}', #{entrypoint_path} not found.")
|
|
56
|
+
not_found.set_backtrace(e.backtrace)
|
|
57
|
+
raise not_found, cause: nil
|
|
58
|
+
rescue Psych::SyntaxError => e
|
|
59
|
+
raise InvalidEntrypointFrontmatter, "#{entrypoint_path}: #{[e.problem, e.context].compact.join(" ")} at line #{e.line + Frontmatter::METADATA_START_LINE_OFFSET} column #{e.column}", cause: nil
|
|
51
60
|
end
|
|
52
61
|
|
|
53
62
|
# @return [MetadataLoader] source YAML reader
|
|
@@ -13,6 +13,8 @@ module SimpleAgentExtension
|
|
|
13
13
|
module Frontmatter
|
|
14
14
|
DELIMITER = "---".freeze
|
|
15
15
|
PATTERN = /\A---\r?\n(.*?)^---[ \t]*\r?\n?/m
|
|
16
|
+
# Metadata starts after the opening delimiter line.
|
|
17
|
+
METADATA_START_LINE_OFFSET = 1
|
|
16
18
|
|
|
17
19
|
class InvalidFrontmatter < Error; end
|
|
18
20
|
|
|
@@ -8,15 +8,23 @@ module SimpleAgentExtension
|
|
|
8
8
|
# reader exposes adaptive, common static, and Agent-specific static fragment
|
|
9
9
|
# collections without defining their field vocabulary.
|
|
10
10
|
class MetadataLoader
|
|
11
|
+
class InvalidMetadata < Error; end
|
|
12
|
+
|
|
11
13
|
FILENAME = "metadata.yaml".freeze
|
|
12
14
|
|
|
15
|
+
# Psych does not know which file it parsed, so the path is attached here.
|
|
16
|
+
# Its backtrace points into Psych itself, so it is dropped.
|
|
17
|
+
#
|
|
13
18
|
# @param [String] dir
|
|
14
19
|
# @return [MetadataLoader] empty when +metadata.yaml+ is absent, never nil
|
|
20
|
+
# @raise [InvalidMetadata] when +metadata.yaml+ has a YAML syntax error
|
|
15
21
|
def self.load(dir)
|
|
16
22
|
path = File.join(dir, FILENAME)
|
|
17
23
|
return new(Metadata.new) unless File.exist?(path)
|
|
18
24
|
|
|
19
25
|
new(Metadata.from(YAML.safe_load(File.read(path)) || {})) # rubocop:disable Style/YAMLFileRead
|
|
26
|
+
rescue Psych::SyntaxError => e
|
|
27
|
+
raise InvalidMetadata, "#{path}: #{[e.problem, e.context].compact.join(" ")} at line #{e.line} column #{e.column}", cause: nil
|
|
20
28
|
end
|
|
21
29
|
|
|
22
30
|
# @param [Metadata] metadata source YAML mapping
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
|
|
1
3
|
module SimpleAgentExtension
|
|
2
4
|
# Coordinates source collection, compilation, and deployment for one
|
|
3
5
|
# source and build roots. Entry points configure these boundaries explicitly.
|
|
@@ -25,12 +27,29 @@ module SimpleAgentExtension
|
|
|
25
27
|
end
|
|
26
28
|
end
|
|
27
29
|
|
|
30
|
+
# Removes each selected Agent's whole artifact subtree, so artifacts whose
|
|
31
|
+
# source package is gone cannot survive into the next deploy. Other
|
|
32
|
+
# Agents' subtrees are left alone.
|
|
33
|
+
#
|
|
34
|
+
# @param [Array<String>] agents target names; all targets when empty
|
|
35
|
+
# @return [Array<String>] Agent directories that existed and were removed
|
|
36
|
+
def clean(agents: [])
|
|
37
|
+
selected_agents(agents)
|
|
38
|
+
.map { |agent| File.join(@build_root, agent.name) }
|
|
39
|
+
.select { |dir| File.exist?(dir) }
|
|
40
|
+
.each { |dir| FileUtils.rm_rf(dir) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Cleans the selected Agents first, so the build tree mirrors the current
|
|
44
|
+
# source tree instead of accumulating every artifact ever compiled.
|
|
45
|
+
#
|
|
28
46
|
# @param [Array<String>] agents target names; all targets when empty
|
|
29
47
|
# @return [Array<String>] directories written to the build tree
|
|
30
48
|
def build(agents: [])
|
|
31
49
|
targets = selected_agents(agents)
|
|
32
50
|
extensions = source_extensions
|
|
33
51
|
warn_unknown_deploy_to_names(extensions)
|
|
52
|
+
clean(agents: agents)
|
|
34
53
|
|
|
35
54
|
targets.flat_map { |agent|
|
|
36
55
|
compiler = Compiler.new(agent: agent, build_root: @build_root)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simple-agent-extension
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ''
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: 'Coding Agent Extension Utility with Simple DSL and Rule Translating
|
|
14
14
|
and Deploying '
|