@iseoane/agent-depot 0.5.2 → 0.6.1

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.es.md +137 -34
  3. package/README.md +120 -29
  4. package/dist/src/app-flow.js +56 -24
  5. package/dist/src/profile-export.js +72 -9
  6. package/dist/src/profile-import.js +3 -2
  7. package/dist/src/profile.js +71 -7
  8. package/dist/src/tui/app-actions.js +82 -33
  9. package/dist/src/tui/app.js +19 -14
  10. package/dist/src/tui/catalog-browse.js +6 -1
  11. package/dist/src/tui/catalog-panel.js +11 -11
  12. package/dist/src/tui/catalog-state.js +28 -0
  13. package/dist/src/tui/catalog-tree.js +22 -5
  14. package/dist/src/tui/catalog-view.js +59 -16
  15. package/dist/src/tui/file-picker-model.js +87 -0
  16. package/dist/src/tui/file-picker.js +142 -0
  17. package/dist/src/tui/header-layout.js +33 -0
  18. package/dist/src/tui/installations-panel.js +3 -3
  19. package/dist/src/tui/installations-rows.js +20 -5
  20. package/dist/src/tui/installations-tree.js +4 -7
  21. package/dist/src/tui/installations-view.js +15 -6
  22. package/dist/src/tui/installations.js +9 -7
  23. package/dist/src/tui/manage-panel.js +7 -6
  24. package/dist/src/tui/mode-keys.js +9 -0
  25. package/dist/src/tui/package-rows.js +17 -5
  26. package/dist/src/tui/panel-parts.js +119 -6
  27. package/dist/src/tui/preview-window.js +51 -0
  28. package/dist/src/tui/profile-view.js +394 -136
  29. package/dist/src/tui/sources-mode.js +3 -1
  30. package/dist/src/tui/sources-recipes.js +50 -19
  31. package/dist/src/tui/sources-view.js +50 -14
  32. package/dist/src/tui/text-wrap.js +53 -0
  33. package/dist/src/tui/theme.js +14 -7
  34. package/dist/src/tui/updates-flow.js +2 -2
  35. package/dist/src/tui/updates-keys.js +34 -5
  36. package/dist/src/tui/updates-model.js +2 -0
  37. package/dist/src/tui/updates-panel.js +40 -15
  38. package/dist/src/tui/updates-preview.js +65 -0
  39. package/dist/src/tui/updates-view.js +17 -2
  40. package/dist/src/tui/updates.js +14 -5
  41. package/dist/src/tui/window.js +8 -6
  42. package/package.json +6 -6
  43. package/skills/doctor-md-agents/README.md +3 -1
  44. package/skills/doctor-md-skill/README.md +2 -2
@@ -1,7 +1,8 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, Text } from "ink";
2
+ import { Box, Text, useWindowSize } from "ink";
3
3
  import { useCallback, useEffect, useRef, useState } from "react";
4
4
  import { theme } from "./theme.js";
5
+ import { previewViewport } from "./preview-window.js";
5
6
  import { computeWindow, useListHeight } from "./window.js";
6
7
  import { BROWSE } from "./updates-mode.js";
7
8
  import { useMirrored } from "./view-state.js";
@@ -9,12 +10,16 @@ import { useUpdatesFlow } from "./updates-flow.js";
9
10
  import { handleUpdatesKey } from "./updates-keys.js";
10
11
  import { viewOf } from "./updates-model.js";
11
12
  import { CheckStatus, EntryLine, ModePanel, UpdatesFooter } from "./updates-panel.js";
13
+ import { previewLineText } from "./updates.js";
14
+ import { updateModePreviewChrome, updateModePreviewLines } from "./updates-preview.js";
12
15
  import { useKeys } from "./keys.js";
16
+ import { BorderPanel, FieldLine } from "./panel-parts.js";
13
17
  const NO_ENVIRONMENT = {};
14
18
  /** How often the age of the check is redrawn. */
15
19
  const CLOCK_TICK_MS = 10_000;
