takomi 2.1.26 → 2.1.28

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.
@@ -40,6 +40,28 @@ function cost(model, input, cache, output, additiveCache = true) { const p = PRI
40
40
  function fmtTokens(n) { if (n >= 1e9) return `${(n/1e9).toFixed(2)}B`; if (n >= 1e6) return `${(n/1e6).toFixed(1)}M`; if (n >= 1e3) return `${(n/1e3).toFixed(1)}K`; return String(Math.round(n || 0)); }
41
41
  function fmtMoney(n) { return `$${(n || 0).toFixed(n > 100 ? 0 : 2)}`; }
42
42
  function ms(n) { if (!n) return '-'; const s = Math.round(n/1000); if (s < 60) return `${s}s`; const m = Math.floor(s/60); if (m < 60) return `${m}m ${s%60}s`; const h = Math.floor(m/60); return `${h}h ${m%60}m`; }
43
+ const ACTIVE_GAP_THRESHOLD_MS = 15 * 60 * 1000;
44
+ function timestampMs(value) {
45
+ if (typeof value === 'number') return Number.isFinite(value) ? value : null;
46
+ if (typeof value === 'string' && value) {
47
+ const parsed = Date.parse(value);
48
+ return Number.isFinite(parsed) ? parsed : null;
49
+ }
50
+ return null;
51
+ }
52
+ function addTimestamp(target, value) {
53
+ const parsed = timestampMs(value);
54
+ if (parsed !== null) target.push(parsed);
55
+ }
56
+ function activeDuration(timestamps, maxGapMs = ACTIVE_GAP_THRESHOLD_MS) {
57
+ const sorted = [...new Set((timestamps || []).filter(Number.isFinite))].sort((a, b) => a - b);
58
+ let total = 0;
59
+ for (let i = 1; i < sorted.length; i += 1) {
60
+ const delta = sorted[i] - sorted[i - 1];
61
+ if (delta > 0 && delta <= maxGapMs) total += delta;
62
+ }
63
+ return total;
64
+ }
43
65
  function parseSince(value) {
44
66
  if (!value) return null;
45
67
  const raw = String(value).trim().toLowerCase();
@@ -88,14 +110,20 @@ async function files(root, suffix = '.jsonl') {
88
110
  await walk(root); return out;
89
111
  }
90
112
 
113
+ function pushTask(taskRows, task) {
114
+ if (!task?.end || task.end === task.start) return;
115
+ task.activeMs = activeDuration(task.activityTimestamps || []);
116
+ taskRows.push(task);
117
+ }
118
+
91
119
  async function scanPiSessions(root, source, events, sessionRows = [], taskRows = []) {
92
120
  for (const file of await files(root)) {
93
121
  let provider = 'unknown', model = 'unknown', session = path.basename(file, '.jsonl'), cwd = '', currentTask = null;
94
- const row = { key: session, session, source, file, project: projectKey(file), cwd, start: '', end: '', turns: 0, messages: 0, toolCalls: 0, subagentCalls: 0, roles: new Map(), stages: new Map(), workflows: new Map(), activeMs: 0 };
122
+ const row = { key: session, session, source, file, project: projectKey(file), cwd, start: '', end: '', turns: 0, messages: 0, toolCalls: 0, subagentCalls: 0, roles: new Map(), stages: new Map(), workflows: new Map(), activeMs: 0, activityTimestamps: [] };
95
123
  const text = await fs.readFile(file, 'utf8').catch(() => '');
96
124
  for (const line of text.split(/\r?\n/)) {
97
125
  const obj = safeJson(line); if (!obj) continue;
98
- if (obj.timestamp) { row.start ||= obj.timestamp; row.end = obj.timestamp; }
126
+ if (obj.timestamp) { row.start ||= obj.timestamp; row.end = obj.timestamp; addTimestamp(row.activityTimestamps, obj.timestamp); }
99
127
  if (obj.type === 'session') { session = obj.id || session; cwd = obj.cwd || cwd; row.key = session; row.session = session; row.cwd = cwd; }
100
128
  if (obj.type === 'model_change') { provider = obj.provider || provider; model = obj.modelId || model; }
101
129
  if (obj.type === 'custom' && obj.customType === 'takomi-runtime-state' && obj.data) {
@@ -112,12 +140,14 @@ async function scanPiSessions(root, source, events, sessionRows = [], taskRows =
112
140
  row.messages += 1;
113
141
  const ts = obj.timestamp || msg.timestamp || '';
114
142
  if (msg.role === 'user') {
115
- if (currentTask?.end && currentTask.end !== currentTask.start) taskRows.push(currentTask);
143
+ pushTask(taskRows, currentTask);
116
144
  row.turns += 1;
117
145
  const textPart = (msg.content || []).find(p => p?.type === 'text')?.text || '';
118
- currentTask = { source, file, session, project: projectKey(file), cwd, start: ts, end: ts, provider, model, turns: 1, toolCalls: 0, title: String(textPart).replace(/\s+/g, ' ').trim() };
146
+ currentTask = { source, file, session, project: projectKey(file), cwd, start: ts, end: ts, provider, model, turns: 1, toolCalls: 0, title: String(textPart).replace(/\s+/g, ' ').trim(), activityTimestamps: [] };
147
+ addTimestamp(currentTask.activityTimestamps, ts);
119
148
  } else if (currentTask && ts) {
120
149
  currentTask.end = ts;
150
+ addTimestamp(currentTask.activityTimestamps, ts);
121
151
  }
122
152
  for (const part of msg.content || []) {
123
153
  if (!part || part.type !== 'toolCall') continue;
@@ -135,8 +165,8 @@ async function scanPiSessions(root, source, events, sessionRows = [], taskRows =
135
165
  const u = msg && msg.usage;
136
166
  if (u) events.push({ source, file, timestamp: obj.timestamp, day: dayOf(obj.timestamp), session, provider, model, project: projectKey(file), kind: 'usage', input: +u.input||0, cache: +u.cacheRead||0, output: +u.output||0, total: +u.totalTokens||0, cost: cost(model, +u.input||0, +u.cacheRead||0, +u.output||0, true) });
137
167
  }
138
- if (currentTask?.end && currentTask.end !== currentTask.start) taskRows.push(currentTask);
139
- row.activeMs = taskRows.filter(t => t.file === file).reduce((sum, t) => sum + taskDuration(t), 0);
168
+ pushTask(taskRows, currentTask);
169
+ row.activeMs = activeDuration(row.activityTimestamps);
140
170
  if (row.messages || row.toolCalls || row.turns) sessionRows.push(row);
141
171
  }
142
172
  }
@@ -336,9 +366,7 @@ function sessionDuration(row) {
336
366
  return row?.activeMs || 0;
337
367
  }
338
368
  function taskDuration(row) {
339
- const start = Date.parse(row?.start || '');
340
- const end = Date.parse(row?.end || '');
341
- return Number.isFinite(start) && Number.isFinite(end) && end >= start ? end - start : 0;
369
+ return row?.activeMs ?? activeDuration(row?.activityTimestamps || []);
342
370
  }
343
371
  function taskLabel(row, width = 34) {
344
372
  const label = row?.title || row?.project || row?.session || 'unknown';
@@ -379,7 +407,7 @@ function renderFocusedView(stats, opts = {}) {
379
407
  ` ${pc.cyan(ms(sessionDuration(r)))} ${pc.dim(r.turns + ' turns')} ${pc.dim(r.toolCalls + ' tools')}`,
380
408
  ` ${pc.dim(r.file || '')}`,
381
409
  ].join('\n'), limit);
382
- if (view === 'tasks-full' || view === 'task-full') return renderFullList('Longest Tasks — Full Prompts', stats.topTasks || [], (r, i) => [
410
+ if (view === 'tasks-full' || view === 'task-full') return renderFullList('Longest Active Turns — Full Prompts', stats.topTasks || [], (r, i) => [
383
411
  ` ${pc.dim(String(i + 1).padStart(2, '0') + '.')} ${pc.cyan(ms(taskDuration(r)))} ${pc.magenta(r.toolCalls + ' tools')} ${pc.dim(dayOf(r.start))}`,
384
412
  ` ${pc.white(indentWrap(r.title || r.project || r.session || 'unknown'))}`,
385
413
  ` ${pc.dim(r.project || '')}`,
@@ -426,7 +454,7 @@ function renderFocusedView(stats, opts = {}) {
426
454
  { width: 8, align: 'right', get: r => pc.cyan(String(r.toolCalls)) },
427
455
  { width: 8, align: 'left', get: () => pc.dim('tools') },
428
456
  ]],
429
- tasks: ['Longest Tasks', stats.topTasks || [], [
457
+ tasks: ['Longest Active Turns', stats.topTasks || [], [
430
458
  { width: 6, align: 'left', get: r => pc.dim(dayOf(r.start).slice(5)) },
431
459
  { width: 9, align: 'right', get: r => pc.cyan(ms(taskDuration(r))) },
432
460
  { width: 11, align: 'right', get: r => pc.magenta(`${r.toolCalls} tools`) },
@@ -518,9 +546,9 @@ export function renderTakomiStats(stats, opts = {}) {
518
546
  // ── Duration Cards ────────────────────────────────────────────────────
519
547
  lines.push('');
520
548
  const cards3 = [
521
- statCard(longestSession ? ms(sessionDuration(longestSession)) : '-', 'Active Session', pc.cyan),
549
+ statCard(longestSession ? ms(sessionDuration(longestSession)) : '-', 'Top Active Session', pc.cyan),
522
550
  statCard(longestSession ? String(longestSession.turns) : '-', 'Turns in Session', pc.cyan),
523
- statCard(longestTask ? ms(taskDuration(longestTask)) : '-', 'Longest Task', pc.magenta),
551
+ statCard(longestTask ? ms(taskDuration(longestTask)) : '-', 'Longest Active Turn', pc.magenta),
524
552
  statCard(longestTask ? String(longestTask.toolCalls) : '-', 'Tools in Task', pc.magenta),
525
553
  statCard(stats.mostSubagentsSession ? String(stats.mostSubagentsSession.subagentCalls) : '0', 'Max Subagents', pc.blue),
526
554
  ];
@@ -530,7 +558,7 @@ export function renderTakomiStats(stats, opts = {}) {
530
558
 
531
559
  // ── Info line ─────────────────────────────────────────────────────────
532
560
  lines.push('');
533
- const infoText = `Peak: ${peak?.key || '-'} · ${streaks.quietDays} quiet days · ${stats.totals.events.toLocaleString()} events${stats.since ? ` · since ${stats.since}` : ''}`;
561
+ const infoText = `Peak: ${peak?.key || '-'} · ${streaks.quietDays} quiet days · ${stats.totals.events.toLocaleString()} events · active gaps ≤15m${stats.since ? ` · since ${stats.since}` : ''}`;
534
562
  lines.push(center(pc.dim(infoText), W));
535
563
 
536
564
  lines.push('');
@@ -586,10 +614,10 @@ export function renderTakomiStats(stats, opts = {}) {
586
614
  ]));
587
615
  }
588
616
 
589
- // ── Longest Tasks ──────────────────────────────────────────────────────
617
+ // ── Longest Active Turns ───────────────────────────────────────────────
590
618
  if (stats.topTasks?.length) {
591
619
  lines.push('');
592
- lines.push(renderTable('Longest Tasks', stats.topTasks.slice(0, 5), [
620
+ lines.push(renderTable('Longest Active Turns', stats.topTasks.slice(0, 5), [
593
621
  { width: 6, align: 'left', get: r => pc.dim(dayOf(r.start).slice(5)) },
594
622
  { width: 9, align: 'right', get: r => pc.cyan(ms(taskDuration(r))) },
595
623
  { width: 11, align: 'right', get: r => pc.magenta(`${r.toolCalls} tools`) },
@@ -12,6 +12,7 @@ import {
12
12
  type SubagentState,
13
13
  } from "./pi-subagents-internal";
14
14
  import { resolveAgentName } from "./agent-aliases";
15
+ import { applyTakomiRoutingDefaults, loadTakomiModelRoutingSnapshotSync } from "../takomi-runtime/model-routing-defaults";
15
16
  import type { TakomiSubagentToolParams, TakomiSubagentToolTask } from "./tool-runner";
16
17
 
17
18
  type ToolUpdate = (partial: AgentToolResult<Details>) => void;
@@ -135,9 +136,18 @@ function defaultChildExtensions(): string[] {
135
136
  return candidates.filter((candidate) => fs.existsSync(candidate));
136
137
  }
137
138
 
138
- function withTakomiAgentDefaults(agent: AgentConfig): AgentConfig {
139
+ function withTakomiAgentDefaults(agent: AgentConfig, cwd: string): AgentConfig {
140
+ const routed = applyTakomiRoutingDefaults({
141
+ agent: agent.name,
142
+ model: agent.model,
143
+ fallbackModels: agent.fallbackModels,
144
+ thinking: agent.thinking,
145
+ }, loadTakomiModelRoutingSnapshotSync(cwd));
139
146
  return {
140
147
  ...agent,
148
+ model: routed.model,
149
+ fallbackModels: routed.fallbackModels,
150
+ thinking: routed.thinking,
141
151
  systemPromptMode: agent.systemPromptMode ?? "replace",
142
152
  inheritProjectContext: agent.inheritProjectContext ?? true,
143
153
  inheritSkills: agent.inheritSkills ?? false,
@@ -147,7 +157,7 @@ function withTakomiAgentDefaults(agent: AgentConfig): AgentConfig {
147
157
  }
148
158
 
149
159
  function discoverUnifiedAgents(discoverPiAgents: any, cwd: string, scope: AgentScope): { agents: AgentConfig[] } {
150
- return { agents: discoverPiAgents(cwd, scope).agents.map(withTakomiAgentDefaults) };
160
+ return { agents: discoverPiAgents(cwd, scope).agents.map((agent: AgentConfig) => withTakomiAgentDefaults(agent, cwd)) };
151
161
  }
152
162
 
153
163
  function agentNameSet(discoverPiAgents: any, cwd: string): Set<string> {
@@ -2,6 +2,7 @@ import path from "node:path";
2
2
  import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
3
3
  import type { TakomiThinkingLevel } from "../../../src/pi-takomi-core";
4
4
  import { loadTakomiProfile } from "../takomi-runtime/profile";
5
+ import { applyTakomiRoutingDefaults, loadTakomiModelRoutingSnapshot } from "../takomi-runtime/model-routing-defaults";
5
6
  import { resolveAgentName } from "./agent-aliases";
6
7
  import { discoverTakomiAgents, type TakomiAgentConfig, type TakomiAgentScope } from "./agents";
7
8
  import { createTakomiDelegationPlan, renderTakomiDelegationPlan } from "./delegation-plan";
@@ -202,10 +203,11 @@ export async function executeTakomiSubagentTool(
202
203
  const agents = discoverTakomiAgents(rootCwd, agentScope);
203
204
  const byName = new Map<string, TakomiAgentConfig>(agents.map((agent) => [agent.name, agent]));
204
205
  const mode = resolveMode(params);
205
- const tasks = resolveTasks(params).map((task) => ({
206
+ const routingSnapshot = await loadTakomiModelRoutingSnapshot(rootCwd);
207
+ const tasks = resolveTasks(params).map((task) => applyTakomiRoutingDefaults({
206
208
  ...task,
207
209
  agent: resolveAgentName(task.agent, byName),
208
- }));
210
+ }, routingSnapshot));
209
211
 
210
212
  if (!mode) {
211
213
  return textResult(
package/.pi/settings.json CHANGED
@@ -5,41 +5,39 @@
5
5
  },
6
6
  "subagents": {
7
7
  "agentOverrides": {
8
- "oracle": {
9
- "model": "oauth-router/gpt-5.5",
10
- "thinking": "high"
8
+ "general": {
9
+ "model": "oauth-router/gpt-5.4",
10
+ "thinking": "high",
11
+ "fallbackModels": [
12
+ "oauth-router/gpt-5.5:low"
13
+ ]
11
14
  },
12
- "reviewer": {
15
+ "orchestrator": {
13
16
  "model": "oauth-router/gpt-5.5",
14
17
  "thinking": "high"
15
18
  },
16
- "planner": {
19
+ "architect": {
17
20
  "model": "oauth-router/gpt-5.5",
18
- "thinking": "medium"
21
+ "thinking": "high"
19
22
  },
20
- "worker": {
23
+ "designer": {
21
24
  "model": "oauth-router/gpt-5.4",
22
25
  "thinking": "high",
23
26
  "fallbackModels": [
24
27
  "oauth-router/gpt-5.5:low"
25
28
  ]
26
29
  },
27
- "contextBuilder": {
30
+ "coder": {
28
31
  "model": "oauth-router/gpt-5.4",
29
- "thinking": "high"
30
- },
31
- "context-builder": {
32
- "model": "oauth-router/gpt-5.4",
33
- "thinking": "high"
34
- },
35
- "scout": {
36
- "model": "oauth-router/gpt-5.4-mini",
37
- "thinking": "high"
32
+ "thinking": "high",
33
+ "fallbackModels": [
34
+ "oauth-router/gpt-5.5:low"
35
+ ]
38
36
  },
39
- "delegate": {
40
- "model": "oauth-router/gpt-5.4-mini",
37
+ "reviewer": {
38
+ "model": "oauth-router/gpt-5.5",
41
39
  "thinking": "high"
42
40
  }
43
41
  }
44
42
  }
45
- }
43
+ }
package/README.md CHANGED
@@ -29,6 +29,11 @@ Optional global skills:
29
29
 
30
30
  ```bash
31
31
  takomi setup skills
32
+ # First install defaults to Core Skills.
33
+ # Repeat installs default to Leave As Is so existing selections are not pruned accidentally.
34
+ # Custom opens a color-coded category TUI with expandable skill rows when a TTY is available.
35
+ # Optional Pi feature packs can also be managed later:
36
+ takomi setup pi-features
32
37
  ```
33
38
 
34
39
  Useful management commands:
@@ -43,6 +48,8 @@ takomi setup project
43
48
 
44
49
  Legacy commands like `takomi install pi`, `takomi sync pi`, `takomi upgrade`, and `takomi init` still work, but the simpler mental model is: **setup once, refresh when stale, run `takomi` to use it.**
45
50
 
51
+ During `takomi setup pi` or `takomi setup pi-features`, Takomi offers optional Pi feature packs with recommended/manual/select-all/skip choices. Current defaults install **Takomi Interview** (`npm:@juicesharp/rpiv-ask-user-question`) so models can ask structured clarification questions. **Takomi Todo** (`npm:@juicesharp/rpiv-todo`), **Takomi Browser QA** (`npm:pi-chrome`), and **Takomi Doc Preview** (`npm:pi-markdown-preview`) remain opt-in. `takomi refresh` runs Pi's package updater so installed optional, custom, old, and new Pi packages are reconciled together.
52
+
46
53
  ### Context Manager
47
54
 
48
55
  Takomi now ships a Pi-native `takomi-context-manager` extension. It reduces prompt bloat with progressive context loading:
@@ -69,7 +76,8 @@ npx takomi install
69
76
  What happens next:
70
77
  - 🔍 **Auto-detects** every AI harness on your machine
71
78
  - 📦 **Creates your command center** at `~/.takomi/`
72
- - 📡 **Syncs your entire toolkit** to every IDE automatically
79
+ - 🧰 **Lets you choose Core, Present Custom, Custom, All, or Leave As Is for skills**
80
+ - 📡 **Syncs your selected toolkit** to every IDE automatically
73
81
  - 🔄 **Keeps KiloCode in sync** across CLI and VS Code
74
82
 
75
83
  ### Option B: Per-Project Setup
@@ -118,7 +126,9 @@ Original Takomi-authored skills in this bundle include `21st-dev-components`, `t
118
126
 
119
127
  Think of skills as specialized team members you can summon on demand. From security audits to AI video generation — there's a skill for that.
120
128
 
121
- The published bundle currently ships **69 top-level skills**, including the original `21st-dev-components` workflow for guided 21st.dev integration. The `pr-comment-fix` skill is sourced from https://gist.github.com/GSonofNun/35c67304c35dac7d6b43308b5371f671.
129
+ Takomi's own installer no longer installs every bundled skill by default. `takomi setup skills` uses **Core Skills** for first-time installs, **Leave As Is** for repeat installs, and ownership-safe cleanup when you explicitly deselect Takomi-managed skills. Manual/user-added skills are preserved. Custom selection opens a color-coded category browser with expandable rows on capable terminals, and falls back to simple prompts elsewhere. The global store/harness sync path uses the same ownership model so deselected Takomi-managed skills and workflows can be pruned from `~/.takomi/` and synced harness folders without touching manual or imported resources.
130
+
131
+ The published bundle currently ships **72 top-level skills**, including the original `21st-dev-components` workflow for guided 21st.dev integration. The `pr-comment-fix` skill is sourced from https://gist.github.com/GSonofNun/35c67304c35dac7d6b43308b5371f671.
122
132
 
123
133
  ### Interactive Search & Install
124
134
  ```bash
@@ -130,7 +140,22 @@ npx -y skills add https://github.com/JStaRFilms/VibeCode-Protocol-Suite
130
140
  ```
131
141
 
132
142
  ### Core Essentials (Start Here)
133
- The non-negotiables for daily development:
143
+ The recommended Takomi installer defaults are:
144
+
145
+ ```txt
146
+ takomi
147
+ sync-docs
148
+ code-review
149
+ security-audit
150
+ optimize-agent-context
151
+ agent-recovery
152
+ avoid-feature-creep
153
+ ai-sdk
154
+ git-commit-generation
155
+ ```
156
+
157
+ For the external `skills` CLI, install only the router skill if you want the smallest possible footprint:
158
+
134
159
  ```bash
135
160
  npx -y skills add https://github.com/JStaRFilms/VibeCode-Protocol-Suite --skill takomi
136
161
  ```
@@ -466,8 +491,9 @@ This is a living system. If you discover improvements:
466
491
 
467
492
  ## 🙏 Acknowledgements
468
493
 
469
- Externally sourced skills in this bundle retain credit to their upstream creators. Takomi-original skills and workflows in this repository, including `21st-dev-components`, remain authored and maintained by J StaR Films Studios.
494
+ Externally sourced skills and optional Pi packages retain credit to their upstream creators. Takomi-original skills, workflows, and Takomi runtime/orchestration extensions in this repository, including `21st-dev-components`, remain authored and maintained by J StaR Films Studios.
470
495
 
496
+ - **Pi Coding Agent**: Built for [Pi](https://github.com/earendil-works/pi) / `@earendil-works/pi-coding-agent` by **Mario Zechner** and Earendil Works — the MIT-licensed coding-agent runtime and extension API that powers the Pi-native Takomi harness.
471
497
  - **Anthropic Skills**: From [anthropics/skills](https://github.com/anthropics/skills) — a massive collection including **Office Suite** (PDF/DOCX/PPTX/XLSX), **Frontend Design**, **Webapp Testing**, **Algorithmic Art**, **Monorepo Management**, and **Skill Creator**.
472
498
  - **Inference.sh Skills**: From [inference.sh/skills](https://github.com/inference-sh/skills) — complete media & automation suite including **Marketing Videos**, **Voice Cloning**, **Social Content**, **Twitter Automation**, **Product Photography**, and **Prompt Engineering**.
473
499
  - **Marketing Skills**: From [coreyhaines31/marketingskills](https://github.com/coreyhaines31/marketingskills) — the complete marketer's toolkit: **Copywriting**, **Pricing Strategy**, **Social Strategy**, **Programmatic SEO**, and **Marketing Ideas**.
@@ -483,6 +509,10 @@ Externally sourced skills in this bundle retain credit to their upstream creator
483
509
  - **Google Stitch Skills**: From [google-labs-code/stitch-skills](https://github.com/google-labs-code/stitch-skills) — Design-to-code suite including **design-md**, **enhance-prompt**, **stitch-loop**, **react-components**, and **shadcn-ui**.
484
510
  - **Jules**: From [sanjay3290/ai-skills](https://github.com/sanjay3290/ai-skills) — delegate coding tasks to Google Jules AI agent.
485
511
  - **Subagent Execution**: Built on **[`pi-subagents`](https://github.com/nicobailon/pi-subagents)** by **Nico Bailon** — providing the underlying Pi extension for delegated subagent runs (result rendering, live progress, single/parallel/chain execution, session/artifact handling, and related subagent tooling), upon which Takomi adds its own lifecycle orchestration, model-routing policy, and workflow metadata.
512
+ - **Takomi Interview / Todo Optional Packs**: Optional setup integrates **[`@juicesharp/rpiv-ask-user-question`](https://github.com/juicesharp/rpiv-mono/tree/main/packages/rpiv-ask-user-question)** and **[`@juicesharp/rpiv-todo`](https://github.com/juicesharp/rpiv-mono/tree/main/packages/rpiv-todo)** by **juicesharp** — MIT-licensed Pi extensions for structured user questions and live todo overlays.
513
+ - **Takomi Browser QA Optional Pack**: Optional setup can install **[`pi-chrome`](https://github.com/tianrendong/pi-chrome)** by **tianrendong** — an MIT-licensed Pi extension for explicitly authorized Chrome/browser automation.
514
+ - **Takomi Doc Preview Optional Pack**: Optional setup can install **[`pi-markdown-preview`](https://github.com/omaclaren/pi-markdown-preview)** by **omaclaren** — an MIT-licensed Pi extension for terminal/browser/PDF markdown and LaTeX previews.
515
+ - **Context Mode Research / Optional Power User Tooling**: **[`context-mode`](https://github.com/mksglu/context-mode)** by **Mert Koseoğlu** is credited as an external context-window and session-continuity project. It is not bundled as a Takomi default; users can install/evaluate it separately under its own license.
486
516
  - **Git Commit Generation**: From the **[`kilocode`](https://github.com/Kilo-Org/kilocode)** repository by **Kilo-Org** (specifically, [git-commit-generation.md](https://github.com/Kilo-Org/kilocode/blob/main/packages/kilo-docs/pages/code-with-ai/features/git-commit-generation.md)) — enabling automated, high-quality conventional git commit messages based on staged changes.
487
517
 
488
518
  ## 📄 License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "takomi",
3
- "version": "2.1.26",
3
+ "version": "2.1.28",
4
4
  "description": "🎯 Stop wrestling with AI. Start building with purpose. The artisan's toolkit for agent workflows, Codex skills, and original Takomi capabilities like 21st.dev integration.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,7 +22,9 @@
22
22
  ".pi/takomi/policies"
23
23
  ],
24
24
  "scripts": {
25
- "test": "echo \"Error: no test specified\" && exit 1"
25
+ "postinstall": "node src/postinstall.js",
26
+ "test": "echo \"Error: no test specified\" && exit 1",
27
+ "test:skills": "node scripts/test-skill-selection.js"
26
28
  },
27
29
  "repository": {
28
30
  "type": "git",