paneltir 0.12.1 → 0.13.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/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // src/version.ts
2
- var PANELTIR_VERSION = "0.12.1";
3
- var PANELTIR_FINGERPRINT = "sha256:3e958b3c4fa8a7c32fb49568d6189c9a0dea22e65263265177ba6d404ffa13ca";
4
- var PANELTIR_FILE_COUNT = 166;
5
- var PANELTIR_TEMPLATE_HASH = "sha256:9d9168001acb58c6ac0c7766515cb0fa95c4fc6d16e6fb17efd59d88e4a3c382";
2
+ var PANELTIR_VERSION = "0.13.0";
3
+ var PANELTIR_FINGERPRINT = "sha256:f3b5664bddf68f9560bd368b01184f97c733b9a5a3706507ae2219c6a41d9a1c";
4
+ var PANELTIR_FILE_COUNT = 167;
5
+ var PANELTIR_TEMPLATE_HASH = "sha256:7c204bad5c7a8b329ad53ac51b5856438fe84735cea47543eb36b976edbb27da";
6
6
  var paneltirBuild = {
7
7
  version: PANELTIR_VERSION,
8
8
  fingerprint: PANELTIR_FINGERPRINT,
@@ -613,6 +613,22 @@ function moveCardInColumns(columns, move) {
613
613
  });
614
614
  }
615
615
 
616
+ // src/panel/requests.ts
617
+ function slug(subject) {
618
+ return subject.replace(/[^A-Za-z0-9._]/g, "-");
619
+ }
620
+ function requestId(kind, day, subject) {
621
+ return subject === void 0 ? `${kind}-${day}` : `${kind}-${slug(subject)}-${day}`;
622
+ }
623
+ function requestKey(id) {
624
+ const match = /^(.+)-\d{4}-\d{2}-\d{2}$/.exec(id);
625
+ return match ? match[1] : null;
626
+ }
627
+ function pendingRequest(cards, kind, doneColumns, subject) {
628
+ const wanted = subject === void 0 ? kind : `${kind}-${slug(subject)}`;
629
+ return cards.find((card) => !doneColumns.has(card.column) && requestKey(card.id) === wanted);
630
+ }
631
+
616
632
  // src/components/DetailSheet/DetailSheet.tsx
617
633
  import { useEffect as useEffect3, useRef as useRef4 } from "react";
618
634
  import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
