@askrjs/cli 0.0.10 → 0.0.12
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/README.md +50 -0
- package/dist/add.d.ts +1 -10
- package/dist/add.js +1 -63
- package/dist/analyze.d.ts +88 -0
- package/dist/analyze.js +97 -0
- package/dist/cli.js +16 -0
- package/dist/create.js +1 -1
- package/dist/{discovery-BX-lnFRK.js → discovery-DUDrZCIC.js} +11 -2
- package/dist/file-changes-BAFLhEHZ.js +66 -0
- package/dist/file-changes-cmFN-rF8.d.ts +11 -0
- package/dist/guardrails-GPM9PJqO.js +121 -0
- package/dist/{planner-BDfYDKnI.js → planner-BEfWRd0x.js} +3 -3
- package/dist/runner-BRjjPhZY.js +1495 -0
- package/dist/runner-Ca43qqzG.js +289 -0
- package/dist/skills/askr-ssr-ssg/SKILL.md +2 -0
- package/dist/{skills-C2KzfTl9.js → skills-CSGdAZHN.js} +50 -1
- package/dist/skills.d.ts +12 -1
- package/dist/skills.js +2 -2
- package/dist/ssg.d.ts +1 -0
- package/dist/ssg.js +5 -4
- package/dist/templates/full-stack/AGENTS.md +6 -0
- package/dist/templates/full-stack/package.json +2 -1
- package/dist/templates/spa/AGENTS.md +7 -1
- package/dist/templates/spa/README.md +1 -1
- package/dist/templates/spa/package.json +2 -1
- package/dist/templates/spa/src/main.tsx +1 -1
- package/dist/templates/spa/src/pages/app/_layout.tsx +11 -8
- package/dist/templates/spa/src/pages/app/admin-home.tsx +97 -90
- package/dist/templates/spa/src/pages/app/agent-runs.tsx +33 -26
- package/dist/templates/spa/src/pages/public/home.tsx +12 -9
- package/dist/templates/ssg/AGENTS.md +6 -0
- package/dist/templates/ssg/package.json +2 -1
- package/dist/templates/ssg/src/components/site-shell.tsx +4 -3
- package/dist/templates/ssg/src/pages/about.tsx +14 -11
- package/dist/templates/ssg/src/pages/content.tsx +10 -7
- package/dist/templates/ssg/src/pages/example.tsx +0 -2
- package/dist/templates/ssg/src/pages/home.tsx +6 -3
- package/dist/templates/ssr/AGENTS.md +6 -0
- package/dist/templates/ssr/package.json +2 -1
- package/dist/templates/startkit/AGENTS.md +3 -0
- package/dist/templates/startkit/package.json +2 -1
- package/dist/templates/startkit/src/components/app-sidebar.tsx +23 -18
- package/dist/templates/startkit/src/components/data-table.tsx +8 -6
- package/dist/templates/startkit/src/pages/workspace/dashboard.tsx +16 -13
- package/dist/templates/startkit/src/routes/auth.ts +1 -2
- package/dist/templates/startkit/src/routes/public.ts +1 -2
- package/dist/templates/startkit/src/routes/workspace/accounts.ts +1 -2
- package/dist/templates/startkit/src/routes/workspace/index.ts +2 -3
- package/dist/update.js +1 -1
- package/package.json +7 -6
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resource } from '@askrjs/askr/resources';
|
|
2
|
+
import { For, Show } from '@askrjs/askr/control';
|
|
2
3
|
import { createPlot } from '@askrjs/charts';
|
|
3
4
|
import { AlertCircleIcon, RefreshCwIcon } from '@askrjs/lucide';
|
|
4
5
|
import { Button } from '@askrjs/themes/components';
|
|
@@ -66,105 +67,111 @@ export default function AdminHomePage() {
|
|
|
66
67
|
</Block>
|
|
67
68
|
) : null}
|
|
68
69
|
|
|
69
|
-
{snapshot
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
<Show when={snapshot}>
|
|
71
|
+
{(currentSnapshot) => (
|
|
72
|
+
<>
|
|
73
|
+
<Block gap="md" class="metric-grid">
|
|
74
|
+
<For each={currentSnapshot.metrics} by={(metric) => metric.label}>
|
|
75
|
+
{(metric) => (
|
|
76
|
+
<MetricCard
|
|
77
|
+
label={metric.label}
|
|
78
|
+
value={metric.value}
|
|
79
|
+
trend={metric.trend}
|
|
80
|
+
/>
|
|
81
|
+
)}
|
|
82
|
+
</For>
|
|
83
|
+
</Block>
|
|
84
|
+
|
|
85
|
+
<Block gap="md" align="stretch" class="chart-grid">
|
|
86
|
+
<Card>
|
|
87
|
+
<CardHeader>
|
|
88
|
+
<CardTitle>Run throughput</CardTitle>
|
|
89
|
+
<CardDescription>
|
|
90
|
+
Accepted commands by work type.
|
|
91
|
+
</CardDescription>
|
|
92
|
+
</CardHeader>
|
|
93
|
+
<CardContent>
|
|
94
|
+
<OperationsPlot.Root
|
|
95
|
+
data={currentSnapshot.throughput}
|
|
96
|
+
rowKey="label"
|
|
97
|
+
label="Run throughput"
|
|
98
|
+
description="Accepted commands by work type."
|
|
99
|
+
>
|
|
100
|
+
<OperationsPlot.Bar x="label" y="value" />
|
|
101
|
+
</OperationsPlot.Root>
|
|
102
|
+
</CardContent>
|
|
103
|
+
</Card>
|
|
104
|
+
<Card>
|
|
105
|
+
<CardHeader>
|
|
106
|
+
<CardTitle>Projection lag</CardTitle>
|
|
107
|
+
<CardDescription>
|
|
108
|
+
Lower is better; stale states stay visible.
|
|
109
|
+
</CardDescription>
|
|
110
|
+
</CardHeader>
|
|
111
|
+
<CardContent>
|
|
112
|
+
<OperationsPlot.Root
|
|
113
|
+
data={currentSnapshot.lag}
|
|
114
|
+
rowKey="label"
|
|
115
|
+
label="Projection lag"
|
|
116
|
+
description="Projection lag over the last hour."
|
|
117
|
+
>
|
|
118
|
+
<OperationsPlot.Line x="label" y="value" />
|
|
119
|
+
<OperationsPlot.Point x="label" y="value" />
|
|
120
|
+
</OperationsPlot.Root>
|
|
121
|
+
</CardContent>
|
|
122
|
+
</Card>
|
|
123
|
+
</Block>
|
|
124
|
+
|
|
125
|
+
{currentSnapshot.consistency !== 'fresh' ? (
|
|
126
|
+
<Alert variant="warning">
|
|
127
|
+
Read models are {currentSnapshot.consistency}. Last processed
|
|
128
|
+
event is {currentSnapshot.lastEventId}.
|
|
129
|
+
</Alert>
|
|
130
|
+
) : null}
|
|
80
131
|
|
|
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."
|
|
95
|
-
>
|
|
96
|
-
<OperationsPlot.Bar x="label" y="value" />
|
|
97
|
-
</OperationsPlot.Root>
|
|
98
|
-
</CardContent>
|
|
99
|
-
</Card>
|
|
100
132
|
<Card>
|
|
101
133
|
<CardHeader>
|
|
102
|
-
<CardTitle>
|
|
134
|
+
<CardTitle>Recent agent runs</CardTitle>
|
|
103
135
|
<CardDescription>
|
|
104
|
-
|
|
136
|
+
Run state is modeled as product state, not a single loading
|
|
137
|
+
boolean.
|
|
105
138
|
</CardDescription>
|
|
106
139
|
</CardHeader>
|
|
107
140
|
<CardContent>
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
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>
|
|
120
|
-
|
|
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}
|
|
127
|
-
|
|
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) => (
|
|
141
|
+
<div class="run-table-wrap">
|
|
142
|
+
<table class="run-table">
|
|
143
|
+
<thead>
|
|
149
144
|
<tr>
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
</
|
|
154
|
-
<td>
|
|
155
|
-
<StatusBadge status={run.status} />
|
|
156
|
-
</td>
|
|
157
|
-
<td>{run.owner}</td>
|
|
158
|
-
<td>{formatRelativeTime(run.updatedAt)}</td>
|
|
145
|
+
<th>Run</th>
|
|
146
|
+
<th>Status</th>
|
|
147
|
+
<th>Owner</th>
|
|
148
|
+
<th>Updated</th>
|
|
159
149
|
</tr>
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
150
|
+
</thead>
|
|
151
|
+
<tbody>
|
|
152
|
+
<For each={currentSnapshot.runs} by={(run) => run.id}>
|
|
153
|
+
{(run) => (
|
|
154
|
+
<tr>
|
|
155
|
+
<td>
|
|
156
|
+
<strong>{run.title}</strong>
|
|
157
|
+
<span>{run.id}</span>
|
|
158
|
+
</td>
|
|
159
|
+
<td>
|
|
160
|
+
<StatusBadge status={run.status} />
|
|
161
|
+
</td>
|
|
162
|
+
<td>{run.owner}</td>
|
|
163
|
+
<td>{formatRelativeTime(run.updatedAt)}</td>
|
|
164
|
+
</tr>
|
|
165
|
+
)}
|
|
166
|
+
</For>
|
|
167
|
+
</tbody>
|
|
168
|
+
</table>
|
|
169
|
+
</div>
|
|
170
|
+
</CardContent>
|
|
171
|
+
</Card>
|
|
172
|
+
</>
|
|
173
|
+
)}
|
|
174
|
+
</Show>
|
|
168
175
|
</Stack>
|
|
169
176
|
);
|
|
170
177
|
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
Clock3Icon,
|
|
5
5
|
ShieldAlertIcon,
|
|
6
6
|
} from '@askrjs/lucide';
|
|
7
|
+
import { For } from '@askrjs/askr/control';
|
|
7
8
|
import {
|
|
8
9
|
Badge,
|
|
9
10
|
Card,
|
|
@@ -18,12 +19,14 @@ import StatusBadge, {
|
|
|
18
19
|
} from '../../components/shared/status-badge';
|
|
19
20
|
|
|
20
21
|
const runs: Array<{
|
|
22
|
+
id: string;
|
|
21
23
|
title: string;
|
|
22
24
|
status: RunStatus;
|
|
23
25
|
event: string;
|
|
24
26
|
description: string;
|
|
25
27
|
}> = [
|
|
26
28
|
{
|
|
29
|
+
id: 'reconcile-billing-projection',
|
|
27
30
|
title: 'Reconcile billing projection',
|
|
28
31
|
status: 'running',
|
|
29
32
|
event: 'tool call: compare-ledger',
|
|
@@ -31,6 +34,7 @@ const runs: Array<{
|
|
|
31
34
|
'Streaming events are appended to the timeline and reconciled by event id.',
|
|
32
35
|
},
|
|
33
36
|
{
|
|
37
|
+
id: 'approve-enterprise-workspace',
|
|
34
38
|
title: 'Approve enterprise workspace',
|
|
35
39
|
status: 'requires-action',
|
|
36
40
|
event: 'approval requested',
|
|
@@ -38,6 +42,7 @@ const runs: Array<{
|
|
|
38
42
|
'Human gates are explicit product states, not hidden inside generated text.',
|
|
39
43
|
},
|
|
40
44
|
{
|
|
45
|
+
id: 'refresh-onboarding-cohort',
|
|
41
46
|
title: 'Refresh onboarding cohort',
|
|
42
47
|
status: 'succeeded',
|
|
43
48
|
event: 'projection caught up',
|
|
@@ -61,32 +66,34 @@ export default function AgentRunsPage() {
|
|
|
61
66
|
</section>
|
|
62
67
|
|
|
63
68
|
<Block gap="md" class="agent-grid">
|
|
64
|
-
{runs
|
|
65
|
-
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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>
|
|
90
97
|
</Block>
|
|
91
98
|
</Stack>
|
|
92
99
|
);
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
CheckCircle2Icon,
|
|
6
6
|
ShieldCheckIcon,
|
|
7
7
|
} from '@askrjs/lucide';
|
|
8
|
+
import { For } from '@askrjs/askr/control';
|
|
8
9
|
import { Link } from '@askrjs/askr/router';
|
|
9
10
|
import { Button } from '@askrjs/themes/components';
|
|
10
11
|
import {
|
|
@@ -111,15 +112,17 @@ export default function HomePage() {
|
|
|
111
112
|
<Section paddingY="xl">
|
|
112
113
|
<Container size="xl">
|
|
113
114
|
<Block gap="md" class="feature-grid">
|
|
114
|
-
{capabilities
|
|
115
|
-
|
|
116
|
-
<
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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>
|
|
123
126
|
</Block>
|
|
124
127
|
</Container>
|
|
125
128
|
</Section>
|
|
@@ -53,3 +53,9 @@ tests/ # Vitest tests
|
|
|
53
53
|
- Use askr-ui components for interactive elements and askr-themes layout/shell primitives for structure
|
|
54
54
|
- Style with `--ak-*` tokens
|
|
55
55
|
- Keep SSG routes synchronous at render time unless you intentionally wire SSR data into generation
|
|
56
|
+
|
|
57
|
+
## Recovery and completion
|
|
58
|
+
|
|
59
|
+
- Run `askr repair` after analyzer failures; it applies only safe mechanical fixes.
|
|
60
|
+
- Resolve remaining semantic diagnostics deliberately.
|
|
61
|
+
- Run `npm run check` before declaring work complete. It requires clean Askr analysis, then runs lint, typecheck, tests, and build.
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
"typecheck": "tsc --noEmit",
|
|
14
14
|
"lint": "vp lint .",
|
|
15
15
|
"lint:fix": "npm run lint -- --fix",
|
|
16
|
+
"analyze": "askr analyze --check",
|
|
16
17
|
"fmt": "vp fmt .",
|
|
17
|
-
"check": "
|
|
18
|
+
"check": "askr check"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
21
|
"@askrjs/askr": ">=0.0.53 <0.1.0",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { For } from '@askrjs/askr/control';
|
|
1
2
|
import { Link } from '@askrjs/askr/router';
|
|
2
3
|
import { Header } from '@askrjs/themes/components';
|
|
3
4
|
import {
|
|
@@ -35,9 +36,9 @@ export function SiteHeader() {
|
|
|
35
36
|
class="navbar-group"
|
|
36
37
|
data-align="end"
|
|
37
38
|
>
|
|
38
|
-
{navItems
|
|
39
|
-
<NavLink href={item.href}>{item.label}</NavLink>
|
|
40
|
-
|
|
39
|
+
<For each={[...navItems]} by={(item) => item.href}>
|
|
40
|
+
{(item) => <NavLink href={item.href}>{item.label}</NavLink>}
|
|
41
|
+
</For>
|
|
41
42
|
</Nav>
|
|
42
43
|
</Box>
|
|
43
44
|
</Container>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { For } from '@askrjs/askr/control';
|
|
1
2
|
import { Link } from '@askrjs/askr/router';
|
|
2
3
|
import { Button } from '@askrjs/ui';
|
|
3
4
|
import {
|
|
@@ -54,17 +55,19 @@ export default function Workflow() {
|
|
|
54
55
|
/>
|
|
55
56
|
|
|
56
57
|
<CardGrid>
|
|
57
|
-
{steps
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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>
|
|
68
71
|
</CardGrid>
|
|
69
72
|
|
|
70
73
|
<Card
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { For } from '@askrjs/askr/control';
|
|
1
2
|
import { Link } from '@askrjs/askr/router';
|
|
2
3
|
import { Button } from '@askrjs/ui';
|
|
3
4
|
import {
|
|
@@ -50,13 +51,15 @@ export default function Content() {
|
|
|
50
51
|
/>
|
|
51
52
|
|
|
52
53
|
<CardGrid>
|
|
53
|
-
{routeMap
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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>
|
|
60
63
|
</CardGrid>
|
|
61
64
|
</>
|
|
62
65
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { For } from '@askrjs/askr/control';
|
|
1
2
|
import { Link } from '@askrjs/askr/router';
|
|
2
3
|
import { Button } from '@askrjs/ui';
|
|
3
4
|
import {
|
|
@@ -42,9 +43,11 @@ export default function Home() {
|
|
|
42
43
|
/>
|
|
43
44
|
|
|
44
45
|
<CardGrid>
|
|
45
|
-
{highlights
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
<For each={highlights} by={(highlight) => highlight.title}>
|
|
47
|
+
{(highlight) => (
|
|
48
|
+
<Card title={highlight.title} description={highlight.body} />
|
|
49
|
+
)}
|
|
50
|
+
</For>
|
|
48
51
|
</CardGrid>
|
|
49
52
|
</>
|
|
50
53
|
);
|
|
@@ -26,3 +26,9 @@ npm run fmt
|
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
Use askr-ui primitives for interactive controls, `--ak-*` theme tokens for styling, and preserve the shared server/browser route registry.
|
|
29
|
+
|
|
30
|
+
## Recovery and completion
|
|
31
|
+
|
|
32
|
+
- Run `askr repair` after analyzer failures; it applies only safe mechanical fixes.
|
|
33
|
+
- Resolve remaining semantic diagnostics deliberately.
|
|
34
|
+
- Run `npm run check` before declaring work complete. It requires clean Askr analysis, then runs lint, typecheck, tests, and build.
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
"typecheck": "tsc --noEmit",
|
|
11
11
|
"lint": "vp lint src server.ts tests vite.config.ts",
|
|
12
12
|
"lint:fix": "npm run lint -- --fix",
|
|
13
|
+
"analyze": "askr analyze --check",
|
|
13
14
|
"fmt": "vp fmt \"src/**/*.{ts,tsx,css}\" \"server.ts\" \"tests/**/*.{ts,tsx}\" \"*.md\"",
|
|
14
|
-
"check": "
|
|
15
|
+
"check": "askr check"
|
|
15
16
|
},
|
|
16
17
|
"dependencies": {
|
|
17
18
|
"@askrjs/askr": ">=0.0.53 <0.1.0",
|
|
@@ -75,3 +75,6 @@ tests/ # Vitest tests
|
|
|
75
75
|
- Run `npm run typecheck` for type-sensitive changes.
|
|
76
76
|
- Run `npm run build` when app boot, routing, or packaging changes.
|
|
77
77
|
- Confirm loading, empty, error, stale, and pending states remain truthful for user-visible flows.
|
|
78
|
+
- Run `askr repair` after analyzer failures; it applies only safe mechanical fixes.
|
|
79
|
+
- Resolve remaining semantic diagnostics deliberately.
|
|
80
|
+
- Run `npm run check` before declaring work complete. It requires clean Askr analysis, then runs lint, typecheck, tests, and build.
|
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
"typecheck": "tsc --noEmit",
|
|
12
12
|
"lint": "vp lint .",
|
|
13
13
|
"lint:fix": "npm run lint -- --fix",
|
|
14
|
+
"analyze": "askr analyze --check",
|
|
14
15
|
"fmt": "vp fmt .",
|
|
15
16
|
"fmt:check": "vp fmt --check .",
|
|
16
|
-
"check": "
|
|
17
|
+
"check": "askr check"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
|
19
20
|
"@askrjs/askr": ">=0.0.53 <0.1.0",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { For } from '@askrjs/askr/control';
|
|
1
2
|
import { Link } from '@askrjs/askr/router';
|
|
2
3
|
import {
|
|
3
4
|
LayoutDashboardIcon,
|
|
@@ -67,27 +68,31 @@ export default function AppSidebar() {
|
|
|
67
68
|
</NavBrand>
|
|
68
69
|
|
|
69
70
|
<NavGroup id="workspace-nav-group" label="Workspace">
|
|
70
|
-
{primaryNav
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
<For each={primaryNav} by={(item) => item.href}>
|
|
72
|
+
{(item) => {
|
|
73
|
+
const Icon = item.icon;
|
|
74
|
+
return (
|
|
75
|
+
<NavLink href={item.href}>
|
|
76
|
+
<Icon size={16} aria-hidden={true} />
|
|
77
|
+
<span>{item.label}</span>
|
|
78
|
+
</NavLink>
|
|
79
|
+
);
|
|
80
|
+
}}
|
|
81
|
+
</For>
|
|
79
82
|
</NavGroup>
|
|
80
83
|
|
|
81
84
|
<NavGroup id="other-nav-group" label="Other" placement="bottom">
|
|
82
|
-
{secondaryNav
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
<For each={secondaryNav} by={(item) => item.href}>
|
|
86
|
+
{(item) => {
|
|
87
|
+
const Icon = item.icon;
|
|
88
|
+
return (
|
|
89
|
+
<NavLink href={item.href}>
|
|
90
|
+
<Icon size={16} aria-hidden={true} />
|
|
91
|
+
<span>{item.label}</span>
|
|
92
|
+
</NavLink>
|
|
93
|
+
);
|
|
94
|
+
}}
|
|
95
|
+
</For>
|
|
91
96
|
</NavGroup>
|
|
92
97
|
</Navbar>
|
|
93
98
|
</aside>
|
|
@@ -58,18 +58,20 @@ export default function DataTable<Row>(props: {
|
|
|
58
58
|
<table class={props.tableClass}>
|
|
59
59
|
<thead>
|
|
60
60
|
<tr>
|
|
61
|
-
{props.columns
|
|
62
|
-
<th class={column.class}>{column.header}</th>
|
|
63
|
-
|
|
61
|
+
<For each={props.columns} by={(column) => column.key}>
|
|
62
|
+
{(column) => <th class={column.class}>{column.header}</th>}
|
|
63
|
+
</For>
|
|
64
64
|
</tr>
|
|
65
65
|
</thead>
|
|
66
66
|
<tbody>
|
|
67
67
|
<For each={props.rows} by={props.rowKey}>
|
|
68
68
|
{(row: Row) => (
|
|
69
69
|
<tr class={props.rowClass?.(row)}>
|
|
70
|
-
{props.columns
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
<For each={props.columns} by={(column) => column.key}>
|
|
71
|
+
{(column) => (
|
|
72
|
+
<td class={column.class}>{column.render(row)}</td>
|
|
73
|
+
)}
|
|
74
|
+
</For>
|
|
73
75
|
</tr>
|
|
74
76
|
)}
|
|
75
77
|
</For>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { state } from '@askrjs/askr';
|
|
2
|
+
import { For } from '@askrjs/askr/control';
|
|
2
3
|
import { resource } from '@askrjs/askr/resources';
|
|
3
4
|
import { Button } from '@askrjs/ui/button';
|
|
4
5
|
import {
|
|
@@ -61,19 +62,21 @@ export default function DashboardPage() {
|
|
|
61
62
|
/>
|
|
62
63
|
|
|
63
64
|
<div class="stat-grid">
|
|
64
|
-
{stats(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
<For each={stats} by={(stat) => stat.key}>
|
|
66
|
+
{(stat) => {
|
|
67
|
+
const Icon =
|
|
68
|
+
iconByStatKey[stat.key as keyof typeof iconByStatKey] ??
|
|
69
|
+
BarChart3Icon;
|
|
70
|
+
return (
|
|
71
|
+
<StatCard
|
|
72
|
+
label={stat.label}
|
|
73
|
+
value={stat.value}
|
|
74
|
+
trend={stat.trend}
|
|
75
|
+
icon={Icon}
|
|
76
|
+
/>
|
|
77
|
+
);
|
|
78
|
+
}}
|
|
79
|
+
</For>
|
|
77
80
|
</div>
|
|
78
81
|
|
|
79
82
|
<section class="panel stack-md">
|