sourceloop 0.1.1

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 (173) hide show
  1. package/README.md +401 -0
  2. package/dist/commands/attach.d.ts +2 -0
  3. package/dist/commands/attach.js +103 -0
  4. package/dist/commands/attach.js.map +1 -0
  5. package/dist/commands/auth.d.ts +2 -0
  6. package/dist/commands/auth.js +33 -0
  7. package/dist/commands/auth.js.map +1 -0
  8. package/dist/commands/chrome.d.ts +2 -0
  9. package/dist/commands/chrome.js +30 -0
  10. package/dist/commands/chrome.js.map +1 -0
  11. package/dist/commands/compose.d.ts +2 -0
  12. package/dist/commands/compose.js +14 -0
  13. package/dist/commands/compose.js.map +1 -0
  14. package/dist/commands/doctor.d.ts +2 -0
  15. package/dist/commands/doctor.js +15 -0
  16. package/dist/commands/doctor.js.map +1 -0
  17. package/dist/commands/import-latest.d.ts +2 -0
  18. package/dist/commands/import-latest.js +42 -0
  19. package/dist/commands/import-latest.js.map +1 -0
  20. package/dist/commands/ingest.d.ts +2 -0
  21. package/dist/commands/ingest.js +14 -0
  22. package/dist/commands/ingest.js.map +1 -0
  23. package/dist/commands/init.d.ts +2 -0
  24. package/dist/commands/init.js +24 -0
  25. package/dist/commands/init.js.map +1 -0
  26. package/dist/commands/notebook-bind.d.ts +2 -0
  27. package/dist/commands/notebook-bind.js +39 -0
  28. package/dist/commands/notebook-bind.js.map +1 -0
  29. package/dist/commands/notebook-create.d.ts +2 -0
  30. package/dist/commands/notebook-create.js +39 -0
  31. package/dist/commands/notebook-create.js.map +1 -0
  32. package/dist/commands/notebook-import.d.ts +2 -0
  33. package/dist/commands/notebook-import.js +39 -0
  34. package/dist/commands/notebook-import.js.map +1 -0
  35. package/dist/commands/notebook-source.d.ts +2 -0
  36. package/dist/commands/notebook-source.js +71 -0
  37. package/dist/commands/notebook-source.js.map +1 -0
  38. package/dist/commands/plan.d.ts +9 -0
  39. package/dist/commands/plan.js +59 -0
  40. package/dist/commands/plan.js.map +1 -0
  41. package/dist/commands/run.d.ts +2 -0
  42. package/dist/commands/run.js +76 -0
  43. package/dist/commands/run.js.map +1 -0
  44. package/dist/commands/status.d.ts +2 -0
  45. package/dist/commands/status.js +15 -0
  46. package/dist/commands/status.js.map +1 -0
  47. package/dist/commands/topic.d.ts +2 -0
  48. package/dist/commands/topic.js +53 -0
  49. package/dist/commands/topic.js.map +1 -0
  50. package/dist/core/attach/launch-managed-chrome.d.ts +27 -0
  51. package/dist/core/attach/launch-managed-chrome.js +136 -0
  52. package/dist/core/attach/launch-managed-chrome.js.map +1 -0
  53. package/dist/core/attach/manage-targets.d.ts +49 -0
  54. package/dist/core/attach/manage-targets.js +179 -0
  55. package/dist/core/attach/manage-targets.js.map +1 -0
  56. package/dist/core/ingest/frontmatter.d.ts +4 -0
  57. package/dist/core/ingest/frontmatter.js +30 -0
  58. package/dist/core/ingest/frontmatter.js.map +1 -0
  59. package/dist/core/ingest/html-to-markdown.d.ts +5 -0
  60. package/dist/core/ingest/html-to-markdown.js +53 -0
  61. package/dist/core/ingest/html-to-markdown.js.map +1 -0
  62. package/dist/core/ingest/ingest-source.d.ts +11 -0
  63. package/dist/core/ingest/ingest-source.js +115 -0
  64. package/dist/core/ingest/ingest-source.js.map +1 -0
  65. package/dist/core/notebooklm/adapter.d.ts +17 -0
  66. package/dist/core/notebooklm/adapter.js +2 -0
  67. package/dist/core/notebooklm/adapter.js.map +1 -0
  68. package/dist/core/notebooklm/auth.d.ts +30 -0
  69. package/dist/core/notebooklm/auth.js +105 -0
  70. package/dist/core/notebooklm/auth.js.map +1 -0
  71. package/dist/core/notebooklm/browser-agent-adapter.d.ts +21 -0
  72. package/dist/core/notebooklm/browser-agent-adapter.js +37 -0
  73. package/dist/core/notebooklm/browser-agent-adapter.js.map +1 -0
  74. package/dist/core/notebooklm/browser-agent.d.ts +121 -0
  75. package/dist/core/notebooklm/browser-agent.js +1604 -0
  76. package/dist/core/notebooklm/browser-agent.js.map +1 -0
  77. package/dist/core/notebooklm/config.d.ts +20 -0
  78. package/dist/core/notebooklm/config.js +133 -0
  79. package/dist/core/notebooklm/config.js.map +1 -0
  80. package/dist/core/notebooklm/fixture-adapter.d.ts +13 -0
  81. package/dist/core/notebooklm/fixture-adapter.js +32 -0
  82. package/dist/core/notebooklm/fixture-adapter.js.map +1 -0
  83. package/dist/core/notebooklm/response-extraction.d.ts +23 -0
  84. package/dist/core/notebooklm/response-extraction.js +348 -0
  85. package/dist/core/notebooklm/response-extraction.js.map +1 -0
  86. package/dist/core/notebooks/bind-notebook.d.ts +21 -0
  87. package/dist/core/notebooks/bind-notebook.js +95 -0
  88. package/dist/core/notebooks/bind-notebook.js.map +1 -0
  89. package/dist/core/notebooks/manage-managed-notebooks.d.ts +70 -0
  90. package/dist/core/notebooks/manage-managed-notebooks.js +491 -0
  91. package/dist/core/notebooks/manage-managed-notebooks.js.map +1 -0
  92. package/dist/core/notebooks/manage-notebook-source-manifests.d.ts +25 -0
  93. package/dist/core/notebooks/manage-notebook-source-manifests.js +127 -0
  94. package/dist/core/notebooks/manage-notebook-source-manifests.js.map +1 -0
  95. package/dist/core/operator/workspace-operator.d.ts +82 -0
  96. package/dist/core/operator/workspace-operator.js +610 -0
  97. package/dist/core/operator/workspace-operator.js.map +1 -0
  98. package/dist/core/outputs/compose-run.d.ts +11 -0
  99. package/dist/core/outputs/compose-run.js +98 -0
  100. package/dist/core/outputs/compose-run.js.map +1 -0
  101. package/dist/core/runs/load-artifacts.d.ts +14 -0
  102. package/dist/core/runs/load-artifacts.js +51 -0
  103. package/dist/core/runs/load-artifacts.js.map +1 -0
  104. package/dist/core/runs/question-planner.d.ts +20 -0
  105. package/dist/core/runs/question-planner.js +276 -0
  106. package/dist/core/runs/question-planner.js.map +1 -0
  107. package/dist/core/runs/render-run-note.d.ts +13 -0
  108. package/dist/core/runs/render-run-note.js +111 -0
  109. package/dist/core/runs/render-run-note.js.map +1 -0
  110. package/dist/core/runs/run-qa.d.ts +28 -0
  111. package/dist/core/runs/run-qa.js +393 -0
  112. package/dist/core/runs/run-qa.js.map +1 -0
  113. package/dist/core/topics/manage-topics.d.ts +27 -0
  114. package/dist/core/topics/manage-topics.js +314 -0
  115. package/dist/core/topics/manage-topics.js.map +1 -0
  116. package/dist/core/vault/notes.d.ts +29 -0
  117. package/dist/core/vault/notes.js +147 -0
  118. package/dist/core/vault/notes.js.map +1 -0
  119. package/dist/core/vault/paths.d.ts +31 -0
  120. package/dist/core/vault/paths.js +44 -0
  121. package/dist/core/vault/paths.js.map +1 -0
  122. package/dist/core/workspace/bootstrap.d.ts +16 -0
  123. package/dist/core/workspace/bootstrap.js +443 -0
  124. package/dist/core/workspace/bootstrap.js.map +1 -0
  125. package/dist/core/workspace/constants.d.ts +3 -0
  126. package/dist/core/workspace/constants.js +16 -0
  127. package/dist/core/workspace/constants.js.map +1 -0
  128. package/dist/core/workspace/init-workspace.d.ts +15 -0
  129. package/dist/core/workspace/init-workspace.js +86 -0
  130. package/dist/core/workspace/init-workspace.js.map +1 -0
  131. package/dist/core/workspace/load-workspace.d.ts +6 -0
  132. package/dist/core/workspace/load-workspace.js +51 -0
  133. package/dist/core/workspace/load-workspace.js.map +1 -0
  134. package/dist/core/workspace/schema.d.ts +19 -0
  135. package/dist/core/workspace/schema.js +19 -0
  136. package/dist/core/workspace/schema.js.map +1 -0
  137. package/dist/index.d.ts +2 -0
  138. package/dist/index.js +41 -0
  139. package/dist/index.js.map +1 -0
  140. package/dist/lib/cli-output.d.ts +2 -0
  141. package/dist/lib/cli-output.js +7 -0
  142. package/dist/lib/cli-output.js.map +1 -0
  143. package/dist/lib/obsidian.d.ts +4 -0
  144. package/dist/lib/obsidian.js +23 -0
  145. package/dist/lib/obsidian.js.map +1 -0
  146. package/dist/lib/slugify.d.ts +1 -0
  147. package/dist/lib/slugify.js +10 -0
  148. package/dist/lib/slugify.js.map +1 -0
  149. package/dist/lib/write-json.d.ts +1 -0
  150. package/dist/lib/write-json.js +5 -0
  151. package/dist/lib/write-json.js.map +1 -0
  152. package/dist/schemas/attach.d.ts +118 -0
  153. package/dist/schemas/attach.js +33 -0
  154. package/dist/schemas/attach.js.map +1 -0
  155. package/dist/schemas/managed-notebook.d.ts +47 -0
  156. package/dist/schemas/managed-notebook.js +30 -0
  157. package/dist/schemas/managed-notebook.js.map +1 -0
  158. package/dist/schemas/notebook-source.d.ts +31 -0
  159. package/dist/schemas/notebook-source.js +23 -0
  160. package/dist/schemas/notebook-source.js.map +1 -0
  161. package/dist/schemas/notebook.d.ts +26 -0
  162. package/dist/schemas/notebook.js +18 -0
  163. package/dist/schemas/notebook.js.map +1 -0
  164. package/dist/schemas/run.d.ts +169 -0
  165. package/dist/schemas/run.js +80 -0
  166. package/dist/schemas/run.js.map +1 -0
  167. package/dist/schemas/source.d.ts +18 -0
  168. package/dist/schemas/source.js +13 -0
  169. package/dist/schemas/source.js.map +1 -0
  170. package/dist/schemas/topic.d.ts +37 -0
  171. package/dist/schemas/topic.js +25 -0
  172. package/dist/schemas/topic.js.map +1 -0
  173. package/package.json +44 -0
