@thxgg/steward 0.1.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/.env.example +7 -0
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/app/app.vue +14 -0
- package/app/assets/css/main.css +129 -0
- package/app/components/CommandPalette.vue +182 -0
- package/app/components/ShortcutsHelp.vue +85 -0
- package/app/components/git/ChangesMinimap.vue +143 -0
- package/app/components/git/CommitList.vue +224 -0
- package/app/components/git/DiffPanel.vue +402 -0
- package/app/components/git/DiffViewer.vue +803 -0
- package/app/components/layout/RepoSelector.vue +358 -0
- package/app/components/layout/Sidebar.vue +91 -0
- package/app/components/prd/Meta.vue +69 -0
- package/app/components/prd/Viewer.vue +285 -0
- package/app/components/tasks/Board.vue +86 -0
- package/app/components/tasks/Card.vue +108 -0
- package/app/components/tasks/Column.vue +108 -0
- package/app/components/tasks/Detail.vue +291 -0
- package/app/components/ui/badge/Badge.vue +26 -0
- package/app/components/ui/badge/index.ts +26 -0
- package/app/components/ui/button/Button.vue +29 -0
- package/app/components/ui/button/index.ts +38 -0
- package/app/components/ui/card/Card.vue +22 -0
- package/app/components/ui/card/CardAction.vue +17 -0
- package/app/components/ui/card/CardContent.vue +17 -0
- package/app/components/ui/card/CardDescription.vue +17 -0
- package/app/components/ui/card/CardFooter.vue +17 -0
- package/app/components/ui/card/CardHeader.vue +17 -0
- package/app/components/ui/card/CardTitle.vue +17 -0
- package/app/components/ui/card/index.ts +7 -0
- package/app/components/ui/combobox/Combobox.vue +19 -0
- package/app/components/ui/combobox/ComboboxAnchor.vue +23 -0
- package/app/components/ui/combobox/ComboboxEmpty.vue +21 -0
- package/app/components/ui/combobox/ComboboxGroup.vue +27 -0
- package/app/components/ui/combobox/ComboboxInput.vue +42 -0
- package/app/components/ui/combobox/ComboboxItem.vue +24 -0
- package/app/components/ui/combobox/ComboboxItemIndicator.vue +23 -0
- package/app/components/ui/combobox/ComboboxList.vue +33 -0
- package/app/components/ui/combobox/ComboboxSeparator.vue +21 -0
- package/app/components/ui/combobox/ComboboxTrigger.vue +24 -0
- package/app/components/ui/combobox/ComboboxViewport.vue +23 -0
- package/app/components/ui/combobox/index.ts +13 -0
- package/app/components/ui/command/Command.vue +103 -0
- package/app/components/ui/command/CommandDialog.vue +33 -0
- package/app/components/ui/command/CommandEmpty.vue +27 -0
- package/app/components/ui/command/CommandGroup.vue +45 -0
- package/app/components/ui/command/CommandInput.vue +54 -0
- package/app/components/ui/command/CommandItem.vue +76 -0
- package/app/components/ui/command/CommandList.vue +25 -0
- package/app/components/ui/command/CommandSeparator.vue +21 -0
- package/app/components/ui/command/CommandShortcut.vue +17 -0
- package/app/components/ui/command/index.ts +25 -0
- package/app/components/ui/dialog/Dialog.vue +19 -0
- package/app/components/ui/dialog/DialogClose.vue +15 -0
- package/app/components/ui/dialog/DialogContent.vue +53 -0
- package/app/components/ui/dialog/DialogDescription.vue +23 -0
- package/app/components/ui/dialog/DialogFooter.vue +15 -0
- package/app/components/ui/dialog/DialogHeader.vue +17 -0
- package/app/components/ui/dialog/DialogOverlay.vue +21 -0
- package/app/components/ui/dialog/DialogScrollContent.vue +59 -0
- package/app/components/ui/dialog/DialogTitle.vue +23 -0
- package/app/components/ui/dialog/DialogTrigger.vue +15 -0
- package/app/components/ui/dialog/index.ts +10 -0
- package/app/components/ui/input/Input.vue +33 -0
- package/app/components/ui/input/index.ts +1 -0
- package/app/components/ui/scroll-area/ScrollArea.vue +33 -0
- package/app/components/ui/scroll-area/ScrollBar.vue +32 -0
- package/app/components/ui/scroll-area/index.ts +2 -0
- package/app/components/ui/separator/Separator.vue +29 -0
- package/app/components/ui/separator/index.ts +1 -0
- package/app/components/ui/sheet/Sheet.vue +19 -0
- package/app/components/ui/sheet/SheetClose.vue +15 -0
- package/app/components/ui/sheet/SheetContent.vue +62 -0
- package/app/components/ui/sheet/SheetDescription.vue +21 -0
- package/app/components/ui/sheet/SheetFooter.vue +16 -0
- package/app/components/ui/sheet/SheetHeader.vue +15 -0
- package/app/components/ui/sheet/SheetOverlay.vue +21 -0
- package/app/components/ui/sheet/SheetTitle.vue +21 -0
- package/app/components/ui/sheet/SheetTrigger.vue +15 -0
- package/app/components/ui/sheet/index.ts +8 -0
- package/app/components/ui/tabs/Tabs.vue +24 -0
- package/app/components/ui/tabs/TabsContent.vue +21 -0
- package/app/components/ui/tabs/TabsList.vue +24 -0
- package/app/components/ui/tabs/TabsTrigger.vue +26 -0
- package/app/components/ui/tabs/index.ts +4 -0
- package/app/components/ui/tooltip/Tooltip.vue +19 -0
- package/app/components/ui/tooltip/TooltipContent.vue +34 -0
- package/app/components/ui/tooltip/TooltipProvider.vue +14 -0
- package/app/components/ui/tooltip/TooltipTrigger.vue +15 -0
- package/app/components/ui/tooltip/index.ts +4 -0
- package/app/composables/useFileWatch.ts +78 -0
- package/app/composables/useGit.ts +180 -0
- package/app/composables/useKeyboard.ts +180 -0
- package/app/composables/usePrd.ts +86 -0
- package/app/composables/useRepos.ts +108 -0
- package/app/composables/useThemeMode.ts +38 -0
- package/app/composables/useToast.ts +31 -0
- package/app/layouts/default.vue +197 -0
- package/app/lib/utils.ts +7 -0
- package/app/pages/[repo]/[prd].vue +263 -0
- package/app/pages/index.vue +257 -0
- package/app/types/git.ts +81 -0
- package/app/types/index.ts +29 -0
- package/app/types/prd.ts +49 -0
- package/app/types/repo.ts +37 -0
- package/app/types/task.ts +134 -0
- package/bin/prd +21 -0
- package/components.json +21 -0
- package/dist/app/types/git.js +1 -0
- package/dist/app/types/prd.js +1 -0
- package/dist/app/types/repo.js +1 -0
- package/dist/app/types/task.js +1 -0
- package/dist/host/src/api/git.js +96 -0
- package/dist/host/src/api/index.js +4 -0
- package/dist/host/src/api/prds.js +195 -0
- package/dist/host/src/api/repos.js +47 -0
- package/dist/host/src/api/state.js +63 -0
- package/dist/host/src/executor.js +109 -0
- package/dist/host/src/index.js +95 -0
- package/dist/host/src/mcp.js +62 -0
- package/dist/host/src/ui.js +64 -0
- package/dist/server/utils/db.js +125 -0
- package/dist/server/utils/git.js +396 -0
- package/dist/server/utils/prd-state.js +229 -0
- package/dist/server/utils/repos.js +256 -0
- package/docs/MCP.md +180 -0
- package/nuxt.config.ts +34 -0
- package/package.json +88 -0
- package/public/favicon.ico +0 -0
- package/public/robots.txt +1 -0
- package/server/api/browse.get.ts +52 -0
- package/server/api/repos/[repoId]/git/commits.get.ts +103 -0
- package/server/api/repos/[repoId]/git/diff.get.ts +77 -0
- package/server/api/repos/[repoId]/git/file-content.get.ts +66 -0
- package/server/api/repos/[repoId]/git/file-diff.get.ts +109 -0
- package/server/api/repos/[repoId]/prd/[prdSlug]/progress.get.ts +36 -0
- package/server/api/repos/[repoId]/prd/[prdSlug]/tasks/[taskId]/commits.get.ts +146 -0
- package/server/api/repos/[repoId]/prd/[prdSlug]/tasks.get.ts +36 -0
- package/server/api/repos/[repoId]/prd/[prdSlug].get.ts +97 -0
- package/server/api/repos/[repoId]/prds.get.ts +85 -0
- package/server/api/repos/[repoId]/refresh-git-repos.post.ts +42 -0
- package/server/api/repos/[repoId].delete.ts +27 -0
- package/server/api/repos/index.get.ts +5 -0
- package/server/api/repos/index.post.ts +39 -0
- package/server/api/watch.get.ts +63 -0
- package/server/plugins/migrate-legacy-state.ts +19 -0
- package/server/tsconfig.json +3 -0
- package/server/utils/db.ts +169 -0
- package/server/utils/git.ts +478 -0
- package/server/utils/prd-state.ts +335 -0
- package/server/utils/repos.ts +322 -0
- package/server/utils/watcher.ts +179 -0
- package/tsconfig.json +4 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { FolderOpen, FileText, ArrowRight, Folder } from 'lucide-vue-next'
|
|
3
|
+
import { Button } from '~/components/ui/button'
|
|
4
|
+
import { Input } from '~/components/ui/input'
|
|
5
|
+
|
|
6
|
+
const router = useRouter()
|
|
7
|
+
const { repos, currentRepo, currentRepoId, addRepo, status: reposStatus } = useRepos()
|
|
8
|
+
const { prds, prdsStatus } = usePrd()
|
|
9
|
+
const { showSuccess } = useToast()
|
|
10
|
+
|
|
11
|
+
// Onboarding state
|
|
12
|
+
const newRepoPath = ref('')
|
|
13
|
+
const addError = ref<string | null>(null)
|
|
14
|
+
const isAdding = ref(false)
|
|
15
|
+
|
|
16
|
+
// Directory browser state (reused from RepoSelector pattern)
|
|
17
|
+
const showBrowser = ref(false)
|
|
18
|
+
const browserPath = ref('')
|
|
19
|
+
const browserDirs = ref<{ name: string; path: string }[]>([])
|
|
20
|
+
const browserLoading = ref(false)
|
|
21
|
+
|
|
22
|
+
async function browseDirectory(path?: string) {
|
|
23
|
+
browserLoading.value = true
|
|
24
|
+
try {
|
|
25
|
+
const data = await $fetch('/api/browse', {
|
|
26
|
+
query: { path: path || browserPath.value || undefined }
|
|
27
|
+
})
|
|
28
|
+
browserPath.value = data.current
|
|
29
|
+
browserDirs.value = data.directories
|
|
30
|
+
} catch {
|
|
31
|
+
// Silently fail - directory browser will show empty state
|
|
32
|
+
} finally {
|
|
33
|
+
browserLoading.value = false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function openBrowser() {
|
|
38
|
+
showBrowser.value = true
|
|
39
|
+
browseDirectory(newRepoPath.value || undefined)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function selectDirectory(path: string) {
|
|
43
|
+
newRepoPath.value = path
|
|
44
|
+
showBrowser.value = false
|
|
45
|
+
addError.value = null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function navigateUp() {
|
|
49
|
+
const parent = browserPath.value.split('/').slice(0, -1).join('/') || '/'
|
|
50
|
+
browseDirectory(parent)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function handleAddRepo() {
|
|
54
|
+
if (!newRepoPath.value.trim()) {
|
|
55
|
+
addError.value = 'Please enter a repository path'
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
isAdding.value = true
|
|
60
|
+
addError.value = null
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const repo = await addRepo(newRepoPath.value.trim())
|
|
64
|
+
newRepoPath.value = ''
|
|
65
|
+
showSuccess('Repository added', repo?.name || 'Successfully added repository')
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (error instanceof Error) {
|
|
68
|
+
const fetchError = error as { data?: { message?: string } }
|
|
69
|
+
addError.value = fetchError.data?.message || error.message
|
|
70
|
+
} else {
|
|
71
|
+
addError.value = 'Failed to add repository'
|
|
72
|
+
}
|
|
73
|
+
} finally {
|
|
74
|
+
isAdding.value = false
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Determine what state we're in
|
|
79
|
+
const showOnboarding = computed(() => {
|
|
80
|
+
return reposStatus.value !== 'pending' && (!repos.value || repos.value.length === 0)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const showSelectPrompt = computed(() => {
|
|
84
|
+
return repos.value && repos.value.length > 0 && !currentRepoId.value
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const showWelcome = computed(() => {
|
|
88
|
+
return currentRepoId.value && (!prds.value || prds.value.length === 0)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
const showPrdList = computed(() => {
|
|
92
|
+
return currentRepoId.value && prds.value && prds.value.length > 0
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// Auto-navigate to first PRD when repo is selected and has documents
|
|
96
|
+
watch(
|
|
97
|
+
[currentRepoId, prds, prdsStatus],
|
|
98
|
+
([repoId, prdList, status]) => {
|
|
99
|
+
const firstPrd = prdList?.[0]
|
|
100
|
+
if (repoId && status === 'success' && firstPrd) {
|
|
101
|
+
router.push(`/${repoId}/${firstPrd.slug}`)
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{ immediate: true }
|
|
105
|
+
)
|
|
106
|
+
</script>
|
|
107
|
+
|
|
108
|
+
<template>
|
|
109
|
+
<div class="flex h-full items-center justify-center p-6 md:p-8">
|
|
110
|
+
<!-- Onboarding: No repos configured -->
|
|
111
|
+
<div v-if="showOnboarding" class="mx-auto max-w-md text-center">
|
|
112
|
+
<div class="mx-auto mb-6 flex size-16 items-center justify-center rounded-full bg-primary/10">
|
|
113
|
+
<FolderOpen class="size-8 text-primary" />
|
|
114
|
+
</div>
|
|
115
|
+
<h2 class="text-2xl font-semibold tracking-tight">
|
|
116
|
+
Welcome to PRD Viewer
|
|
117
|
+
</h2>
|
|
118
|
+
<p class="mt-2 text-muted-foreground">
|
|
119
|
+
Add a repository to get started. The repository should contain PRD documents in a <code class="rounded bg-muted px-1.5 py-0.5 text-sm">docs/prd/</code> directory.
|
|
120
|
+
</p>
|
|
121
|
+
|
|
122
|
+
<form class="mt-6 space-y-4 text-left" @submit.prevent="handleAddRepo">
|
|
123
|
+
<div class="space-y-2">
|
|
124
|
+
<label for="repo-path" class="text-sm font-medium">
|
|
125
|
+
Repository Path
|
|
126
|
+
</label>
|
|
127
|
+
<div class="flex gap-2">
|
|
128
|
+
<Input
|
|
129
|
+
id="repo-path"
|
|
130
|
+
v-model="newRepoPath"
|
|
131
|
+
placeholder="/path/to/your/project"
|
|
132
|
+
:disabled="isAdding"
|
|
133
|
+
class="flex-1"
|
|
134
|
+
/>
|
|
135
|
+
<Button
|
|
136
|
+
type="button"
|
|
137
|
+
variant="outline"
|
|
138
|
+
size="icon"
|
|
139
|
+
:disabled="isAdding"
|
|
140
|
+
@click="openBrowser"
|
|
141
|
+
>
|
|
142
|
+
<Folder class="size-4" />
|
|
143
|
+
</Button>
|
|
144
|
+
</div>
|
|
145
|
+
<p v-if="addError" class="text-sm text-destructive">
|
|
146
|
+
{{ addError }}
|
|
147
|
+
</p>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<!-- Directory Browser -->
|
|
151
|
+
<div v-if="showBrowser" class="space-y-2 rounded-md border p-3">
|
|
152
|
+
<div class="flex items-center gap-2 text-sm">
|
|
153
|
+
<button
|
|
154
|
+
type="button"
|
|
155
|
+
class="hover:text-foreground text-muted-foreground"
|
|
156
|
+
:disabled="browserPath === '/'"
|
|
157
|
+
@click="navigateUp"
|
|
158
|
+
>
|
|
159
|
+
..
|
|
160
|
+
</button>
|
|
161
|
+
<span class="flex-1 truncate font-mono text-xs text-muted-foreground">
|
|
162
|
+
{{ browserPath }}
|
|
163
|
+
</span>
|
|
164
|
+
<Button
|
|
165
|
+
type="button"
|
|
166
|
+
variant="ghost"
|
|
167
|
+
size="sm"
|
|
168
|
+
class="h-7 text-xs"
|
|
169
|
+
@click="selectDirectory(browserPath)"
|
|
170
|
+
>
|
|
171
|
+
Select
|
|
172
|
+
</Button>
|
|
173
|
+
</div>
|
|
174
|
+
<div class="max-h-[200px] overflow-y-auto">
|
|
175
|
+
<div v-if="browserLoading" class="py-2 text-center text-sm text-muted-foreground">
|
|
176
|
+
Loading...
|
|
177
|
+
</div>
|
|
178
|
+
<div v-else-if="!browserDirs.length" class="py-2 text-center text-sm text-muted-foreground">
|
|
179
|
+
No subdirectories
|
|
180
|
+
</div>
|
|
181
|
+
<button
|
|
182
|
+
v-for="dir in browserDirs"
|
|
183
|
+
v-else
|
|
184
|
+
:key="dir.path"
|
|
185
|
+
type="button"
|
|
186
|
+
class="flex w-full items-center gap-2 rounded px-2 py-1 text-sm hover:bg-accent"
|
|
187
|
+
@click="browseDirectory(dir.path)"
|
|
188
|
+
>
|
|
189
|
+
<Folder class="size-4 text-muted-foreground" />
|
|
190
|
+
<span class="truncate">{{ dir.name }}</span>
|
|
191
|
+
</button>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<Button type="submit" class="w-full" :disabled="isAdding">
|
|
196
|
+
<span v-if="isAdding">Adding...</span>
|
|
197
|
+
<span v-else>Add Repository</span>
|
|
198
|
+
</Button>
|
|
199
|
+
</form>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<!-- Select prompt: Repos exist but none selected -->
|
|
203
|
+
<div v-else-if="showSelectPrompt" class="mx-auto max-w-md text-center">
|
|
204
|
+
<div class="mx-auto mb-6 flex size-16 items-center justify-center rounded-full bg-primary/10">
|
|
205
|
+
<FolderOpen class="size-8 text-primary" />
|
|
206
|
+
</div>
|
|
207
|
+
<h2 class="text-2xl font-semibold tracking-tight">
|
|
208
|
+
Select a Repository
|
|
209
|
+
</h2>
|
|
210
|
+
<p class="mt-2 text-muted-foreground">
|
|
211
|
+
Choose a repository from the dropdown in the header to view its PRD documents.
|
|
212
|
+
</p>
|
|
213
|
+
<div class="mt-6 flex items-center justify-center gap-2 text-sm text-muted-foreground">
|
|
214
|
+
<ArrowRight class="size-4" />
|
|
215
|
+
<span>Use the repository selector above</span>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
<!-- Welcome: Repo selected but no PRDs -->
|
|
220
|
+
<div v-else-if="showWelcome" class="mx-auto max-w-md text-center">
|
|
221
|
+
<div class="mx-auto mb-6 flex size-16 items-center justify-center rounded-full bg-muted">
|
|
222
|
+
<FileText class="size-8 text-muted-foreground" />
|
|
223
|
+
</div>
|
|
224
|
+
<h2 class="text-2xl font-semibold tracking-tight">
|
|
225
|
+
No PRDs Found
|
|
226
|
+
</h2>
|
|
227
|
+
<p class="mt-2 text-muted-foreground">
|
|
228
|
+
This repository doesn't have any PRD documents yet. Add markdown files to <code class="rounded bg-muted px-1.5 py-0.5 text-sm">docs/prd/</code> to get started.
|
|
229
|
+
</p>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<!-- PRD list: Repo selected with PRDs - show welcome with PRD count -->
|
|
233
|
+
<div v-else-if="showPrdList" class="mx-auto max-w-md text-center">
|
|
234
|
+
<div class="mx-auto mb-6 flex size-16 items-center justify-center rounded-full bg-primary/10">
|
|
235
|
+
<FileText class="size-8 text-primary" />
|
|
236
|
+
</div>
|
|
237
|
+
<h2 class="text-2xl font-semibold tracking-tight">
|
|
238
|
+
{{ currentRepo?.name }}
|
|
239
|
+
</h2>
|
|
240
|
+
<p class="mt-2 text-muted-foreground">
|
|
241
|
+
{{ prds?.length }} PRD{{ prds?.length === 1 ? '' : 's' }} available. Select one from the sidebar to view details.
|
|
242
|
+
</p>
|
|
243
|
+
<div class="mt-6 flex items-center justify-center gap-2 text-sm text-muted-foreground">
|
|
244
|
+
<ArrowRight class="size-4 rotate-180" />
|
|
245
|
+
<span>Choose a PRD from the sidebar</span>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<!-- Loading state -->
|
|
250
|
+
<div v-else class="mx-auto max-w-md text-center">
|
|
251
|
+
<div class="mx-auto mb-6 flex size-16 items-center justify-center">
|
|
252
|
+
<div class="size-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
|
|
253
|
+
</div>
|
|
254
|
+
<p class="text-muted-foreground">Loading...</p>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
</template>
|
package/app/types/git.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git commit information
|
|
3
|
+
*/
|
|
4
|
+
export interface GitCommit {
|
|
5
|
+
/** Full commit SHA */
|
|
6
|
+
sha: string
|
|
7
|
+
/** Short commit SHA (7 characters) */
|
|
8
|
+
shortSha: string
|
|
9
|
+
/** Commit message (first line) */
|
|
10
|
+
message: string
|
|
11
|
+
/** Author name */
|
|
12
|
+
author: string
|
|
13
|
+
/** Commit date as ISO string */
|
|
14
|
+
date: string
|
|
15
|
+
/** Number of files changed */
|
|
16
|
+
filesChanged: number
|
|
17
|
+
/** Total lines added */
|
|
18
|
+
additions: number
|
|
19
|
+
/** Total lines deleted */
|
|
20
|
+
deletions: number
|
|
21
|
+
/** Relative path to git repo for pseudo-monorepos (e.g., "code-hospitality-backend") */
|
|
22
|
+
repoPath?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* File change status in a commit
|
|
27
|
+
*/
|
|
28
|
+
export type FileStatus = 'added' | 'modified' | 'deleted' | 'renamed'
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* File diff summary within a commit
|
|
32
|
+
*/
|
|
33
|
+
export interface FileDiff {
|
|
34
|
+
/** Current file path */
|
|
35
|
+
path: string
|
|
36
|
+
/** Change status */
|
|
37
|
+
status: FileStatus
|
|
38
|
+
/** Original file path (for renames) */
|
|
39
|
+
oldPath?: string
|
|
40
|
+
/** Lines added in this file */
|
|
41
|
+
additions: number
|
|
42
|
+
/** Lines deleted in this file */
|
|
43
|
+
deletions: number
|
|
44
|
+
/** Whether this is a binary file */
|
|
45
|
+
binary?: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Type of diff line
|
|
50
|
+
*/
|
|
51
|
+
export type DiffLineType = 'add' | 'remove' | 'context'
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Single line in a diff hunk
|
|
55
|
+
*/
|
|
56
|
+
export interface DiffLine {
|
|
57
|
+
/** Line type: addition, removal, or context */
|
|
58
|
+
type: DiffLineType
|
|
59
|
+
/** Line content (without +/- prefix) */
|
|
60
|
+
content: string
|
|
61
|
+
/** Line number in old file (for remove/context lines) */
|
|
62
|
+
oldNumber?: number
|
|
63
|
+
/** Line number in new file (for add/context lines) */
|
|
64
|
+
newNumber?: number
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A hunk (section) of a diff
|
|
69
|
+
*/
|
|
70
|
+
export interface DiffHunk {
|
|
71
|
+
/** Starting line in old file */
|
|
72
|
+
oldStart: number
|
|
73
|
+
/** Number of lines from old file */
|
|
74
|
+
oldLines: number
|
|
75
|
+
/** Starting line in new file */
|
|
76
|
+
newStart: number
|
|
77
|
+
/** Number of lines in new file */
|
|
78
|
+
newLines: number
|
|
79
|
+
/** Lines in this hunk */
|
|
80
|
+
lines: DiffLine[]
|
|
81
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Repository types
|
|
2
|
+
export type { RepoConfig, AddRepoRequest, GitRepoInfo } from './repo'
|
|
3
|
+
|
|
4
|
+
// PRD types
|
|
5
|
+
export type { PrdListItem, PrdMetadata, PrdDocument } from './prd'
|
|
6
|
+
|
|
7
|
+
// Task types
|
|
8
|
+
export type {
|
|
9
|
+
TaskCategory,
|
|
10
|
+
TaskPriority,
|
|
11
|
+
TaskStatus,
|
|
12
|
+
Task,
|
|
13
|
+
TasksPrdInfo,
|
|
14
|
+
TasksFile,
|
|
15
|
+
ProgressPattern,
|
|
16
|
+
CommitRef,
|
|
17
|
+
TaskLog,
|
|
18
|
+
ProgressFile,
|
|
19
|
+
} from './task'
|
|
20
|
+
|
|
21
|
+
// Git types
|
|
22
|
+
export type {
|
|
23
|
+
GitCommit,
|
|
24
|
+
FileStatus,
|
|
25
|
+
FileDiff,
|
|
26
|
+
DiffLineType,
|
|
27
|
+
DiffLine,
|
|
28
|
+
DiffHunk,
|
|
29
|
+
} from './git'
|
package/app/types/prd.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD list item returned when listing PRDs in a repository
|
|
3
|
+
*/
|
|
4
|
+
export interface PrdListItem {
|
|
5
|
+
/** URL-safe identifier (derived from filename) */
|
|
6
|
+
slug: string
|
|
7
|
+
/** PRD title extracted from markdown H1 */
|
|
8
|
+
name: string
|
|
9
|
+
/** Relative path to the .md file */
|
|
10
|
+
source: string
|
|
11
|
+
/** Whether tracked task/progress state exists for this PRD */
|
|
12
|
+
hasState: boolean
|
|
13
|
+
/** Total number of tasks if state exists */
|
|
14
|
+
taskCount?: number
|
|
15
|
+
/** Number of completed tasks if state exists */
|
|
16
|
+
completedCount?: number
|
|
17
|
+
/** File modification timestamp (ms since epoch) */
|
|
18
|
+
modifiedAt: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* PRD metadata extracted from document header
|
|
23
|
+
*/
|
|
24
|
+
export interface PrdMetadata {
|
|
25
|
+
/** Document author */
|
|
26
|
+
author?: string
|
|
27
|
+
/** Creation/update date */
|
|
28
|
+
date?: string
|
|
29
|
+
/** Document status (Draft, In Progress, Complete, etc.) */
|
|
30
|
+
status?: string
|
|
31
|
+
/** Shortcut story ID if linked */
|
|
32
|
+
shortcutStory?: string
|
|
33
|
+
/** Shortcut story URL if linked */
|
|
34
|
+
shortcutUrl?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Full PRD document with content and metadata
|
|
39
|
+
*/
|
|
40
|
+
export interface PrdDocument {
|
|
41
|
+
/** URL-safe identifier */
|
|
42
|
+
slug: string
|
|
43
|
+
/** PRD title */
|
|
44
|
+
name: string
|
|
45
|
+
/** Raw markdown content */
|
|
46
|
+
content: string
|
|
47
|
+
/** Extracted metadata */
|
|
48
|
+
metadata: PrdMetadata
|
|
49
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Information about a discovered git repository within a pseudo-monorepo
|
|
3
|
+
*/
|
|
4
|
+
export interface GitRepoInfo {
|
|
5
|
+
/** Relative path from registered repo root (e.g., "code-hospitality-backend") */
|
|
6
|
+
relativePath: string
|
|
7
|
+
/** Absolute filesystem path to the git repo */
|
|
8
|
+
absolutePath: string
|
|
9
|
+
/** Display name (usually folder name) */
|
|
10
|
+
name: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Repository configuration stored server-side
|
|
15
|
+
*/
|
|
16
|
+
export interface RepoConfig {
|
|
17
|
+
/** Unique identifier (UUID) */
|
|
18
|
+
id: string
|
|
19
|
+
/** Display name for the repository */
|
|
20
|
+
name: string
|
|
21
|
+
/** Absolute filesystem path to the repository */
|
|
22
|
+
path: string
|
|
23
|
+
/** ISO timestamp when the repo was added */
|
|
24
|
+
addedAt: string
|
|
25
|
+
/** Discovered git repositories for pseudo-monorepos (empty/undefined for standard repos) */
|
|
26
|
+
gitRepos?: GitRepoInfo[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Request body for adding a new repository
|
|
31
|
+
*/
|
|
32
|
+
export interface AddRepoRequest {
|
|
33
|
+
/** Absolute filesystem path to the repository */
|
|
34
|
+
path: string
|
|
35
|
+
/** Optional display name (defaults to directory name) */
|
|
36
|
+
name?: string
|
|
37
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task category
|
|
3
|
+
*/
|
|
4
|
+
export type TaskCategory = 'setup' | 'feature' | 'integration' | 'testing' | 'documentation'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Task priority level
|
|
8
|
+
*/
|
|
9
|
+
export type TaskPriority = 'critical' | 'high' | 'medium' | 'low'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Task status
|
|
13
|
+
*/
|
|
14
|
+
export type TaskStatus = 'pending' | 'in_progress' | 'completed'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Individual task from tasks.json
|
|
18
|
+
*/
|
|
19
|
+
export interface Task {
|
|
20
|
+
/** Unique task identifier (e.g., "task-001") */
|
|
21
|
+
id: string
|
|
22
|
+
/** Task category */
|
|
23
|
+
category: TaskCategory
|
|
24
|
+
/** Short task title */
|
|
25
|
+
title: string
|
|
26
|
+
/** Detailed description of what needs to be done */
|
|
27
|
+
description: string
|
|
28
|
+
/** Specific implementation steps */
|
|
29
|
+
steps: string[]
|
|
30
|
+
/** Verification criteria for completion */
|
|
31
|
+
passes: string[]
|
|
32
|
+
/** IDs of tasks this task depends on */
|
|
33
|
+
dependencies: string[]
|
|
34
|
+
/** Task priority */
|
|
35
|
+
priority: TaskPriority
|
|
36
|
+
/** Current task status */
|
|
37
|
+
status: TaskStatus
|
|
38
|
+
/** ISO timestamp when task was started */
|
|
39
|
+
startedAt?: string
|
|
40
|
+
/** ISO timestamp when task was completed */
|
|
41
|
+
completedAt?: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* PRD metadata in tasks.json
|
|
46
|
+
*/
|
|
47
|
+
export interface TasksPrdInfo {
|
|
48
|
+
/** PRD name/title */
|
|
49
|
+
name: string
|
|
50
|
+
/** Path to source PRD markdown file */
|
|
51
|
+
source: string
|
|
52
|
+
/** Shortcut story ID if linked */
|
|
53
|
+
shortcutStory?: string
|
|
54
|
+
/** ISO timestamp when tasks were created */
|
|
55
|
+
createdAt: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Full tasks.json structure
|
|
60
|
+
*/
|
|
61
|
+
export interface TasksFile {
|
|
62
|
+
/** PRD information */
|
|
63
|
+
prd: TasksPrdInfo
|
|
64
|
+
/** Array of tasks */
|
|
65
|
+
tasks: Task[]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Pattern discovered during implementation
|
|
70
|
+
*/
|
|
71
|
+
export interface ProgressPattern {
|
|
72
|
+
/** Pattern name */
|
|
73
|
+
name: string
|
|
74
|
+
/** Pattern description */
|
|
75
|
+
description: string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Commit reference with repository context for pseudo-monorepos
|
|
80
|
+
*/
|
|
81
|
+
export interface CommitRef {
|
|
82
|
+
/** Git commit SHA */
|
|
83
|
+
sha: string
|
|
84
|
+
/** Relative path to git repo from registered repo root (e.g., "code-hospitality-backend") */
|
|
85
|
+
repo: string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Log entry for a completed task
|
|
90
|
+
*/
|
|
91
|
+
export interface TaskLog {
|
|
92
|
+
/** Task ID */
|
|
93
|
+
taskId: string
|
|
94
|
+
/** Task status */
|
|
95
|
+
status: TaskStatus
|
|
96
|
+
/** ISO timestamp when task was started */
|
|
97
|
+
startedAt: string
|
|
98
|
+
/** ISO timestamp when task was completed */
|
|
99
|
+
completedAt?: string
|
|
100
|
+
/** Description of what was implemented */
|
|
101
|
+
implemented?: string
|
|
102
|
+
/** Files that were changed */
|
|
103
|
+
filesChanged?: string[]
|
|
104
|
+
/** Learnings or patterns discovered */
|
|
105
|
+
learnings?: string
|
|
106
|
+
/** Git commit SHAs associated with this task - supports both legacy strings and CommitRef objects */
|
|
107
|
+
commits?: (string | CommitRef)[]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Progress tracking file structure (progress.json)
|
|
112
|
+
*/
|
|
113
|
+
export interface ProgressFile {
|
|
114
|
+
/** PRD name */
|
|
115
|
+
prdName: string
|
|
116
|
+
/** Shortcut story ID if linked */
|
|
117
|
+
shortcutStory?: string
|
|
118
|
+
/** Total number of tasks */
|
|
119
|
+
totalTasks: number
|
|
120
|
+
/** Number of completed tasks */
|
|
121
|
+
completed: number
|
|
122
|
+
/** Number of in-progress tasks */
|
|
123
|
+
inProgress: number
|
|
124
|
+
/** Number of blocked tasks */
|
|
125
|
+
blocked: number
|
|
126
|
+
/** ISO timestamp when work started */
|
|
127
|
+
startedAt: string | null
|
|
128
|
+
/** ISO timestamp of last update */
|
|
129
|
+
lastUpdated: string
|
|
130
|
+
/** Discovered patterns */
|
|
131
|
+
patterns: ProgressPattern[]
|
|
132
|
+
/** Task completion logs */
|
|
133
|
+
taskLogs: TaskLog[]
|
|
134
|
+
}
|
package/bin/prd
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { dirname, resolve } from 'node:path'
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
5
|
+
|
|
6
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
7
|
+
const entryPath = resolve(packageRoot, 'dist', 'host', 'src', 'index.js')
|
|
8
|
+
|
|
9
|
+
if (!existsSync(entryPath)) {
|
|
10
|
+
console.error('Steward host runtime is not built.')
|
|
11
|
+
console.error('Run `npm run build:host` in this package before invoking `prd`.')
|
|
12
|
+
process.exit(1)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { main } = await import(pathToFileURL(entryPath).href)
|
|
16
|
+
|
|
17
|
+
await main(process.argv.slice(2)).catch((error) => {
|
|
18
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
19
|
+
console.error(`Error: ${message}`)
|
|
20
|
+
process.exit(1)
|
|
21
|
+
})
|
package/components.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://shadcn-vue.com/schema.json",
|
|
3
|
+
"style": "new-york",
|
|
4
|
+
"typescript": true,
|
|
5
|
+
"tailwind": {
|
|
6
|
+
"config": "",
|
|
7
|
+
"css": "app/assets/css/main.css",
|
|
8
|
+
"baseColor": "neutral",
|
|
9
|
+
"cssVariables": true,
|
|
10
|
+
"prefix": ""
|
|
11
|
+
},
|
|
12
|
+
"iconLibrary": "lucide",
|
|
13
|
+
"aliases": {
|
|
14
|
+
"components": "@/components",
|
|
15
|
+
"utils": "@/lib/utils",
|
|
16
|
+
"ui": "@/components/ui",
|
|
17
|
+
"lib": "@/lib",
|
|
18
|
+
"composables": "@/composables"
|
|
19
|
+
},
|
|
20
|
+
"registries": {}
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|