javi-forge 0.1.0 → 1.0.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.
Files changed (159) hide show
  1. package/.releaserc +2 -1
  2. package/README.md +143 -31
  3. package/ai-config/commands/workflows/diagnose.md +70 -0
  4. package/ai-config/commands/workflows/discover.md +86 -0
  5. package/dist/commands/doctor.js +24 -1
  6. package/dist/commands/init.js +48 -1
  7. package/dist/commands/llmstxt.d.ts +9 -0
  8. package/dist/commands/llmstxt.js +93 -0
  9. package/dist/commands/llmstxt.test.d.ts +2 -0
  10. package/dist/commands/plugin.d.ts +24 -0
  11. package/dist/commands/plugin.js +78 -0
  12. package/dist/commands/plugin.test.d.ts +2 -0
  13. package/dist/constants.d.ts +8 -0
  14. package/dist/constants.js +8 -0
  15. package/dist/index.js +33 -4
  16. package/dist/lib/plugin.d.ts +39 -0
  17. package/dist/lib/plugin.js +228 -0
  18. package/dist/lib/plugin.test.d.ts +2 -0
  19. package/dist/types/index.d.ts +42 -0
  20. package/dist/ui/App.d.ts +2 -1
  21. package/dist/ui/App.js +2 -1
  22. package/dist/ui/LlmsTxt.d.ts +8 -0
  23. package/dist/ui/LlmsTxt.js +48 -0
  24. package/dist/ui/Plugin.d.ts +9 -0
  25. package/dist/ui/Plugin.js +96 -0
  26. package/modules/obsidian-brain/README.md +32 -0
  27. package/modules/obsidian-brain/core/templates/braindump.md +15 -0
  28. package/modules/obsidian-brain/core/templates/consolidation.md +42 -0
  29. package/modules/obsidian-brain/core/templates/daily-note.md +18 -0
  30. package/modules/obsidian-brain/core/templates/resource-capture.md +14 -0
  31. package/modules/obsidian-brain/developer/templates/adr.md +40 -0
  32. package/modules/obsidian-brain/developer/templates/coding-session.md +24 -0
  33. package/modules/obsidian-brain/developer/templates/debug-journal.md +22 -0
  34. package/modules/obsidian-brain/developer/templates/sdd-feedback.md +27 -0
  35. package/modules/obsidian-brain/developer/templates/tech-debt.md +20 -0
  36. package/modules/obsidian-brain/pm-lead/templates/daily-brief.md +25 -0
  37. package/modules/obsidian-brain/pm-lead/templates/meeting-notes.md +24 -0
  38. package/modules/obsidian-brain/pm-lead/templates/risk-registry.md +12 -0
  39. package/modules/obsidian-brain/pm-lead/templates/sprint-review.md +27 -0
  40. package/modules/obsidian-brain/pm-lead/templates/stakeholder-update.md +24 -0
  41. package/modules/obsidian-brain/pm-lead/templates/team-intelligence.md +19 -0
  42. package/modules/obsidian-brain/pm-lead/templates/weekly-brief.md +29 -0
  43. package/package.json +1 -1
  44. package/schemas/plugin.schema.json +62 -0
  45. package/ai-config/skills/docs/api-documentation/SKILL.md +0 -293
  46. package/ai-config/skills/docs/docs-spring/SKILL.md +0 -377
  47. package/ai-config/skills/docs/mustache-templates/SKILL.md +0 -190
  48. package/ai-config/skills/docs/technical-docs/SKILL.md +0 -447
  49. package/dist/commands/analyze.d.ts.map +0 -1
  50. package/dist/commands/analyze.js.map +0 -1
  51. package/dist/commands/analyze.test.d.ts.map +0 -1
  52. package/dist/commands/analyze.test.js +0 -145
  53. package/dist/commands/analyze.test.js.map +0 -1
  54. package/dist/commands/doctor.d.ts.map +0 -1
  55. package/dist/commands/doctor.js.map +0 -1
  56. package/dist/commands/doctor.test.d.ts.map +0 -1
  57. package/dist/commands/doctor.test.js +0 -200
  58. package/dist/commands/doctor.test.js.map +0 -1
  59. package/dist/commands/init.d.ts.map +0 -1
  60. package/dist/commands/init.js.map +0 -1
  61. package/dist/commands/init.test.d.ts.map +0 -1
  62. package/dist/commands/init.test.js +0 -271
  63. package/dist/commands/init.test.js.map +0 -1
  64. package/dist/commands/sync.d.ts.map +0 -1
  65. package/dist/commands/sync.js.map +0 -1
  66. package/dist/constants.d.ts.map +0 -1
  67. package/dist/constants.js.map +0 -1
  68. package/dist/e2e/aggressive.e2e.test.d.ts.map +0 -1
  69. package/dist/e2e/aggressive.e2e.test.js +0 -350
  70. package/dist/e2e/aggressive.e2e.test.js.map +0 -1
  71. package/dist/e2e/commands.e2e.test.d.ts.map +0 -1
  72. package/dist/e2e/commands.e2e.test.js +0 -213
  73. package/dist/e2e/commands.e2e.test.js.map +0 -1
  74. package/dist/index.d.ts.map +0 -1
  75. package/dist/index.js.map +0 -1
  76. package/dist/lib/common.d.ts.map +0 -1
  77. package/dist/lib/common.js.map +0 -1
  78. package/dist/lib/common.test.d.ts.map +0 -1
  79. package/dist/lib/common.test.js +0 -316
  80. package/dist/lib/common.test.js.map +0 -1
  81. package/dist/lib/frontmatter.d.ts.map +0 -1
  82. package/dist/lib/frontmatter.js.map +0 -1
  83. package/dist/lib/frontmatter.test.d.ts.map +0 -1
  84. package/dist/lib/frontmatter.test.js +0 -257
  85. package/dist/lib/frontmatter.test.js.map +0 -1
  86. package/dist/lib/template.d.ts.map +0 -1
  87. package/dist/lib/template.js.map +0 -1
  88. package/dist/lib/template.test.d.ts.map +0 -1
  89. package/dist/lib/template.test.js +0 -201
  90. package/dist/lib/template.test.js.map +0 -1
  91. package/dist/types/index.d.ts.map +0 -1
  92. package/dist/types/index.js.map +0 -1
  93. package/dist/ui/AnalyzeUI.d.ts.map +0 -1
  94. package/dist/ui/AnalyzeUI.js.map +0 -1
  95. package/dist/ui/App.d.ts.map +0 -1
  96. package/dist/ui/App.js.map +0 -1
  97. package/dist/ui/CIContext.d.ts.map +0 -1
  98. package/dist/ui/CIContext.js.map +0 -1
  99. package/dist/ui/CISelector.d.ts.map +0 -1
  100. package/dist/ui/CISelector.js.map +0 -1
  101. package/dist/ui/Doctor.d.ts.map +0 -1
  102. package/dist/ui/Doctor.js.map +0 -1
  103. package/dist/ui/Header.d.ts.map +0 -1
  104. package/dist/ui/Header.js.map +0 -1
  105. package/dist/ui/MemorySelector.d.ts.map +0 -1
  106. package/dist/ui/MemorySelector.js.map +0 -1
  107. package/dist/ui/NameInput.d.ts.map +0 -1
  108. package/dist/ui/NameInput.js.map +0 -1
  109. package/dist/ui/OptionSelector.d.ts.map +0 -1
  110. package/dist/ui/OptionSelector.js.map +0 -1
  111. package/dist/ui/Progress.d.ts.map +0 -1
  112. package/dist/ui/Progress.js.map +0 -1
  113. package/dist/ui/StackSelector.d.ts.map +0 -1
  114. package/dist/ui/StackSelector.js.map +0 -1
  115. package/dist/ui/Summary.d.ts.map +0 -1
  116. package/dist/ui/Summary.js.map +0 -1
  117. package/dist/ui/SyncUI.d.ts.map +0 -1
  118. package/dist/ui/SyncUI.js.map +0 -1
  119. package/dist/ui/Welcome.d.ts.map +0 -1
  120. package/dist/ui/Welcome.js.map +0 -1
  121. package/dist/ui/theme.d.ts.map +0 -1
  122. package/dist/ui/theme.js.map +0 -1
  123. package/modules/obsidian-brain/.obsidian/plugins/dataview/data.json +0 -25
  124. package/modules/obsidian-brain/.obsidian/plugins/obsidian-kanban/data.json +0 -29
  125. package/modules/obsidian-brain/.obsidian/plugins/templater-obsidian/data.json +0 -18
  126. package/src/commands/analyze.test.ts +0 -145
  127. package/src/commands/analyze.ts +0 -69
  128. package/src/commands/doctor.test.ts +0 -208
  129. package/src/commands/doctor.ts +0 -163
  130. package/src/commands/init.test.ts +0 -298
  131. package/src/commands/init.ts +0 -285
  132. package/src/constants.ts +0 -69
  133. package/src/e2e/aggressive.e2e.test.ts +0 -557
  134. package/src/e2e/commands.e2e.test.ts +0 -298
  135. package/src/index.tsx +0 -106
  136. package/src/lib/common.test.ts +0 -318
  137. package/src/lib/common.ts +0 -127
  138. package/src/lib/frontmatter.test.ts +0 -291
  139. package/src/lib/frontmatter.ts +0 -77
  140. package/src/lib/template.test.ts +0 -226
  141. package/src/lib/template.ts +0 -99
  142. package/src/types/index.ts +0 -53
  143. package/src/ui/AnalyzeUI.tsx +0 -133
  144. package/src/ui/App.tsx +0 -175
  145. package/src/ui/CIContext.tsx +0 -25
  146. package/src/ui/CISelector.tsx +0 -72
  147. package/src/ui/Doctor.tsx +0 -122
  148. package/src/ui/Header.tsx +0 -48
  149. package/src/ui/MemorySelector.tsx +0 -73
  150. package/src/ui/NameInput.tsx +0 -82
  151. package/src/ui/OptionSelector.tsx +0 -100
  152. package/src/ui/Progress.tsx +0 -88
  153. package/src/ui/StackSelector.tsx +0 -101
  154. package/src/ui/Summary.tsx +0 -134
  155. package/src/ui/Welcome.tsx +0 -54
  156. package/src/ui/theme.ts +0 -10
  157. package/stryker.config.json +0 -19
  158. package/tsconfig.json +0 -19
  159. package/vitest.config.ts +0 -16