@@ -0,0 +1,443 @@
1
+ import { access, mkdir, rm, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export const SUPPORTED_AGENT_BOOTSTRAPS = ["codex", "claude", "gemini"];
4
+ export async function validateWorkspaceAgentBootstrap(input) {
5
+ switch (input.ai) {
6
+ case "codex": {
7
+ const skillDir = getSkillDir(input.rootDir, input.ai);
8
+ if (!input.force && (await pathExists(skillDir))) {
9
+ throw new Error(`Codex bootstrap already exists at ${skillDir}. Re-run with --force to overwrite the generated scaffold.`);
10
+ }
11
+ return;
12
+ }
13
+ case "claude": {
14
+ const skillDir = getSkillDir(input.rootDir, input.ai);
15
+ if (!input.force && (await pathExists(skillDir))) {
16
+ throw new Error(`Claude bootstrap already exists at ${skillDir}. Re-run with --force to overwrite the generated scaffold.`);
17
+ }
18
+ return;
19
+ }
20
+ case "gemini": {
21
+ const skillDir = getSkillDir(input.rootDir, input.ai);
22
+ if (!input.force && (await pathExists(skillDir))) {
23
+ throw new Error(`Gemini bootstrap already exists at ${skillDir}. Re-run with --force to overwrite the generated scaffold.`);
24
+ }
25
+ return;
26
+ }
27
+ default:
28
+ throw new Error(`Unsupported AI bootstrap target: ${input.ai}`);
29
+ }
30
+ }
31
+ export async function bootstrapWorkspaceAgent(input) {
32
+ await validateWorkspaceAgentBootstrap(input);
33
+ switch (input.ai) {
34
+ case "codex":
35
+ return bootstrapCodexWorkspace(input.rootDir, input.force);
36
+ case "claude":
37
+ return bootstrapClaudeWorkspace(input.rootDir, input.force);
38
+ case "gemini":
39
+ return bootstrapGeminiWorkspace(input.rootDir, input.force);
40
+ default:
41
+ throw new Error(`Unsupported AI bootstrap target: ${input.ai}`);
42
+ }
43
+ }
44
+ async function bootstrapCodexWorkspace(rootDir, force) {
45
+ const { skillDir, referencesDir, skillPath, playbookPath } = getBootstrapPaths(rootDir, "codex");
46
+ if (force && (await pathExists(skillDir))) {
47
+ await rm(skillDir, { recursive: true, force: true });
48
+ }
49
+ await mkdir(referencesDir, { recursive: true });
50
+ await writeFile(skillPath, buildCodexSkillMarkdown(), "utf8");
51
+ await writeFile(playbookPath, buildCodexPlaybookReference(), "utf8");
52
+ return {
53
+ ai: "codex",
54
+ created: [
55
+ path.relative(rootDir, skillPath),
56
+ path.relative(rootDir, playbookPath)
57
+ ]
58
+ };
59
+ }
60
+ function buildCodexSkillMarkdown() {
61
+ return `---
62
+ name: sourceloop-operator
63
+ description: Operate a SourceLoop workspace with the standard research loop. Use when working inside a SourceLoop project and you need to inspect state, prepare NotebookLM, choose the right kickoff path, import or declare evidence, plan questions, and run bounded research passes.
64
+ ---
65
+
66
+ Use this skill when the current project is a SourceLoop workspace.
67
+
68
+ ## Core loop
69
+
70
+ 1. Run \`sourceloop status --json\`
71
+ 2. Run \`sourceloop doctor --json\`
72
+ 3. Fix blocking prerequisites before planning or running
73
+ 4. Prepare the managed browser before notebook creation or execution
74
+ 5. Choose the kickoff path:
75
+ - no topic provided
76
+ - topic only
77
+ - topic plus sources
78
+ - existing NotebookLM URL
79
+ 6. Prepare notebook before evidence import or declaration
80
+ 7. Require usable evidence before planning
81
+ 8. Execution defaults:
82
+ - planning defaults to 10 questions unless the user asked for a different count
83
+ - once a 10-question batch is planned, prefer running the full batch unless the user explicitly asked for a smaller partial pass
84
+ 9. Re-check \`status --json\` after each meaningful step
85
+ 10. If a NotebookLM step may take a while, say so briefly before waiting
86
+ 11. If a run command already has a chosen \`--limit\`, let that command finish its full requested scope before asking what to do next
87
+
88
+ ## NotebookLM entry rules
89
+
90
+ - Only verify deterministic entry checks:
91
+ - NotebookLM home or target notebook opens
92
+ - the user is logged in
93
+ - create/bind flow is reachable
94
+ - If these checks fail, do not wander through the UI.
95
+ - Stop and ask the user to fix login, permissions, or landing-page state.
96
+ - If only another Chrome is available, do not silently continue on that path.
97
+ - Ask the user whether to keep going with that Chrome or switch back to the SourceLoop browser first.
98
+
99
+ ## Kickoff paths
100
+
101
+ - Topic only:
102
+ - create the topic if needed
103
+ - confirm the planned question count at kickoff (default: 10)
104
+ - prepare attached Chrome
105
+ - create a managed notebook
106
+ - ask the user which sources to import before planning
107
+ - No topic provided:
108
+ - ask the user which topic to research
109
+ - mention that the default planned question count is 10 and they can change it now
110
+ - do not create notebooks, import sources, or plan questions yet
111
+ - Topic plus sources:
112
+ - create the topic if needed
113
+ - confirm the planned question count at kickoff (default: 10)
114
+ - prepare attached Chrome
115
+ - create a managed notebook
116
+ - import the provided files or URLs
117
+ - plan only after evidence is usable
118
+ - Existing NotebookLM URL:
119
+ - bind the existing notebook
120
+ - declare notebook-backed evidence if the sources already exist there
121
+ - otherwise ask which sources still need to be added
122
+ - do not search for replacement source materials unless the user explicitly asks you to find them
123
+ - if only another Chrome is available, ask the user before continuing
124
+
125
+ ## Command selection
126
+
127
+ - No topic: \`sourceloop topic create ...\`
128
+ - User asked to start research without a topic: ask which topic to research before doing anything else
129
+ - When asking for the topic, also mention that the default planned question count is 10 and the user can override it
130
+ - No trusted isolated Chrome target: \`sourceloop chrome launch\`
131
+ - Treat \`sourceloop chrome launch\` as the visible setup step for login and first NotebookLM checks
132
+ - Managed isolated Chrome target exists but is not validated yet: \`sourceloop attach validate <target>\`
133
+ - Topic only and no notebook yet: \`sourceloop notebook-create ...\` then ask for source inputs
134
+ - Topic plus sources and no notebook yet: \`sourceloop notebook-create ...\`
135
+ - Existing NotebookLM URL: \`sourceloop notebook-bind ...\` then \`sourceloop notebook-source declare ...\`
136
+ - Local source files: \`sourceloop ingest ...\` then \`sourceloop notebook-import --source-id ...\` (also for the first source on an empty managed notebook)
137
+ - Remote URLs: \`sourceloop notebook-import --url ...\` (also for the first source on an empty managed notebook)
138
+ - For SourceLoop-managed notebooks, treat the requested notebook title as a label only and read the returned binding id from JSON or \`status --json\`
139
+ - Ready topic with no run: \`sourceloop plan ... --max-questions 10 --json\`
140
+ - Planned or incomplete run: \`sourceloop run ... --json\`
141
+ - Existing latest answer only: \`sourceloop import-latest ...\`
142
+ - Treat \`--limit\` as the execution scope for one run command. Do not stop halfway through that requested limit just to ask again.
143
+
144
+ ## Safety
145
+
146
+ - Do not skip \`status --json\` and \`doctor --json\`
147
+ - Do not freestyle inside NotebookLM when the first entry checks fail
148
+ - Treat shared or unknown Chrome profile isolation as a warning that should be surfaced before more NotebookLM work
149
+ - Do not silently fall back to another Chrome session
150
+ - Ask the user before continuing with a non-SourceLoop browser
151
+ - Do not autonomously search the web or choose source materials unless the user explicitly asked you to find sources
152
+ - Do not run \`plan\` without usable evidence
153
+ - Do not run \`run\` without a notebook binding and planned run
154
+ - Do not use \`--question-id\` and \`--from-question\` together
155
+ - Do not impose a partial run limit by default when the planned batch should be executed end to end
156
+ - Do not turn bounded execution into per-question interruptions unless the user explicitly asked for checkpoint-style approval
157
+
158
+ For the full operator flow, read [references/playbook.md](references/playbook.md).
159
+ `;
160
+ }
161
+ function buildCodexPlaybookReference() {
162
+ return `# SourceLoop Codex Playbook
163
+
164
+ ## Preferred order
165
+
166
+ 1. \`sourceloop status --json\`
167
+ 2. \`sourceloop doctor --json\`
168
+ 3. topic preparation
169
+ 4. managed isolated Chrome / NotebookLM session preparation
170
+ 5. \`chrome launch\`
171
+ 6. \`attach validate\`
172
+ 7. \`notebook-create\` or \`notebook-bind\`
173
+ 8. \`ingest\`, \`notebook-import\`, or \`notebook-source declare\`
174
+ 9. \`status --json\` and \`doctor --json\` again
175
+ 10. \`plan --max-questions 10 --json\`
176
+ 11. \`run --json\`
177
+
178
+ ## Start conditions
179
+
180
+ The operator should classify the user's first request into one of these:
181
+
182
+ 1. no topic provided
183
+ 2. topic only
184
+ 3. topic plus sources
185
+ 4. existing NotebookLM URL
186
+
187
+ ## First-entry rules for NotebookLM
188
+
189
+ - Check only the expected entry state:
190
+ - NotebookLM home opens
191
+ - login is complete
192
+ - create or bind flow is reachable
193
+ - If any of these checks fail, do not explore the UI further.
194
+ - Stop and ask the user to fix login, permissions, or landing-page state.
195
+ - If only another Chrome is available, do not silently continue on that path.
196
+ - Ask the user whether to keep going with that Chrome or switch back to the SourceLoop browser first.
197
+
198
+ ## Decision rules
199
+
200
+ - If doctor has errors, resolve them first.
201
+ - Prefer \`sourceloop chrome launch\` so NotebookLM research uses a SourceLoop-managed isolated profile instead of a shared default browser profile.
202
+ - Use \`chrome launch\` as the visible setup step, then keep later notebook actions hidden unless you need \`--show-browser\` for debugging.
203
+ - Prefer URL-less \`sourceloop attach validate <target>\` before notebook creation when only NotebookLM home readiness is needed.
204
+ - If the user did not provide a topic, ask for the topic first and mention that planning defaults to 10 questions unless they want another count.
205
+ - If no trusted isolated Chrome target exists, launch browser state before notebook actions.
206
+ - If only another Chrome is available, ask the user whether to keep going with that Chrome before using it.
207
+ - If the user provided only a topic, confirm the question count (default: 10), create a managed notebook, and ask which sources to import.
208
+ - If the user did not provide sources, do not search for or choose source materials unless the user explicitly asked you to find sources.
209
+ - If the user provided topic plus sources, create a managed notebook and import those sources.
210
+ - If the user provided a NotebookLM URL, bind the existing notebook and continue from its source state.
211
+ - If a run already exists, resume it before creating a new pass.
212
+ - If the user planned 10 questions and did not ask for a partial pass, run the full remaining batch instead of adding a default \`--limit\`.
213
+ - If you already chose a bounded run command such as \`run --limit 1\`, let that command complete before asking whether to continue with another run.
214
+ - Tell the user that NotebookLM actions can take a bit before you wait on them, and if the wait becomes long, ask whether to keep waiting or just report the current state.
215
+ `;
216
+ }
217
+ async function bootstrapClaudeWorkspace(rootDir, force) {
218
+ const { skillDir, referencesDir, skillPath, playbookPath } = getBootstrapPaths(rootDir, "claude");
219
+ if (force && (await pathExists(skillDir))) {
220
+ await rm(skillDir, { recursive: true, force: true });
221
+ }
222
+ await mkdir(referencesDir, { recursive: true });
223
+ await writeFile(skillPath, buildClaudeSkillMarkdown(), "utf8");
224
+ await writeFile(playbookPath, buildSharedPlaybookReference(), "utf8");
225
+ return {
226
+ ai: "claude",
227
+ created: [
228
+ path.relative(rootDir, skillPath),
229
+ path.relative(rootDir, playbookPath)
230
+ ]
231
+ };
232
+ }
233
+ async function bootstrapGeminiWorkspace(rootDir, force) {
234
+ const { skillDir, referencesDir, skillPath, playbookPath } = getBootstrapPaths(rootDir, "gemini");
235
+ if (force && (await pathExists(skillDir))) {
236
+ await rm(skillDir, { recursive: true, force: true });
237
+ }
238
+ await mkdir(referencesDir, { recursive: true });
239
+ await writeFile(skillPath, buildGeminiSkillMarkdown(), "utf8");
240
+ await writeFile(playbookPath, buildSharedPlaybookReference(), "utf8");
241
+ return {
242
+ ai: "gemini",
243
+ created: [
244
+ path.relative(rootDir, skillPath),
245
+ path.relative(rootDir, playbookPath)
246
+ ]
247
+ };
248
+ }
249
+ function buildClaudeSkillMarkdown() {
250
+ return `---
251
+ name: sourceloop-operator
252
+ description: Operate a SourceLoop workspace with the standard research loop. Use when working inside a SourceLoop project and you need to inspect state, prepare NotebookLM, choose the right kickoff path, import or declare evidence, plan questions, and run bounded research passes.
253
+ ---
254
+
255
+ Use this skill when the current project is a SourceLoop workspace.
256
+
257
+ ## Core loop
258
+
259
+ 1. Run \`sourceloop status --json\`
260
+ 2. Run \`sourceloop doctor --json\`
261
+ 3. Fix blocking prerequisites before planning or running
262
+ 4. Prepare the managed browser before notebook creation or execution
263
+ 5. Choose the kickoff path:
264
+ - no topic provided
265
+ - topic only
266
+ - topic plus sources
267
+ - existing NotebookLM URL
268
+ 6. Prepare notebook before evidence import or declaration
269
+ 7. Require usable evidence before planning
270
+ 8. Planning defaults to 10 questions unless the user asked for another count
271
+ 9. Once a 10-question batch is planned, prefer running the full batch unless the user explicitly asked for a smaller partial pass
272
+ 10. Re-check \`sourceloop status --json\` after each meaningful step
273
+
274
+ ## NotebookLM entry rules
275
+
276
+ - Only verify deterministic entry checks:
277
+ - NotebookLM home or target notebook opens
278
+ - the user is logged in
279
+ - create or bind flow is reachable
280
+ - If these checks fail, do not wander through the UI.
281
+ - Stop and ask the user to fix login, permissions, or landing-page state.
282
+ - If only another Chrome is available, do not silently continue on that path.
283
+ - Ask the user whether to keep going with that Chrome or switch back to the SourceLoop browser first.
284
+
285
+ ## Command selection
286
+
287
+ - No trusted isolated Chrome target: \`sourceloop chrome launch\`
288
+ - Managed isolated Chrome target exists but is not validated yet: \`sourceloop attach validate <target>\`
289
+ - Topic only and no notebook yet: \`sourceloop notebook-create ...\`, then ask which sources to import
290
+ - Topic plus sources and no notebook yet: \`sourceloop notebook-create ...\`, then import the provided files or URLs
291
+ - Existing NotebookLM URL: \`sourceloop notebook-bind ...\`, then \`sourceloop notebook-source declare ...\`
292
+ - Local source files: \`sourceloop ingest ...\`, then \`sourceloop notebook-import --source-id ...\`
293
+ - Remote URLs: \`sourceloop notebook-import --url ...\`
294
+ - Ready topic with no run: \`sourceloop plan ... --max-questions 10 --json\`
295
+ - Planned or incomplete run: \`sourceloop run ... --json\`
296
+ - Existing latest answer only: \`sourceloop import-latest ...\`
297
+
298
+ ## Safety
299
+
300
+ - Do not skip \`status --json\` and \`doctor --json\`
301
+ - Do not freestyle inside NotebookLM when the first entry checks fail
302
+ - Treat shared or unknown Chrome profile isolation as a warning that should be surfaced before more NotebookLM work
303
+ - Do not silently fall back to another Chrome session
304
+ - Do not autonomously search the web or choose source materials unless the user explicitly asked you to find sources
305
+ - Do not run \`plan\` without usable evidence
306
+ - Do not run \`run\` without a notebook binding and planned run
307
+ - Do not use \`--question-id\` and \`--from-question\` together
308
+ - Do not impose a partial run limit by default when the planned batch should be executed end to end
309
+
310
+ For the full operator flow, read [references/playbook.md](references/playbook.md).
311
+ `;
312
+ }
313
+ function buildGeminiSkillMarkdown() {
314
+ return `---
315
+ name: sourceloop-operator
316
+ description: Operate a SourceLoop workspace with the standard research loop. Use when working inside a SourceLoop project and you need to inspect state, prepare NotebookLM, choose the right kickoff path, import or declare evidence, plan questions, and run bounded research passes.
317
+ ---
318
+
319
+ Use this skill when the current project is a SourceLoop workspace.
320
+
321
+ ## Core loop
322
+
323
+ 1. Run \`sourceloop status --json\`
324
+ 2. Run \`sourceloop doctor --json\`
325
+ 3. Fix blocking prerequisites before planning or running
326
+ 4. Prepare the managed browser before notebook creation or execution
327
+ 5. Choose the kickoff path:
328
+ - no topic provided
329
+ - topic only
330
+ - topic plus sources
331
+ - existing NotebookLM URL
332
+ 6. Prepare notebook before evidence import or declaration
333
+ 7. Require usable evidence before planning
334
+ 8. Planning defaults to 10 questions unless the user asked for another count
335
+ 9. Once a 10-question batch is planned, prefer running the full batch unless the user explicitly asked for a smaller partial pass
336
+ 10. Re-check \`sourceloop status --json\` after each meaningful step
337
+
338
+ ## NotebookLM entry rules
339
+
340
+ - Only verify deterministic entry checks:
341
+ - NotebookLM home or target notebook opens
342
+ - the user is logged in
343
+ - create or bind flow is reachable
344
+ - If these checks fail, do not wander through the UI.
345
+ - Stop and ask the user to fix login, permissions, or landing-page state.
346
+ - If only another Chrome is available, do not silently continue on that path.
347
+ - Ask the user whether to keep going with that Chrome or switch back to the SourceLoop browser first.
348
+
349
+ ## Command selection
350
+
351
+ - No trusted isolated Chrome target: \`sourceloop chrome launch\`
352
+ - Managed isolated Chrome target exists but is not validated yet: \`sourceloop attach validate <target>\`
353
+ - Topic only and no notebook yet: \`sourceloop notebook-create ...\`, then ask which sources to import
354
+ - Topic plus sources and no notebook yet: \`sourceloop notebook-create ...\`, then import the provided files or URLs
355
+ - Existing NotebookLM URL: \`sourceloop notebook-bind ...\`, then \`sourceloop notebook-source declare ...\`
356
+ - Local source files: \`sourceloop ingest ...\`, then \`sourceloop notebook-import --source-id ...\`
357
+ - Remote URLs: \`sourceloop notebook-import --url ...\`
358
+ - Ready topic with no run: \`sourceloop plan ... --max-questions 10 --json\`
359
+ - Planned or incomplete run: \`sourceloop run ... --json\`
360
+ - Existing latest answer only: \`sourceloop import-latest ...\`
361
+
362
+ ## Safety
363
+
364
+ - Do not skip \`status --json\` and \`doctor --json\`
365
+ - Do not freestyle inside NotebookLM when the first entry checks fail
366
+ - Treat shared or unknown Chrome profile isolation as a warning that should be surfaced before more NotebookLM work
367
+ - Do not silently fall back to another Chrome session
368
+ - Do not autonomously search the web or choose source materials unless the user explicitly asked you to find sources
369
+ - Do not run \`plan\` without usable evidence
370
+ - Do not run \`run\` without a notebook binding and planned run
371
+ - Do not use \`--question-id\` and \`--from-question\` together
372
+ - Do not impose a partial run limit by default when the planned batch should be executed end to end
373
+
374
+ For the full operator flow, read [references/playbook.md](references/playbook.md).
375
+ `;
376
+ }
377
+ function buildSharedPlaybookReference() {
378
+ return `# SourceLoop Operator Playbook
379
+
380
+ ## Preferred order
381
+
382
+ 1. \`sourceloop status --json\`
383
+ 2. \`sourceloop doctor --json\`
384
+ 3. topic preparation
385
+ 4. managed isolated Chrome / NotebookLM session preparation
386
+ 5. \`chrome launch\`
387
+ 6. \`attach validate\`
388
+ 7. \`notebook-create\` or \`notebook-bind\`
389
+ 8. \`ingest\`, \`notebook-import\`, or \`notebook-source declare\`
390
+ 9. \`status --json\` and \`doctor --json\` again
391
+ 10. \`plan --max-questions 10 --json\`
392
+ 11. \`run --json\`
393
+
394
+ ## Decision rules
395
+
396
+ - If doctor has errors, resolve them first.
397
+ - Prefer \`sourceloop chrome launch\` so NotebookLM research uses a SourceLoop-managed isolated profile instead of a shared default browser profile.
398
+ - Prefer URL-less \`sourceloop attach validate <target>\` before notebook creation when only NotebookLM home readiness is needed.
399
+ - If the user did not provide a topic, ask for the topic first and mention that planning defaults to 10 questions unless they want another count.
400
+ - If no trusted isolated Chrome target exists, launch browser state before notebook actions.
401
+ - If only another Chrome is available, ask the user whether to keep going with that Chrome before using it.
402
+ - If the user provided only a topic, confirm the question count, create a managed notebook, and ask which sources to import.
403
+ - If the user did not provide sources, do not search for or choose source materials unless the user explicitly asked you to find sources.
404
+ - If the user provided topic plus sources, create a managed notebook and import those sources.
405
+ - If the user provided a NotebookLM URL, bind the existing notebook and continue from its source state.
406
+ - If a run already exists, resume it before creating a new pass.
407
+ - If the user planned 10 questions and did not ask for a partial pass, run the full remaining batch instead of adding a default \`--limit\`.
408
+ `;
409
+ }
410
+ function getBootstrapPaths(rootDir, ai) {
411
+ const skillDir = getSkillDir(rootDir, ai);
412
+ const referencesDir = path.join(skillDir, "references");
413
+ const skillPath = path.join(skillDir, "SKILL.md");
414
+ const playbookPath = path.join(referencesDir, "playbook.md");
415
+ return {
416
+ skillDir,
417
+ referencesDir,
418
+ skillPath,
419
+ playbookPath
420
+ };
421
+ }
422
+ function getSkillDir(rootDir, ai) {
423
+ switch (ai) {
424
+ case "codex":
425
+ return path.join(rootDir, ".codex", "skills", "sourceloop-operator");
426
+ case "claude":
427
+ return path.join(rootDir, ".claude", "skills", "sourceloop-operator");
428
+ case "gemini":
429
+ return path.join(rootDir, ".agents", "skills", "sourceloop-operator");
430
+ default:
431
+ throw new Error(`Unsupported AI bootstrap target: ${ai}`);
432
+ }
433
+ }
434
+ async function pathExists(targetPath) {
435
+ try {
436
+ await access(targetPath);
437
+ return true;
438
+ }
439
+ catch {
440
+ return false;
441
+ }
442
+ }
443
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../../src/core/workspace/bootstrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare const SOURCELOOP_CONFIG_DIR = ".sourceloop";
2
+ export declare const SOURCELOOP_CONFIG_PATH = ".sourceloop/config.json";
3
+ export declare const WORKSPACE_DIRECTORIES: readonly [".sourceloop/chrome-profiles", "vault/chrome-targets", "vault/topics", "vault/sources", "vault/notebook-sources", "vault/notebook-setups", "vault/notebook-imports", "vault/bundles", "vault/notebooks", "vault/runs", "vault/outputs"];
@@ -0,0 +1,16 @@
1
+ export const SOURCELOOP_CONFIG_DIR = ".sourceloop";
2
+ export const SOURCELOOP_CONFIG_PATH = `${SOURCELOOP_CONFIG_DIR}/config.json`;
3
+ export const WORKSPACE_DIRECTORIES = [
4
+ ".sourceloop/chrome-profiles",
5
+ "vault/chrome-targets",
6
+ "vault/topics",
7
+ "vault/sources",
8
+ "vault/notebook-sources",
9
+ "vault/notebook-setups",
10
+ "vault/notebook-imports",
11
+ "vault/bundles",
12
+ "vault/notebooks",
13
+ "vault/runs",
14
+ "vault/outputs"
15
+ ];
16
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/core/workspace/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AACnD,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,qBAAqB,cAAc,CAAC;AAE7E,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,6BAA6B;IAC7B,sBAAsB;IACtB,cAAc;IACd,eAAe;IACf,wBAAwB;IACxB,uBAAuB;IACvB,wBAAwB;IACxB,eAAe;IACf,iBAAiB;IACjB,YAAY;IACZ,eAAe;CACP,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { type SupportedAgentBootstrap, type WorkspaceBootstrapResult } from "./bootstrap.js";
2
+ type InitializeWorkspaceInput = {
3
+ directory: string;
4
+ force: boolean;
5
+ ai?: SupportedAgentBootstrap;
6
+ };
7
+ type InitializeWorkspaceResult = {
8
+ rootDir: string;
9
+ configPath: string;
10
+ created: string[];
11
+ bootstrap?: WorkspaceBootstrapResult;
12
+ message: string;
13
+ };
14
+ export declare function initializeWorkspace(input: InitializeWorkspaceInput): Promise<InitializeWorkspaceResult>;
15
+ export {};
@@ -0,0 +1,86 @@
1
+ import { mkdir, stat, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { SOURCELOOP_CONFIG_DIR, SOURCELOOP_CONFIG_PATH, WORKSPACE_DIRECTORIES } from "./constants.js";
4
+ import { workspaceConfigSchema } from "./schema.js";
5
+ import { bootstrapWorkspaceAgent, validateWorkspaceAgentBootstrap } from "./bootstrap.js";
6
+ export async function initializeWorkspace(input) {
7
+ const rootDir = path.resolve(input.directory);
8
+ const configPath = path.join(rootDir, SOURCELOOP_CONFIG_PATH);
9
+ await mkdir(rootDir, { recursive: true });
10
+ const configExists = await pathExists(configPath);
11
+ const bootstrapOnlyAddition = Boolean(input.ai && configExists && !input.force);
12
+ if (input.ai) {
13
+ await validateWorkspaceAgentBootstrap({
14
+ rootDir,
15
+ ai: input.ai,
16
+ force: input.force
17
+ });
18
+ }
19
+ if (configExists && !input.force && !bootstrapOnlyAddition) {
20
+ throw new Error(`SourceLoop config already exists at ${configPath}. Re-run with --force to overwrite it.`);
21
+ }
22
+ const created = [];
23
+ for (const relativeDirectory of WORKSPACE_DIRECTORIES) {
24
+ const absoluteDirectory = path.join(rootDir, relativeDirectory);
25
+ const existed = await pathExists(absoluteDirectory);
26
+ await mkdir(absoluteDirectory, { recursive: true });
27
+ if (!existed) {
28
+ created.push(relativeDirectory);
29
+ }
30
+ }
31
+ const configDirectory = path.join(rootDir, SOURCELOOP_CONFIG_DIR);
32
+ const shouldWriteConfig = !configExists || input.force;
33
+ if (shouldWriteConfig) {
34
+ await mkdir(configDirectory, { recursive: true });
35
+ const config = workspaceConfigSchema.parse(buildWorkspaceConfig());
36
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
37
+ }
38
+ const bootstrap = input.ai
39
+ ? await bootstrapWorkspaceAgent({
40
+ rootDir,
41
+ ai: input.ai,
42
+ force: input.force
43
+ })
44
+ : undefined;
45
+ const configStatus = shouldWriteConfig ? (configExists ? "updated" : "created") : "unchanged";
46
+ const createdSummary = created.length === 0 ? "workspace directories already existed" : `created ${created.length} workspace directories`;
47
+ const bootstrapSummary = bootstrap
48
+ ? `; ${bootstrap.ai} bootstrap created ${bootstrap.created.length} file(s)`
49
+ : "";
50
+ return {
51
+ rootDir,
52
+ configPath,
53
+ created,
54
+ ...(bootstrap ? { bootstrap } : {}),
55
+ message: `Initialized SourceLoop workspace at ${rootDir} (${createdSummary}; config ${configStatus}${bootstrapSummary}).`
56
+ };
57
+ }
58
+ function buildWorkspaceConfig() {
59
+ return {
60
+ version: 1,
61
+ createdAt: new Date().toISOString(),
62
+ paths: {
63
+ chromeProfiles: ".sourceloop/chrome-profiles",
64
+ chromeTargets: "vault/chrome-targets",
65
+ topics: "vault/topics",
66
+ sources: "vault/sources",
67
+ notebookSources: "vault/notebook-sources",
68
+ notebookSetups: "vault/notebook-setups",
69
+ notebookImports: "vault/notebook-imports",
70
+ notebooks: "vault/notebooks",
71
+ bundles: "vault/bundles",
72
+ runs: "vault/runs",
73
+ outputs: "vault/outputs"
74
+ }
75
+ };
76
+ }
77
+ async function pathExists(targetPath) {
78
+ try {
79
+ await stat(targetPath);
80
+ return true;
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ }
86
+ //# sourceMappingURL=init-workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-workspace.js","sourceRoot":"","sources":["../../../src/core/workspace/init-workspace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type WorkspaceConfig } from "./schema.js";
2
+ export type LoadedWorkspace = {
3
+ rootDir: string;
4
+ config: WorkspaceConfig;
5
+ };
6
+ export declare function loadWorkspace(startDir?: string): Promise<LoadedWorkspace>;
@@ -0,0 +1,51 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { SOURCELOOP_CONFIG_PATH } from "./constants.js";
4
+ import { workspaceConfigSchema } from "./schema.js";
5
+ export async function loadWorkspace(startDir = process.cwd()) {
6
+ const rootDir = await findWorkspaceRoot(startDir);
7
+ const configPath = path.join(rootDir, SOURCELOOP_CONFIG_PATH);
8
+ const configRaw = await readFile(configPath, "utf8");
9
+ const config = workspaceConfigSchema.parse(withWorkspaceDefaults(JSON.parse(configRaw)));
10
+ return {
11
+ rootDir,
12
+ config
13
+ };
14
+ }
15
+ function withWorkspaceDefaults(config) {
16
+ return {
17
+ version: 1,
18
+ createdAt: config.createdAt ?? new Date(0).toISOString(),
19
+ paths: {
20
+ chromeProfiles: config.paths?.chromeProfiles ?? ".sourceloop/chrome-profiles",
21
+ chromeTargets: config.paths?.chromeTargets ?? "vault/chrome-targets",
22
+ topics: config.paths?.topics ?? "vault/topics",
23
+ sources: config.paths?.sources ?? "vault/sources",
24
+ notebookSources: config.paths?.notebookSources ?? "vault/notebook-sources",
25
+ notebookSetups: config.paths?.notebookSetups ?? "vault/notebook-setups",
26
+ notebookImports: config.paths?.notebookImports ?? "vault/notebook-imports",
27
+ notebooks: config.paths?.notebooks ?? "vault/notebooks",
28
+ bundles: config.paths?.bundles ?? "vault/bundles",
29
+ runs: config.paths?.runs ?? "vault/runs",
30
+ outputs: config.paths?.outputs ?? "vault/outputs"
31
+ }
32
+ };
33
+ }
34
+ async function findWorkspaceRoot(startDir) {
35
+ let currentDir = path.resolve(startDir);
36
+ while (true) {
37
+ const candidate = path.join(currentDir, SOURCELOOP_CONFIG_PATH);
38
+ try {
39
+ await readFile(candidate, "utf8");
40
+ return currentDir;
41
+ }
42
+ catch {
43
+ const parentDir = path.dirname(currentDir);
44
+ if (parentDir === currentDir) {
45
+ throw new Error(`No SourceLoop workspace found from ${startDir}. Run "sourceloop init" first.`);
46
+ }
47
+ currentDir = parentDir;
48
+ }
49
+ }
50
+ }
51
+ //# sourceMappingURL=load-workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-workspace.js","sourceRoot":"","sources":["../../../src/core/workspace/load-workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAwB,MAAM,aAAa,CAAC;AAO1E,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE;IAC1D,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,qBAAqB,CAAC,KAAK,CAAC,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAA6B,CAAC,CAAC,CAAC;IAErH,OAAO;QACL,OAAO;QACP,MAAM;KACP,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAgC;IAC7D,OAAO;QACL,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;QACxD,KAAK,EAAE;YACL,cAAc,EAAE,MAAM,CAAC,KAAK,EAAE,cAAc,IAAI,6BAA6B;YAC7E,aAAa,EAAE,MAAM,CAAC,KAAK,EAAE,aAAa,IAAI,sBAAsB;YACpE,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,IAAI,cAAc;YAC9C,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,eAAe;YACjD,eAAe,EAAE,MAAM,CAAC,KAAK,EAAE,eAAe,IAAI,wBAAwB;YAC1E,cAAc,EAAE,MAAM,CAAC,KAAK,EAAE,cAAc,IAAI,uBAAuB;YACvE,eAAe,EAAE,MAAM,CAAC,KAAK,EAAE,eAAe,IAAI,wBAAwB;YAC1E,SAAS,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,IAAI,iBAAiB;YACvD,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,eAAe;YACjD,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,IAAI,YAAY;YACxC,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,eAAe;SAClD;KACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,QAAgB;IAC/C,IAAI,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAExC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;QAEhE,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAClC,OAAO,UAAU,CAAC;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAE3C,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CACb,sCAAsC,QAAQ,gCAAgC,CAC/E,CAAC;YACJ,CAAC;YAED,UAAU,GAAG,SAAS,CAAC;QACzB,CAAC;IACH,CAAC;AACH,CAAC"}