@askrjs/cli 0.0.15 → 0.0.16

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.
@@ -1,6 +1,6 @@
1
1
  import { t as inspectBundledSkills } from "./skills-CSGdAZHN.js";
2
2
  import { discoverWorkspaceProject } from "./discovery-DUDrZCIC.js";
3
- import { analysisHasBlockingFindings, runAnalysis } from "./runner-BSs9Ow3t.js";
3
+ import { analysisHasBlockingFindings, runAnalysis } from "./runner-42nqhW9u.js";
4
4
  import fs from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  import { spawn } from "node:child_process";
@@ -229,16 +229,38 @@ async function runCheck(options, runtime = {}) {
229
229
  const selected = VALIDATION_SCRIPTS.filter((name) => scripts[name]);
230
230
  const manager = managerCommand(await existingLockfiles(project.root), rootWorkspace.manifest);
231
231
  const results = [];
232
- if (analysisHasBlockingFindings(analysis)) for (const name of selected) results.push({
232
+ const selectedChecks = [...Boolean(await fs.stat(path.join(project.root, "database", "index.ts")).catch(() => null)) ? ["database"] : [], ...selected];
233
+ if (analysisHasBlockingFindings(analysis)) for (const name of selectedChecks) results.push({
233
234
  name,
234
235
  status: "skipped",
235
- command: `${manager.executable} run ${name}`,
236
+ command: name === "database" ? "askr database validate" : `${manager.executable} run ${name}`,
236
237
  exitCode: null,
237
238
  stdout: "",
238
239
  stderr: "",
239
240
  reason: "static analysis must pass first"
240
241
  });
241
- else for (const [index, name] of selected.entries()) {
242
+ else for (const [index, name] of selectedChecks.entries()) {
243
+ if (name === "database") {
244
+ const result = await (runtime.runDatabaseValidation ?? (await import("./database.js")).runDatabaseValidation)(project.root);
245
+ results.push({
246
+ name,
247
+ command: "askr database validate",
248
+ ...result
249
+ });
250
+ if (result.status === "failed") {
251
+ for (const skipped of selectedChecks.slice(index + 1)) results.push({
252
+ name: skipped,
253
+ status: "skipped",
254
+ command: `${manager.executable} run ${skipped}`,
255
+ exitCode: null,
256
+ stdout: "",
257
+ stderr: "",
258
+ reason: "database validation failed"
259
+ });
260
+ break;
261
+ }
262
+ continue;
263
+ }
242
264
  const args = manager.args(name);
243
265
  const result = await (runtime.runScript ?? defaultRunScript)(manager.executable, args, project.root);
244
266
  results.push({
@@ -247,7 +269,7 @@ async function runCheck(options, runtime = {}) {
247
269
  ...result
248
270
  });
249
271
  if (result.status === "failed") {
250
- for (const skipped of selected.slice(index + 1)) results.push({
272
+ for (const skipped of selectedChecks.slice(index + 1)) results.push({
251
273
  name: skipped,
252
274
  status: "skipped",
253
275
  command: `${manager.executable} run ${skipped}`,
@@ -34,9 +34,7 @@ Use this when the app renders outside the browser or produces static output. The
34
34
  ## Copy This Shape
35
35
 
36
36
  ```ts
37
- import { createRouteRegistry } from "@askrjs/askr/router";
38
-
39
- const registry = createRouteRegistry(() => {
37
+ registerRoutes(() => {
40
38
  page("/docs/{slug}", DocsPage, {
41
39
  entries: async () => [{ slug: "getting-started" }, { slug: "routing" }],
42
40
  });
@@ -6,7 +6,6 @@ import {
6
6
  SettingsIcon,
7
7
  SunIcon,
8
8
  } from '@askrjs/lucide';
9
- import { For } from '@askrjs/askr/control';
10
9
  import { Link, navigate } from '@askrjs/askr/router';
11
10
  import { Button } from '@askrjs/themes/components';
12
11
  import { Container, Inline, Stack } from '@askrjs/themes/components';
@@ -43,16 +42,14 @@ export default function AppLayout({ children }: { children?: unknown }) {
43
42
  </Link>
44
43
  </NavBrand>
45
44
  <NavGroup label="Workspace">
46
- <For each={[...appNavItems]} by={(item) => item.href}>
47
- {(item) => (
48
- <NavLink href={item.href} match={item.match}>
49
- <Inline as="span" gap="2" align="center">
50
- {icons[item.icon]}
51
- <span>{item.label}</span>
52
- </Inline>
53
- </NavLink>
54
- )}
55
- </For>
45
+ {appNavItems.map((item) => (
46
+ <NavLink href={item.href} match={item.match}>
47
+ <Inline as="span" gap="2" align="center">
48
+ {icons[item.icon]}
49
+ <span>{item.label}</span>
50
+ </Inline>
51
+ </NavLink>
52
+ ))}
56
53
  </NavGroup>
57
54
  <NavGroup label="Session" align="end">
58
55
  <NavLink href="/" match="exact">
@@ -1,5 +1,4 @@
1
1
  import { resource } from '@askrjs/askr/resources';
2
- import { For, Show } from '@askrjs/askr/control';
3
2
  import { createPlot } from '@askrjs/charts';
4
3
  import { AlertCircleIcon, RefreshCwIcon } from '@askrjs/lucide';
5
4
  import { Button } from '@askrjs/themes/components';
@@ -27,154 +26,145 @@ export default function AdminHomePage() {
27
26
  const operations = resource(({ signal }) => loadOperations({ signal }), []);
28
27
  const snapshot = operations.value;
29
28
 
30
- const errorState = (
31
- <Section>
32
- <EmptyState
33
- icon={<AlertCircleIcon size={24} aria-hidden="true" />}
34
- title="Operations could not load"
35
- description="The dashboard keeps failures recoverable. Retry the owning resource instead of hiding the error in a toast."
36
- actions={<Button onPress={() => operations.refresh()}>Retry</Button>}
37
- />
38
- </Section>
39
- );
29
+ if (operations.error && !snapshot) {
30
+ return (
31
+ <Section>
32
+ <EmptyState
33
+ icon={<AlertCircleIcon size={24} aria-hidden="true" />}
34
+ title="Operations could not load"
35
+ description="The dashboard keeps failures recoverable. Retry the owning resource instead of hiding the error in a toast."
36
+ actions={<Button onPress={() => operations.refresh()}>Retry</Button>}
37
+ />
38
+ </Section>
39
+ );
40
+ }
40
41
 
41
42
  return (
42
- <Show when={!operations.error || snapshot} fallback={errorState}>
43
- <Stack gap="5">
44
- <section class="page-heading">
45
- <Stack gap="2">
46
- <Badge>projection v{snapshot?.version ?? '...'}</Badge>
47
- <h1>Workspace home</h1>
48
- <p class="lead">
49
- A consistency-aware dashboard for agent runs, queue health, and
50
- event-sourced read models.
51
- </p>
52
- </Stack>
53
- <Inline gap="2" align="center">
54
- {operations.pending && snapshot ? <Badge>refreshing</Badge> : null}
55
- <Button variant="secondary" onPress={() => operations.refresh()}>
56
- <RefreshCwIcon size={14} aria-hidden="true" /> Refresh
57
- </Button>
58
- </Inline>
59
- </section>
43
+ <Stack gap="5">
44
+ <section class="page-heading">
45
+ <Stack gap="2">
46
+ <Badge>projection v{snapshot?.version ?? '...'}</Badge>
47
+ <h1>Workspace home</h1>
48
+ <p class="lead">
49
+ A consistency-aware dashboard for agent runs, queue health, and
50
+ event-sourced read models.
51
+ </p>
52
+ </Stack>
53
+ <Inline gap="2" align="center">
54
+ {operations.pending && snapshot ? <Badge>refreshing</Badge> : null}
55
+ <Button variant="secondary" onPress={() => operations.refresh()}>
56
+ <RefreshCwIcon size={14} aria-hidden="true" /> Refresh
57
+ </Button>
58
+ </Inline>
59
+ </section>
60
+
61
+ {operations.pending && !snapshot ? (
62
+ <Block gap="md">
63
+ <Skeleton style="height: 8rem" />
64
+ <Skeleton style="height: 8rem" />
65
+ <Skeleton style="height: 8rem" />
66
+ </Block>
67
+ ) : null}
60
68
 
61
- {operations.pending && !snapshot ? (
62
- <Block gap="md">
63
- <Skeleton style="height: 8rem" />
64
- <Skeleton style="height: 8rem" />
65
- <Skeleton style="height: 8rem" />
69
+ {snapshot ? (
70
+ <>
71
+ <Block gap="md" class="metric-grid">
72
+ {snapshot.metrics.map((metric) => (
73
+ <MetricCard
74
+ label={metric.label}
75
+ value={metric.value}
76
+ trend={metric.trend}
77
+ />
78
+ ))}
66
79
  </Block>
67
- ) : null}
68
80
 
69
- <Show when={snapshot}>
70
- {(currentSnapshot) => (
71
- <>
72
- <Block gap="md" class="metric-grid">
73
- <For
74
- each={currentSnapshot.metrics}
75
- by={(metric) => metric.label}
81
+ <Block gap="md" align="stretch" class="chart-grid">
82
+ <Card>
83
+ <CardHeader>
84
+ <CardTitle>Run throughput</CardTitle>
85
+ <CardDescription>
86
+ Accepted commands by work type.
87
+ </CardDescription>
88
+ </CardHeader>
89
+ <CardContent>
90
+ <OperationsPlot.Root
91
+ data={snapshot.throughput}
92
+ rowKey="label"
93
+ label="Run throughput"
94
+ description="Accepted commands by work type."
76
95
  >
77
- {(metric) => (
78
- <MetricCard
79
- label={metric.label}
80
- value={metric.value}
81
- trend={metric.trend}
82
- />
83
- )}
84
- </For>
85
- </Block>
86
-
87
- <Block gap="md" align="stretch" class="chart-grid">
88
- <Card>
89
- <CardHeader>
90
- <CardTitle>Run throughput</CardTitle>
91
- <CardDescription>
92
- Accepted commands by work type.
93
- </CardDescription>
94
- </CardHeader>
95
- <CardContent>
96
- <OperationsPlot.Root
97
- data={currentSnapshot.throughput}
98
- rowKey="label"
99
- label="Run throughput"
100
- description="Accepted commands by work type."
101
- >
102
- <OperationsPlot.Bar x="label" y="value" />
103
- </OperationsPlot.Root>
104
- </CardContent>
105
- </Card>
106
- <Card>
107
- <CardHeader>
108
- <CardTitle>Projection lag</CardTitle>
109
- <CardDescription>
110
- Lower is better; stale states stay visible.
111
- </CardDescription>
112
- </CardHeader>
113
- <CardContent>
114
- <OperationsPlot.Root
115
- data={currentSnapshot.lag}
116
- rowKey="label"
117
- label="Projection lag"
118
- description="Projection lag over the last hour."
119
- >
120
- <OperationsPlot.Line x="label" y="value" />
121
- <OperationsPlot.Point x="label" y="value" />
122
- </OperationsPlot.Root>
123
- </CardContent>
124
- </Card>
125
- </Block>
96
+ <OperationsPlot.Bar x="label" y="value" />
97
+ </OperationsPlot.Root>
98
+ </CardContent>
99
+ </Card>
100
+ <Card>
101
+ <CardHeader>
102
+ <CardTitle>Projection lag</CardTitle>
103
+ <CardDescription>
104
+ Lower is better; stale states stay visible.
105
+ </CardDescription>
106
+ </CardHeader>
107
+ <CardContent>
108
+ <OperationsPlot.Root
109
+ data={snapshot.lag}
110
+ rowKey="label"
111
+ label="Projection lag"
112
+ description="Projection lag over the last hour."
113
+ >
114
+ <OperationsPlot.Line x="label" y="value" />
115
+ <OperationsPlot.Point x="label" y="value" />
116
+ </OperationsPlot.Root>
117
+ </CardContent>
118
+ </Card>
119
+ </Block>
126
120
 
127
- {currentSnapshot.consistency !== 'fresh' ? (
128
- <Alert variant="warning">
129
- Read models are {currentSnapshot.consistency}. Last processed
130
- event is {currentSnapshot.lastEventId}.
131
- </Alert>
132
- ) : null}
121
+ {snapshot.consistency !== 'fresh' ? (
122
+ <Alert variant="warning">
123
+ Read models are {snapshot.consistency}. Last processed event is{' '}
124
+ {snapshot.lastEventId}.
125
+ </Alert>
126
+ ) : null}
133
127
 
134
- <Card>
135
- <CardHeader>
136
- <CardTitle>Recent agent runs</CardTitle>
137
- <CardDescription>
138
- Run state is modeled as product state, not a single loading
139
- boolean.
140
- </CardDescription>
141
- </CardHeader>
142
- <CardContent>
143
- <div class="run-table-wrap">
144
- <table class="run-table">
145
- <thead>
146
- <tr>
147
- <th>Run</th>
148
- <th>Status</th>
149
- <th>Owner</th>
150
- <th>Updated</th>
151
- </tr>
152
- </thead>
153
- <tbody>
154
- <For each={currentSnapshot.runs} by={(run) => run.id}>
155
- {(run) => (
156
- <tr>
157
- <td>
158
- <strong>{run.title}</strong>
159
- <span>{run.id}</span>
160
- </td>
161
- <td>
162
- <StatusBadge status={run.status} />
163
- </td>
164
- <td>{run.owner}</td>
165
- <td>{formatRelativeTime(run.updatedAt)}</td>
166
- </tr>
167
- )}
168
- </For>
169
- </tbody>
170
- </table>
171
- </div>
172
- </CardContent>
173
- </Card>
174
- </>
175
- )}
176
- </Show>
177
- </Stack>
178
- </Show>
128
+ <Card>
129
+ <CardHeader>
130
+ <CardTitle>Recent agent runs</CardTitle>
131
+ <CardDescription>
132
+ Run state is modeled as product state, not a single loading
133
+ boolean.
134
+ </CardDescription>
135
+ </CardHeader>
136
+ <CardContent>
137
+ <div class="run-table-wrap">
138
+ <table class="run-table">
139
+ <thead>
140
+ <tr>
141
+ <th>Run</th>
142
+ <th>Status</th>
143
+ <th>Owner</th>
144
+ <th>Updated</th>
145
+ </tr>
146
+ </thead>
147
+ <tbody>
148
+ {snapshot.runs.map((run) => (
149
+ <tr>
150
+ <td>
151
+ <strong>{run.title}</strong>
152
+ <span>{run.id}</span>
153
+ </td>
154
+ <td>
155
+ <StatusBadge status={run.status} />
156
+ </td>
157
+ <td>{run.owner}</td>
158
+ <td>{formatRelativeTime(run.updatedAt)}</td>
159
+ </tr>
160
+ ))}
161
+ </tbody>
162
+ </table>
163
+ </div>
164
+ </CardContent>
165
+ </Card>
166
+ </>
167
+ ) : null}
168
+ </Stack>
179
169
  );
180
170
  }
@@ -4,7 +4,6 @@ import {
4
4
  Clock3Icon,
5
5
  ShieldAlertIcon,
6
6
  } from '@askrjs/lucide';
7
- import { For } from '@askrjs/askr/control';
8
7
  import {
9
8
  Badge,
10
9
  Card,
@@ -19,14 +18,12 @@ import StatusBadge, {
19
18
  } from '../../components/shared/status-badge';
20
19
 
21
20
  const runs: Array<{
22
- id: string;
23
21
  title: string;
24
22
  status: RunStatus;
25
23
  event: string;
26
24
  description: string;
27
25
  }> = [
28
26
  {
29
- id: 'reconcile-billing-projection',
30
27
  title: 'Reconcile billing projection',
31
28
  status: 'running',
32
29
  event: 'tool call: compare-ledger',
@@ -34,7 +31,6 @@ const runs: Array<{
34
31
  'Streaming events are appended to the timeline and reconciled by event id.',
35
32
  },
36
33
  {
37
- id: 'approve-enterprise-workspace',
38
34
  title: 'Approve enterprise workspace',
39
35
  status: 'requires-action',
40
36
  event: 'approval requested',
@@ -42,7 +38,6 @@ const runs: Array<{
42
38
  'Human gates are explicit product states, not hidden inside generated text.',
43
39
  },
44
40
  {
45
- id: 'refresh-onboarding-cohort',
46
41
  title: 'Refresh onboarding cohort',
47
42
  status: 'succeeded',
48
43
  event: 'projection caught up',
@@ -66,34 +61,32 @@ export default function AgentRunsPage() {
66
61
  </section>
67
62
 
68
63
  <Block gap="md" class="agent-grid">
69
- <For each={runs} by={(run) => run.id}>
70
- {(run) => (
71
- <Card>
72
- <CardHeader>
73
- <Inline justify="between" align="start" gap="3">
74
- <span class="card-icon">
75
- {run.status === 'succeeded' ? (
76
- <CheckCircle2Icon size={18} aria-hidden="true" />
77
- ) : run.status === 'requires-action' ? (
78
- <ShieldAlertIcon size={18} aria-hidden="true" />
79
- ) : (
80
- <BotIcon size={18} aria-hidden="true" />
81
- )}
82
- </span>
83
- <StatusBadge status={run.status} />
84
- </Inline>
85
- <CardTitle>{run.title}</CardTitle>
86
- <CardDescription>{run.description}</CardDescription>
87
- </CardHeader>
88
- <CardContent>
89
- <Inline gap="2" align="center">
90
- <Clock3Icon size={14} aria-hidden="true" />
91
- <span>{run.event}</span>
92
- </Inline>
93
- </CardContent>
94
- </Card>
95
- )}
96
- </For>
64
+ {runs.map((run) => (
65
+ <Card>
66
+ <CardHeader>
67
+ <Inline justify="between" align="start" gap="3">
68
+ <span class="card-icon">
69
+ {run.status === 'succeeded' ? (
70
+ <CheckCircle2Icon size={18} aria-hidden="true" />
71
+ ) : run.status === 'requires-action' ? (
72
+ <ShieldAlertIcon size={18} aria-hidden="true" />
73
+ ) : (
74
+ <BotIcon size={18} aria-hidden="true" />
75
+ )}
76
+ </span>
77
+ <StatusBadge status={run.status} />
78
+ </Inline>
79
+ <CardTitle>{run.title}</CardTitle>
80
+ <CardDescription>{run.description}</CardDescription>
81
+ </CardHeader>
82
+ <CardContent>
83
+ <Inline gap="2" align="center">
84
+ <Clock3Icon size={14} aria-hidden="true" />
85
+ <span>{run.event}</span>
86
+ </Inline>
87
+ </CardContent>
88
+ </Card>
89
+ ))}
97
90
  </Block>
98
91
  </Stack>
99
92
  );
@@ -5,7 +5,6 @@ import {
5
5
  CheckCircle2Icon,
6
6
  ShieldCheckIcon,
7
7
  } from '@askrjs/lucide';
8
- import { For } from '@askrjs/askr/control';
9
8
  import { Link } from '@askrjs/askr/router';
10
9
  import { Button } from '@askrjs/themes/components';
11
10
  import {
@@ -112,17 +111,15 @@ export default function HomePage() {
112
111
  <Section paddingY="xl">
113
112
  <Container size="xl">
114
113
  <Block gap="md" class="feature-grid">
115
- <For each={capabilities} by={(item) => item.title}>
116
- {(item) => (
117
- <Card>
118
- <CardHeader>
119
- <span class="card-icon">{item.icon}</span>
120
- <CardTitle>{item.title}</CardTitle>
121
- <CardDescription>{item.description}</CardDescription>
122
- </CardHeader>
123
- </Card>
124
- )}
125
- </For>
114
+ {capabilities.map((item) => (
115
+ <Card>
116
+ <CardHeader>
117
+ <span class="card-icon">{item.icon}</span>
118
+ <CardTitle>{item.title}</CardTitle>
119
+ <CardDescription>{item.description}</CardDescription>
120
+ </CardHeader>
121
+ </Card>
122
+ ))}
126
123
  </Block>
127
124
  </Container>
128
125
  </Section>
@@ -1,4 +1,3 @@
1
- import { For } from '@askrjs/askr/control';
2
1
  import { Link } from '@askrjs/askr/router';
3
2
  import { Header } from '@askrjs/themes/components';
4
3
  import {
@@ -36,9 +35,9 @@ export function SiteHeader() {
36
35
  class="navbar-group"
37
36
  data-align="end"
38
37
  >
39
- <For each={[...navItems]} by={(item) => item.href}>
40
- {(item) => <NavLink href={item.href}>{item.label}</NavLink>}
41
- </For>
38
+ {navItems.map((item) => (
39
+ <NavLink href={item.href}>{item.label}</NavLink>
40
+ ))}
42
41
  </Nav>
43
42
  </Box>
44
43
  </Container>
@@ -1,4 +1,3 @@
1
- import { For } from '@askrjs/askr/control';
2
1
  import { Link } from '@askrjs/askr/router';
3
2
  import { Button } from '@askrjs/ui';
4
3
  import {
@@ -55,19 +54,17 @@ export default function Workflow() {
55
54
  />
56
55
 
57
56
  <CardGrid>
58
- <For each={steps} by={(step) => step.number}>
59
- {(step) => (
60
- <Card
61
- eyebrow={step.number}
62
- title={step.title}
63
- description={step.body}
64
- >
65
- <p>
66
- <code>{step.command}</code>
67
- </p>
68
- </Card>
69
- )}
70
- </For>
57
+ {steps.map((step) => (
58
+ <Card
59
+ eyebrow={step.number}
60
+ title={step.title}
61
+ description={step.body}
62
+ >
63
+ <p>
64
+ <code>{step.command}</code>
65
+ </p>
66
+ </Card>
67
+ ))}
71
68
  </CardGrid>
72
69
 
73
70
  <Card
@@ -1,4 +1,3 @@
1
- import { For } from '@askrjs/askr/control';
2
1
  import { Link } from '@askrjs/askr/router';
3
2
  import { Button } from '@askrjs/ui';
4
3
  import {
@@ -51,15 +50,13 @@ export default function Content() {
51
50
  />
52
51
 
53
52
  <CardGrid>
54
- <For each={[...routeMap]} by={(route) => route.path}>
55
- {(route) => (
56
- <Card
57
- eyebrow={route.path}
58
- title={route.title}
59
- description={route.note}
60
- />
61
- )}
62
- </For>
53
+ {routeMap.map((route) => (
54
+ <Card
55
+ eyebrow={route.path}
56
+ title={route.title}
57
+ description={route.note}
58
+ />
59
+ ))}
63
60
  </CardGrid>
64
61
  </>
65
62
  );
@@ -1,4 +1,3 @@
1
- import { For } from '@askrjs/askr/control';
2
1
  import { Link } from '@askrjs/askr/router';
3
2
  import { Button } from '@askrjs/ui';
4
3
  import {
@@ -43,11 +42,9 @@ export default function Home() {
43
42
  />
44
43
 
45
44
  <CardGrid>
46
- <For each={highlights} by={(highlight) => highlight.title}>
47
- {(highlight) => (
48
- <Card title={highlight.title} description={highlight.body} />
49
- )}
50
- </For>
45
+ {highlights.map((highlight) => (
46
+ <Card title={highlight.title} description={highlight.body} />
47
+ ))}
51
48
  </CardGrid>
52
49
  </>
53
50
  );