yamine 0.14.1 → 0.14.2
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 +17 -0
- data/lib/yamine/cli/skills.rb +187 -0
- data/lib/yamine/cli.rb +4 -1
- data/lib/yamine/version.rb +1 -1
- data/lib/yamine.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 56dbba9d974dc247a9f96271e6dd40e0e68b170a6ad20f9e0660aa6fa1d20a28
|
|
4
|
+
data.tar.gz: 218da1110324af22c665581eeb26101a29628f1f4e96fed47e492aebd6c18488
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e26fe517122747c2793eb2b441fe83289898796d47c99330d2589413873fa7a12ae1cc2a8b1da69928b39bc6f8ed01aa6e7647f682a872c80886d04d1a862be
|
|
7
|
+
data.tar.gz: ce6b77f5a0263594e698cae37ddd3673a8dc6e16a63b946f8bd0f3b939747d80ce86bbc19e100da84a4a7b38aa942de39d86cdc509e348d71d817bc7a6def433
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.14.2] — 2026-09-15
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`yamine skills install` — one command into your harness's local skills
|
|
8
|
+
directory.** With `ask-skills` / `ask-agent` the bundled yamine skill
|
|
9
|
+
at `lib/ask/skills/yamine/SKILL.md` is auto-discovered. Without them,
|
|
10
|
+
most harnesses (including ZCode) already discover
|
|
11
|
+
`~/.agents/skills/` — this command copies there so nobody has to find
|
|
12
|
+
the gem path by hand: `yamine skills install` (global default),
|
|
13
|
+
`yamine skills install --local` (per-project), `yamine skills
|
|
14
|
+
install --dir <path>` (anywhere), with `uninstall` symmetric. The gem
|
|
15
|
+
now auto-updates that copy: the installed skill is marked with
|
|
16
|
+
`.yamine-managed` and every `yamine <anything>` silently refreshes it
|
|
17
|
+
when the bundled version differs — hand-copied skills are never
|
|
18
|
+
touched.
|
|
19
|
+
|
|
3
20
|
## [0.14.1] — 2026-09-13
|
|
4
21
|
|
|
5
22
|
### Removed
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module Yamine
|
|
6
|
+
class CLI
|
|
7
|
+
# Manages the yamine SKILL.md copy in conventional skill directories.
|
|
8
|
+
#
|
|
9
|
+
# The bundled skill lives inside the gem at lib/ask/skills/yamine/SKILL.md.
|
|
10
|
+
# With ask-skills / ask-agent it is auto-discovered. Without them, users
|
|
11
|
+
# still need it where harnesses look — most commonly ~/.agents/skills/.
|
|
12
|
+
# This command handles that copy so nobody has to find the gem path by hand.
|
|
13
|
+
module SkillsCommand
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
SKILL_NAME = "yamine"
|
|
17
|
+
SOURCE_REL = "ask/skills/yamine/SKILL.md"
|
|
18
|
+
|
|
19
|
+
# Keeps installed copies in sync after `gem update yamine`. Called
|
|
20
|
+
# once per yamine invocation, silently, before the subcommand runs.
|
|
21
|
+
# Only touches copies that were created by `yamine skills install`
|
|
22
|
+
# (identified by the marker file next to SKILL.md) so a hand-copied
|
|
23
|
+
# or hand-edited skill is never overwritten. Failures are swallowed
|
|
24
|
+
# — a stale skill is better than a broken boot.
|
|
25
|
+
def auto_sync
|
|
26
|
+
candidates.each do |dest|
|
|
27
|
+
next unless File.file?(dest)
|
|
28
|
+
next unless managed?(dest)
|
|
29
|
+
sync_copy(dest)
|
|
30
|
+
end
|
|
31
|
+
rescue StandardError
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def run(_ctx, args)
|
|
36
|
+
sub = args.shift
|
|
37
|
+
if sub == "--help" || sub == "-h" || sub.nil?
|
|
38
|
+
return install(["--help"])
|
|
39
|
+
end
|
|
40
|
+
case sub
|
|
41
|
+
when "install" then install(args)
|
|
42
|
+
when "uninstall" then uninstall(args)
|
|
43
|
+
when nil
|
|
44
|
+
raise Error, "Usage: yamine skills install [--global] [--local] [--dir <path>] | " \
|
|
45
|
+
"yamine skills uninstall [--global] [--local] [--dir <path>]"
|
|
46
|
+
else
|
|
47
|
+
raise Error, "Unknown skills subcommand #{sub.inspect}. " \
|
|
48
|
+
"Try: yamine skills install --global"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def install(args)
|
|
53
|
+
opts, rest = take_flags(args)
|
|
54
|
+
unless rest.empty?
|
|
55
|
+
raise Error, "Unknown argument #{rest.first.inspect}. " \
|
|
56
|
+
"Try: yamine skills install --global"
|
|
57
|
+
end
|
|
58
|
+
dest = target_dirs(opts).first
|
|
59
|
+
source = bundled_skill_path
|
|
60
|
+
unless File.file?(source)
|
|
61
|
+
raise Error, "Bundled skill not found at #{source} — reinstall yamine"
|
|
62
|
+
end
|
|
63
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
|
64
|
+
FileUtils.cp(source, dest)
|
|
65
|
+
stamp(dest)
|
|
66
|
+
puts "Installed yamine skill -> #{dest}"
|
|
67
|
+
if opts[:dir].nil? && !opts[:local]
|
|
68
|
+
collisions = other_skill_dirs(opts)
|
|
69
|
+
existing = collisions.select { |p| File.file?(p) }
|
|
70
|
+
unless existing.empty?
|
|
71
|
+
puts "Note: also found at #{existing.join(", ")} (yamine is already available there)"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
0
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def uninstall(args)
|
|
78
|
+
opts, rest = take_flags(args)
|
|
79
|
+
unless rest.empty?
|
|
80
|
+
raise Error, "Unknown argument #{rest.first.inspect}."
|
|
81
|
+
end
|
|
82
|
+
dest = target_dirs(opts).first
|
|
83
|
+
if File.file?(dest)
|
|
84
|
+
FileUtils.rm(dest)
|
|
85
|
+
FileUtils.rm_f(managed_marker(dest))
|
|
86
|
+
puts "Removed #{dest}"
|
|
87
|
+
else
|
|
88
|
+
puts "Not installed at #{dest} (nothing to do)"
|
|
89
|
+
end
|
|
90
|
+
0
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def target_dirs(opts)
|
|
94
|
+
return [File.expand_path(File.join(opts[:dir], SKILL_NAME, "SKILL.md"))] if opts[:dir]
|
|
95
|
+
|
|
96
|
+
if opts[:local]
|
|
97
|
+
[File.join(Dir.pwd, ".agents", "skills", SKILL_NAME, "SKILL.md")]
|
|
98
|
+
else
|
|
99
|
+
[File.expand_path("~/.agents/skills/#{SKILL_NAME}/SKILL.md")]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def other_skill_dirs(opts)
|
|
104
|
+
[
|
|
105
|
+
File.join(Dir.pwd, ".agents", "skills", SKILL_NAME, "SKILL.md"),
|
|
106
|
+
File.expand_path("~/.agents/skills/#{SKILL_NAME}/SKILL.md"),
|
|
107
|
+
File.expand_path("~/.config/ask/skills/#{SKILL_NAME}/SKILL.md")
|
|
108
|
+
].reject { |p| p == target_dirs(opts).first }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def bundled_skill_path
|
|
112
|
+
File.expand_path("../../ask/skills/yamine/SKILL.md", __dir__)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def managed_marker(dest)
|
|
116
|
+
File.join(File.dirname(dest), ".yamine-managed")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def managed?(dest)
|
|
120
|
+
File.file?(managed_marker(dest))
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def stamp(dest)
|
|
124
|
+
File.write(managed_marker(dest), "managed by yamine skills install; safe to auto-update\n")
|
|
125
|
+
rescue StandardError
|
|
126
|
+
nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def candidates
|
|
130
|
+
[
|
|
131
|
+
File.expand_path("~/.agents/skills/#{SKILL_NAME}/SKILL.md"),
|
|
132
|
+
File.join(Dir.pwd, ".agents", "skills", SKILL_NAME, "SKILL.md")
|
|
133
|
+
]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def sync_copy(dest)
|
|
137
|
+
source = bundled_skill_path
|
|
138
|
+
FileUtils.cp(source, dest) if File.exist?(source) && File.read(source) != File.read(dest)
|
|
139
|
+
stamp(dest)
|
|
140
|
+
rescue StandardError
|
|
141
|
+
nil
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def take_flags(args)
|
|
145
|
+
opts = {}
|
|
146
|
+
rest = []
|
|
147
|
+
i = 0
|
|
148
|
+
while i < args.length
|
|
149
|
+
arg = args[i]
|
|
150
|
+
case arg
|
|
151
|
+
when "--local"
|
|
152
|
+
opts[:local] = true
|
|
153
|
+
i += 1
|
|
154
|
+
when "--global"
|
|
155
|
+
i += 1
|
|
156
|
+
when "--dir"
|
|
157
|
+
opts[:dir] = args.fetch(i + 1) { raise Error, "--dir needs a path" }
|
|
158
|
+
i += 2
|
|
159
|
+
when "--help", "-h"
|
|
160
|
+
puts <<~HELP
|
|
161
|
+
yamine skills install Install the yamine SKILL.md into your skills directories
|
|
162
|
+
|
|
163
|
+
Usage:
|
|
164
|
+
yamine skills install [--global] -> ~/.agents/skills/yamine/SKILL.md (default)
|
|
165
|
+
yamine skills install --local -> .agents/skills/yamine/SKILL.md
|
|
166
|
+
yamine skills install --dir <path> -> <path>/yamine/SKILL.md
|
|
167
|
+
|
|
168
|
+
The skill is already bundled at lib/ask/skills/yamine/SKILL.md inside the gem.
|
|
169
|
+
Most harnesses discover ~/.agents/skills/ automatically. Use --local for per-project.
|
|
170
|
+
|
|
171
|
+
Other subcommands:
|
|
172
|
+
yamine skills uninstall [--global|--local|--dir <path>]
|
|
173
|
+
HELP
|
|
174
|
+
exit 0
|
|
175
|
+
when /\A--/
|
|
176
|
+
raise Error, "Unknown flag #{arg} for yamine skills install. " \
|
|
177
|
+
"Try --global, --local, or --dir <path>."
|
|
178
|
+
else
|
|
179
|
+
rest << arg
|
|
180
|
+
i += 1
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
[opts, rest]
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
data/lib/yamine/cli.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Yamine
|
|
|
10
10
|
# In non-interactive environments (no TTY or CI=1) we fail early with
|
|
11
11
|
# a clear message instead of prompting (portless lesson).
|
|
12
12
|
class CLI
|
|
13
|
-
SUBCOMMANDS = %w[get alias hosts list doctor trust clean prune proxy service sudoers kamal stop restart log status open setup start init db worktree].freeze
|
|
13
|
+
SUBCOMMANDS = %w[get alias hosts list doctor trust clean prune proxy service sudoers kamal stop restart log status open setup start init db worktree skills].freeze
|
|
14
14
|
|
|
15
15
|
def self.run(argv)
|
|
16
16
|
new.run(argv)
|
|
@@ -24,6 +24,7 @@ module Yamine
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def run(argv)
|
|
27
|
+
SkillsCommand.auto_sync rescue nil
|
|
27
28
|
args = argv.dup
|
|
28
29
|
if args.empty?
|
|
29
30
|
return BootCommand.run_inferred(Context.new, args)
|
|
@@ -60,6 +61,7 @@ module Yamine
|
|
|
60
61
|
when "start" then SystemCommand.start(ctx, args)
|
|
61
62
|
when "db" then SystemCommand.db(ctx, args)
|
|
62
63
|
when "worktree" then WorktreeCommand.run(ctx, args)
|
|
64
|
+
when "skills" then SkillsCommand.run(ctx, args)
|
|
63
65
|
when "kamal" then SystemCommand.kamal(ctx, args)
|
|
64
66
|
when "stop"
|
|
65
67
|
exit RoutesCommand.stop(ctx, args)
|
|
@@ -94,6 +96,7 @@ module Yamine
|
|
|
94
96
|
yamine prune Remove stale routes
|
|
95
97
|
yamine db list|create|drop Per-worktree databases
|
|
96
98
|
yamine worktree list|add|remove|clean Worktrees: create, inspect, full cleanup
|
|
99
|
+
yamine skills install Install yamine skill into ~/.agents/skills/ (or --local)
|
|
97
100
|
yamine proxy start|stop Control the proxy
|
|
98
101
|
yamine service install|status|uninstall OS startup service
|
|
99
102
|
yamine sudoers Print scoped passwordless-sudo rules for port 443
|
data/lib/yamine/version.rb
CHANGED
data/lib/yamine.rb
CHANGED
|
@@ -32,6 +32,7 @@ require_relative "yamine/cli/boot"
|
|
|
32
32
|
require_relative "yamine/cli/routes"
|
|
33
33
|
require_relative "yamine/cli/system"
|
|
34
34
|
require_relative "yamine/cli/worktree"
|
|
35
|
+
require_relative "yamine/cli/skills"
|
|
35
36
|
require_relative "yamine/cli"
|
|
36
37
|
|
|
37
38
|
# Stable named .localhost URLs for Ruby development.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yamine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.14.
|
|
4
|
+
version: 0.14.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kaka Ruto
|
|
@@ -88,6 +88,7 @@ files:
|
|
|
88
88
|
- lib/yamine/cli/boot.rb
|
|
89
89
|
- lib/yamine/cli/context.rb
|
|
90
90
|
- lib/yamine/cli/routes.rb
|
|
91
|
+
- lib/yamine/cli/skills.rb
|
|
91
92
|
- lib/yamine/cli/system.rb
|
|
92
93
|
- lib/yamine/cli/worktree.rb
|
|
93
94
|
- lib/yamine/command.rb
|