create-interview-cockpit 0.3.0 → 0.4.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/README.md CHANGED
@@ -57,6 +57,29 @@ AI_MODEL=gemini-2.5-flash
57
57
  GOOGLE_API_KEY=your-key-here
58
58
  ```
59
59
 
60
+ ## Upgrading
61
+
62
+ When a new version of the template is released, run this from inside your cockpit project directory:
63
+
64
+ ```bash
65
+ npx create-interview-cockpit upgrade
66
+ ```
67
+
68
+ The CLI will:
69
+ - Compare your installed version (`cockpit.json`) against the latest
70
+ - Overwrite `client/` and `server/` with the updated template files
71
+ - Merge any new scripts and dependencies into your `package.json` (your pinned versions are preserved)
72
+ - **Leave untouched:** `.env`, `data/` (your topics, questions, and context files), and `node_modules/`
73
+
74
+ After upgrading, reinstall dependencies and restart:
75
+
76
+ ```bash
77
+ npm install
78
+ cd client && npm install && cd ..
79
+ cd server && npm install && cd ..
80
+ npm run dev
81
+ ```
82
+
60
83
  ## License
61
84
 
62
85
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-interview-cockpit",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Scaffold a personal AI-powered interview prep cockpit",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,6 +15,8 @@
15
15
  "react-markdown": "^9.0.0",
16
16
  "react-syntax-highlighter": "^15.6.1",
17
17
  "remark-gfm": "^4.0.0",
18
+ "vizcraft": "^1.17.0",
19
+ "yaml": "^2.8.3",
18
20
  "zustand": "^5.0.0"
19
21
  },
20
22
  "devDependencies": {
@@ -5894,6 +5896,12 @@
5894
5896
  "url": "https://github.com/sponsors/jonschlinkert"
5895
5897
  }
5896
5898
  },
5899
+ "node_modules/vizcraft": {
5900
+ "version": "1.17.0",
5901
+ "resolved": "https://registry.npmjs.org/vizcraft/-/vizcraft-1.17.0.tgz",
5902
+ "integrity": "sha512-BCXjnWWEfMHWnAYD6DQvVXJFz0Q/Da1ukULXdMKIrZiNfblVVyYO/b/MWPO54cRzJESJZle4U52G1v2U/zRqQg==",
5903
+ "license": "MIT"
5904
+ },
5897
5905
  "node_modules/vscode-jsonrpc": {
5898
5906
  "version": "8.2.0",
5899
5907
  "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
@@ -5959,6 +5967,21 @@
5959
5967
  "dev": true,
5960
5968
  "license": "ISC"
5961
5969
  },
5970
+ "node_modules/yaml": {
5971
+ "version": "2.8.3",
5972
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
5973
+ "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
5974
+ "license": "ISC",
5975
+ "bin": {
5976
+ "yaml": "bin.mjs"
5977
+ },
5978
+ "engines": {
5979
+ "node": ">= 14.6"
5980
+ },
5981
+ "funding": {
5982
+ "url": "https://github.com/sponsors/eemeli"
5983
+ }
5984
+ },
5962
5985
  "node_modules/zod": {
5963
5986
  "version": "4.3.6",
5964
5987
  "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
@@ -17,6 +17,8 @@
17
17
  "react-markdown": "^9.0.0",
18
18
  "react-syntax-highlighter": "^15.6.1",
19
19
  "remark-gfm": "^4.0.0",
20
+ "vizcraft": "^1.17.0",
21
+ "yaml": "^2.8.3",
20
22
  "zustand": "^5.0.0"
21
23
  },
22
24
  "devDependencies": {
@@ -4,12 +4,14 @@ import Sidebar from "./components/Sidebar";
4
4
  import ChatView from "./components/ChatView";
5
5
  import CodeContextPanel from "./components/CodeContextPanel";
6
6
  import FileViewerModal from "./components/FileViewerModal";
7
- import { Code, Plane, PanelLeftClose, PanelLeft } from "lucide-react";
7
+ import AiSettingsModal from "./components/AiSettingsModal";
8
+ import { Code, Plane, PanelLeftClose, PanelLeft, Settings } from "lucide-react";
8
9
 
9
10
  export default function App() {
10
11
  const {
11
12
  fetchTopics,
12
13
  fetchWorkspaces,
14
+ fetchAiSettings,
13
15
  fetchQuestions,
14
16
  selectQuestion,
15
17
  currentQuestion,
@@ -19,12 +21,16 @@ export default function App() {
19
21
  toggleSidebar,
20
22
  viewingFile,
21
23
  closeFileViewer,
24
+ showSettings,
25
+ openSettings,
26
+ closeSettings,
22
27
  } = useStore();
23
28
 
24
29
  useEffect(() => {
25
30
  const init = async () => {
26
31
  await fetchWorkspaces();
27
32
  await fetchTopics();
33
+ fetchAiSettings();
28
34
  // Restore last-viewed question after page refresh
29
35
  const topicId = sessionStorage.getItem("lastTopicId");
30
36
  const questionId = sessionStorage.getItem("lastQuestionId");
@@ -71,17 +77,26 @@ export default function App() {
71
77
  {currentQuestion ? currentQuestion.title : "Interview Cockpit"}
72
78
  </span>
73
79
  </div>
74
- <button
75
- onClick={toggleCodePanel}
76
- className={`p-1.5 rounded transition-colors ${
77
- showCodePanel
78
- ? "bg-cyan-500/20 text-cyan-400"
79
- : "text-slate-500 hover:text-slate-300"
80
- }`}
81
- title="Toggle code context"
82
- >
83
- <Code className="w-4 h-4" />
84
- </button>
80
+ <div className="flex items-center gap-1">
81
+ <button
82
+ onClick={openSettings}
83
+ className="p-1.5 rounded transition-colors text-slate-500 hover:text-slate-300 hover:bg-slate-800"
84
+ title="AI Settings"
85
+ >
86
+ <Settings className="w-4 h-4" />
87
+ </button>
88
+ <button
89
+ onClick={toggleCodePanel}
90
+ className={`p-1.5 rounded transition-colors ${
91
+ showCodePanel
92
+ ? "bg-cyan-500/20 text-cyan-400"
93
+ : "text-slate-500 hover:text-slate-300"
94
+ }`}
95
+ title="Toggle code context"
96
+ >
97
+ <Code className="w-4 h-4" />
98
+ </button>
99
+ </div>
85
100
  </header>
86
101
 
87
102
  {/* Content area */}
@@ -124,6 +139,7 @@ export default function App() {
124
139
  {viewingFile && (
125
140
  <FileViewerModal filePath={viewingFile} onClose={closeFileViewer} />
126
141
  )}
142
+ {showSettings && <AiSettingsModal />}
127
143
  </div>
128
144
  );
129
145
  }
@@ -2,6 +2,44 @@ import type { Topic, Question, ContextFile, WorkspacesRegistry } from "./types";
2
2
 
3
3
  const BASE = "/api";
4
4
 
5
+ export interface PromptGroup {
6
+ /** Display name shown in the settings UI. */
7
+ label: string;
8
+ /** Optional helper text shown below the section header. */
9
+ description?: string;
10
+ /** Which option key is active by default in new chats. */
11
+ default: string;
12
+ /** Map of option key -> prompt text appended to the user message. */
13
+ options: Record<string, string>;
14
+ }
15
+
16
+ export interface AiSettings {
17
+ systemPrompt: string;
18
+ responseProfiles: Record<
19
+ string,
20
+ { maxOutputTokens: number; maxSteps: number }
21
+ >;
22
+ vizGuide: string;
23
+ /** All user-selectable prompt groups. Add new entries here to extend the UI. */
24
+ promptGroups: Record<string, PromptGroup>;
25
+ }
26
+
27
+ export async function fetchAiSettings(): Promise<AiSettings> {
28
+ const res = await fetch(`${BASE}/settings`);
29
+ return res.json();
30
+ }
31
+
32
+ export async function saveAiSettings(
33
+ patch: Partial<AiSettings>,
34
+ ): Promise<AiSettings> {
35
+ const res = await fetch(`${BASE}/settings`, {
36
+ method: "PATCH",
37
+ headers: { "Content-Type": "application/json" },
38
+ body: JSON.stringify(patch),
39
+ });
40
+ return res.json();
41
+ }
42
+
5
43
  export async function fetchTopics(): Promise<Topic[]> {
6
44
  const res = await fetch(`${BASE}/topics`);
7
45
  return res.json();
@@ -81,6 +119,7 @@ export async function createQuestion(
81
119
 
82
120
  export async function fetchQuestion(id: string): Promise<Question> {
83
121
  const res = await fetch(`${BASE}/questions/${id}`);
122
+ if (!res.ok) throw new Error(`Question not found: ${id}`);
84
123
  return res.json();
85
124
  }
86
125