16
20
  export function UpdatesView({ operations, environment, onCapturingChange, listHeight, now }) {
17
21
  const height = useListHeight(listHeight, 9);
22
+ const { rows: terminalRows, columns: terminalColumns } = useWindowSize();
18
23
  const env = environment ?? NO_ENVIRONMENT;
19
24
  const clock = now ?? Date.now;
20
25
  // Scope, cursor, fold and selection are mirrored in refs updated first, so keys delivered in one burst act on the latest state.
@@ -74,8 +79,17 @@ export function UpdatesView({ operations, environment, onCapturingChange, listHe
74
79
  dataRef.current = data;
75
80
  const { shown, updatable, unknown, upToDate, entries } = viewOf(data, scope, showUnknown);
76
81
  const index = Math.min(cursor, Math.max(entries.length - 1, 0));
82
+ const previewGeometry = (current) => {
83
+ const cursor = "cursor" in current ? current.cursor : 0;
84
+ return previewViewport(updateModePreviewLines(current).map(previewLineText), cursor, terminalRows || 24, terminalColumns || 80, updateModePreviewChrome(current));
85
+ };
77
86
  const keyContext = {
78
87
  height,
88
+ previewMetrics: current => {
89
+ const geometry = previewGeometry(current);
90
+ return { length: geometry.total, pageSize: geometry.height };
91
+ },
92
+ previewAtEnd: current => previewGeometry(current).reachedEnd,
79
93
  latest,
80
94
  checked: () => checkedRef.current,
81
95
  setChecked,
@@ -97,5 +111,6 @@ export function UpdatesView({ operations, environment, onCapturingChange, listHe
97
111
  const window = computeWindow(entries.length, index, height);
98
112
  const view = { shown, updatable, unknown, upToDate, entries };
99
113
  const hasErrors = data?.appError !== undefined || (data?.results ?? []).some((result) => result.error !== undefined);
100
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: theme.accent, bold: true, children: ["Updates (scope: ", scope, ") t all \u00B7 p project \u00B7 g user-global"] }), state.status === "loading" ? _jsx(Text, { children: state.label }) : null, data ? _jsx(CheckStatus, { data: data, now: clock() }) : null, data && !hasErrors && shown.length === 0 ? _jsx(Text, { children: "No installations" }) : null, entries.slice(window.start, window.end).map((entry, offset) => (_jsx(EntryLine, { entry: entry, selected: window.start + offset === index, checked: entry.kind === "update" && checked.includes(entry.row.key), view: view, showUnknown: showUnknown }, entry.kind === "summary" ? "summary" : entry.row.key))), _jsx(UpdatesFooter, { indicator: window.indicator, upToDate: upToDate, updatableCount: updatable.length, checkedCount: checked.length, message: message }), _jsx(ModePanel, { mode: mode })] }));
114
+ const showingBrowse = mode.kind === "browse";
115
+ return (_jsxs(Box, { flexDirection: "column", children: [showingBrowse ? _jsxs(Text, { color: theme.title, bold: true, children: ["Updates (scope: ", scope, ") t all \u00B7 p project \u00B7 g user-global \u00B7 u update"] }) : null, state.status === "loading" ? _jsx(BorderPanel, { title: "Update check", children: _jsx(FieldLine, { label: "Status", value: state.label }) }) : null, showingBrowse && data ? _jsx(CheckStatus, { data: data, now: clock() }) : null, showingBrowse && data && !hasErrors && shown.length === 0 ? _jsx(Text, { children: "No installations" }) : null, showingBrowse ? entries.slice(window.start, window.end).map((entry, offset) => (_jsx(EntryLine, { entry: entry, selected: window.start + offset === index, checked: entry.kind === "update" && checked.includes(entry.row.key), view: view, showUnknown: showUnknown }, entry.kind === "summary" ? "summary" : entry.row.key))) : null, showingBrowse ? _jsx(UpdatesFooter, { indicator: window.indicator, upToDate: upToDate, updatableCount: updatable.length, checkedCount: checked.length, message: message }) : null, _jsx(ModePanel, { mode: mode })] }));
101
116
  }