@@ -1,73 +0,0 @@
1
- import React, { useState, useEffect } from 'react'
2
- import { Box, Text, useInput } from 'ink'
3
- import type { MemoryOption } from '../types/index.js'
4
- import { theme } from './theme.js'
5
- import { useCIMode } from './CIContext.js'
6
-
7
- const MEMORY_OPTIONS: { id: MemoryOption; label: string; description: string }[] = [
8
- { id: 'engram', label: 'Engram', description: 'SQLite-backed persistent memory with MCP' },
9
- { id: 'obsidian-brain', label: 'Obsidian Brain', description: 'Obsidian vault with Kanban + Dataview' },
10
- { id: 'memory-simple', label: 'Simple Memory', description: 'File-based .project/ memory' },
11
- { id: 'none', label: 'None', description: 'Skip memory module' },
12
- ]
13
-
14
- interface Props {
15
- onConfirm: (memory: MemoryOption) => void
16
- }
17
-
18
- export default function MemorySelector({ onConfirm }: Props) {
19
- const isCI = useCIMode()
20
- const [cursor, setCursor] = useState(0)
21
-
22
- // Auto-confirm in CI mode
23
- useEffect(() => {
24
- if (isCI) {
25
- onConfirm(MEMORY_OPTIONS[cursor].id)
26
- }
27
- }, [isCI]) // eslint-disable-line react-hooks/exhaustive-deps
28
-
29
- useInput((_, key) => {
30
- if (key.upArrow) setCursor(c => Math.max(0, c - 1))
31
- if (key.downArrow) setCursor(c => Math.min(MEMORY_OPTIONS.length - 1, c + 1))
32
- if (key.return) {
33
- onConfirm(MEMORY_OPTIONS[cursor].id)
34
- }
35
- }, { isActive: !isCI })
36
-
37
- return (
38
- <Box flexDirection="column">
39
- <Text bold>Select memory module:</Text>
40
-
41
- <Box
42
- marginTop={1}
43
- flexDirection="column"
44
- borderStyle="single"
45
- borderLeft
46
- borderRight={false}
47
- borderTop={false}
48
- borderBottom={false}
49
- borderColor={theme.muted}
50
- paddingLeft={1}
51
- >
52
- {MEMORY_OPTIONS.map((m, i) => (
53
- <Box key={m.id}>
54
- <Text color={i === cursor ? theme.primary : 'white'}>
55
- {i === cursor ? '\u25b6 ' : ' '}
56
- {i === cursor ? '\u25c9' : '\u25cb'} {m.label}
57
- </Text>
58
- <Text color={theme.muted} dimColor> {m.description}</Text>
59
- </Box>
60
- ))}
61
- </Box>
62
-
63
- <Box marginTop={1} gap={2}>
64
- <Text color={theme.primary}>
65
- {MEMORY_OPTIONS[cursor].label}
66
- </Text>
67
- <Text color={theme.muted} dimColor>
68
- {'\u2191\u2193'} navigate Enter confirm
69
- </Text>
70
- </Box>
71
- </Box>
72
- )
73
- }
@@ -1,82 +0,0 @@
1
- import React, { useState, useEffect } from 'react'
2
- import { Box, Text, useInput } from 'ink'
3
- import path from 'path'
4
- import { theme } from './theme.js'
5
- import { useCIMode } from './CIContext.js'
6
-
7
- interface Props {
8
- defaultName: string
9
- onConfirm: (name: string, dir: string) => void
10
- }
11
-
12
- const VALID_NAME = /^[a-z0-9][a-z0-9._-]*$/
13
-
14
- function validateName(name: string): string | null {
15
- if (name.length === 0) return 'Name cannot be empty'
16
- if (name !== name.toLowerCase()) return 'Use lowercase only'
17
- if (name.includes(' ')) return 'No spaces allowed (use - or _)'
18
- if (!VALID_NAME.test(name)) return 'Use lowercase letters, numbers, hyphens, dots'
19
- if (name.length > 60) return 'Name too long (max 60 chars)'
20
- return null
21
- }
22
-
23
- export default function NameInput({ defaultName, onConfirm }: Props) {
24
- const isCI = useCIMode()
25
- const [value, setValue] = useState(defaultName)
26
- const trimmed = value.trim()
27
- const error = validateName(trimmed)
28
- const isValid = error === null && trimmed.length > 0
29
- const targetDir = path.resolve(process.cwd(), trimmed || '.')
30
-
31
- // Auto-confirm in CI mode
32
- useEffect(() => {
33
- if (isCI && isValid) {
34
- onConfirm(trimmed, targetDir)
35
- }
36
- }, [isCI]) // eslint-disable-line react-hooks/exhaustive-deps
37
-
38
- useInput((input, key) => {
39
- if (key.return && isValid) {
40
- onConfirm(trimmed, targetDir)
41
- } else if (key.backspace || key.delete) {
42
- setValue(v => v.slice(0, -1))
43
- } else if (input && !key.ctrl && !key.meta) {
44
- setValue(v => v + input)
45
- }
46
- }, { isActive: !isCI })
47
-
48
- return (
49
- <Box flexDirection="column">
50
- <Text bold>Project name:</Text>
51
- <Box marginTop={1}>
52
- <Text color={isValid ? theme.primary : theme.error}>{'\u25b6'} </Text>
53
- <Text>{value}</Text>
54
- <Text color={theme.muted}>{'\u2588'}</Text>
55
- </Box>
56
-
57
- {/* Validation feedback */}
58
- {trimmed.length > 0 && error && (
59
- <Box marginTop={1}>
60
- <Text color={theme.error}> {'\u2717'} {error}</Text>
61
- </Box>
62
- )}
63
- {isValid && (
64
- <Box marginTop={1}>
65
- <Text color={theme.success}> {'\u2713'} Valid name</Text>
66
- </Box>
67
- )}
68
-
69
- {/* Target directory */}
70
- <Box marginTop={1}>
71
- <Text color={theme.muted}> Will create: </Text>
72
- <Text color={isValid ? theme.primary : theme.muted} dimColor={!isValid}>
73
- {targetDir}
74
- </Text>
75
- </Box>
76
-
77
- <Box marginTop={1}>
78
- <Text color={theme.muted} dimColor>Enter to confirm</Text>
79
- </Box>
80
- </Box>
81
- )
82
- }
@@ -1,100 +0,0 @@
1
- import React, { useState, useEffect } from 'react'
2
- import { Box, Text, useInput } from 'ink'
3
- import { theme } from './theme.js'
4
- import { useCIMode } from './CIContext.js'
5
-
6
- interface OptionItem {
7
- id: string
8
- label: string
9
- description: string
10
- default: boolean
11
- }
12
-
13
- const OPTIONS: OptionItem[] = [
14
- { id: 'aiSync', label: 'AI Config Sync', description: 'Copy agents, skills, and configs to .ai-config/', default: true },
15
- { id: 'sdd', label: 'SDD (openspec/)', description: 'Spec-Driven Development workflow', default: true },
16
- { id: 'ghagga', label: 'GHAGGA Review', description: 'Multi-agent AI code review system', default: false },
17
- ]
18
-
19
- interface Props {
20
- onConfirm: (selected: { aiSync: boolean; sdd: boolean; ghagga: boolean }) => void
21
- presetGhagga?: boolean
22
- }
23
-
24
- export default function OptionSelector({ onConfirm, presetGhagga = false }: Props) {
25
- const isCI = useCIMode()
26
- const [cursor, setCursor] = useState(0)
27
- const [selected, setSelected] = useState<Set<string>>(() => {
28
- const defaults = new Set(OPTIONS.filter(o => o.default).map(o => o.id))
29
- if (presetGhagga) defaults.add('ghagga')
30
- return defaults
31
- })
32
-
33
- // Auto-confirm in CI mode with defaults
34
- useEffect(() => {
35
- if (isCI) {
36
- onConfirm({
37
- aiSync: selected.has('aiSync'),
38
- sdd: selected.has('sdd'),
39
- ghagga: selected.has('ghagga'),
40
- })
41
- }
42
- }, [isCI]) // eslint-disable-line react-hooks/exhaustive-deps
43
-
44
- useInput((input, key) => {
45
- if (key.upArrow) setCursor(c => Math.max(0, c - 1))
46
- if (key.downArrow) setCursor(c => Math.min(OPTIONS.length - 1, c + 1))
47
- if (input === ' ') {
48
- const opt = OPTIONS[cursor].id
49
- setSelected(prev => {
50
- const next = new Set(prev)
51
- next.has(opt) ? next.delete(opt) : next.add(opt)
52
- return next
53
- })
54
- }
55
- if (key.return) {
56
- onConfirm({
57
- aiSync: selected.has('aiSync'),
58
- sdd: selected.has('sdd'),
59
- ghagga: selected.has('ghagga'),
60
- })
61
- }
62
- }, { isActive: !isCI })
63
-
64
- return (
65
- <Box flexDirection="column">
66
- <Text bold>Select additional options:</Text>
67
-
68
- <Box
69
- marginTop={1}
70
- flexDirection="column"
71
- borderStyle="single"
72
- borderLeft
73
- borderRight={false}
74
- borderTop={false}
75
- borderBottom={false}
76
- borderColor={theme.muted}
77
- paddingLeft={1}
78
- >
79
- {OPTIONS.map((opt, i) => (
80
- <Box key={opt.id}>
81
- <Text color={i === cursor ? theme.primary : 'white'}>
82
- {i === cursor ? '\u25b6 ' : ' '}
83
- {selected.has(opt.id) ? '\u25c9' : '\u25cb'} {opt.label}
84
- </Text>
85
- <Text color={theme.muted} dimColor> {opt.description}</Text>
86
- </Box>
87
- ))}
88
- </Box>
89
-
90
- <Box marginTop={1} gap={2}>
91
- <Text color={selected.size > 0 ? theme.accent : theme.muted}>
92
- {selected.size} selected
93
- </Text>
94
- <Text color={theme.muted} dimColor>
95
- {'\u2191\u2193'} navigate Space toggle Enter confirm
96
- </Text>
97
- </Box>
98
- </Box>
99
- )
100
- }
@@ -1,88 +0,0 @@
1
- import React, { useEffect, useRef } from 'react'
2
- import { Box, Text } from 'ink'
3
- import Spinner from 'ink-spinner'
4
- import type { InitStep } from '../types/index.js'
5
- import { theme } from './theme.js'
6
-
7
- interface Props {
8
- steps: InitStep[]
9
- projectName?: string
10
- contextLine?: string
11
- onDone?: () => void
12
- }
13
-
14
- const STATUS_ICON: Record<string, string> = {
15
- pending: '\u25cb',
16
- done: '\u2713',
17
- error: '\u2717',
18
- skipped: '\u2013',
19
- }
20
-
21
- const STATUS_COLOR: Record<string, string> = {
22
- pending: theme.muted,
23
- running: theme.warning,
24
- done: theme.success,
25
- error: theme.error,
26
- skipped: theme.muted,
27
- }
28
-
29
- export default function Progress({ steps, projectName, contextLine, onDone }: Props) {
30
- const doneRef = useRef(false)
31
-
32
- const total = steps.length
33
- const completed = steps.filter(s => s.status === 'done' || s.status === 'skipped').length
34
- const hasError = steps.some(s => s.status === 'error')
35
- const allFinished = total > 0 && steps.every(
36
- s => s.status === 'done' || s.status === 'error' || s.status === 'skipped'
37
- )
38
-
39
- useEffect(() => {
40
- if (allFinished && !hasError && !doneRef.current && onDone) {
41
- doneRef.current = true
42
- const t = setTimeout(onDone, 600)
43
- return () => clearTimeout(t)
44
- }
45
- return undefined
46
- }, [allFinished, hasError, onDone])
47
-
48
- return (
49
- <Box flexDirection="column">
50
- {/* Context header */}
51
- <Box marginBottom={1} flexDirection="column">
52
- {contextLine && (
53
- <Text color={theme.muted}>
54
- {'Initializing: '}
55
- <Text color={theme.primary}>{contextLine}</Text>
56
- </Text>
57
- )}
58
- {total > 0 && (
59
- <Text color={theme.muted}>
60
- {'Progress: '}
61
- <Text color={completed === total ? theme.success : theme.warning}>
62
- {completed}/{total} steps
63
- </Text>
64
- </Text>
65
- )}
66
- </Box>
67
-
68
- <Box flexDirection="column">
69
- {steps.map(step => (
70
- <Box key={step.id} marginLeft={2}>
71
- {step.status === 'running' ? (
72
- <Text color={theme.warning}>
73
- <Spinner type="dots" />
74
- {' '}{step.label}
75
- {step.detail ? <Text color={theme.muted} dimColor> {step.detail}</Text> : null}
76
- </Text>
77
- ) : (
78
- <Text color={STATUS_COLOR[step.status] as any}>
79
- {STATUS_ICON[step.status]} {step.label}
80
- {step.detail ? <Text color={theme.muted} dimColor> {step.detail}</Text> : null}
81
- </Text>
82
- )}
83
- </Box>
84
- ))}
85
- </Box>
86
- </Box>
87
- )
88
- }
@@ -1,101 +0,0 @@
1
- import React, { useState, useEffect, useRef } from 'react'
2
- import { Box, Text, useInput } from 'ink'
3
- import { detectStack, STACK_LABELS } from '../lib/common.js'
4
- import type { Stack } from '../types/index.js'
5
- import { theme } from './theme.js'
6
- import { useCIMode } from './CIContext.js'
7
-
8
- const ALL_STACKS: Stack[] = ['node', 'python', 'go', 'rust', 'java-gradle', 'java-maven', 'elixir']
9
-
10
- interface Props {
11
- projectDir: string
12
- onConfirm: (stack: Stack) => void
13
- }
14
-
15
- export default function StackSelector({ projectDir, onConfirm }: Props) {
16
- const isCI = useCIMode()
17
- const autoConfirmed = useRef(false)
18
- const [cursor, setCursor] = useState(0)
19
- const [detectedStack, setDetectedStack] = useState<Stack | null>(null)
20
- const [detecting, setDetecting] = useState(true)
21
-
22
- useEffect(() => {
23
- detectStack(projectDir).then(result => {
24
- if (result) {
25
- setDetectedStack(result.stackType)
26
- const idx = ALL_STACKS.indexOf(result.stackType)
27
- if (idx >= 0) setCursor(idx)
28
- }
29
- setDetecting(false)
30
- })
31
- }, [projectDir])
32
-
33
- // Auto-confirm in CI mode once detection is done
34
- useEffect(() => {
35
- if (isCI && !detecting && !autoConfirmed.current) {
36
- autoConfirmed.current = true
37
- onConfirm(ALL_STACKS[cursor])
38
- }
39
- }, [isCI, detecting]) // eslint-disable-line react-hooks/exhaustive-deps
40
-
41
- useInput((input, key) => {
42
- if (detecting) return
43
- if (key.upArrow) setCursor(c => Math.max(0, c - 1))
44
- if (key.downArrow) setCursor(c => Math.min(ALL_STACKS.length - 1, c + 1))
45
- if (key.return) {
46
- onConfirm(ALL_STACKS[cursor])
47
- }
48
- }, { isActive: !isCI })
49
-
50
- if (detecting) {
51
- return (
52
- <Box>
53
- <Text color={theme.muted}>Detecting project stack...</Text>
54
- </Box>
55
- )
56
- }
57
-
58
- return (
59
- <Box flexDirection="column">
60
- <Text bold>Select project stack:</Text>
61
- {detectedStack && (
62
- <Text color={theme.success}>
63
- {' '}Auto-detected: {STACK_LABELS[detectedStack]}
64
- </Text>
65
- )}
66
-
67
- <Box
68
- marginTop={1}
69
- flexDirection="column"
70
- borderStyle="single"
71
- borderLeft
72
- borderRight={false}
73
- borderTop={false}
74
- borderBottom={false}
75
- borderColor={theme.muted}
76
- paddingLeft={1}
77
- >
78
- {ALL_STACKS.map((stack, i) => (
79
- <Box key={stack}>
80
- <Text color={i === cursor ? theme.primary : 'white'}>
81
- {i === cursor ? '\u25b6 ' : ' '}
82
- {stack === detectedStack ? '\u25c9' : '\u25cb'} {STACK_LABELS[stack]}
83
- {stack === detectedStack && (
84
- <Text color={theme.success} dimColor> (detected)</Text>
85
- )}
86
- </Text>
87
- </Box>
88
- ))}
89
- </Box>
90
-
91
- <Box marginTop={1} gap={2}>
92
- <Text color={theme.primary}>
93
- {STACK_LABELS[ALL_STACKS[cursor]]}
94
- </Text>
95
- <Text color={theme.muted} dimColor>
96
- {'\u2191\u2193'} navigate Enter confirm
97
- </Text>
98
- </Box>
99
- </Box>
100
- )
101
- }
@@ -1,134 +0,0 @@
1
- import React, { useEffect } from 'react'
2
- import { Box, Text, useApp, useInput } from 'ink'
3
- import type { InitStep } from '../types/index.js'
4
- import { theme } from './theme.js'
5
- import { useCIMode } from './CIContext.js'
6
-
7
- interface Props {
8
- steps: InitStep[]
9
- dryRun: boolean
10
- projectName: string
11
- stack?: string
12
- elapsedMs?: number
13
- }
14
-
15
- /** Map stack to the install command hint */
16
- function getInstallHint(stack?: string): string | null {
17
- switch (stack) {
18
- case 'node': return 'pnpm install'
19
- case 'python': return 'pip install -r requirements.txt'
20
- case 'go': return 'go mod tidy'
21
- case 'rust': return 'cargo build'
22
- case 'java-gradle': return './gradlew build'
23
- case 'java-maven': return 'mvn install'
24
- case 'elixir': return 'mix deps.get'
25
- default: return null
26
- }
27
- }
28
-
29
- export default function Summary({ steps, dryRun, projectName, stack, elapsedMs }: Props) {
30
- const { exit } = useApp()
31
- const isCI = useCIMode()
32
-
33
- const done = steps.filter(s => s.status === 'done').length
34
- const skipped = steps.filter(s => s.status === 'skipped').length
35
- const errors = steps.filter(s => s.status === 'error')
36
- const elapsed = elapsedMs != null
37
- ? `${(elapsedMs / 1000).toFixed(1)}s`
38
- : null
39
-
40
- // Auto-exit in CI mode
41
- useEffect(() => {
42
- if (isCI) {
43
- const t = setTimeout(() => exit(), 100)
44
- return () => clearTimeout(t)
45
- }
46
- return undefined
47
- }, [isCI, exit])
48
-
49
- useInput((_, key) => {
50
- if (key.return || key.escape) exit()
51
- }, { isActive: !isCI })
52
-
53
- const installHint = getInstallHint(stack)
54
-
55
- return (
56
- <Box flexDirection="column">
57
- {/* Title */}
58
- <Text bold color={errors.length > 0 ? theme.warning : theme.success}>
59
- {dryRun ? '\u25cb Dry run complete' : '\u2713 Project scaffolded'}
60
- {elapsed && <Text color={theme.muted}> Completed in {elapsed}</Text>}
61
- </Text>
62
-
63
- {/* Project info */}
64
- <Box marginTop={1}>
65
- <Text color={theme.muted}> Project: </Text>
66
- <Text color={theme.primary} bold>{projectName}</Text>
67
- {stack && <Text color={theme.muted}> ({stack})</Text>}
68
- </Box>
69
-
70
- {/* Dry run note */}
71
- {dryRun && (
72
- <Box marginTop={1}>
73
- <Text color={theme.warning} bold> No changes were made (dry run)</Text>
74
- </Box>
75
- )}
76
-
77
- {/* Step details */}
78
- <Box marginTop={1} flexDirection="column">
79
- {steps.map(step => (
80
- <Box key={step.id} marginLeft={2}>
81
- {step.status === 'done' ? (
82
- <Text color={theme.success}>
83
- {'\u2713'} {step.label}
84
- {step.detail ? <Text color={theme.muted} dimColor> {step.detail}</Text> : null}
85
- </Text>
86
- ) : step.status === 'skipped' ? (
87
- <Text color={theme.muted}>
88
- {'\u2013'} {step.label}
89
- {step.detail ? <Text dimColor> {step.detail}</Text> : null}
90
- </Text>
91
- ) : step.status === 'error' ? (
92
- <Text color={theme.error}>
93
- {'\u2717'} {step.label}
94
- {step.detail ? <Text color={theme.muted} dimColor> {step.detail}</Text> : null}
95
- </Text>
96
- ) : null}
97
- </Box>
98
- ))}
99
- </Box>
100
-
101
- {/* Totals */}
102
- <Box marginTop={1} flexDirection="column">
103
- <Text color={theme.success}> {'\u2713'} {done} steps completed</Text>
104
- {skipped > 0 && (
105
- <Text color={theme.muted}> {'\u2013'} {skipped} steps skipped</Text>
106
- )}
107
- {errors.length > 0 && (
108
- <Box flexDirection="column">
109
- <Text color={theme.error}> {'\u2717'} {errors.length} errors:</Text>
110
- {errors.map(e => (
111
- <Text key={`err-${e.id}`} color={theme.error}> {'\u2022'} {e.label}: {e.detail}</Text>
112
- ))}
113
- </Box>
114
- )}
115
- </Box>
116
-
117
- {/* Next steps */}
118
- {!dryRun && errors.length === 0 && (
119
- <Box marginTop={1} flexDirection="column">
120
- <Text color={theme.muted} bold> Next steps:</Text>
121
- <Text color={theme.primary}> cd {projectName}</Text>
122
- {installHint && <Text color={theme.primary}> {installHint}</Text>}
123
- <Text color={theme.primary}> npx javi-ai sync</Text>
124
- <Text color={theme.primary}> javi-forge doctor</Text>
125
- </Box>
126
- )}
127
-
128
- {/* Exit hint */}
129
- <Box marginTop={1}>
130
- <Text color={theme.muted} dimColor>Press Enter to exit</Text>
131
- </Box>
132
- </Box>
133
- )
134
- }
@@ -1,54 +0,0 @@
1
- import React, { useEffect } from 'react'
2
- import { Box, Text } from 'ink'
3
- import Spinner from 'ink-spinner'
4
- import Header from './Header.js'
5
- import { theme } from './theme.js'
6
- import { useCIMode } from './CIContext.js'
7
-
8
- interface Props {
9
- onDone: () => void
10
- }
11
-
12
- export default function Welcome({ onDone }: Props) {
13
- const isCI = useCIMode()
14
-
15
- useEffect(() => {
16
- // Skip welcome delay in CI mode
17
- const timer = setTimeout(onDone, isCI ? 0 : 1500)
18
- return () => clearTimeout(timer)
19
- }, [onDone, isCI])
20
-
21
- return (
22
- <Box flexDirection="column" padding={1}>
23
- <Header />
24
-
25
- <Box flexDirection="column" marginTop={1} marginLeft={2}>
26
- <Text>Bootstrap AI-ready projects with:</Text>
27
- <Box marginTop={1} flexDirection="column">
28
- <Text>
29
- <Text color={theme.primary}>{'\u25c6'} Templates </Text>
30
- <Text color={theme.muted}> Go, Java, Node, Python, Rust CI</Text>
31
- </Text>
32
- <Text>
33
- <Text color={theme.success}>{'\u25c6'} Memory </Text>
34
- <Text color={theme.muted}> Engram, Obsidian Brain</Text>
35
- </Text>
36
- <Text>
37
- <Text color={theme.accent}>{'\u25c6'} SDD </Text>
38
- <Text color={theme.muted}> Spec-Driven Development</Text>
39
- </Text>
40
- <Text>
41
- <Text color={theme.warning}>{'\u25c6'} Review </Text>
42
- <Text color={theme.muted}> GHAGGA code review</Text>
43
- </Text>
44
- </Box>
45
- <Box marginTop={1}>
46
- <Text color={theme.muted}>
47
- <Spinner type="dots" />
48
- {' '}Detecting your environment...
49
- </Text>
50
- </Box>
51
- </Box>
52
- </Box>
53
- )
54
- }
package/src/ui/theme.ts DELETED
@@ -1,10 +0,0 @@
1
- export const theme = {
2
- primary: '#f97316', // orange
3
- success: 'green',
4
- warning: 'yellow',
5
- error: 'red',
6
- muted: 'gray',
7
- accent: 'magenta',
8
- } as const
9
-
10
- export type ThemeColor = typeof theme[keyof typeof theme]
@@ -1,19 +0,0 @@
1
- {
2
- "testRunner": "vitest",
3
- "mutate": [
4
- "src/lib/frontmatter.ts",
5
- "src/lib/common.ts",
6
- "src/lib/template.ts",
7
- "src/commands/init.ts",
8
- "src/commands/doctor.ts",
9
- "src/commands/analyze.ts",
10
- "src/constants.ts"
11
- ],
12
- "plugins": [
13
- "@stryker-mutator/vitest-runner",
14
- "@stryker-mutator/typescript-checker"
15
- ],
16
- "checkers": ["typescript"],
17
- "tsconfigFile": "tsconfig.json",
18
- "thresholds": { "high": 80, "low": 60, "break": 50 }
19
- }
package/tsconfig.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "jsx": "react",
7
- "jsxFactory": "React.createElement",
8
- "outDir": "dist",
9
- "rootDir": "src",
10
- "strict": true,
11
- "esModuleInterop": true,
12
- "skipLibCheck": true,
13
- "declaration": true,
14
- "declarationMap": true,
15
- "sourceMap": true
16
- },
17
- "include": ["src/**/*"],
18
- "exclude": ["node_modules", "dist"]
19
- }