@nac3/forge-cli 0.2.0-alpha.59z.121 → 0.2.0-alpha.59z.158
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.
- package/dist/agents/bridge.d.ts +69 -0
- package/dist/agents/bridge.d.ts.map +1 -0
- package/dist/agents/bridge.js +295 -0
- package/dist/agents/bridge.js.map +1 -0
- package/dist/agents/cli_real.d.ts +38 -0
- package/dist/agents/cli_real.d.ts.map +1 -0
- package/dist/agents/cli_real.js +46 -0
- package/dist/agents/cli_real.js.map +1 -0
- package/dist/agents/config_schema.d.ts +1 -1
- package/dist/agents/config_schema.d.ts.map +1 -1
- package/dist/agents/config_schema.js +16 -0
- package/dist/agents/config_schema.js.map +1 -1
- package/dist/agents/defaults.d.ts.map +1 -1
- package/dist/agents/defaults.js.map +1 -1
- package/dist/agents/delegate.d.ts +36 -0
- package/dist/agents/delegate.d.ts.map +1 -0
- package/dist/agents/delegate.js +68 -0
- package/dist/agents/delegate.js.map +1 -0
- package/dist/agents/iterative_step.d.ts +95 -0
- package/dist/agents/iterative_step.d.ts.map +1 -0
- package/dist/agents/iterative_step.js +249 -0
- package/dist/agents/iterative_step.js.map +1 -0
- package/dist/agents/llm_step.d.ts +50 -0
- package/dist/agents/llm_step.d.ts.map +1 -0
- package/dist/agents/llm_step.js +28 -0
- package/dist/agents/llm_step.js.map +1 -0
- package/dist/agents/real_step_factory.d.ts +95 -0
- package/dist/agents/real_step_factory.d.ts.map +1 -0
- package/dist/agents/real_step_factory.js +51 -0
- package/dist/agents/real_step_factory.js.map +1 -0
- package/dist/agents/runner.d.ts +14 -0
- package/dist/agents/runner.d.ts.map +1 -1
- package/dist/agents/runner.js +10 -1
- package/dist/agents/runner.js.map +1 -1
- package/dist/agents/runtime.d.ts +115 -0
- package/dist/agents/runtime.d.ts.map +1 -0
- package/dist/agents/runtime.js +429 -0
- package/dist/agents/runtime.js.map +1 -0
- package/dist/agents/system_prompt.d.ts +36 -0
- package/dist/agents/system_prompt.d.ts.map +1 -0
- package/dist/agents/system_prompt.js +101 -0
- package/dist/agents/system_prompt.js.map +1 -0
- package/dist/agents/types.d.ts +12 -1
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/agents/types.js.map +1 -1
- package/dist/bin/yf.d.ts.map +1 -1
- package/dist/bin/yf.js +9 -0
- package/dist/bin/yf.js.map +1 -1
- package/dist/chat/claude_cli.d.ts +42 -0
- package/dist/chat/claude_cli.d.ts.map +1 -0
- package/dist/chat/claude_cli.js +299 -0
- package/dist/chat/claude_cli.js.map +1 -0
- package/dist/chat/graph_snapshot.d.ts +38 -0
- package/dist/chat/graph_snapshot.d.ts.map +1 -0
- package/dist/chat/graph_snapshot.js +159 -0
- package/dist/chat/graph_snapshot.js.map +1 -0
- package/dist/chat/ingest_session.d.ts +24 -4
- package/dist/chat/ingest_session.d.ts.map +1 -1
- package/dist/chat/ingest_session.js +139 -4
- package/dist/chat/ingest_session.js.map +1 -1
- package/dist/chat/panel.d.ts.map +1 -1
- package/dist/chat/panel.js +1358 -0
- package/dist/chat/panel.js.map +1 -1
- package/dist/chat/server.d.ts +21 -0
- package/dist/chat/server.d.ts.map +1 -1
- package/dist/chat/server.js +904 -4
- package/dist/chat/server.js.map +1 -1
- package/dist/chat/tools/nac3_invoke.d.ts +5 -0
- package/dist/chat/tools/nac3_invoke.d.ts.map +1 -1
- package/dist/chat/tools/nac3_invoke.js +1 -0
- package/dist/chat/tools/nac3_invoke.js.map +1 -1
- package/dist/chat/tools.d.ts.map +1 -1
- package/dist/chat/tools.js +12 -0
- package/dist/chat/tools.js.map +1 -1
- package/dist/commands/agent.d.ts +12 -1
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +92 -1
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/chat.d.ts +10 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +12 -1
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/director.d.ts +37 -0
- package/dist/commands/director.d.ts.map +1 -0
- package/dist/commands/director.js +290 -0
- package/dist/commands/director.js.map +1 -0
- package/dist/commands/keys_setup.d.ts +1 -0
- package/dist/commands/keys_setup.d.ts.map +1 -1
- package/dist/commands/keys_setup.js +61 -11
- package/dist/commands/keys_setup.js.map +1 -1
- package/dist/core/pilot_setup.d.ts +14 -1
- package/dist/core/pilot_setup.d.ts.map +1 -1
- package/dist/core/pilot_setup.js +62 -1
- package/dist/core/pilot_setup.js.map +1 -1
- package/dist/core/provider_keys.d.ts.map +1 -1
- package/dist/core/provider_keys.js +12 -5
- package/dist/core/provider_keys.js.map +1 -1
- package/dist/design/yujin_design_resolver.d.ts +77 -0
- package/dist/design/yujin_design_resolver.d.ts.map +1 -0
- package/dist/design/yujin_design_resolver.js +113 -0
- package/dist/design/yujin_design_resolver.js.map +1 -0
- package/dist/docs/doctrine/default-design-system.md +120 -0
- package/dist/docs/doctrine/language-sovereignty.md +143 -0
- package/dist/docs/doctrine/testing-policy.md +211 -0
- package/dist/nac3/approval_queue.d.ts +8 -0
- package/dist/nac3/approval_queue.d.ts.map +1 -1
- package/dist/nac3/approval_queue.js.map +1 -1
- package/dist/nac3/internal_manifest.d.ts.map +1 -1
- package/dist/nac3/internal_manifest.js +178 -0
- package/dist/nac3/internal_manifest.js.map +1 -1
- package/dist/office/excel.d.ts +22 -0
- package/dist/office/excel.d.ts.map +1 -1
- package/dist/office/excel.js +82 -0
- package/dist/office/excel.js.map +1 -1
- package/dist/qa/brain_matrix/fixtures/scenario_a11y_focus_after_open.json +22 -0
- package/dist/qa/brain_matrix/fixtures/scenario_a11y_label_unlabeled_button.json +26 -0
- package/dist/qa/brain_matrix/fixtures/scenario_agent_director_consults_workflow_before_dispatch.json +42 -0
- package/dist/qa/brain_matrix/fixtures/scenario_analyst_extracts_then_returns.json +28 -0
- package/dist/qa/brain_matrix/fixtures/scenario_coder_consults_nac_spec_before_writing.json +28 -0
- package/dist/qa/brain_matrix/fixtures/scenario_default_design_system_override_shadcn.json +45 -0
- package/dist/qa/brain_matrix/fixtures/scenario_default_design_system_use_card_template.json +44 -0
- package/dist/qa/brain_matrix/fixtures/scenario_director_delegates_to_analyst.json +26 -0
- package/dist/qa/brain_matrix/fixtures/scenario_director_no_self_delegation.json +20 -0
- package/dist/qa/brain_matrix/fixtures/scenario_director_terminal_done_marker.json +19 -0
- package/dist/qa/brain_matrix/fixtures/scenario_doctrine_discover_default_design_system.json +50 -0
- package/dist/qa/brain_matrix/fixtures/scenario_doctrine_discover_language_sovereignty.json +53 -0
- package/dist/qa/brain_matrix/fixtures/scenario_doctrine_discover_testing_policy.json +55 -0
- package/dist/qa/brain_matrix/fixtures/scenario_language_sovereignty_not_conducive_spa_python.json +61 -0
- package/dist/qa/brain_matrix/fixtures/scenario_language_sovereignty_user_picks_python.json +48 -0
- package/dist/qa/brain_matrix/fixtures/scenario_mcp_invoke_destructive_pauses_for_r8.json +42 -0
- package/dist/qa/brain_matrix/fixtures/scenario_mcp_invoke_happy_path_writes_pizarron.json +76 -0
- package/dist/qa/brain_matrix/fixtures/scenario_mcp_invoke_network_timeout_graceful.json +41 -0
- package/dist/qa/brain_matrix/fixtures/scenario_no_hardcoded_color_use_token.json +47 -0
- package/dist/qa/brain_matrix/fixtures/scenario_office_excel_update_cell_picks_right_verb.json +26 -0
- package/dist/qa/brain_matrix/fixtures/scenario_scaffolder_destructive_pauses_for_approval.json +23 -0
- package/dist/qa/brain_matrix/fixtures/scenario_sumie_icon_pick_from_catalog.json +56 -0
- package/dist/qa/brain_matrix/fixtures/scenario_tester_uses_shell_exec_only.json +27 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workflow/graph.d.ts +59 -0
- package/dist/workflow/graph.d.ts.map +1 -0
- package/dist/workflow/graph.js +137 -0
- package/dist/workflow/graph.js.map +1 -0
- package/dist/workflow/state.d.ts +34 -0
- package/dist/workflow/state.d.ts.map +1 -1
- package/dist/workflow/state.js.map +1 -1
- package/docs/doctrine/default-design-system.md +120 -0
- package/docs/doctrine/language-sovereignty.md +143 -0
- package/docs/doctrine/testing-policy.md +211 -0
- package/package.json +102 -100
- package/src/i18n/catalogs/ar.json +20 -1
- package/src/i18n/catalogs/de.json +20 -1
- package/src/i18n/catalogs/en.json +20 -21
- package/src/i18n/catalogs/es.json +20 -21
- package/src/i18n/catalogs/fr.json +20 -1
- package/src/i18n/catalogs/hi.json +20 -1
- package/src/i18n/catalogs/it.json +20 -1
- package/src/i18n/catalogs/ja.json +20 -1
- package/src/i18n/catalogs/pt.json +20 -1
- package/src/i18n/catalogs/zh.json +20 -1
- package/templates/sumi-context.template.md +57 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "default_design_system_use_card_template",
|
|
3
|
+
"description": "User asks for a component. Validates Coder fetches the Yujin template instead of inventing CSS.",
|
|
4
|
+
"setup": {},
|
|
5
|
+
"turns": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "user_says",
|
|
8
|
+
"text": "Necesito una card para mostrar productos."
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"invariants": [
|
|
12
|
+
{
|
|
13
|
+
"name": "no_invent_verbs",
|
|
14
|
+
"kind": "no_invent_verb",
|
|
15
|
+
"params": {}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "uses_yujin_template",
|
|
19
|
+
"kind": "verb_invoked",
|
|
20
|
+
"params": {
|
|
21
|
+
"verb_id": "yujin.design.get-template"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"fake_responses": [
|
|
26
|
+
{
|
|
27
|
+
"text": "Traigo el template Yujin de card.",
|
|
28
|
+
"tool_calls": [
|
|
29
|
+
{
|
|
30
|
+
"name": "nac3_invoke",
|
|
31
|
+
"args": {
|
|
32
|
+
"verb_id": "yujin.design.get-template",
|
|
33
|
+
"args": {
|
|
34
|
+
"slug": "card"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"text": "Listo, aca esta el template de yj-card con tokens del Design System."
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "director_delegates_to_analyst",
|
|
3
|
+
"description": "Director live execution -- Director receives a spec and decides to dispatch the Analyst sub-agent via yujin.agent.delegate, NOT a direct nac3 read/write. Validates that the iterative_step + brain understand inter-agent delegation as a first-class verb.",
|
|
4
|
+
"setup": {
|
|
5
|
+
"extra_system_prompt": "DIRECTOR ROLE active. You are the orchestrator. Delegate spec extraction to the Analyst sub-agent via yujin.agent.delegate {to_slug: 'analyst', payload, tokens_used}. Do NOT call reader/pizarron verbs directly -- those are Analyst's job.\n\nAvailable verbs:\n- yujin.agent.delegate [u s] args:{to_slug<required>, payload<required>, tokens_used?}"
|
|
6
|
+
},
|
|
7
|
+
"turns": [
|
|
8
|
+
{ "kind": "user_says", "text": "Extrae los requisitos del spec docA. Pasalo al equipo." }
|
|
9
|
+
],
|
|
10
|
+
"invariants": [
|
|
11
|
+
{ "name": "delegates_to_analyst", "kind": "verb_invoked", "params": { "verb_id": "yujin.agent.delegate" } },
|
|
12
|
+
{ "name": "no_direct_reader", "kind": "verb_not_invoked", "params": { "verb_id": "yujin.reader.read-section" } },
|
|
13
|
+
{ "name": "no_invent_verbs", "kind": "no_invent_verb", "params": {} }
|
|
14
|
+
],
|
|
15
|
+
"fake_responses": [
|
|
16
|
+
{
|
|
17
|
+
"text": "Delego a Analyst.",
|
|
18
|
+
"tool_calls": [
|
|
19
|
+
{ "name": "nac3_invoke", "args": { "verb_id": "yujin.agent.delegate", "args": { "to_slug": "analyst", "payload": "extract spec docA requirements", "tokens_used": 25 } } }
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"text": "Analyst returned the plan. Listo."
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "director_no_self_delegation",
|
|
3
|
+
"description": "Director live execution -- delegations are LATERAL: dispatch to another role's slug (analyst / coder / tester / scaffolder), never to_slug='director'. The bridge ACL would reject a self-loop. Validates the brain answers without invoking yujin.agent.delegate at all when the user's request is something the director itself can handle.",
|
|
4
|
+
"setup": {
|
|
5
|
+
"extra_system_prompt": "DIRECTOR ROLE active. Delegations are LATERAL -- you call analyst / coder / tester / scaffolder. NEVER call to_slug='director' (self-loop -- the bridge ACL rejects it). When a task is reasoning-only (no spec extraction, no code generation, no fs writes), handle it yourself without delegating.\n\nVerbs:\n- yujin.agent.delegate [u s] args:{to_slug<required>, payload<required>}"
|
|
6
|
+
},
|
|
7
|
+
"turns": [
|
|
8
|
+
{ "kind": "user_says", "text": "Hazme un breakdown de los siguientes 3 pasos en texto, sin tools." }
|
|
9
|
+
],
|
|
10
|
+
"invariants": [
|
|
11
|
+
{ "name": "no_delegate_for_text_only", "kind": "verb_not_invoked", "params": { "verb_id": "yujin.agent.delegate" } },
|
|
12
|
+
{ "name": "no_invent_verbs", "kind": "no_invent_verb", "params": {} },
|
|
13
|
+
{ "name": "answers_with_steps", "kind": "text_contains_any", "params": { "substrs": ["1", "primero", "step 1", "paso 1", "1)", "1.", "2.", "2)"] } }
|
|
14
|
+
],
|
|
15
|
+
"fake_responses": [
|
|
16
|
+
{
|
|
17
|
+
"text": "Breakdown directo, sin delegar: 1) Parsear spec. 2) Listar entidades. 3) Priorizar slices."
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "director_terminal_done_marker",
|
|
3
|
+
"description": "Director live execution -- when the orchestrator finishes a multi-step plan and there is nothing left, the assistant emits a clear terminal marker ('[DONE]' / 'listo' / 'terminado') so the iterative_step's done detector concludes the run. Validates the terminal-marker pathway.",
|
|
4
|
+
"setup": {
|
|
5
|
+
"extra_system_prompt": "DIRECTOR ROLE active. When all sub-agents have returned + the plan is closed, emit a clear terminal marker. Acceptable forms: '[DONE]', 'listo.', 'terminado.', 'todo listo.' -- in any locale you have already adopted. Do NOT keep delegating once the goal is met."
|
|
6
|
+
},
|
|
7
|
+
"turns": [
|
|
8
|
+
{ "kind": "user_says", "text": "Confirma que el sprint X ya esta cerrado." }
|
|
9
|
+
],
|
|
10
|
+
"invariants": [
|
|
11
|
+
{ "name": "emits_terminal", "kind": "regex_match", "params": { "pattern": "(\\[DONE\\]|\\blisto\\b|\\bterminado\\b|todo listo)" } },
|
|
12
|
+
{ "name": "no_extra_delegates", "kind": "verb_not_invoked", "params": { "verb_id": "yujin.agent.delegate" } }
|
|
13
|
+
],
|
|
14
|
+
"fake_responses": [
|
|
15
|
+
{
|
|
16
|
+
"text": "Sprint X cerrado. Todos los sub-agentes devolvieron OK. [DONE]"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "doctrine_discover_default_design_system",
|
|
3
|
+
"description": "Brain is asked about the default design system doctrine. Validates discovery or direct answer.",
|
|
4
|
+
"setup": {},
|
|
5
|
+
"turns": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "user_says",
|
|
8
|
+
"text": "Cual es el design system por default de Forge?"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"invariants": [
|
|
12
|
+
{
|
|
13
|
+
"name": "no_invent_verbs",
|
|
14
|
+
"kind": "no_invent_verb",
|
|
15
|
+
"params": {}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "mentions_yujin_ds",
|
|
19
|
+
"kind": "text_contains_any",
|
|
20
|
+
"params": {
|
|
21
|
+
"substrs": [
|
|
22
|
+
"Yujin Design System",
|
|
23
|
+
"Yujin DS",
|
|
24
|
+
"yujin.design"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"fake_responses": [
|
|
30
|
+
{
|
|
31
|
+
"text": "Traigo la doctrina de design system.",
|
|
32
|
+
"tool_calls": [
|
|
33
|
+
{
|
|
34
|
+
"name": "nac3_invoke",
|
|
35
|
+
"args": {
|
|
36
|
+
"verb_id": "yujin.doctrine.discover",
|
|
37
|
+
"args": {
|
|
38
|
+
"slugs": [
|
|
39
|
+
"default-design-system"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"text": "El default es Yujin Design System (30 componentes, 258 tokens DTCG, 170 sumi-e). Override via yujin.design.override (R8 modal)."
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "doctrine_discover_language_sovereignty",
|
|
3
|
+
"description": "Brain is asked about language choice doctrine. Validates discovery via yujin.doctrine.discover OR direct in-prompt answer using the language-sovereignty rule.",
|
|
4
|
+
"setup": {},
|
|
5
|
+
"turns": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "user_says",
|
|
8
|
+
"text": "Que dice la doctrina sobre eleccion de lenguaje de programacion?"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"invariants": [
|
|
12
|
+
{
|
|
13
|
+
"name": "no_invent_verbs",
|
|
14
|
+
"kind": "no_invent_verb",
|
|
15
|
+
"params": {}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "mentions_sovereignty",
|
|
19
|
+
"kind": "text_contains_any",
|
|
20
|
+
"params": {
|
|
21
|
+
"substrs": [
|
|
22
|
+
"user",
|
|
23
|
+
"usuario",
|
|
24
|
+
"pick",
|
|
25
|
+
"elige",
|
|
26
|
+
"language",
|
|
27
|
+
"sovereignty"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"fake_responses": [
|
|
33
|
+
{
|
|
34
|
+
"text": "Traigo la doctrina de language sovereignty.",
|
|
35
|
+
"tool_calls": [
|
|
36
|
+
{
|
|
37
|
+
"name": "nac3_invoke",
|
|
38
|
+
"args": {
|
|
39
|
+
"verb_id": "yujin.doctrine.discover",
|
|
40
|
+
"args": {
|
|
41
|
+
"slugs": [
|
|
42
|
+
"language-sovereignty"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"text": "Per docs/doctrine/language-sovereignty.md: el user picks the language; el Director suggests but never imposes. Si el lenguaje no es conducente para el componente, Director responde con la tabla."
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "doctrine_discover_testing_policy",
|
|
3
|
+
"description": "Brain is asked about TDD policy. Validates discovery or direct answer about TDD-first scope.",
|
|
4
|
+
"setup": {},
|
|
5
|
+
"turns": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "user_says",
|
|
8
|
+
"text": "Cuando hago TDD primero y cuando codigo primero en Forge?"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"invariants": [
|
|
12
|
+
{
|
|
13
|
+
"name": "no_invent_verbs",
|
|
14
|
+
"kind": "no_invent_verb",
|
|
15
|
+
"params": {}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "mentions_tdd_scope",
|
|
19
|
+
"kind": "text_contains_any",
|
|
20
|
+
"params": {
|
|
21
|
+
"substrs": [
|
|
22
|
+
"TDD",
|
|
23
|
+
"tests first",
|
|
24
|
+
"red",
|
|
25
|
+
"green",
|
|
26
|
+
"opt-in",
|
|
27
|
+
"backend",
|
|
28
|
+
"API",
|
|
29
|
+
"transformer"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"fake_responses": [
|
|
35
|
+
{
|
|
36
|
+
"text": "Traigo la doctrina de testing-policy.",
|
|
37
|
+
"tool_calls": [
|
|
38
|
+
{
|
|
39
|
+
"name": "nac3_invoke",
|
|
40
|
+
"args": {
|
|
41
|
+
"verb_id": "yujin.doctrine.discover",
|
|
42
|
+
"args": {
|
|
43
|
+
"slugs": [
|
|
44
|
+
"testing-policy"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"text": "TDD-first es opt-in. Lo uses en backend/API/transformer/financial/integration; code-first en UI/exploratory."
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
package/dist/qa/brain_matrix/fixtures/scenario_language_sovereignty_not_conducive_spa_python.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "language_sovereignty_not_conducive_spa_python",
|
|
3
|
+
"description": "User requests Python for SPA frontend. Validates Director responds with the table row + DOES NOT switch.",
|
|
4
|
+
"setup": {},
|
|
5
|
+
"turns": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "user_says",
|
|
8
|
+
"text": "Quiero el frontend SPA en Python."
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"invariants": [
|
|
12
|
+
{
|
|
13
|
+
"name": "no_invent_verbs",
|
|
14
|
+
"kind": "no_invent_verb",
|
|
15
|
+
"params": {}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "explains_not_conducive",
|
|
19
|
+
"kind": "text_contains_any",
|
|
20
|
+
"params": {
|
|
21
|
+
"substrs": [
|
|
22
|
+
"no es conducente",
|
|
23
|
+
"not conducive",
|
|
24
|
+
"browser",
|
|
25
|
+
"navegador",
|
|
26
|
+
"runtime",
|
|
27
|
+
"TS",
|
|
28
|
+
"JS"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "no_silent_switch",
|
|
34
|
+
"kind": "verb_not_invoked",
|
|
35
|
+
"params": {
|
|
36
|
+
"verb_id": "yujin.workflow.set"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"fake_responses": [
|
|
41
|
+
{
|
|
42
|
+
"text": "Voy a consultar la doctrina antes de responder.",
|
|
43
|
+
"tool_calls": [
|
|
44
|
+
{
|
|
45
|
+
"name": "nac3_invoke",
|
|
46
|
+
"args": {
|
|
47
|
+
"verb_id": "yujin.doctrine.discover",
|
|
48
|
+
"args": {
|
|
49
|
+
"slugs": [
|
|
50
|
+
"language-sovereignty"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"text": "Python no es conducente para un SPA frontend porque no es runtime del navegador. Las opciones validas son: TS, JS, WASM target (Rust, Go). Cual preferis?"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "language_sovereignty_user_picks_python",
|
|
3
|
+
"description": "User declares a language preference. Validates Director switches via yujin.workflow.set.",
|
|
4
|
+
"setup": {},
|
|
5
|
+
"turns": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "user_says",
|
|
8
|
+
"text": "Quiero el backend en Python con FastAPI."
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"invariants": [
|
|
12
|
+
{
|
|
13
|
+
"name": "no_invent_verbs",
|
|
14
|
+
"kind": "no_invent_verb",
|
|
15
|
+
"params": {}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "persists_choice",
|
|
19
|
+
"kind": "verb_invoked",
|
|
20
|
+
"params": {
|
|
21
|
+
"verb_id": "yujin.workflow.set"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"fake_responses": [
|
|
26
|
+
{
|
|
27
|
+
"text": "Listo, backend en Python. Persisto la decision.",
|
|
28
|
+
"tool_calls": [
|
|
29
|
+
{
|
|
30
|
+
"name": "nac3_invoke",
|
|
31
|
+
"args": {
|
|
32
|
+
"verb_id": "yujin.workflow.set",
|
|
33
|
+
"args": {
|
|
34
|
+
"path": "language_choices.backend",
|
|
35
|
+
"value": {
|
|
36
|
+
"lang": "python",
|
|
37
|
+
"decided_by": "user"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"text": "Backend en Python guardado."
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "mcp_invoke_destructive_pauses_for_r8",
|
|
3
|
+
"description": "F101 slice 6 -- the brain invokes a destructive tool from a remote MCP bridge (crm.delete_contact). The dispatcher returns nac3_panel_approval_required envelope (same shape the panel modal subscribes to for native NAC-3 destructive verbs). The brain acknowledges the modal + does NOT retry the invoke in the same turn. Locks the MCP path through the unified R8 destructive-always-confirm gate.",
|
|
4
|
+
"setup": {
|
|
5
|
+
"expose_mcp_verbs": true,
|
|
6
|
+
"extra_system_prompt": "ACTIVE MCP BRIDGES (already discovered in session):\n- bridge crm: enabled, auth_loaded=true, tools include destructive delete_contact (policy=destructive, requires R8).\n\nWhen you invoke a destructive remote tool, the dispatcher fires the same panel R8 modal as native NAC-3 verbs. After the envelope returns nac3_panel_approval_required=true, your turn ENDS -- do NOT retry. The user resolves via the modal."
|
|
7
|
+
},
|
|
8
|
+
"turns": [
|
|
9
|
+
{ "kind": "user_says", "text": "Borra el contacto id=42 del CRM" },
|
|
10
|
+
{
|
|
11
|
+
"kind": "tool_result",
|
|
12
|
+
"tool_name": "yujin.mcp.invoke-tool",
|
|
13
|
+
"is_error": false,
|
|
14
|
+
"result": {
|
|
15
|
+
"nac3_panel_approval_required": true,
|
|
16
|
+
"trace_id": "tr-mcp-7",
|
|
17
|
+
"verb": "yujin.mcp.invoke-tool",
|
|
18
|
+
"args": { "bridge_id": "crm", "tool_slug": "delete_contact", "args": { "id": 42 } },
|
|
19
|
+
"remote_policy": "destructive",
|
|
20
|
+
"bridge_id": "crm",
|
|
21
|
+
"tool_slug": "delete_contact"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"invariants": [
|
|
26
|
+
{ "name": "uses_invoke_tool", "kind": "verb_invoked", "params": { "verb_id": "yujin.mcp.invoke-tool" } },
|
|
27
|
+
{ "name": "no_invent_verbs", "kind": "no_invent_verb", "params": {} },
|
|
28
|
+
{ "name": "acknowledges_modal", "kind": "text_contains_any", "params": { "substrs": ["modal", "approval", "aprobar", "espera", "espero", "pending", "confirmacion", "confirmar"] } },
|
|
29
|
+
{ "name": "no_retry_after_modal", "kind": "max_tool_calls", "params": { "n": 1 } }
|
|
30
|
+
],
|
|
31
|
+
"fake_responses": [
|
|
32
|
+
{
|
|
33
|
+
"text": "Tool destructive. Abro el modal R8 antes de borrar.",
|
|
34
|
+
"tool_calls": [
|
|
35
|
+
{ "name": "nac3_invoke", "args": { "verb_id": "yujin.mcp.invoke-tool", "args": { "bridge_id": "crm", "tool_slug": "delete_contact", "args": { "id": 42 } } } }
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"text": "El modal de approval esta abierto. Espero a que confirmes para ejecutar el borrado."
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "mcp_invoke_happy_path_writes_pizarron",
|
|
3
|
+
"description": "F101 slice 6 -- successful 2-hop happy path that goes one step further: after invoke-tool returns a structured result the brain renders the result in the pizarron via yujin.pizarron.create-tab. Validates that MCP-fetched data uses the same display path as native data (no special-case render).",
|
|
4
|
+
"setup": {
|
|
5
|
+
"expose_mcp_verbs": true,
|
|
6
|
+
"extra_system_prompt": "ACTIVE MCP BRIDGES (already discovered in session):\n- bridge crm: enabled, auth_loaded=true, tools include get_deal.\n\nThe canonical pattern: show-tools -> invoke-tool -> if result is structured + non-trivial, render in pizarron (NOT inline in chat). Inline text reserved for short summaries or confirmations.\n\nVerbs:\n- yujin.mcp.show-tools args:{bridge_id<required>}\n- yujin.mcp.invoke-tool args:{bridge_id<required>, tool_slug<required>, args?}\n- yujin.pizarron.create-tab args:{title<required>, content<required>}"
|
|
7
|
+
},
|
|
8
|
+
"turns": [
|
|
9
|
+
{ "kind": "user_says", "text": "Traeme el deal 1234 del CRM y mostralo." },
|
|
10
|
+
{
|
|
11
|
+
"kind": "tool_result",
|
|
12
|
+
"tool_name": "yujin.mcp.show-tools",
|
|
13
|
+
"is_error": false,
|
|
14
|
+
"result": {
|
|
15
|
+
"ok": true,
|
|
16
|
+
"bridge_id": "crm",
|
|
17
|
+
"endpoint": "http://localhost:3001",
|
|
18
|
+
"tools": [
|
|
19
|
+
{ "id": "get_deal", "label": "Get deal by id", "description": "Fetches one deal" }
|
|
20
|
+
],
|
|
21
|
+
"count": 1
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"kind": "tool_result",
|
|
26
|
+
"tool_name": "yujin.mcp.invoke-tool",
|
|
27
|
+
"is_error": false,
|
|
28
|
+
"result": {
|
|
29
|
+
"ok": true,
|
|
30
|
+
"bridge_id": "crm",
|
|
31
|
+
"tool_slug": "get_deal",
|
|
32
|
+
"response": {
|
|
33
|
+
"id": 1234, "name": "ACME Onboarding", "amount": 50000,
|
|
34
|
+
"stage": "proposal", "owner": "ana@acme.com",
|
|
35
|
+
"created_at": "2026-05-12", "next_action": "follow up week of 06-10"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"kind": "tool_result",
|
|
41
|
+
"tool_name": "yujin.pizarron.create-tab",
|
|
42
|
+
"is_error": false,
|
|
43
|
+
"result": { "ok": true, "tab_id": "tab-mcp-deal-1234" }
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"invariants": [
|
|
47
|
+
{ "name": "uses_show_tools", "kind": "verb_invoked", "params": { "verb_id": "yujin.mcp.show-tools" } },
|
|
48
|
+
{ "name": "uses_invoke_tool", "kind": "verb_invoked", "params": { "verb_id": "yujin.mcp.invoke-tool" } },
|
|
49
|
+
{ "name": "renders_pizarron", "kind": "verb_invoked", "params": { "verb_id": "yujin.pizarron.create-tab" } },
|
|
50
|
+
{ "name": "no_invent_verbs", "kind": "no_invent_verb", "params": {} },
|
|
51
|
+
{ "name": "tools_in_order", "kind": "verb_invoked_in_order", "params": { "sequence": ["yujin.mcp.show-tools", "yujin.mcp.invoke-tool", "yujin.pizarron.create-tab"] } }
|
|
52
|
+
],
|
|
53
|
+
"fake_responses": [
|
|
54
|
+
{
|
|
55
|
+
"text": "Verifico el catalogo de tools del crm.",
|
|
56
|
+
"tool_calls": [
|
|
57
|
+
{ "name": "nac3_invoke", "args": { "verb_id": "yujin.mcp.show-tools", "args": { "bridge_id": "crm" } } }
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"text": "Pido el deal.",
|
|
62
|
+
"tool_calls": [
|
|
63
|
+
{ "name": "nac3_invoke", "args": { "verb_id": "yujin.mcp.invoke-tool", "args": { "bridge_id": "crm", "tool_slug": "get_deal", "args": { "id": 1234 } } } }
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"text": "Lo renderizo en el pizarron.",
|
|
68
|
+
"tool_calls": [
|
|
69
|
+
{ "name": "nac3_invoke", "args": { "verb_id": "yujin.pizarron.create-tab", "args": { "title": "Deal 1234 ACME Onboarding", "content": "amount $50000 / proposal / owner ana@acme.com / next: follow up week of 06-10" } } }
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"text": "Listo, el deal esta en la pestana del pizarron."
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "mcp_invoke_network_timeout_graceful",
|
|
3
|
+
"description": "F101 slice 6 -- invoke-tool returns ok=false bridge_invoke_failed with reason=network_timeout. The brain explains the failure to the user in plain language + does NOT retry blindly + does NOT invent a fallback verb. It surfaces the bridge_id + tool_slug so the user can decide (retry later, switch bridge, etc).",
|
|
4
|
+
"setup": {
|
|
5
|
+
"expose_mcp_verbs": true,
|
|
6
|
+
"extra_system_prompt": "ACTIVE MCP BRIDGES (already discovered in session):\n- bridge crm: enabled, auth_loaded=true, tools include list_contacts.\n\nWhen a remote MCP invoke fails with network_timeout / connection_refused / bridge_unreachable, surface the failure cleanly. NEVER retry more than once -- the user should decide whether to wait, switch bridge, or try a native verb."
|
|
7
|
+
},
|
|
8
|
+
"turns": [
|
|
9
|
+
{ "kind": "user_says", "text": "Trae los contactos del CRM" },
|
|
10
|
+
{
|
|
11
|
+
"kind": "tool_result",
|
|
12
|
+
"tool_name": "yujin.mcp.invoke-tool",
|
|
13
|
+
"is_error": true,
|
|
14
|
+
"result": {
|
|
15
|
+
"ok": false,
|
|
16
|
+
"error_code": "bridge_invoke_failed",
|
|
17
|
+
"reason": "network_timeout",
|
|
18
|
+
"bridge_id": "crm",
|
|
19
|
+
"tool_slug": "list_contacts",
|
|
20
|
+
"ms_elapsed": 30000
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"invariants": [
|
|
25
|
+
{ "name": "uses_invoke_tool", "kind": "verb_invoked", "params": { "verb_id": "yujin.mcp.invoke-tool" } },
|
|
26
|
+
{ "name": "no_invent_verbs", "kind": "no_invent_verb", "params": {} },
|
|
27
|
+
{ "name": "surfaces_bridge_id", "kind": "text_contains_any", "params": { "substrs": ["crm", "bridge", "timeout", "network", "red", "conexion", "conexión", "respondio", "respondió", "no responde"] } },
|
|
28
|
+
{ "name": "no_blind_retry", "kind": "max_tool_calls", "params": { "n": 1 } }
|
|
29
|
+
],
|
|
30
|
+
"fake_responses": [
|
|
31
|
+
{
|
|
32
|
+
"text": "Invoco list_contacts.",
|
|
33
|
+
"tool_calls": [
|
|
34
|
+
{ "name": "nac3_invoke", "args": { "verb_id": "yujin.mcp.invoke-tool", "args": { "bridge_id": "crm", "tool_slug": "list_contacts", "args": {} } } }
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"text": "El bridge crm no respondio en 30s (network_timeout). No reintento solo. ¿Querés que espere y reintente, o trato con otro bridge?"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "no_hardcoded_color_use_token",
|
|
3
|
+
"description": "When asked for a color in CSS, brain refuses hardcoded hex + recommends tokens. Anti-reinvent guard.",
|
|
4
|
+
"setup": {},
|
|
5
|
+
"turns": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "user_says",
|
|
8
|
+
"text": "Hace el boton de comprar de color #3A4DBC."
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"invariants": [
|
|
12
|
+
{
|
|
13
|
+
"name": "no_invent_verbs",
|
|
14
|
+
"kind": "no_invent_verb",
|
|
15
|
+
"params": {}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "recommends_token",
|
|
19
|
+
"kind": "text_contains_any",
|
|
20
|
+
"params": {
|
|
21
|
+
"substrs": [
|
|
22
|
+
"--color",
|
|
23
|
+
"var(--",
|
|
24
|
+
"token",
|
|
25
|
+
"yujin.design.tokens"
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"fake_responses": [
|
|
31
|
+
{
|
|
32
|
+
"text": "Mejor uso un token. Consulto los tokens disponibles.",
|
|
33
|
+
"tool_calls": [
|
|
34
|
+
{
|
|
35
|
+
"name": "nac3_invoke",
|
|
36
|
+
"args": {
|
|
37
|
+
"verb_id": "yujin.design.tokens",
|
|
38
|
+
"args": {}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"text": "El indigo de marca es var(--color-accent) (= var(--color-indigo-600) = #2D3A8C). Lo uso en lugar de hardcodear #3A4DBC."
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "office_excel_update_cell_picks_right_verb",
|
|
3
|
+
"description": "F106 closure -- user asks to mutate ONE cell of an existing workbook. The brain picks office.excel.update-cell (preserves rest of file), NOT office.excel.write (which would rewrite the whole workbook from scratch + lose other sheets / formatting). Locks the cost-aware default for single-cell edits.",
|
|
4
|
+
"setup": {
|
|
5
|
+
"extra_system_prompt": "OFFICE VERBS active:\n- office.excel.update-cell [u s] args:{path<required>, sheet?, cell<required>, value?}\n- office.excel.write [u d] args:{path<required>, rows<required>, sheet?, overwrite?}\nWhen the user wants to change ONE cell, use update-cell -- it preserves every other cell + sheet. office.excel.write replaces the entire workbook + is the wrong tool for single-cell edits."
|
|
6
|
+
},
|
|
7
|
+
"turns": [
|
|
8
|
+
{ "kind": "user_says", "text": "En el archivo data/q3.xlsx cambia la celda B5 a 500" }
|
|
9
|
+
],
|
|
10
|
+
"invariants": [
|
|
11
|
+
{ "name": "uses_update_cell", "kind": "verb_invoked", "params": { "verb_id": "office.excel.update-cell" } },
|
|
12
|
+
{ "name": "no_full_rewrite", "kind": "verb_not_invoked", "params": { "verb_id": "office.excel.write" } },
|
|
13
|
+
{ "name": "no_invent_verbs", "kind": "no_invent_verb", "params": {} }
|
|
14
|
+
],
|
|
15
|
+
"fake_responses": [
|
|
16
|
+
{
|
|
17
|
+
"text": "Cambio solo esa celda con update-cell -- preservo el resto.",
|
|
18
|
+
"tool_calls": [
|
|
19
|
+
{ "name": "nac3_invoke", "args": { "verb_id": "office.excel.update-cell", "args": { "path": "data/q3.xlsx", "cell": "B5", "value": 500 } } }
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"text": "Listo, B5 ahora vale 500."
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
package/dist/qa/brain_matrix/fixtures/scenario_scaffolder_destructive_pauses_for_approval.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "scaffolder_destructive_pauses_for_approval",
|
|
3
|
+
"description": "Director live execution -- Scaffolder asked to write a project file invokes yujin.fs.write-project-file which the panel intercepts as approval_required. The brain does NOT retry or invent a workaround; the response acknowledges the pending modal and stops. Validates R8 destructive-always-confirm + paused_for_approval status path.",
|
|
4
|
+
"setup": {
|
|
5
|
+
"extra_system_prompt": "SCAFFOLDER ROLE active. You write minimal scaffolding files. Verb yujin.fs.write-project-file is policy=destructive: every call triggers a user approval modal. After invoking, your turn ends -- you do NOT call it again until the modal resolves.\n\nVerbs:\n- yujin.fs.write-project-file [u d] args:{path<required>, body<required>}"
|
|
6
|
+
},
|
|
7
|
+
"turns": [
|
|
8
|
+
{ "kind": "user_says", "text": "Aplica el slice X: crea src/foo.ts con export const foo = 1." }
|
|
9
|
+
],
|
|
10
|
+
"invariants": [
|
|
11
|
+
{ "name": "writes_file_once", "kind": "verb_invoked", "params": { "verb_id": "yujin.fs.write-project-file" } },
|
|
12
|
+
{ "name": "no_invent_verbs", "kind": "no_invent_verb", "params": {} },
|
|
13
|
+
{ "name": "acknowledges_modal", "kind": "text_contains_any", "params": { "substrs": ["modal", "aprobar", "approval", "espera", "espero", "pending", "pausa"] } }
|
|
14
|
+
],
|
|
15
|
+
"fake_responses": [
|
|
16
|
+
{
|
|
17
|
+
"text": "Abro el modal R8. Espero la aprobacion.",
|
|
18
|
+
"tool_calls": [
|
|
19
|
+
{ "name": "nac3_invoke", "args": { "verb_id": "yujin.fs.write-project-file", "args": { "path": "src/foo.ts", "body": "export const foo = 1;" } } }
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|