@@ -111,7 +111,7 @@ export async function loadUpdateRows(operations, environment, options) {
111
111
  catch (error) {
112
112
  appError = errorText(error);
113
113
  }
114
- for (const check of appChecks) {
114
+ for (const check of appChecks.filter(candidate => candidate.inspection.status === "installed")) {
115
115
  rows.push({
116
116
  kind: "app",
117
117
  key: `app:${check.entry.file}`,
@@ -161,6 +161,12 @@ export function formatAgo(elapsedMs) {
161
161
  return `${minutes} min ago`;
162
162
  return `${Math.floor(minutes / 60)} h ago`;
163
163
  }
164
+ export function previewText(text) {
165
+ return { kind: "text", text };
166
+ }
167
+ export function previewLineText(line) {
168
+ return line.kind === "field" ? `${line.label} ${line.value}` : line.text;
169
+ }
164
170
  /** Plan step: previews the selection from the assessed snapshots, scope by scope. Changes nothing. */
165
171
  export async function prepareUpdates(data, selected) {
166
172
  const groups = [];
@@ -176,7 +182,7 @@ export async function prepareUpdates(data, selected) {
176
182
  continue;
177
183
  const { context } = loaded;
178
184
  const outcome = await previewUpdateCandidates(items, context);
179
- lines.push(...outcome.plans.flatMap((plan) => describeUpdatePreview(plan, scope, context.projectRoot, "prompt")));
185
+ lines.push(...outcome.plans.flatMap((plan) => describeUpdatePreview(plan, scope, context.projectRoot, "prompt").map(previewText)));
180
186
  failures.push(...outcome.failures);
181
187
  groups.push({
182
188
  scope,
@@ -192,7 +198,10 @@ export async function prepareUpdates(data, selected) {
192
198
  try {
193
199
  const plan = await data.apps.planLifecycle(row.check.entry, "update", undefined, row.check);
194
200
  appPlans.push(plan);
195
- lines.push(`Environment: ${plan.environment}`, row.path, `Version: ${plan.previousVersion} -> ${plan.latestVersion ?? "unknown"}`, ...(plan.argv ? [`argv: ${JSON.stringify(plan.argv)}`, `Executable: ${plan.executable ?? `${JSON.stringify(plan.argv[0])} not found in PATH`}`] : []), ...(plan.manual ? [`Manual: ${plan.manual}`] : []), `Working directory: ${plan.cwd}`, ...(plan.warning ? [plan.warning] : []));
201
+ lines.push({ kind: "field", label: "Environment:", value: plan.environment }, previewText(row.path), { kind: "field", label: "Version:", value: `${plan.previousVersion} -> ${plan.latestVersion ?? "unknown"}` }, ...(plan.argv ? [
202
+ { kind: "field", label: "argv:", value: JSON.stringify(plan.argv) },
203
+ { kind: "field", label: "Executable:", value: plan.executable ?? `${JSON.stringify(plan.argv[0])} not found in PATH` },
204
+ ] : []), ...(plan.manual ? [{ kind: "field", label: "Manual:", value: plan.manual }] : []), { kind: "field", label: "Working directory:", value: plan.cwd }, ...(plan.warning ? [previewText(plan.warning)] : []));
196
205
  }
197
206
  catch (error) {
198
207
  appFailures.push(`Failed ${row.path}: preview failed: ${errorText(error)}`);
@@ -205,9 +214,9 @@ export async function prepareUpdates(data, selected) {
205
214
  const plan = await data.packages.planLifecycle(row.check.installed.selection, "update");
206
215
  const approval = await data.packages.approvalStatus(row.check.installed.selection, "update");
207
216
  packagePlans.push({ plan, approval });
208
- lines.push(...packagePlanLines(plan, approval));
217
+ lines.push(...packagePlanLines(plan, approval).map(previewText));
209
218
  if (approval === "needs approval")
210
- lines.push("This apply approves the declaration above.");
219
+ lines.push(previewText("This apply approves the declaration above."));
211
220
  }
212
221
  catch (error) {
213
222
  packageFailures.push(`Failed ${row.path}: preview failed: ${errorText(error)}`);
@@ -1,9 +1,10 @@
1
1
  import { useWindowSize } from "ink";
2
+ import { headerTabRowCount } from "./header-layout.js";
2
3
  /** Rows assumed when the terminal size is unknown (for example under test renderers). */
3
4
  export const FALLBACK_TERMINAL_ROWS = 24;
4
- /** Lines the shell itself uses: title, tab bar, separator and footer hint. */
5
- export const APP_CHROME_ROWS = 4;
6
- const MINIMUM_ROWS = 3;
5
+ /** Header borders, title, one tab row and footer; extra tab rows are added per terminal width. */
6
+ export const APP_CHROME_ROWS = 5;
7
+ const DEFAULT_MINIMUM_ROWS = 3;
7
8
  /** The slice of a list that fits in `height` rows while keeping `selected` visible (roughly centred). */
8
9
  export function computeWindow(total, selected, height) {
9
10
  const size = Math.max(Math.floor(height), 1);
@@ -22,9 +23,10 @@ export function pageStep(height) {
22
23
  * Rows available to a list: the terminal height minus the shell chrome and the
23
24
  * view's own `reserved` lines. `override` fixes the height for tests.
24
25
  */
25
- export function useListHeight(override, reserved) {
26
- const { rows } = useWindowSize();
26
+ export function useListHeight(override, reserved, minimumRows = DEFAULT_MINIMUM_ROWS) {
27
+ const { rows, columns } = useWindowSize();
27
28
  if (override !== undefined)
28
29
  return Math.max(override, 1);
29
- return Math.max((rows || FALLBACK_TERMINAL_ROWS) - APP_CHROME_ROWS - reserved, MINIMUM_ROWS);
30
+ const chromeRows = APP_CHROME_ROWS + headerTabRowCount(Math.max(columns - 4, 1)) - 1;
31
+ return Math.max((rows || FALLBACK_TERMINAL_ROWS) - chromeRows - reserved, minimumRows);
30
32
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iseoane/agent-depot",
3
- "version": "0.5.2",
3
+ "version": "0.6.1",
4
4
  "description": "A cross-platform catalog for reusable coding-agent skills.",
5
5
  "license": "MIT",
6
6
  "author": "iseoane",
@@ -28,8 +28,6 @@
28
28
  "!dist/src/**/*.map",
29
29
  "resources/pi",
30
30
  "skills",
31
- "!skills/**/test_*.py",
32
- "!skills/**/testdata",
33
31
  "README.md",
34
32
  "README.es.md",
35
33
  "LICENSE",
@@ -82,12 +80,14 @@
82
80
  "audit:dupes": "npx --yes fallow@3.30.0 dupes",
83
81
  "audit:health": "pnpm test:coverage && npx --yes fallow@3.30.0 health --coverage coverage/v8 --min-score 85",
84
82
  "audit:all": "pnpm audit:dead-code && pnpm audit:dupes && pnpm audit:health",
85
- "test:e2e": "pnpm build && node --test --test-timeout=120000 \"dist/tests/e2e/*.e2e.test.js\"",
83
+ "test:e2e": "pnpm build && node --import ./dist/tests/isolated-env.js --test --test-timeout=120000 \"dist/tests/e2e/*.e2e.test.js\"",
86
84
  "prepublishOnly": "pnpm build && pnpm test",
87
- "test:pack": "pnpm build && node scripts/pack-smoke.mjs"
85
+ "test:pack": "pnpm build && node scripts/pack-smoke.mjs",
86
+ "test:skills": "node scripts/test-skills.mjs"
88
87
  },
89
88
  "dependencies": {
90
89
  "ink": "^7.1.1",
91
- "react": "^19.3.0"
90
+ "react": "^19.3.0",
91
+ "string-width": "^8.3.0"
92
92
  }
93
93
  }
@@ -44,8 +44,10 @@ Cada cita del juez se verifica mecánicamente contra la transcripción por subca
44
44
 
45
45
  ## Tests
46
46
 
47
+ Desde la raíz del repositorio Agent Depot, ejecuta:
48
+
47
49
  ```bash
48
- python3 -m unittest discover -s scripts -p 'test_*.py'
50
+ pnpm test:skills
49
51
  ```
50
52
 
51
53
  Sin dependencias: stdlib de Python 3.9+.
@@ -20,8 +20,8 @@ ln -s /path/to/doctor-md-skill ~/.agents/skills/doctor-md-skill
20
20
 
21
21
  ## Run the tests
22
22
 
23
- Stdlib `unittest` only, Python 3.9+, no network access required:
23
+ The tests use Python 3.9+ and the standard library. They need no network access. From the Agent Depot repository root, run:
24
24
 
25
25
  ```bash
26
- python3 -m unittest discover -s scripts -p 'test_*.py'
26
+ pnpm test:skills
27
27
  ```