@@ -1626,6 +1642,28 @@ var UI = {
1626
1642
  releaseNone: "This version was published without notes.",
1627
1643
  updateCardTitle: (version) => `Update Paneltir to v${version}`,
1628
1644
  updateCardBody: (version) => `Run \`npm install paneltir@${version}\` in this project, then check the panel still builds and renders. The version in use is reported by \`paneltirBuild\`, so it can be confirmed rather than assumed.`,
1645
+ versionBumpAsk: "Ask for a version bump",
1646
+ versionBumpOpen: "A version bump is already asked for \u2014 open it",
1647
+ versionBumpHow: "Writes an order card: every target up one version and one build, kept in step. Nothing changes until a session does it, and the card is dirty until Save like any edit.",
1648
+ versionBumpTitle: "Bump the version by one, on every target",
1649
+ versionBumpBody: "Asked from the panel. Move the version up one step and the build number up one on every target this project ships \u2014 a web app\u2019s package.json, an iOS app\u2019s MARKETING_VERSION and CURRENT_PROJECT_VERSION, an Android app\u2019s versionName and versionCode, and anything else that carries one \u2014 so every target ends on the same version and the same build. For example: build 18, version 1.0.18 becomes build 19, version 1.0.19. A target with no build number moves its version only. If the targets already disagree, bring them all to the highest plus one and say which were behind. Follow this project\u2019s own release rules \u2014 changelog, tag, notes \u2014 if it has any.",
1650
+ versionBumpChecks: [
1651
+ "Found every target that carries a version or a build number",
1652
+ "Moved each one: version up one step, build up one",
1653
+ "Every target ends on the same version and the same build"
1654
+ ],
1655
+ installAsk: "Add a card asking Claude to install it",
1656
+ installAskOpen: "Already asked for \u2014 open the card",
1657
+ installAskHow: "The card carries the repository snippet above, so the session that picks it up installs exactly this. It is dirty until Save, like any edit.",
1658
+ installCardTitle: (tool) => `Install ${tool} in this repository`,
1659
+ installCardBody: (tool, file, snippet) => `Asked from the Tools screen. Merge these two keys into ${file} \u2014 merge, never replace: the file holds other settings. Then commit it, so ${tool} installs for anyone who starts a session in this repository.
1660
+
1661
+ ${snippet}`,
1662
+ installCardChecks: [
1663
+ "Merged into the settings file without replacing anything already there",
1664
+ "Committed to the repository",
1665
+ "The Tools screen shows it as Active"
1666
+ ],
1629
1667
  cardDetail: "Card",
1630
1668
  panelSettings: "Panel settings",
1631
1669
  title: "Title",
@@ -1961,6 +1999,28 @@ var UI = {
1961
1999
  releaseNone: "Esta versi\xF3n se public\xF3 sin notas.",
1962
2000
  updateCardTitle: (version) => `Actualizar Paneltir a la v${version}`,
1963
2001
  updateCardBody: (version) => `Ejecuta \`npm install paneltir@${version}\` en este proyecto y comprueba que el panel sigue construy\xE9ndose y dibuj\xE1ndose. La versi\xF3n en uso la reporta \`paneltirBuild\`, as\xED que se puede confirmar en vez de darla por hecha.`,
2002
+ versionBumpAsk: "Pedir subir versi\xF3n",
2003
+ versionBumpOpen: "Ya hay una subida de versi\xF3n pedida \u2014 abrirla",
2004
+ versionBumpHow: "Escribe una tarjeta de orden: todos los targets suben una versi\xF3n y un build, a la par. No cambia nada hasta que una sesi\xF3n lo haga, y la tarjeta queda sin guardar hasta Guardar, como cualquier edici\xF3n.",
2005
+ versionBumpTitle: "Subir la versi\xF3n en uno, en todos los targets",
2006
+ versionBumpBody: "Pedido desde el panel. Sube la versi\xF3n un paso y el n\xFAmero de build en uno en todos los targets que publica este proyecto \u2014 el package.json de una web, MARKETING_VERSION y CURRENT_PROJECT_VERSION de una app de iOS, versionName y versionCode de una de Android, y cualquier otro que lleve uno \u2014 para que todos acaben en la misma versi\xF3n y el mismo build. Por ejemplo: build 18, versi\xF3n 1.0.18 pasa a build 19, versi\xF3n 1.0.19. Un target sin n\xFAmero de build mueve solo la versi\xF3n. Si los targets ya no coinciden, ll\xE9valos todos al m\xE1s alto m\xE1s uno y di cu\xE1les iban por detr\xE1s. Sigue las reglas de release del propio proyecto \u2014 changelog, tag, notas \u2014 si las tiene.",
2007
+ versionBumpChecks: [
2008
+ "Encontrados todos los targets que llevan versi\xF3n o n\xFAmero de build",
2009
+ "Movido cada uno: versi\xF3n un paso arriba, build uno arriba",
2010
+ "Todos los targets acaban en la misma versi\xF3n y el mismo build"
2011
+ ],
2012
+ installAsk: "A\xF1adir una tarjeta para que Claude lo instale",
2013
+ installAskOpen: "Ya pedido \u2014 abrir la tarjeta",
2014
+ installAskHow: "La tarjeta lleva el fragmento del repositorio de arriba, as\xED que la sesi\xF3n que la recoja instala exactamente esto. Queda sin guardar hasta Guardar, como cualquier edici\xF3n.",
2015
+ installCardTitle: (tool) => `Instalar ${tool} en este repositorio`,
2016
+ installCardBody: (tool, file, snippet) => `Pedido desde la pantalla de Herramientas. Fusiona estas dos claves en ${file} \u2014 fusiona, nunca reemplaces: el archivo lleva otros ajustes. Despu\xE9s haz commit, para que ${tool} se instale para cualquiera que abra una sesi\xF3n en este repositorio.
2017
+
2018
+ ${snippet}`,
2019
+ installCardChecks: [
2020
+ "Fusionado en el archivo de ajustes sin reemplazar nada de lo que ya hab\xEDa",
2021
+ "Commiteado en el repositorio",
2022
+ "La pantalla de Herramientas lo muestra como Activo"
2023
+ ],
1964
2024
  cardDetail: "Tarjeta",
1965
2025
  panelSettings: "Ajustes del panel",
1966
2026
  title: "T\xEDtulo",
@@ -2703,7 +2763,16 @@ function ToolRow({
2703
2763
  tool.state === "misconfigured" && /* @__PURE__ */ jsx19("p", { className: "pt-warn", children: ui.stateMisconfiguredWhy })
2704
2764
  ] });
2705
2765
  }
