igniter-extensions 0.5.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 +7 -0
- data/README.md +381 -0
- data/lib/igniter/extensions/contracts/aggregate_pack.rb +103 -0
- data/lib/igniter/extensions/contracts/audit/builder.rb +132 -0
- data/lib/igniter/extensions/contracts/audit/event.rb +34 -0
- data/lib/igniter/extensions/contracts/audit/snapshot.rb +44 -0
- data/lib/igniter/extensions/contracts/audit_pack.rb +60 -0
- data/lib/igniter/extensions/contracts/branch_pack.rb +199 -0
- data/lib/igniter/extensions/contracts/capabilities/declaration.rb +31 -0
- data/lib/igniter/extensions/contracts/capabilities/error.rb +35 -0
- data/lib/igniter/extensions/contracts/capabilities/policy.rb +20 -0
- data/lib/igniter/extensions/contracts/capabilities/report.rb +47 -0
- data/lib/igniter/extensions/contracts/capabilities/violation.rb +30 -0
- data/lib/igniter/extensions/contracts/capabilities_pack.rb +146 -0
- data/lib/igniter/extensions/contracts/collection_pack.rb +212 -0
- data/lib/igniter/extensions/contracts/commerce_pack.rb +91 -0
- data/lib/igniter/extensions/contracts/compose_pack.rb +213 -0
- data/lib/igniter/extensions/contracts/content_addressing/cache.rb +59 -0
- data/lib/igniter/extensions/contracts/content_addressing/content_key.rb +63 -0
- data/lib/igniter/extensions/contracts/content_addressing/declaration.rb +47 -0
- data/lib/igniter/extensions/contracts/content_addressing_pack.rb +90 -0
- data/lib/igniter/extensions/contracts/creator/profile.rb +196 -0
- data/lib/igniter/extensions/contracts/creator/report.rb +85 -0
- data/lib/igniter/extensions/contracts/creator/scaffold.rb +461 -0
- data/lib/igniter/extensions/contracts/creator/scope.rb +79 -0
- data/lib/igniter/extensions/contracts/creator/wizard.rb +269 -0
- data/lib/igniter/extensions/contracts/creator/workflow.rb +189 -0
- data/lib/igniter/extensions/contracts/creator/workflow_step.rb +51 -0
- data/lib/igniter/extensions/contracts/creator/write_result.rb +48 -0
- data/lib/igniter/extensions/contracts/creator/write_step.rb +63 -0
- data/lib/igniter/extensions/contracts/creator/writer.rb +131 -0
- data/lib/igniter/extensions/contracts/creator_pack.rb +128 -0
- data/lib/igniter/extensions/contracts/dataflow/aggregate_operators.rb +119 -0
- data/lib/igniter/extensions/contracts/dataflow/aggregate_state.rb +60 -0
- data/lib/igniter/extensions/contracts/dataflow/builder.rb +66 -0
- data/lib/igniter/extensions/contracts/dataflow/collection_result.rb +70 -0
- data/lib/igniter/extensions/contracts/dataflow/diff.rb +37 -0
- data/lib/igniter/extensions/contracts/dataflow/item_result.rb +44 -0
- data/lib/igniter/extensions/contracts/dataflow/result.rb +58 -0
- data/lib/igniter/extensions/contracts/dataflow/session.rb +173 -0
- data/lib/igniter/extensions/contracts/dataflow/window_filter.rb +49 -0
- data/lib/igniter/extensions/contracts/dataflow_pack.rb +66 -0
- data/lib/igniter/extensions/contracts/debug/pack_audit.rb +181 -0
- data/lib/igniter/extensions/contracts/debug/pack_snapshot.rb +46 -0
- data/lib/igniter/extensions/contracts/debug/profile_snapshot.rb +50 -0
- data/lib/igniter/extensions/contracts/debug/report.rb +50 -0
- data/lib/igniter/extensions/contracts/debug_pack.rb +115 -0
- data/lib/igniter/extensions/contracts/differential/divergence.rb +37 -0
- data/lib/igniter/extensions/contracts/differential/formatter.rb +85 -0
- data/lib/igniter/extensions/contracts/differential/report.rb +83 -0
- data/lib/igniter/extensions/contracts/differential/runner.rb +136 -0
- data/lib/igniter/extensions/contracts/differential_pack.rb +61 -0
- data/lib/igniter/extensions/contracts/execution_report_pack.rb +38 -0
- data/lib/igniter/extensions/contracts/incremental/formatter.rb +60 -0
- data/lib/igniter/extensions/contracts/incremental/node_state.rb +30 -0
- data/lib/igniter/extensions/contracts/incremental/result.rb +65 -0
- data/lib/igniter/extensions/contracts/incremental/session.rb +146 -0
- data/lib/igniter/extensions/contracts/incremental_pack.rb +40 -0
- data/lib/igniter/extensions/contracts/invariants/builder.rb +27 -0
- data/lib/igniter/extensions/contracts/invariants/cases_report.rb +47 -0
- data/lib/igniter/extensions/contracts/invariants/error.rb +34 -0
- data/lib/igniter/extensions/contracts/invariants/invariant.rb +30 -0
- data/lib/igniter/extensions/contracts/invariants/report.rb +45 -0
- data/lib/igniter/extensions/contracts/invariants/suite.rb +36 -0
- data/lib/igniter/extensions/contracts/invariants/violation.rb +39 -0
- data/lib/igniter/extensions/contracts/invariants_pack.rb +88 -0
- data/lib/igniter/extensions/contracts/journal_pack.rb +55 -0
- data/lib/igniter/extensions/contracts/language/formula_pack.rb +185 -0
- data/lib/igniter/extensions/contracts/language/piecewise_pack.rb +166 -0
- data/lib/igniter/extensions/contracts/language/scale_pack.rb +147 -0
- data/lib/igniter/extensions/contracts/lookup_pack.rb +50 -0
- data/lib/igniter/extensions/contracts/mcp/creator_session.rb +105 -0
- data/lib/igniter/extensions/contracts/mcp/tool_argument.rb +35 -0
- data/lib/igniter/extensions/contracts/mcp/tool_definition.rb +33 -0
- data/lib/igniter/extensions/contracts/mcp/tool_result.rb +28 -0
- data/lib/igniter/extensions/contracts/mcp_pack.rb +335 -0
- data/lib/igniter/extensions/contracts/provenance/builder.rb +80 -0
- data/lib/igniter/extensions/contracts/provenance/lineage.rb +59 -0
- data/lib/igniter/extensions/contracts/provenance/node_trace.rb +53 -0
- data/lib/igniter/extensions/contracts/provenance/text_formatter.rb +62 -0
- data/lib/igniter/extensions/contracts/provenance_pack.rb +52 -0
- data/lib/igniter/extensions/contracts/reactive/builder.rb +43 -0
- data/lib/igniter/extensions/contracts/reactive/dispatch_result.rb +59 -0
- data/lib/igniter/extensions/contracts/reactive/engine.rb +79 -0
- data/lib/igniter/extensions/contracts/reactive/event.rb +36 -0
- data/lib/igniter/extensions/contracts/reactive/matcher.rb +20 -0
- data/lib/igniter/extensions/contracts/reactive/plan.rb +58 -0
- data/lib/igniter/extensions/contracts/reactive/subscription.rb +29 -0
- data/lib/igniter/extensions/contracts/reactive_pack.rb +169 -0
- data/lib/igniter/extensions/contracts/saga/compensation.rb +25 -0
- data/lib/igniter/extensions/contracts/saga/compensation_record.rb +28 -0
- data/lib/igniter/extensions/contracts/saga/compensation_set.rb +47 -0
- data/lib/igniter/extensions/contracts/saga/formatter.rb +39 -0
- data/lib/igniter/extensions/contracts/saga/result.rb +56 -0
- data/lib/igniter/extensions/contracts/saga/runner.rb +124 -0
- data/lib/igniter/extensions/contracts/saga_pack.rb +56 -0
- data/lib/igniter/extensions/contracts.rb +445 -0
- data/lib/igniter/extensions.rb +6 -0
- data/lib/igniter-extensions.rb +3 -0
- metadata +152 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Extensions
|
|
5
|
+
module Contracts
|
|
6
|
+
module Mcp
|
|
7
|
+
class ToolDefinition
|
|
8
|
+
attr_reader :name, :summary, :mutating, :target, :arguments
|
|
9
|
+
|
|
10
|
+
def initialize(name:, summary:, mutating: false, target: nil, arguments: [])
|
|
11
|
+
@name = name.to_sym
|
|
12
|
+
@summary = summary
|
|
13
|
+
@mutating = mutating == true
|
|
14
|
+
@target = target&.to_sym
|
|
15
|
+
@arguments = arguments.freeze
|
|
16
|
+
freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_h
|
|
20
|
+
payload = {
|
|
21
|
+
name: name,
|
|
22
|
+
summary: summary,
|
|
23
|
+
mutating: mutating,
|
|
24
|
+
arguments: arguments.map(&:to_h)
|
|
25
|
+
}
|
|
26
|
+
payload[:target] = target if target
|
|
27
|
+
payload
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Extensions
|
|
5
|
+
module Contracts
|
|
6
|
+
module Mcp
|
|
7
|
+
class ToolResult
|
|
8
|
+
attr_reader :tool_name, :payload, :mutating
|
|
9
|
+
|
|
10
|
+
def initialize(tool_name:, payload:, mutating:)
|
|
11
|
+
@tool_name = tool_name.to_sym
|
|
12
|
+
@payload = payload
|
|
13
|
+
@mutating = mutating == true
|
|
14
|
+
freeze
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_h
|
|
18
|
+
{
|
|
19
|
+
tool_name: tool_name,
|
|
20
|
+
mutating: mutating,
|
|
21
|
+
payload: payload
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "mcp/tool_definition"
|
|
4
|
+
require_relative "mcp/tool_argument"
|
|
5
|
+
require_relative "mcp/tool_result"
|
|
6
|
+
require_relative "mcp/creator_session"
|
|
7
|
+
|
|
8
|
+
module Igniter
|
|
9
|
+
module Extensions
|
|
10
|
+
module Contracts
|
|
11
|
+
module McpPack
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def manifest
|
|
15
|
+
Igniter::Contracts::PackManifest.new(
|
|
16
|
+
name: :extensions_mcp,
|
|
17
|
+
requires_packs: [DebugPack, CreatorPack],
|
|
18
|
+
metadata: { category: :tooling }
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def install_into(kernel)
|
|
23
|
+
kernel
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def tools
|
|
27
|
+
@tools ||= [
|
|
28
|
+
Mcp::ToolDefinition.new(
|
|
29
|
+
name: :inspect_profile,
|
|
30
|
+
summary: "Return a structured profile snapshot.",
|
|
31
|
+
target: :profile_or_environment
|
|
32
|
+
),
|
|
33
|
+
Mcp::ToolDefinition.new(
|
|
34
|
+
name: :inspect_pack,
|
|
35
|
+
summary: "Return a structured installed-pack snapshot.",
|
|
36
|
+
target: :profile_or_environment,
|
|
37
|
+
arguments: [
|
|
38
|
+
Mcp::ToolArgument.new(name: :pack, type: :pack_reference,
|
|
39
|
+
summary: "Installed pack name or pack module.", required: true)
|
|
40
|
+
]
|
|
41
|
+
),
|
|
42
|
+
Mcp::ToolDefinition.new(
|
|
43
|
+
name: :audit_pack,
|
|
44
|
+
summary: "Audit a custom pack against creator/debug quality seams.",
|
|
45
|
+
target: :optional_profile_or_environment,
|
|
46
|
+
arguments: [
|
|
47
|
+
Mcp::ToolArgument.new(name: :pack, type: :pack_reference, summary: "Custom pack module to audit.",
|
|
48
|
+
required: true)
|
|
49
|
+
]
|
|
50
|
+
),
|
|
51
|
+
Mcp::ToolDefinition.new(
|
|
52
|
+
name: :debug_report,
|
|
53
|
+
summary: "Compile or execute and return a structured debug report.",
|
|
54
|
+
target: :environment,
|
|
55
|
+
arguments: [
|
|
56
|
+
Mcp::ToolArgument.new(name: :inputs, type: :map,
|
|
57
|
+
summary: "Runtime inputs used for execution when a graph is available."),
|
|
58
|
+
Mcp::ToolArgument.new(name: :compiled_graph, type: :compiled_graph,
|
|
59
|
+
summary: "Previously compiled graph to execute instead of compiling a block.")
|
|
60
|
+
]
|
|
61
|
+
),
|
|
62
|
+
Mcp::ToolDefinition.new(
|
|
63
|
+
name: :creator_wizard,
|
|
64
|
+
summary: "Build a stateful creator wizard payload.",
|
|
65
|
+
target: :optional_profile_or_environment,
|
|
66
|
+
arguments: creator_arguments(include_scope: true, include_root: true)
|
|
67
|
+
),
|
|
68
|
+
Mcp::ToolDefinition.new(
|
|
69
|
+
name: :creator_session_start,
|
|
70
|
+
summary: "Create a serialized creator session payload.",
|
|
71
|
+
target: :optional_profile_or_environment,
|
|
72
|
+
arguments: creator_arguments(include_scope: true, include_root: true)
|
|
73
|
+
),
|
|
74
|
+
Mcp::ToolDefinition.new(
|
|
75
|
+
name: :creator_session_apply,
|
|
76
|
+
summary: "Apply updates to a serialized creator session payload.",
|
|
77
|
+
target: :optional_profile_or_environment,
|
|
78
|
+
arguments: [
|
|
79
|
+
Mcp::ToolArgument.new(name: :session, type: :session_state,
|
|
80
|
+
summary: "Previously serialized creator session payload.", required: true),
|
|
81
|
+
Mcp::ToolArgument.new(name: :updates, type: :map,
|
|
82
|
+
summary: "Partial wizard updates to merge into the session.", required: true)
|
|
83
|
+
]
|
|
84
|
+
),
|
|
85
|
+
Mcp::ToolDefinition.new(
|
|
86
|
+
name: :creator_session_workflow,
|
|
87
|
+
summary: "Build workflow payload from a serialized creator session.",
|
|
88
|
+
target: :optional_profile_or_environment,
|
|
89
|
+
arguments: [
|
|
90
|
+
Mcp::ToolArgument.new(name: :session, type: :session_state,
|
|
91
|
+
summary: "Previously serialized creator session payload.", required: true)
|
|
92
|
+
]
|
|
93
|
+
),
|
|
94
|
+
Mcp::ToolDefinition.new(
|
|
95
|
+
name: :creator_session_write_plan,
|
|
96
|
+
summary: "Build writer plan payload from a serialized creator session.",
|
|
97
|
+
target: :optional_profile_or_environment,
|
|
98
|
+
arguments: [
|
|
99
|
+
Mcp::ToolArgument.new(name: :session, type: :session_state,
|
|
100
|
+
summary: "Previously serialized creator session payload.", required: true)
|
|
101
|
+
]
|
|
102
|
+
),
|
|
103
|
+
Mcp::ToolDefinition.new(
|
|
104
|
+
name: :creator_session_write,
|
|
105
|
+
summary: "Write scaffold files from a serialized creator session.",
|
|
106
|
+
mutating: true,
|
|
107
|
+
target: :optional_profile_or_environment,
|
|
108
|
+
arguments: [
|
|
109
|
+
Mcp::ToolArgument.new(name: :session, type: :session_state,
|
|
110
|
+
summary: "Previously serialized creator session payload.", required: true)
|
|
111
|
+
]
|
|
112
|
+
),
|
|
113
|
+
Mcp::ToolDefinition.new(
|
|
114
|
+
name: :creator_workflow,
|
|
115
|
+
summary: "Build a creator workflow payload.",
|
|
116
|
+
target: :optional_profile_or_environment,
|
|
117
|
+
arguments: creator_arguments(include_scope: true)
|
|
118
|
+
),
|
|
119
|
+
Mcp::ToolDefinition.new(
|
|
120
|
+
name: :creator_write_plan,
|
|
121
|
+
summary: "Build a creator writer plan payload.",
|
|
122
|
+
target: :optional_profile_or_environment,
|
|
123
|
+
arguments: creator_arguments(include_scope: true, include_root: true, require_name: true,
|
|
124
|
+
require_root: true)
|
|
125
|
+
),
|
|
126
|
+
Mcp::ToolDefinition.new(
|
|
127
|
+
name: :creator_write,
|
|
128
|
+
summary: "Write a creator scaffold to disk.",
|
|
129
|
+
mutating: true,
|
|
130
|
+
target: :optional_profile_or_environment,
|
|
131
|
+
arguments: creator_arguments(include_scope: true, include_root: true, require_name: true,
|
|
132
|
+
require_root: true)
|
|
133
|
+
)
|
|
134
|
+
].freeze
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def tool_catalog
|
|
138
|
+
tools.map(&:to_h)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def call(tool_name, target: nil, **arguments, &block)
|
|
142
|
+
definition = tool_definition(tool_name)
|
|
143
|
+
payload = dispatch(definition.name, target: target, **arguments, &block)
|
|
144
|
+
Mcp::ToolResult.new(
|
|
145
|
+
tool_name: definition.name,
|
|
146
|
+
payload: payload,
|
|
147
|
+
mutating: definition.mutating
|
|
148
|
+
)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def tool_definition(tool_name)
|
|
152
|
+
tools.find { |definition| definition.name == tool_name.to_sym } ||
|
|
153
|
+
raise(ArgumentError, "unknown MCP tool #{tool_name.inspect}")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def dispatch(tool_name, target: nil, **arguments, &block)
|
|
157
|
+
case tool_name.to_sym
|
|
158
|
+
when :inspect_profile
|
|
159
|
+
profile_from(target).then { |profile| DebugPack.profile_snapshot(profile).to_h }
|
|
160
|
+
when :inspect_pack
|
|
161
|
+
profile = profile_from(target)
|
|
162
|
+
DebugPack.pack_snapshot(arguments.fetch(:pack), profile: profile).to_h
|
|
163
|
+
when :audit_pack
|
|
164
|
+
DebugPack.audit(arguments.fetch(:pack), profile: profile_from(target, optional: true)).to_h
|
|
165
|
+
when :debug_report
|
|
166
|
+
environment = environment_from(target)
|
|
167
|
+
DebugPack.report(
|
|
168
|
+
environment,
|
|
169
|
+
inputs: arguments[:inputs],
|
|
170
|
+
compiled_graph: arguments[:compiled_graph],
|
|
171
|
+
&block
|
|
172
|
+
).to_h
|
|
173
|
+
when :creator_wizard
|
|
174
|
+
CreatorPack.wizard(
|
|
175
|
+
name: arguments[:name],
|
|
176
|
+
kind: arguments[:kind],
|
|
177
|
+
namespace: arguments.fetch(:namespace, "MyCompany::IgniterPacks"),
|
|
178
|
+
profile: arguments[:profile],
|
|
179
|
+
capabilities: arguments[:capabilities],
|
|
180
|
+
scope: arguments[:scope],
|
|
181
|
+
root: arguments[:root],
|
|
182
|
+
mode: arguments.fetch(:mode, :skip_existing),
|
|
183
|
+
pack: arguments[:pack],
|
|
184
|
+
target_profile: profile_from(target, optional: true)
|
|
185
|
+
).to_h
|
|
186
|
+
when :creator_session_start
|
|
187
|
+
creator_session_from(arguments, target: target).to_h
|
|
188
|
+
when :creator_session_apply
|
|
189
|
+
session = session_from(arguments.fetch(:session) { arguments.fetch("session") }, target: target)
|
|
190
|
+
session.apply(**symbolize_keys(arguments.fetch(:updates) { arguments.fetch("updates") })).to_h
|
|
191
|
+
when :creator_session_workflow
|
|
192
|
+
session_from(arguments.fetch(:session) { arguments.fetch("session") }, target: target).workflow_payload
|
|
193
|
+
when :creator_session_write_plan
|
|
194
|
+
session_from(arguments.fetch(:session) { arguments.fetch("session") }, target: target).write_plan_payload
|
|
195
|
+
when :creator_session_write
|
|
196
|
+
session_from(arguments.fetch(:session) { arguments.fetch("session") }, target: target).write_payload
|
|
197
|
+
when :creator_workflow
|
|
198
|
+
CreatorPack.workflow(
|
|
199
|
+
name: arguments.fetch(:name),
|
|
200
|
+
kind: arguments[:kind],
|
|
201
|
+
namespace: arguments.fetch(:namespace, "MyCompany::IgniterPacks"),
|
|
202
|
+
profile: arguments[:profile],
|
|
203
|
+
capabilities: arguments[:capabilities],
|
|
204
|
+
scope: arguments.fetch(:scope, :monorepo_package),
|
|
205
|
+
pack: arguments[:pack],
|
|
206
|
+
target_profile: profile_from(target, optional: true)
|
|
207
|
+
).to_h
|
|
208
|
+
when :creator_write_plan
|
|
209
|
+
CreatorPack.writer(
|
|
210
|
+
name: arguments.fetch(:name),
|
|
211
|
+
kind: arguments[:kind],
|
|
212
|
+
namespace: arguments.fetch(:namespace, "MyCompany::IgniterPacks"),
|
|
213
|
+
profile: arguments[:profile],
|
|
214
|
+
capabilities: arguments[:capabilities],
|
|
215
|
+
scope: arguments.fetch(:scope, :monorepo_package),
|
|
216
|
+
pack: arguments[:pack],
|
|
217
|
+
target_profile: profile_from(target, optional: true),
|
|
218
|
+
root: arguments.fetch(:root),
|
|
219
|
+
mode: arguments.fetch(:mode, :skip_existing)
|
|
220
|
+
).plan.to_h
|
|
221
|
+
when :creator_write
|
|
222
|
+
CreatorPack.write(
|
|
223
|
+
name: arguments.fetch(:name),
|
|
224
|
+
kind: arguments[:kind],
|
|
225
|
+
namespace: arguments.fetch(:namespace, "MyCompany::IgniterPacks"),
|
|
226
|
+
profile: arguments[:profile],
|
|
227
|
+
capabilities: arguments[:capabilities],
|
|
228
|
+
scope: arguments.fetch(:scope, :monorepo_package),
|
|
229
|
+
pack: arguments[:pack],
|
|
230
|
+
target_profile: profile_from(target, optional: true),
|
|
231
|
+
root: arguments.fetch(:root),
|
|
232
|
+
mode: arguments.fetch(:mode, :skip_existing)
|
|
233
|
+
).to_h
|
|
234
|
+
else
|
|
235
|
+
raise ArgumentError, "unsupported MCP tool #{tool_name.inspect}"
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def profile_from(target, optional: false)
|
|
240
|
+
profile =
|
|
241
|
+
case target
|
|
242
|
+
when nil
|
|
243
|
+
nil
|
|
244
|
+
else
|
|
245
|
+
target.respond_to?(:profile) ? target.profile : target
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
return profile if optional || profile
|
|
249
|
+
|
|
250
|
+
raise ArgumentError, "McpPack tool requires an environment or profile target"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def environment_from(target)
|
|
254
|
+
return target if target.respond_to?(:profile) && target.respond_to?(:execute)
|
|
255
|
+
|
|
256
|
+
raise ArgumentError, "McpPack debug_report requires an environment target"
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def creator_arguments(include_scope: false, include_root: false, require_name: false, require_root: false)
|
|
260
|
+
arguments = [
|
|
261
|
+
Mcp::ToolArgument.new(name: :name, type: :string, summary: "Pack name without the trailing _pack suffix.",
|
|
262
|
+
required: require_name),
|
|
263
|
+
Mcp::ToolArgument.new(name: :kind, type: :symbol, summary: "Explicit pack kind when not inferred.",
|
|
264
|
+
enum: %i[feature operational bundle]),
|
|
265
|
+
Mcp::ToolArgument.new(name: :namespace, type: :string,
|
|
266
|
+
summary: "Ruby namespace for generated pack constants.", default: "MyCompany::IgniterPacks"),
|
|
267
|
+
Mcp::ToolArgument.new(name: :profile, type: :symbol, summary: "Named creator profile.",
|
|
268
|
+
enum: CreatorPack.available_profiles),
|
|
269
|
+
Mcp::ToolArgument.new(name: :capabilities, type: :symbol_array,
|
|
270
|
+
summary: "Capabilities used to infer or refine the profile."),
|
|
271
|
+
Mcp::ToolArgument.new(name: :pack, type: :pack_reference,
|
|
272
|
+
summary: "Custom pack module for audit-aware creator flows."),
|
|
273
|
+
Mcp::ToolArgument.new(name: :mode, type: :symbol, summary: "Writer behavior when files already exist.",
|
|
274
|
+
default: :skip_existing, enum: %i[skip_existing overwrite])
|
|
275
|
+
]
|
|
276
|
+
if include_scope
|
|
277
|
+
arguments << Mcp::ToolArgument.new(name: :scope, type: :symbol, summary: "Target packaging scope.",
|
|
278
|
+
required: false, enum: CreatorPack.available_scopes)
|
|
279
|
+
end
|
|
280
|
+
if include_root
|
|
281
|
+
arguments << Mcp::ToolArgument.new(name: :root, type: :string,
|
|
282
|
+
summary: "Filesystem root for generated files.", required: require_root)
|
|
283
|
+
end
|
|
284
|
+
if include_scope && require_root
|
|
285
|
+
arguments.find { |argument| argument.name == :scope }&.tap do |argument|
|
|
286
|
+
arguments[arguments.index(argument)] = Mcp::ToolArgument.new(
|
|
287
|
+
name: :scope,
|
|
288
|
+
type: :symbol,
|
|
289
|
+
summary: "Target packaging scope.",
|
|
290
|
+
required: true,
|
|
291
|
+
enum: CreatorPack.available_scopes
|
|
292
|
+
)
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
arguments
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def creator_session_from(arguments, target:)
|
|
299
|
+
Mcp::CreatorSession.new(
|
|
300
|
+
name: arguments[:name],
|
|
301
|
+
kind: arguments[:kind],
|
|
302
|
+
namespace: arguments.fetch(:namespace, "MyCompany::IgniterPacks"),
|
|
303
|
+
profile: arguments[:profile],
|
|
304
|
+
capabilities: arguments[:capabilities],
|
|
305
|
+
scope: arguments[:scope],
|
|
306
|
+
root: arguments[:root],
|
|
307
|
+
mode: arguments.fetch(:mode, :skip_existing),
|
|
308
|
+
pack: arguments[:pack],
|
|
309
|
+
target_profile: profile_from(target, optional: true)
|
|
310
|
+
)
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def session_from(payload, target:)
|
|
314
|
+
Mcp::CreatorSession.from_h(
|
|
315
|
+
symbolize_keys(payload),
|
|
316
|
+
target_profile: profile_from(target, optional: true)
|
|
317
|
+
)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def symbolize_keys(value)
|
|
321
|
+
case value
|
|
322
|
+
when Hash
|
|
323
|
+
value.each_with_object({}) do |(key, nested), memo|
|
|
324
|
+
memo[key.respond_to?(:to_sym) ? key.to_sym : key] = symbolize_keys(nested)
|
|
325
|
+
end
|
|
326
|
+
when Array
|
|
327
|
+
value.map { |item| symbolize_keys(item) }
|
|
328
|
+
else
|
|
329
|
+
value
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Extensions
|
|
5
|
+
module Contracts
|
|
6
|
+
module Provenance
|
|
7
|
+
class Builder
|
|
8
|
+
def self.build(output_name, result)
|
|
9
|
+
new(result).build(output_name)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(result)
|
|
13
|
+
@result = result
|
|
14
|
+
@compiled_graph = result.compiled_graph
|
|
15
|
+
@operations = @compiled_graph.operations.reject(&:output?).each_with_object({}) do |operation, memo|
|
|
16
|
+
memo[operation.name] = operation
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def build(output_name)
|
|
21
|
+
output_name = output_name.to_sym
|
|
22
|
+
|
|
23
|
+
raise Igniter::Contracts::Error, "execution result does not carry a compiled graph" unless @compiled_graph
|
|
24
|
+
|
|
25
|
+
unless output_names.include?(output_name)
|
|
26
|
+
raise Igniter::Contracts::Error,
|
|
27
|
+
"no output named '#{output_name}' in compiled graph"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
source_operation = @operations.fetch(output_name) do
|
|
31
|
+
raise Igniter::Contracts::Error,
|
|
32
|
+
"source node '#{output_name}' for output '#{output_name}' not found in compiled graph"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Lineage.new(build_trace(source_operation, {}))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def output_names
|
|
41
|
+
@output_names ||= @compiled_graph.operations.select(&:output?).map(&:name)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def build_trace(operation, memo)
|
|
45
|
+
return memo[operation.name] if memo.key?(operation.name)
|
|
46
|
+
|
|
47
|
+
memo[operation.name] = nil
|
|
48
|
+
|
|
49
|
+
contributing = dependency_names_for(operation).each_with_object({}) do |dependency_name, acc|
|
|
50
|
+
dependency_operation = @operations[dependency_name]
|
|
51
|
+
next unless dependency_operation
|
|
52
|
+
|
|
53
|
+
acc[dependency_name] = build_trace(dependency_operation, memo)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
trace = NodeTrace.new(
|
|
57
|
+
name: operation.name,
|
|
58
|
+
kind: operation.kind,
|
|
59
|
+
value: resolved_value_for(operation),
|
|
60
|
+
contributing: contributing
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
memo[operation.name] = trace
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def dependency_names_for(operation)
|
|
67
|
+
names = []
|
|
68
|
+
names.concat(Array(operation.attributes[:depends_on])) if operation.attribute?(:depends_on)
|
|
69
|
+
names << operation.attributes[:from] if operation.attribute?(:from)
|
|
70
|
+
names.map(&:to_sym).uniq
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def resolved_value_for(operation)
|
|
74
|
+
@result.state.fetch(operation.name)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Extensions
|
|
5
|
+
module Contracts
|
|
6
|
+
module Provenance
|
|
7
|
+
class Lineage
|
|
8
|
+
attr_reader :trace
|
|
9
|
+
|
|
10
|
+
def initialize(trace)
|
|
11
|
+
@trace = trace
|
|
12
|
+
freeze
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def output_name
|
|
16
|
+
trace.name
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def value
|
|
20
|
+
trace.value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def contributing_inputs
|
|
24
|
+
trace.contributing_inputs
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def sensitive_to?(input_name)
|
|
28
|
+
trace.sensitive_to?(input_name)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def path_to(input_name)
|
|
32
|
+
trace.path_to(input_name)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def explain
|
|
36
|
+
TextFormatter.format(trace)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
alias to_s explain
|
|
40
|
+
|
|
41
|
+
def to_h
|
|
42
|
+
serialize(trace)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def serialize(trace)
|
|
48
|
+
{
|
|
49
|
+
node: trace.name,
|
|
50
|
+
kind: trace.kind,
|
|
51
|
+
value: trace.value,
|
|
52
|
+
contributing: trace.contributing.transform_values { |dependency| serialize(dependency) }
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Extensions
|
|
5
|
+
module Contracts
|
|
6
|
+
module Provenance
|
|
7
|
+
class NodeTrace
|
|
8
|
+
attr_reader :name, :kind, :value, :contributing
|
|
9
|
+
|
|
10
|
+
def initialize(name:, kind:, value:, contributing: {})
|
|
11
|
+
@name = name.to_sym
|
|
12
|
+
@kind = kind.to_sym
|
|
13
|
+
@value = value
|
|
14
|
+
@contributing = contributing.freeze
|
|
15
|
+
freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def input?
|
|
19
|
+
kind == :input
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def leaf?
|
|
23
|
+
contributing.empty?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def contributing_inputs
|
|
27
|
+
return { name => value } if input?
|
|
28
|
+
|
|
29
|
+
contributing.each_value.with_object({}) do |trace, memo|
|
|
30
|
+
memo.merge!(trace.contributing_inputs)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def sensitive_to?(input_name)
|
|
35
|
+
contributing_inputs.key?(input_name.to_sym)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def path_to(input_name)
|
|
39
|
+
target = input_name.to_sym
|
|
40
|
+
return [name] if name == target
|
|
41
|
+
|
|
42
|
+
contributing.each_value do |trace|
|
|
43
|
+
path = trace.path_to(target)
|
|
44
|
+
return [name] + path if path
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Extensions
|
|
5
|
+
module Contracts
|
|
6
|
+
module Provenance
|
|
7
|
+
module TextFormatter
|
|
8
|
+
VALUE_MAX_LENGTH = 60
|
|
9
|
+
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def format(trace)
|
|
13
|
+
lines = []
|
|
14
|
+
render(trace, lines, prefix: "", is_root: true, is_last: true)
|
|
15
|
+
lines.join("\n")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def render(trace, lines, prefix:, is_root:, is_last:)
|
|
19
|
+
if is_root
|
|
20
|
+
connector = ""
|
|
21
|
+
child_padding = ""
|
|
22
|
+
elsif is_last
|
|
23
|
+
connector = "└─ "
|
|
24
|
+
child_padding = " "
|
|
25
|
+
else
|
|
26
|
+
connector = "├─ "
|
|
27
|
+
child_padding = "│ "
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
child_prefix = prefix + child_padding
|
|
31
|
+
lines << "#{prefix}#{connector}#{trace.name} = #{format_value(trace.value)} [#{trace.kind}]"
|
|
32
|
+
|
|
33
|
+
dependencies = trace.contributing.values
|
|
34
|
+
dependencies.each_with_index do |dependency, index|
|
|
35
|
+
render(
|
|
36
|
+
dependency,
|
|
37
|
+
lines,
|
|
38
|
+
prefix: child_prefix,
|
|
39
|
+
is_root: false,
|
|
40
|
+
is_last: index == dependencies.length - 1
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def format_value(value)
|
|
46
|
+
rendered = case value
|
|
47
|
+
when nil then "nil"
|
|
48
|
+
when String, Symbol then value.inspect
|
|
49
|
+
when Hash then "{#{value.map { |key, entry| "#{key}: #{entry.inspect}" }.join(", ")}}"
|
|
50
|
+
when Array then "[#{value.map(&:inspect).join(", ")}]"
|
|
51
|
+
else value.inspect
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
return rendered if rendered.length <= VALUE_MAX_LENGTH
|
|
55
|
+
|
|
56
|
+
"#{rendered[0, VALUE_MAX_LENGTH - 3]}..."
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|