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.
- package/README.md +401 -0
- package/dist/commands/attach.d.ts +2 -0
- package/dist/commands/attach.js +103 -0
- package/dist/commands/attach.js.map +1 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +33 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/chrome.d.ts +2 -0
- package/dist/commands/chrome.js +30 -0
- package/dist/commands/chrome.js.map +1 -0
- package/dist/commands/compose.d.ts +2 -0
- package/dist/commands/compose.js +14 -0
- package/dist/commands/compose.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +15 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/import-latest.d.ts +2 -0
- package/dist/commands/import-latest.js +42 -0
- package/dist/commands/import-latest.js.map +1 -0
- package/dist/commands/ingest.d.ts +2 -0
- package/dist/commands/ingest.js +14 -0
- package/dist/commands/ingest.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +24 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/notebook-bind.d.ts +2 -0
- package/dist/commands/notebook-bind.js +39 -0
- package/dist/commands/notebook-bind.js.map +1 -0
- package/dist/commands/notebook-create.d.ts +2 -0
- package/dist/commands/notebook-create.js +39 -0
- package/dist/commands/notebook-create.js.map +1 -0
- package/dist/commands/notebook-import.d.ts +2 -0
- package/dist/commands/notebook-import.js +39 -0
- package/dist/commands/notebook-import.js.map +1 -0
- package/dist/commands/notebook-source.d.ts +2 -0
- package/dist/commands/notebook-source.js +71 -0
- package/dist/commands/notebook-source.js.map +1 -0
- package/dist/commands/plan.d.ts +9 -0
- package/dist/commands/plan.js +59 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +76 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +15 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/topic.d.ts +2 -0
- package/dist/commands/topic.js +53 -0
- package/dist/commands/topic.js.map +1 -0
- package/dist/core/attach/launch-managed-chrome.d.ts +27 -0
- package/dist/core/attach/launch-managed-chrome.js +136 -0
- package/dist/core/attach/launch-managed-chrome.js.map +1 -0
- package/dist/core/attach/manage-targets.d.ts +49 -0
- package/dist/core/attach/manage-targets.js +179 -0
- package/dist/core/attach/manage-targets.js.map +1 -0
- package/dist/core/ingest/frontmatter.d.ts +4 -0
- package/dist/core/ingest/frontmatter.js +30 -0
- package/dist/core/ingest/frontmatter.js.map +1 -0
- package/dist/core/ingest/html-to-markdown.d.ts +5 -0
- package/dist/core/ingest/html-to-markdown.js +53 -0
- package/dist/core/ingest/html-to-markdown.js.map +1 -0
- package/dist/core/ingest/ingest-source.d.ts +11 -0
- package/dist/core/ingest/ingest-source.js +115 -0
- package/dist/core/ingest/ingest-source.js.map +1 -0
- package/dist/core/notebooklm/adapter.d.ts +17 -0
- package/dist/core/notebooklm/adapter.js +2 -0
- package/dist/core/notebooklm/adapter.js.map +1 -0
- package/dist/core/notebooklm/auth.d.ts +30 -0
- package/dist/core/notebooklm/auth.js +105 -0
- package/dist/core/notebooklm/auth.js.map +1 -0
- package/dist/core/notebooklm/browser-agent-adapter.d.ts +21 -0
- package/dist/core/notebooklm/browser-agent-adapter.js +37 -0
- package/dist/core/notebooklm/browser-agent-adapter.js.map +1 -0
- package/dist/core/notebooklm/browser-agent.d.ts +121 -0
- package/dist/core/notebooklm/browser-agent.js +1604 -0
- package/dist/core/notebooklm/browser-agent.js.map +1 -0
- package/dist/core/notebooklm/config.d.ts +20 -0
- package/dist/core/notebooklm/config.js +133 -0
- package/dist/core/notebooklm/config.js.map +1 -0
- package/dist/core/notebooklm/fixture-adapter.d.ts +13 -0
- package/dist/core/notebooklm/fixture-adapter.js +32 -0
- package/dist/core/notebooklm/fixture-adapter.js.map +1 -0
- package/dist/core/notebooklm/response-extraction.d.ts +23 -0
- package/dist/core/notebooklm/response-extraction.js +348 -0
- package/dist/core/notebooklm/response-extraction.js.map +1 -0
- package/dist/core/notebooks/bind-notebook.d.ts +21 -0
- package/dist/core/notebooks/bind-notebook.js +95 -0
- package/dist/core/notebooks/bind-notebook.js.map +1 -0
- package/dist/core/notebooks/manage-managed-notebooks.d.ts +70 -0
- package/dist/core/notebooks/manage-managed-notebooks.js +491 -0
- package/dist/core/notebooks/manage-managed-notebooks.js.map +1 -0
- package/dist/core/notebooks/manage-notebook-source-manifests.d.ts +25 -0
- package/dist/core/notebooks/manage-notebook-source-manifests.js +127 -0
- package/dist/core/notebooks/manage-notebook-source-manifests.js.map +1 -0
- package/dist/core/operator/workspace-operator.d.ts +82 -0
- package/dist/core/operator/workspace-operator.js +610 -0
- package/dist/core/operator/workspace-operator.js.map +1 -0
- package/dist/core/outputs/compose-run.d.ts +11 -0
- package/dist/core/outputs/compose-run.js +98 -0
- package/dist/core/outputs/compose-run.js.map +1 -0
- package/dist/core/runs/load-artifacts.d.ts +14 -0
- package/dist/core/runs/load-artifacts.js +51 -0
- package/dist/core/runs/load-artifacts.js.map +1 -0
- package/dist/core/runs/question-planner.d.ts +20 -0
- package/dist/core/runs/question-planner.js +276 -0
- package/dist/core/runs/question-planner.js.map +1 -0
- package/dist/core/runs/render-run-note.d.ts +13 -0
- package/dist/core/runs/render-run-note.js +111 -0
- package/dist/core/runs/render-run-note.js.map +1 -0
- package/dist/core/runs/run-qa.d.ts +28 -0
- package/dist/core/runs/run-qa.js +393 -0
- package/dist/core/runs/run-qa.js.map +1 -0
- package/dist/core/topics/manage-topics.d.ts +27 -0
- package/dist/core/topics/manage-topics.js +314 -0
- package/dist/core/topics/manage-topics.js.map +1 -0
- package/dist/core/vault/notes.d.ts +29 -0
- package/dist/core/vault/notes.js +147 -0
- package/dist/core/vault/notes.js.map +1 -0
- package/dist/core/vault/paths.d.ts +31 -0
- package/dist/core/vault/paths.js +44 -0
- package/dist/core/vault/paths.js.map +1 -0
- package/dist/core/workspace/bootstrap.d.ts +16 -0
- package/dist/core/workspace/bootstrap.js +443 -0
- package/dist/core/workspace/bootstrap.js.map +1 -0
- package/dist/core/workspace/constants.d.ts +3 -0
- package/dist/core/workspace/constants.js +16 -0
- package/dist/core/workspace/constants.js.map +1 -0
- package/dist/core/workspace/init-workspace.d.ts +15 -0
- package/dist/core/workspace/init-workspace.js +86 -0
- package/dist/core/workspace/init-workspace.js.map +1 -0
- package/dist/core/workspace/load-workspace.d.ts +6 -0
- package/dist/core/workspace/load-workspace.js +51 -0
- package/dist/core/workspace/load-workspace.js.map +1 -0
- package/dist/core/workspace/schema.d.ts +19 -0
- package/dist/core/workspace/schema.js +19 -0
- package/dist/core/workspace/schema.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cli-output.d.ts +2 -0
- package/dist/lib/cli-output.js +7 -0
- package/dist/lib/cli-output.js.map +1 -0
- package/dist/lib/obsidian.d.ts +4 -0
- package/dist/lib/obsidian.js +23 -0
- package/dist/lib/obsidian.js.map +1 -0
- package/dist/lib/slugify.d.ts +1 -0
- package/dist/lib/slugify.js +10 -0
- package/dist/lib/slugify.js.map +1 -0
- package/dist/lib/write-json.d.ts +1 -0
- package/dist/lib/write-json.js +5 -0
- package/dist/lib/write-json.js.map +1 -0
- package/dist/schemas/attach.d.ts +118 -0
- package/dist/schemas/attach.js +33 -0
- package/dist/schemas/attach.js.map +1 -0
- package/dist/schemas/managed-notebook.d.ts +47 -0
- package/dist/schemas/managed-notebook.js +30 -0
- package/dist/schemas/managed-notebook.js.map +1 -0
- package/dist/schemas/notebook-source.d.ts +31 -0
- package/dist/schemas/notebook-source.js +23 -0
- package/dist/schemas/notebook-source.js.map +1 -0
- package/dist/schemas/notebook.d.ts +26 -0
- package/dist/schemas/notebook.js +18 -0
- package/dist/schemas/notebook.js.map +1 -0
- package/dist/schemas/run.d.ts +169 -0
- package/dist/schemas/run.js +80 -0
- package/dist/schemas/run.js.map +1 -0
- package/dist/schemas/source.d.ts +18 -0
- package/dist/schemas/source.js +13 -0
- package/dist/schemas/source.js.map +1 -0
- package/dist/schemas/topic.d.ts +37 -0
- package/dist/schemas/topic.js +25 -0
- package/dist/schemas/topic.js.map +1 -0
- 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":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AASjF,MAAM,CAAC,KAAK,UAAU,+BAA+B,CAAC,KAIrD;IACC,QAAQ,KAAK,CAAC,EAAE,EAAE,CAAC;QACjB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;gBACjD,MAAM,IAAI,KAAK,CACb,qCAAqC,QAAQ,4DAA4D,CAC1G,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;gBACjD,MAAM,IAAI,KAAK,CACb,sCAAsC,QAAQ,4DAA4D,CAC3G,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;gBACjD,MAAM,IAAI,KAAK,CACb,sCAAsC,QAAQ,4DAA4D,CAC3G,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,oCAAoC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IACpE,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,KAI7C;IACC,MAAM,+BAA+B,CAAC,KAAK,CAAC,CAAC;IAE7C,QAAQ,KAAK,CAAC,EAAE,EAAE,CAAC;QACjB,KAAK,OAAO;YACV,OAAO,uBAAuB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC7D,KAAK,QAAQ;YACX,OAAO,wBAAwB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9D,KAAK,QAAQ;YACX,OAAO,wBAAwB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9D;YACE,MAAM,IAAI,KAAK,CAAC,oCAAoC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IACpE,CAAC;AACH,CAAC;AAED,KAAK,UAAU,uBAAuB,CAAC,OAAe,EAAE,KAAc;IACpE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAEjG,IAAI,KAAK,IAAI,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC1C,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,SAAS,EAAE,uBAAuB,EAAE,EAAE,MAAM,CAAC,CAAC;IAC9D,MAAM,SAAS,CAAC,YAAY,EAAE,2BAA2B,EAAE,EAAE,MAAM,CAAC,CAAC;IAErE,OAAO;QACL,EAAE,EAAE,OAAO;QACX,OAAO,EAAE;YACP,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;YACjC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;SACrC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB;IAC9B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkGR,CAAC;AACF,CAAC;AAED,SAAS,2BAA2B;IAClC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDR,CAAC;AACF,CAAC;AAED,KAAK,UAAU,wBAAwB,CAAC,OAAe,EAAE,KAAc;IACrE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAElG,IAAI,KAAK,IAAI,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC1C,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,SAAS,EAAE,wBAAwB,EAAE,EAAE,MAAM,CAAC,CAAC;IAC/D,MAAM,SAAS,CAAC,YAAY,EAAE,4BAA4B,EAAE,EAAE,MAAM,CAAC,CAAC;IAEtE,OAAO;QACL,EAAE,EAAE,QAAQ;QACZ,OAAO,EAAE;YACP,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;YACjC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;SACrC;KACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,wBAAwB,CAAC,OAAe,EAAE,KAAc;IACrE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAElG,IAAI,KAAK,IAAI,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC1C,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,SAAS,EAAE,wBAAwB,EAAE,EAAE,MAAM,CAAC,CAAC;IAC/D,MAAM,SAAS,CAAC,YAAY,EAAE,4BAA4B,EAAE,EAAE,MAAM,CAAC,CAAC;IAEtE,OAAO;QACL,EAAE,EAAE,QAAQ;QACZ,OAAO,EAAE;YACP,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;YACjC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;SACrC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB;IAC/B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6DR,CAAC;AACF,CAAC;AAED,SAAS,wBAAwB;IAC/B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6DR,CAAC;AACF,CAAC;AAED,SAAS,4BAA4B;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BR,CAAC;AACF,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAe,EAAE,EAA2B;IACrE,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IAE7D,OAAO;QACL,QAAQ;QACR,aAAa;QACb,SAAS;QACT,YAAY;KACb,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,OAAe,EAAE,EAA2B;IAC/D,QAAQ,EAAE,EAAE,CAAC;QACX,KAAK,OAAO;YACV,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC;QACvE,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC;QACxE,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC;QACxE;YACE,MAAM,IAAI,KAAK,CAAC,oCAAoC,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,UAAkB;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -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,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"}
|