2706
- function MarketplaceView({ report, loading, failure, lang, ui, onRefresh }) {
2766
+ function MarketplaceView({
2767
+ report,
2768
+ loading,
2769
+ failure,
2770
+ lang,
2771
+ ui,
2772
+ onRefresh,
2773
+ onRequestInstall,
2774
+ installRequested
2775
+ }) {
2707
2776
  const [filter, setFilter] = useState8("all");
2708
2777
  const [open, setOpen] = useState8(null);
2709
2778
  const tools = report?.tools ?? [];
@@ -2793,7 +2862,12 @@ function MarketplaceView({ report, loading, failure, lang, ui, onRefresh }) {
2793
2862
  tool: open,
2794
2863
  ui,
2795
2864
  marketplace: report?.marketplace ?? { name: MARKETPLACE_NAME, url: MARKETPLACE_URL },
2796
- registered: report?.project.marketplaceRegistered ?? false
2865
+ registered: report?.project.marketplaceRegistered ?? false,
2866
+ requested: installRequested?.(open.id) ?? false,
2867
+ onRequest: onRequestInstall ? (marketplace) => {
2868
+ onRequestInstall(open, marketplace);
2869
+ setOpen(null);
2870
+ } : void 0
2797
2871
  }
2798
2872
  ),
2799
2873
  /* @__PURE__ */ jsx19("button", { type: "button", className: "pt-btn pt-btn--sm", onClick: () => setOpen(null), children: ui.close })
@@ -2804,20 +2878,28 @@ function InstallSheet({
2804
2878
  tool,
2805
2879
  ui,
2806
2880
  marketplace,
2807
- registered
2881
+ registered,
2882
+ requested,
2883
+ onRequest
2808
2884
  }) {
2809
2885
  const copy = { copy: ui.copy, copied: ui.copied, selected: ui.copySelected };
2810
2886
  return /* @__PURE__ */ jsxs17(Fragment5, { children: [
2811
2887
  tool.claudePlugin ? /* @__PURE__ */ jsxs17(Fragment5, { children: [
2812
- /* @__PURE__ */ jsx19(DetailSection, { title: ui.installRepoTitle, subtitle: ui.installRepoHint, children: /* @__PURE__ */ jsx19(
2813
- CopyBlock,
2814
- {
2815
- text: repositorySnippet(tool.id, marketplace),
2816
- label: SETTINGS_PATH,
2817
- note: registered ? ui.installRepoMergeRegistered : ui.installRepoMerge,
2818
- labels: copy
2819
- }
2820
- ) }),
2888
+ /* @__PURE__ */ jsxs17(DetailSection, { title: ui.installRepoTitle, subtitle: ui.installRepoHint, children: [
2889
+ /* @__PURE__ */ jsx19(
2890
+ CopyBlock,
2891
+ {
2892
+ text: repositorySnippet(tool.id, marketplace),
2893
+ label: SETTINGS_PATH,
2894
+ note: registered ? ui.installRepoMergeRegistered : ui.installRepoMerge,
2895
+ labels: copy
2896
+ }
2897
+ ),
2898
+ onRequest && tool.state !== "active" && /* @__PURE__ */ jsxs17("div", { className: "pt-panel-row", style: { marginTop: 10 }, children: [
2899
+ /* @__PURE__ */ jsx19("button", { type: "button", className: "pt-btn pt-btn--sm pt-btn--primary", onClick: () => onRequest(marketplace), children: requested ? ui.installAskOpen : ui.installAsk }),
2900
+ /* @__PURE__ */ jsx19("p", { className: "pt-marketplace__detail", children: ui.installAskHow })
2901
+ ] })
2902
+ ] }),
2821
2903
  /* @__PURE__ */ jsx19(DetailSection, { title: ui.installCliTitle, subtitle: ui.installCliHint, children: /* @__PURE__ */ jsx19(CopyBlock, { text: commandSnippet(tool.id, marketplace), labels: copy }) })
2822
2904
  ] }) : /* @__PURE__ */ jsx19("p", { className: "pt-panel-text", children: ui.installNoPlugin }),
2823
2905
  tool.standaloneCommand ? /* @__PURE__ */ jsx19(DetailSection, { title: ui.installStandaloneTitle, subtitle: ui.installStandaloneHint, children: /* @__PURE__ */ jsx19(CopyBlock, { text: tool.standaloneCommand, labels: copy }) }) : /* @__PURE__ */ jsx19(DetailSection, { title: ui.installStandaloneTitle, children: /* @__PURE__ */ jsx19("p", { className: "pt-panel-text", children: ui.installNoStandalone }) }),
@@ -3423,6 +3505,55 @@ function PanelApp({
3423
3505
  },
3424
3506
  [state.cards, state.areas, state.columns]
3425
3507
  );
3508
+ const requestCard = useCallback5(
3509
+ (kind, subject, content) => {
3510
+ const waiting = pendingRequest(state.cards, kind, doneColumns, subject);
3511
+ if (waiting) {
3512
+ setOpenCardId(waiting.id);
3513
+ return;
3514
+ }
3515
+ const card = {
3516
+ ...emptyCard(queueColumn(state.columns), state.areas[0]?.id ?? "general"),
3517
+ id: requestId(kind, today(), subject),
3518
+ title: content.title,
3519
+ body: content.body,
3520
+ priority: "medium",
3521
+ risk: "low",
3522
+ owner: "claude",
3523
+ order: true,
3524
+ intent: "do",
3525
+ checks: content.checks.map((text2) => ({ id: newId(), text: text2, done: false }))
3526
+ };
3527
+ setState((current) => ({ ...current, updatedAt: today(), cards: [...current.cards, card] }));
3528
+ setOpenCardId(card.id);
3529
+ },
3530
+ [state.cards, state.columns, state.areas, doneColumns]
3531
+ );
3532
+ const requestVersionBump = useCallback5(() => {
3533
+ requestCard("version-bump", void 0, {
3534
+ title: { en: UI.en.versionBumpTitle, es: UI.es.versionBumpTitle },
3535
+ body: { en: UI.en.versionBumpBody, es: UI.es.versionBumpBody },
3536
+ checks: UI.en.versionBumpChecks.map((en, i) => ({ en, es: UI.es.versionBumpChecks[i] }))
3537
+ });
3538
+ }, [requestCard]);
3539
+ const requestInstall = useCallback5(
3540
+ (tool, marketplace) => {
3541
+ const snippet = repositorySnippet(tool.id, marketplace).trimEnd();
3542
+ requestCard("install", tool.id, {
3543
+ title: { en: UI.en.installCardTitle(tool.name), es: UI.es.installCardTitle(tool.name) },
3544
+ body: {
3545
+ en: UI.en.installCardBody(tool.name, SETTINGS_PATH, snippet),
3546
+ es: UI.es.installCardBody(tool.name, SETTINGS_PATH, snippet)
3547
+ },
3548
+ checks: UI.en.installCardChecks.map((en, i) => ({ en, es: UI.es.installCardChecks[i] }))
3549
+ });
3550
+ },
3551
+ [requestCard]
3552
+ );
3553
+ const versionBumpWaiting = useMemo5(
3554
+ () => pendingRequest(state.cards, "version-bump", doneColumns),
3555
+ [state.cards, doneColumns]
3556
+ );
3426
3557
  const moveCard = useCallback5(
3427
3558
  (move) => {
3428
3559
  setState((current) => {
@@ -3848,7 +3979,9 @@ function PanelApp({
3848
3979
  failure: marketFailure,
3849
3980
  lang,
3850
3981
  ui,
3851
- onRefresh: marketplaceEndpoint ? () => loadMarketplace(true) : void 0
3982
+ onRefresh: marketplaceEndpoint ? () => loadMarketplace(true) : void 0,
3983
+ onRequestInstall: saveEndpoint ? requestInstall : void 0,
3984
+ installRequested: (toolId) => Boolean(pendingRequest(state.cards, "install", doneColumns, toolId))
3852
3985
  }
3853
3986
  ),
3854
3987
  view === "analysis" && state.analysis && /* @__PURE__ */ jsx21(
@@ -3949,7 +4082,17 @@ function PanelApp({
3949
4082
  },
3950
4083
  area.id
3951
4084
  ))
3952
- ] })
4085
+ ] }),
4086
+ saveEndpoint && /* @__PURE__ */ jsx21("div", { className: "pt-panel-row pt-requests", children: /* @__PURE__ */ jsx21(
4087
+ "button",
4088
+ {
4089
+ type: "button",
4090
+ className: "pt-btn pt-btn--sm",
4091
+ title: ui.versionBumpHow,
4092
+ onClick: requestVersionBump,
4093
+ children: versionBumpWaiting ? ui.versionBumpOpen : `\u2B06 ${ui.versionBumpAsk}`
4094
+ }
4095
+ ) })
3953
4096
  ] }),
