house-lights 1.2.0 → 1.3.0
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/package.json +1 -1
- package/skills/cast-list/assets/map.html +60 -33
- package/skills/cast-list/examples/agents/bodega-cupons/actions/AbrirChamadoERP.mcs.yml +15 -0
- package/skills/cast-list/examples/agents/bodega-cupons/actions/ConsultarCuponsAtivos.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-cupons/actions/EnviarCupomEmail.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-cupons/actions/PromocoesMCP.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-cupons/agent.mcs.yml +12 -0
- package/skills/cast-list/examples/agents/bodega-cupons/connectionreferences.mcs.yml +7 -7
- package/skills/cast-list/examples/agents/bodega-cupons/topics/ConsultarRegulamentoCupons.topic.mcs.yml +3 -3
- package/skills/cast-list/examples/agents/bodega-cupons/topics/Escalate.topic.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-cupons/topics/Fallback.topic.mcs.yml +5 -5
- package/skills/cast-list/examples/agents/bodega-cupons/topics/Greeting.topic.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-cupons/topics/OnError.topic.mcs.yml +7 -7
- package/skills/cast-list/examples/agents/bodega-cupons/topics/RecebimentoOrquestrador.topic.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-devolucoes/actions/AbrirChamadoERP.mcs.yml +15 -0
- package/skills/cast-list/examples/agents/bodega-devolucoes/actions/AbrirSolicitacaoDevolucao.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-devolucoes/agent.mcs.yml +13 -0
- package/skills/cast-list/examples/agents/bodega-devolucoes/connectionreferences.mcs.yml +2 -2
- package/skills/cast-list/examples/agents/bodega-devolucoes/topics/ConsultarPoliticaDevolucoes.topic.mcs.yml +3 -3
- package/skills/cast-list/examples/agents/bodega-devolucoes/topics/Escalate.topic.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-devolucoes/topics/Fallback.topic.mcs.yml +5 -5
- package/skills/cast-list/examples/agents/bodega-devolucoes/topics/Greeting.topic.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-devolucoes/topics/OnError.topic.mcs.yml +7 -7
- package/skills/cast-list/examples/agents/bodega-devolucoes/topics/RecebimentoOrquestrador.topic.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-frete/actions/EnviarOpcoesFrete.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-frete/agent.mcs.yml +7 -0
- package/skills/cast-list/examples/agents/bodega-frete/connectionreferences.mcs.yml +2 -2
- package/skills/cast-list/examples/agents/bodega-frete/topics/ConsultarTabelaFrete.topic.mcs.yml +3 -3
- package/skills/cast-list/examples/agents/bodega-frete/topics/Desambiguar.topic.mcs.yml +11 -0
- package/skills/cast-list/examples/agents/bodega-frete/topics/Fallback.topic.mcs.yml +5 -5
- package/skills/cast-list/examples/agents/bodega-frete/topics/Greeting.topic.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-frete/topics/OnError.topic.mcs.yml +7 -7
- package/skills/cast-list/examples/agents/bodega-frete/topics/RecebimentoOrquestrador.topic.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-loja-fisica/actions/EnviarInstrucoesRetirada.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-loja-fisica/agent.mcs.yml +12 -0
- package/skills/cast-list/examples/agents/bodega-loja-fisica/connectionreferences.mcs.yml +2 -2
- package/skills/cast-list/examples/agents/bodega-loja-fisica/topics/ConsultarGuiaLojaFisica.topic.mcs.yml +3 -3
- package/skills/cast-list/examples/agents/bodega-loja-fisica/topics/Escalate.topic.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-loja-fisica/topics/Fallback.topic.mcs.yml +5 -5
- package/skills/cast-list/examples/agents/bodega-loja-fisica/topics/Greeting.topic.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-loja-fisica/topics/OnError.topic.mcs.yml +6 -6
- package/skills/cast-list/examples/agents/bodega-loja-fisica/topics/RecebimentoOrquestrador.topic.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-orquestrador/agent.mcs.yml +2 -0
- package/skills/cast-list/examples/agents/bodega-pedidos/actions/AbrirChamadoERP.mcs.yml +15 -0
- package/skills/cast-list/examples/agents/bodega-pedidos/actions/EnviarResumoPedido.mcs.yml +1 -1
- package/skills/cast-list/examples/agents/bodega-pedidos/agent.mcs.yml +10 -0
- package/skills/cast-list/examples/agents/bodega-pedidos/connectionreferences.mcs.yml +2 -2
- package/skills/cast-list/examples/agents/bodega-pedidos/topics/ConsultarPoliticasPedidos Copy.topic.mcs.yml +56 -0
- package/skills/cast-list/examples/profile.cast.yaml +28 -0
- package/skills/cast-list/references/format.md +10 -3
- package/skills/cast-list/references/keys.md +14 -1
- package/skills/cast-list/references/profile.md +35 -0
- package/skills/cast-list/scripts/build.mjs +6 -4
- package/skills/cast-list/scripts/engine.mjs +132 -129
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
mcs.metadata:
|
|
2
|
+
componentName: Consultar Políticas de Pedidos Copy
|
|
3
|
+
description: "Responde dúvidas sobre pedidos com base nas políticas publicadas no SharePoint."
|
|
4
|
+
kind: AdaptiveDialog
|
|
5
|
+
inputs:
|
|
6
|
+
- kind: AutomaticTaskInput
|
|
7
|
+
propertyName: PerguntaCliente
|
|
8
|
+
description: "A pergunta do cliente sobre pedidos, status, cancelamento ou pagamento."
|
|
9
|
+
shouldPromptUser: true
|
|
10
|
+
|
|
11
|
+
modelDescription: "Responde perguntas sobre status de pedido, prazos de processamento, cancelamento antes do envio e formas de pagamento da Bodega Azul."
|
|
12
|
+
beginDialog:
|
|
13
|
+
kind: OnRecognizedIntent
|
|
14
|
+
id: main
|
|
15
|
+
intent: {}
|
|
16
|
+
actions:
|
|
17
|
+
- kind: CreateSearchQuery
|
|
18
|
+
id: createSearchQuery_LQyhy8
|
|
19
|
+
userInput: =Topic.PerguntaCliente
|
|
20
|
+
result: Topic.SearchQuery
|
|
21
|
+
|
|
22
|
+
- kind: SearchAndSummarizeContent
|
|
23
|
+
id: searchContent_O7ySob
|
|
24
|
+
autoSend: false
|
|
25
|
+
variable: Topic.SearchResult
|
|
26
|
+
userInput: =Topic.SearchQuery.SearchQuery
|
|
27
|
+
fileSearchDataSource:
|
|
28
|
+
searchFilesMode:
|
|
29
|
+
kind: DoNotSearchFiles
|
|
30
|
+
|
|
31
|
+
knowledgeSources:
|
|
32
|
+
kind: SearchSpecificKnowledgeSources
|
|
33
|
+
knowledgeSources:
|
|
34
|
+
- cr7b2_bodegaPedidos.knowledge.PoliticasPedidos
|
|
35
|
+
|
|
36
|
+
applyModelKnowledgeSetting: false
|
|
37
|
+
responseCaptureType: FullResponse
|
|
38
|
+
|
|
39
|
+
- kind: SetVariable
|
|
40
|
+
id: setVariable_f4lXT8
|
|
41
|
+
variable: Topic.Resposta
|
|
42
|
+
value: =Topic.SearchResult.Text.Content
|
|
43
|
+
|
|
44
|
+
inputType:
|
|
45
|
+
properties:
|
|
46
|
+
PerguntaCliente:
|
|
47
|
+
displayName: PerguntaCliente
|
|
48
|
+
description: "A pergunta do cliente sobre pedidos, status, cancelamento ou pagamento."
|
|
49
|
+
type: String
|
|
50
|
+
|
|
51
|
+
outputType:
|
|
52
|
+
properties:
|
|
53
|
+
Resposta:
|
|
54
|
+
displayName: Resposta
|
|
55
|
+
description: Resposta fundamentada na base de conhecimento do agente.
|
|
56
|
+
type: String
|
|
@@ -9,6 +9,34 @@ min_agents: 3 # …with at least 3 agents in that role
|
|
|
9
9
|
rules:
|
|
10
10
|
- { id: escala, role: specialist, has: "trigger:OnEscalate", says: "Todo especialista escala para humano" }
|
|
11
11
|
- { id: kpi, role: specialist, has: "declared:kpi", says: "Todo especialista tem KPI" }
|
|
12
|
+
# what the text says against how the agent is set up
|
|
13
|
+
- { id: knowledge-only, when: "text:knowledge-only", not: "setting:model-knowledge=on",
|
|
14
|
+
says: "Quem pede só o conhecimento tem o conhecimento geral desligado" }
|
|
15
|
+
|
|
16
|
+
# what to look for in the text (JavaScript regular expressions; case does not matter)
|
|
17
|
+
markers:
|
|
18
|
+
- { id: size, name: Tamanho da resposta, in: instructions,
|
|
19
|
+
pattern: '(no máximo|at most|up to)\s*(\d+)\s*(bullets?|frases?|sentences?)', value: "$2 $3" }
|
|
20
|
+
- { id: no-emoji, name: Sem emojis, pattern: '(sem|não use|no)\s+emojis?' }
|
|
21
|
+
- { id: knowledge-only, name: Só o conhecimento, pattern: '(somente|apenas|only)[^.]{0,40}(conhecimento|knowledge)' }
|
|
22
|
+
|
|
23
|
+
# the block the team pastes in every specialist
|
|
24
|
+
blocks:
|
|
25
|
+
- id: links
|
|
26
|
+
name: Formatação de links
|
|
27
|
+
text: |
|
|
28
|
+
Links:
|
|
29
|
+
- Formate links assim: [título](url), nunca a URL crua.
|
|
30
|
+
- Um link de ação vai no topo; o link da base de ajuda vai no fim.
|
|
31
|
+
- Nunca escreva as palavras «clique aqui».
|
|
32
|
+
- No máximo 3 links por resposta.
|
|
33
|
+
|
|
34
|
+
# the kinds of specialist: one may be of two types; patterns are also found within each type
|
|
35
|
+
types:
|
|
36
|
+
- { id: transaction, name: transação, when: { any: ["flow:*"] } }
|
|
37
|
+
- { id: guidance, name: orientação, when: { all: ["text:link-domain:*", "node:SearchAndSummarizeContent"] } }
|
|
38
|
+
- { id: triage, name: triagem, when: { any: ["trigger:OnSelectIntent"] } }
|
|
39
|
+
|
|
12
40
|
focus_areas:
|
|
13
41
|
- { id: confiabilidade, name: Confiabilidade, facts: ["trigger:OnError", "node:LogCustomTelemetryEvent", "ms:rai-error-handling"] }
|
|
14
42
|
ignore: ["trigger:OnConversationStart"]
|
|
@@ -11,7 +11,12 @@ format: cast-list/cast-v1
|
|
|
11
11
|
name: Customer service agents
|
|
12
12
|
agents: # the roles of the last run (an agent the profile does not name keeps its role here)
|
|
13
13
|
- { id: main-orchestrator, role: orchestrator }
|
|
14
|
-
- { id: returns, role: specialist }
|
|
14
|
+
- { id: returns, role: specialist, types: [ transaction ] } # types a person gives win over the profile's
|
|
15
|
+
blocks: # the blocks of text of the last run, so a block keeps its id across small edits
|
|
16
|
+
- id: links
|
|
17
|
+
text: |
|
|
18
|
+
Links:
|
|
19
|
+
- Format links as [title](url), never the bare URL.
|
|
15
20
|
declared: # what a document or a person says — always with its source
|
|
16
21
|
- { agent: returns, key: kpi, value: "time to answer < 2 min", source: "backlog, epic 12" }
|
|
17
22
|
- { agent: shipping, key: "trigger:OnEscalate", value: "yes", source: "handoff, section 4" }
|
|
@@ -19,11 +24,13 @@ decisions: # what the team decided about a pattern
|
|
|
19
24
|
- { key: "trigger:OnEscalate", status: accepted, scope: platform, why: "Nobody is left without a person" }
|
|
20
25
|
```
|
|
21
26
|
|
|
27
|
+
- **blocks** and the roles are written by the run; **types** are written only by a person (the ones the profile works
|
|
28
|
+
out are not kept, so a change in the profile still holds).
|
|
22
29
|
- **declared**: `key` is either something only people know (`kpi`, `owner`, any word: it becomes `declared:<key>`) or a
|
|
23
30
|
fact key (`trigger:OnEscalate`, `references/keys.md`) when a document claims the agent does it. `value` `no` / `não` /
|
|
24
31
|
`false` claims it does **not**. A claim never changes what the YAML shows: when they disagree, it is a conflict.
|
|
25
|
-
- **decisions**: `status` is `accepted` or `rejected`; `scope` is `platform` (every agent), `specialist
|
|
26
|
-
`orchestrator
|
|
32
|
+
- **decisions**: `status` is `accepted` or `rejected`; `scope` is `platform` (every agent), `specialist`,
|
|
33
|
+
`orchestrator` or `type:<id>` (the specialists of a type — «tipo:<name>» in the workbook); `why` says it. An accepted pattern is checked on every agent of its scope on the next run — even when
|
|
27
34
|
its share would no longer make it a pattern. A rejected one is shown and nobody is off it.
|
|
28
35
|
|
|
29
36
|
## The workbook back
|
|
@@ -21,7 +21,8 @@ Every fact cast-list finds is a key. The keys below have a name; any other key k
|
|
|
21
21
|
| `setting:generative=on` | Generative orchestration on | Orquestração generativa ligada |
|
|
22
22
|
| `setting:generative=off` | Generative orchestration off | Orquestração generativa desligada |
|
|
23
23
|
| `topics:with-model-description` | Topics described for the model | Tópicos descritos para o modelo |
|
|
24
|
-
| `
|
|
24
|
+
| `reaches:<value>` | A topic leads to it (part of trigger:<value>) <value> | Um tópico leva a ele (parte de trigger:<valor>) <valor> |
|
|
25
|
+
| `calls:topic:<value>` | Calls the topic (one not found) <value> | Chama o tópico (não encontrado) <valor> |
|
|
25
26
|
| `trigger:<value>` | Trigger <value> | Gatilho <valor> |
|
|
26
27
|
| `setting:<value>` | Setting <value> | Configuração <valor> |
|
|
27
28
|
|
|
@@ -40,6 +41,10 @@ Every fact cast-list finds is a key. The keys below have a name; any other key k
|
|
|
40
41
|
| Key | Name | Nome |
|
|
41
42
|
|---|---|---|
|
|
42
43
|
| `trigger:OnKnowledgeRequested` | Before searching knowledge | Antes de buscar no conhecimento |
|
|
44
|
+
| `setting:semantic-search=on` | Semantic search on | Busca semântica ligada |
|
|
45
|
+
| `setting:semantic-search=off` | Semantic search off | Busca semântica desligada |
|
|
46
|
+
| `setting:web-browsing=on` | Web browsing on | Navegação na web ligada |
|
|
47
|
+
| `setting:web-browsing=off` | Web browsing off | Navegação na web desligada |
|
|
43
48
|
| `node:SearchAndSummarizeContent` | Generative answers from knowledge | Respostas generativas sobre o conhecimento |
|
|
44
49
|
| `node:SearchKnowledgeSources` | Searches the knowledge sources | Busca nas fontes de conhecimento |
|
|
45
50
|
| `yaml-only:triggerCondition` | Search condition only in YAML (the editor may erase it) | Condição de busca só no YAML (a tela pode apagar) |
|
|
@@ -62,6 +67,8 @@ Every fact cast-list finds is a key. The keys below have a name; any other key k
|
|
|
62
67
|
|
|
63
68
|
| Key | Name | Nome |
|
|
64
69
|
|---|---|---|
|
|
70
|
+
| `setting:model-knowledge=on` | The model's general knowledge on | Conhecimento geral do modelo ligado |
|
|
71
|
+
| `setting:model-knowledge=off` | The model's general knowledge off | Conhecimento geral do modelo desligado |
|
|
65
72
|
| `instructions:present` | Has instructions | Tem instruções |
|
|
66
73
|
| `node:LogCustomTelemetryEvent` | Logs telemetry | Registra telemetria |
|
|
67
74
|
| `node:CSATQuestion` | Asks for satisfaction (CSAT) | Pergunta a satisfação (CSAT) |
|
|
@@ -69,6 +76,12 @@ Every fact cast-list finds is a key. The keys below have a name; any other key k
|
|
|
69
76
|
| `model:<value>` | Model <value> | Modelo <valor> |
|
|
70
77
|
| `auth:<value>` | Sign-in <value> | Login <valor> |
|
|
71
78
|
|
|
79
|
+
## F. Text — F. Texto
|
|
80
|
+
|
|
81
|
+
| Key | Name | Nome |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `text:<value>` | Text: <value> | Texto: <valor> |
|
|
84
|
+
|
|
72
85
|
## Microsoft patterns — Padrões Microsoft
|
|
73
86
|
|
|
74
87
|
| Key | Name | Nome |
|
|
@@ -29,4 +29,39 @@ microsoft_patterns: on # off leaves the ms: facts out
|
|
|
29
29
|
| `focus_areas` | none | Each a filter of the matrix. |
|
|
30
30
|
| `ignore` | none | Keys that say nothing for this team. |
|
|
31
31
|
|
|
32
|
+
## What to look for in the text, and the types
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
rules:
|
|
36
|
+
# a condition and a «not»: the text says one thing, the setup another
|
|
37
|
+
- { id: knowledge-only, when: "text:knowledge-only", not: "setting:model-knowledge=on",
|
|
38
|
+
says: "Who asks for the knowledge alone has the model's general knowledge off" }
|
|
39
|
+
markers: # JavaScript regular expressions; case does not matter
|
|
40
|
+
- { id: size, name: Answer size, in: instructions,
|
|
41
|
+
pattern: '(at most|up to|no máximo)\s*(\d+)\s*(bullets?|sentences?|frases?)', value: "$2 $3" }
|
|
42
|
+
- { id: no-emoji, name: No emojis, pattern: '(no|never use|sem)\s+emojis?' }
|
|
43
|
+
- { id: knowledge-only, name: Knowledge only, pattern: '(only|somente)[^.]{0,40}(knowledge|conhecimento)' }
|
|
44
|
+
blocks: # the text the team pastes in every agent of a role
|
|
45
|
+
- id: links
|
|
46
|
+
name: Link formatting
|
|
47
|
+
role: specialist # the default
|
|
48
|
+
text: |
|
|
49
|
+
Links:
|
|
50
|
+
- Format links as [title](url), never the bare URL.
|
|
51
|
+
types: # a specialist gets every type whose «when» its facts match
|
|
52
|
+
- { id: transaction, name: transaction, when: { any: ["flow:*"] } }
|
|
53
|
+
- { id: guidance, name: guidance, when: { all: ["text:link-domain:*", "node:SearchAndSummarizeContent"], none: ["flow:*"] }, min_agents: 2 }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Field | What it does |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `rules[].when` / `has` / `not` | A rule is for the agents (of its `role`, or all) that have every key of `when`; it is broken when one lacks `has`, or has `not`. A key may end in `*`. A broken `not` rule is said as a contradiction. |
|
|
59
|
+
| `markers` | `in`: `instructions` (the default), `descriptions` (what topics and actions tell the model) or `all`. With no `value`: a fact `text:<id>`. With a `value` (`$1`…`$9` from the regex): `text:<id>=<value>` — agents of a role that set it in different ways are told («unify?»), and one that sets it twice («which one holds?»). Two are built in: `text:link-domain:<host>` for each link in the instructions, `text:email`. In YAML, write a regex in single quotes. |
|
|
60
|
+
| `blocks` | The official text. The family of blocks like it (half of it, at least) takes its id and name; every agent of `role` without it, or with a variation, is off it — with or without a majority. With no `blocks`, the blocks two agents or more share are still found, named by their first words. |
|
|
61
|
+
| `types` | `when` with `all`, `any` (one of), `none`. A specialist may be of two types; the cast file's `types` for an agent win. Patterns are found within each type with its `min_agents` (2 unless said), scope `type:<id>`; a pattern of a type never blames who is not of it. A type with fewer agents is told, and compared in nothing. |
|
|
62
|
+
|
|
63
|
+
A key in `when`, `has`, `not` or a type must start like a real kind of key (`references/keys.md`): a typo is
|
|
64
|
+
`✗ profile:line`, as is a regex that does not compile, an unknown `in`, a type with no `when`, or a rule with
|
|
65
|
+
nothing to check.
|
|
66
|
+
|
|
32
67
|
The keys are the same in any language; only the screen words change with `--lang`.
|
|
@@ -82,11 +82,11 @@ const profile = option('--profile') ? read(resolve(option('--profile')), readPro
|
|
|
82
82
|
const out = resolve(option('--out') ?? dirname(folder))
|
|
83
83
|
const castFile = join(out, `${id}.cast.yaml`)
|
|
84
84
|
const castText = existsSync(castFile) ? readFileSync(castFile, 'utf8') : undefined
|
|
85
|
-
const cast = castText !== undefined ? read(castFile, readCast).cast : { ...(profile.name ? { name: profile.name } : {}), agents: [], declared: [], decisions: [] }
|
|
85
|
+
const cast = castText !== undefined ? read(castFile, readCast).cast : { ...(profile.name ? { name: profile.name } : {}), agents: [], blocks: [], declared: [], decisions: [] }
|
|
86
86
|
|
|
87
87
|
// ---- the way back: an edited workbook as the changes it proposes ----
|
|
88
88
|
if (argv.includes('--from')) {
|
|
89
|
-
const edited = fromSheets(cast, readXlsx(readFileSync(resolve(option('--from')))))
|
|
89
|
+
const edited = fromSheets(cast, readXlsx(readFileSync(resolve(option('--from')))), Object.fromEntries(profile.types.map((t) => [t.id, t.name])))
|
|
90
90
|
if (edited.problems.length) {
|
|
91
91
|
for (const p of edited.problems) console.error(`✗ ${basename(option('--from'))}: ${p}`)
|
|
92
92
|
process.exit(1)
|
|
@@ -145,8 +145,10 @@ const all = argv.includes('--all')
|
|
|
145
145
|
const want = { html: all || argv.includes('--html'), xlsx: all || argv.includes('--xlsx'), lesson: all || argv.includes('--lesson') }
|
|
146
146
|
if (!want.xlsx && !want.lesson) want.html = true
|
|
147
147
|
mkdirSync(out, { recursive: true })
|
|
148
|
-
// the cast file keeps this run's roles
|
|
149
|
-
|
|
148
|
+
// the cast file keeps this run's roles and blocks of text (so a block keeps its id across small edits), under what
|
|
149
|
+
// people wrote in it; the types a person gave an agent stay, the ones the profile works out are not written
|
|
150
|
+
const givenTypes = Object.fromEntries(cast.agents.filter((a) => a.types?.length).map((a) => [a.id, a.types]))
|
|
151
|
+
const kept = { ...cast, agents: result.agents.map((a) => ({ id: a.id, role: a.role, ...(givenTypes[a.id] ? { types: givenTypes[a.id] } : {}) })), blocks: result.blocks.map((b) => ({ id: b.id, text: b.text })) }
|
|
150
152
|
const castYaml = writeCast(kept, castText)
|
|
151
153
|
writeFileSync(castFile, castYaml)
|
|
152
154
|
console.log(`✓ cast ${castFile}`)
|