3954
4097
  /* @__PURE__ */ jsx21(
3955
4098
  Board,
@@ -4504,10 +4647,12 @@ export {
4504
4647
  panelForm,
4505
4648
  paneltirBuild,
4506
4649
  paperForm,
4650
+ pendingRequest,
4507
4651
  queueColumn,
4508
4652
  readBoard,
4509
4653
  readStoredLang,
4510
4654
  repositorySnippet,
4655
+ requestId,
4511
4656
  resetGuide,
4512
4657
  sparkPoints,
4513
4658
  stampForColumn,
package/fingerprint.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "paneltir",
3
- "version": "0.12.1",
4
- "hash": "sha256:3e958b3c4fa8a7c32fb49568d6189c9a0dea22e65263265177ba6d404ffa13ca",
5
- "fileCount": 166,
6
- "templateHash": "sha256:9d9168001acb58c6ac0c7766515cb0fa95c4fc6d16e6fb17efd59d88e4a3c382",
7
- "generatedAt": "2026-09-18T06:35:32.449Z"
3
+ "version": "0.13.0",
4
+ "hash": "sha256:f3b5664bddf68f9560bd368b01184f97c733b9a5a3706507ae2219c6a41d9a1c",
5
+ "fileCount": 167,
6
+ "templateHash": "sha256:7c204bad5c7a8b329ad53ac51b5856438fe84735cea47543eb36b976edbb27da",
7
+ "generatedAt": "2026-09-26T09:08:52.803Z"
8
8
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paneltir",
3
- "version": "0.12.1",
3
+ "version": "0.13.0",
4
4
  "description": "A React UI kit for admin panels: page frame, a board with touch-ready drag and drop, detail sheets, and a whole panel in one component. Structure and behaviour are fixed; colour and brand belong to each project.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "DFKlabs (https://DFKlabs.com)",
@@ -99,63 +99,35 @@
99
99
  "node": ">=20"
100
100
  },
101
101
  "paneltirRelease": {
102
- "version": "0.12.1",
103
- "date": "2026-09-18",
102
+ "version": "0.13.0",
103
+ "date": "2026-09-26",
104
104
  "entries": [
105
105
  {
106
- "kind": "fix",
106
+ "kind": "feature",
107
107
  "text": {
108
- "en": "A card dragged down its own column landed one slot below the placeholder. The index the drag emitted counted the card being dragged, and neither reducer allowed for it once the card was taken out; it is adjusted once now, where the numbers are still rendered ones, and `toIndex` says what it means.",
109
- "es": "Una tarjeta arrastrada hacia abajo en su propia columna caía un hueco por debajo del marcador. El índice que emitía el arrastre contaba la propia tarjeta arrastrada y ninguno de los dos reductores lo descontaba al sacarla; ahora se ajusta una sola vez, donde los números aún son los dibujados, y `toIndex` dice lo que significa."
108
+ "en": "The board can ask for a version bump. \"Ask for a version bump\", under the filter row, writes an order card: every target the project ships moves up one version and one build, and they all end on the same pair — build 18, 1.0.18 becomes build 19, 1.0.19. The panel still runs nothing; the card is the request, and only one is open at a time.",
109
+ "es": "El tablero puede pedir una subida de versión. «Pedir subir versión», bajo la fila de filtros, escribe una tarjeta de orden: todos los targets del proyecto suben una versión y un build, y acaban en el mismo par — build 18, 1.0.18 pasa a build 19, 1.0.19. El panel sigue sin ejecutar nada; la tarjeta es la petición, y solo hay una abierta a la vez."
110
110
  }
111
111
  },
112
112
  {
113
- "kind": "fix",
113
+ "kind": "feature",
114
114
  "text": {
115
- "en": "Hovering the drop placeholder no longer sends the target to the bottom of the column. It appeared under the pointer the moment a slot was chosen and read as \"not a card, so the end\", which moved it, put the card back under the pointer, and chose again.",
116
- "es": "Pasar por encima del marcador de soltado ya no manda el destino al final de la columna. Aparecía bajo el puntero en cuanto se elegía un hueco y se leía como «no es una tarjeta, así que el final», lo que lo movía, devolvía la tarjeta bajo el puntero y volvía a elegir."
115
+ "en": "Installing a tool can be handed to Claude. The install sheet gains \"Add a card asking Claude to install it\", which writes an order carrying the exact settings snippet, and whose last step is the Tools screen reading Active. Offered for plugins not already active, one request per tool.",
116
+ "es": "Instalar una herramienta se le puede pasar a Claude. La hoja de instalar gana «Añadir una tarjeta para que Claude lo instale», que escribe una orden con el fragmento de ajustes exacto y cuyo último paso es que la pantalla de Herramientas diga Activo. Para plugins que no estén ya activos, una petición por herramienta."
117
117
  }
118
118
  },
119
119
  {
120
- "kind": "fix",
120
+ "kind": "feature",
121
121
  "text": {
122
- "en": "A drop in a done column that holds archived cards lands where it was dropped. The move was anchored against a list that still carried the archived cards, so the slot on screen and the slot in the file disagreed.",
123
- "es": "Soltar en una columna Hecho con tarjetas archivadas cae donde se soltó. El movimiento se anclaba contra una lista que aún llevaba las archivadas, así que el hueco en pantalla y el hueco en el archivo no coincidían."
122
+ "en": "`pendingRequest` and `requestId` are exported, so a project building its own screen can write the same requests with the same one-open-at-a-time rule.",
123
+ "es": "`pendingRequest` y `requestId` se exportan, así que un proyecto que construye su propia pantalla puede escribir las mismas peticiones con la misma regla de una abierta a la vez."
124
124
  }
125
125
  },
126
126
  {
127
- "kind": "fix",
127
+ "kind": "chore",
128
128
  "text": {
129
- "en": "A save in flight no longer discards what was typed while it ran. The board was put back to the moment Save was pressed when the commit came back; edited meanwhile, it now keeps the edits and reads as unsaved again, which is what they are.",
130
- "es": "Un guardado en curso ya no descarta lo que se escribió mientras corría. El tablero volvía al momento de pulsar Guardar cuando regresaba el commit; si se editó entre medias, ahora conserva los cambios y vuelve a leerse como sin guardar, que es lo que son."
131
- }
132
- },
133
- {
134
- "kind": "fix",
135
- "text": {
136
- "en": "The update card and the four \"ask for this again\" cards no longer assume a column called `next`. On a board whose owner renamed it they landed in a column that does not exist, which the write path refuses — one tap made the board unsaveable. `queueColumn` picks where queued work already sits.",
137
- "es": "La tarjeta de actualización y las cuatro de «pídelo otra vez» ya no dan por hecho una columna llamada `next`. En un tablero cuyo dueño la renombró caían en una columna que no existe, cosa que la ruta de escritura rechaza: un toque dejaba el tablero sin poder guardarse. `queueColumn` elige donde ya está el trabajo en cola."
138
- }
139
- },
140
- {
141
- "kind": "fix",
142
- "text": {
143
- "en": "A card added with \"+\" straight into the working column is stamped as started, as a card moved there would be. It carried no `startedAt`, so the day it reached done the panel flagged it as never picked up.",
144
- "es": "Una tarjeta añadida con «+» directamente en la columna de trabajo se sella como empezada, igual que si se hubiera movido ahí. No llevaba `startedAt`, así que el día que llegaba a Hecho el panel la marcaba como nunca recogida."
145
- }
146
- },
147
- {
148
- "kind": "fix",
149
- "text": {
150
- "en": "`paneltir run` never mints an id the board already has. At the cap the list stops growing and the length-based id repeated, so two runs shared a key in the revisions log and one bell entry stood for both.",
151
- "es": "`paneltir run` nunca acuña un id que el tablero ya tiene. En el tope la lista deja de crecer y el id basado en la longitud se repetía, así que dos pasadas compartían clave en el registro y una entrada de la campana valía por las dos."
152
- }
153
- },
154
- {
155
- "kind": "fix",
156
- "text": {
157
- "en": "Selecting text inside a sheet and letting go over the backdrop no longer closes it. Only a press that begins and ends on the backdrop is somebody asking to leave.",
158
- "es": "Seleccionar texto dentro de una hoja y soltar sobre el fondo ya no la cierra. Solo una pulsación que empieza y termina en el fondo es alguien pidiendo salir."
129
+ "en": "The skill a project keeps says how to carry out both cards, and `check:docs` fails if the version-bump rule stops reaching it.",
130
+ "es": "La skill que guarda cada proyecto explica cómo llevar a cabo las dos tarjetas, y `check:docs` falla si la regla de subir versión deja de llegarle."
159
131
  }
160
132
  }
161
133
  ]
@@ -85,6 +85,30 @@ and it is asked on the board as a choice with `value: null`, which the bell
85
85
  surfaces. Stopping to ask something you could have chosen costs the owner a
86
86
  session; choosing it and saying so costs them one line.
87
87
 
88
+ ## Cards the panel wrote for the owner
89
+
90
+ Some cards were not typed: a button wrote them, because the panel runs nothing
91
+ and can only record the ask. Their id says which button, and each is an order
92
+ with `intent: do` — carry it out like any other.
93
+
94
+ **`version-bump-<date>` — bump the version by one, on every target.** Find
95
+ every place this project carries a version or a build number: a web app's
96
+ `package.json`, an iOS app's `MARKETING_VERSION` and `CURRENT_PROJECT_VERSION`,
97
+ an Android app's `versionName` and `versionCode`, and anything else. Move the
98
+ version up one step and the build up one on each, so every target ends on the
99
+ same pair — build 18, version 1.0.18 becomes build 19, version 1.0.19. A target
100
+ with no build number moves its version only. If the targets already disagree,
101
+ bring them all to the highest plus one and say which were behind. Follow the
102
+ project's own release rules if it has any. Only one is ever open at a time:
103
+ the button opens the waiting card instead of writing a second, so two bumps in
104
+ a row are two separate asks, not a duplicate.
105
+
106
+ **`install-<tool>-<date>` — install a plugin in this repository.** The body
107
+ carries the exact snippet. Merge its two keys into `.claude/settings.json` —
108
+ merge, never replace: the file holds other settings — and commit. The last
109
+ step is the panel's Tools screen reading *Active*, which is how the install is
110
+ checked rather than assumed.
111
+
88
112
  ## Read the analysis before deciding anything
89
113
 
90
114
  `analysis.choices` holds the questions the owner has already answered, grouped