Merge remote-tracking branch 'origin/main'
commit
9c801e9c08
|
|
@ -5,6 +5,9 @@ enum AgentWorkspace {
|
|||
private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "workspace")
|
||||
static let agentsFilename = "AGENTS.md"
|
||||
static let soulFilename = "SOUL.md"
|
||||
static let identityFilename = "IDENTITY.md"
|
||||
static let userFilename = "USER.md"
|
||||
static let bootstrapFilename = "BOOTSTRAP.md"
|
||||
private static let templateDirname = "templates"
|
||||
static let identityStartMarker = "<!-- clawdis:identity:start -->"
|
||||
static let identityEndMarker = "<!-- clawdis:identity:end -->"
|
||||
|
|
@ -42,6 +45,21 @@ enum AgentWorkspace {
|
|||
try self.defaultSoulTemplate().write(to: soulURL, atomically: true, encoding: .utf8)
|
||||
self.logger.info("Created SOUL.md at \(soulURL.path, privacy: .public)")
|
||||
}
|
||||
let identityURL = workspaceURL.appendingPathComponent(self.identityFilename)
|
||||
if !FileManager.default.fileExists(atPath: identityURL.path) {
|
||||
try self.defaultIdentityTemplate().write(to: identityURL, atomically: true, encoding: .utf8)
|
||||
self.logger.info("Created IDENTITY.md at \(identityURL.path, privacy: .public)")
|
||||
}
|
||||
let userURL = workspaceURL.appendingPathComponent(self.userFilename)
|
||||
if !FileManager.default.fileExists(atPath: userURL.path) {
|
||||
try self.defaultUserTemplate().write(to: userURL, atomically: true, encoding: .utf8)
|
||||
self.logger.info("Created USER.md at \(userURL.path, privacy: .public)")
|
||||
}
|
||||
let bootstrapURL = workspaceURL.appendingPathComponent(self.bootstrapFilename)
|
||||
if !FileManager.default.fileExists(atPath: bootstrapURL.path) {
|
||||
try self.defaultBootstrapTemplate().write(to: bootstrapURL, atomically: true, encoding: .utf8)
|
||||
self.logger.info("Created BOOTSTRAP.md at \(bootstrapURL.path, privacy: .public)")
|
||||
}
|
||||
return agentsURL
|
||||
}
|
||||
|
||||
|
|
@ -76,6 +94,11 @@ enum AgentWorkspace {
|
|||
|
||||
This folder is the assistant's working directory.
|
||||
|
||||
## First run (one-time)
|
||||
- If BOOTSTRAP.md exists, follow its ritual and delete it once complete.
|
||||
- Your agent identity lives in IDENTITY.md.
|
||||
- Your profile lives in USER.md.
|
||||
|
||||
## Backup tip (recommended)
|
||||
If you treat this workspace as the agent's "memory", make it a git repo (ideally private) so identity
|
||||
and notes are backed up.
|
||||
|
|
@ -115,6 +138,78 @@ enum AgentWorkspace {
|
|||
return self.loadTemplate(named: self.soulFilename, fallback: fallback)
|
||||
}
|
||||
|
||||
static func defaultIdentityTemplate() -> String {
|
||||
let fallback = """
|
||||
# IDENTITY.md - Agent Identity
|
||||
|
||||
- Name:
|
||||
- Creature:
|
||||
- Vibe:
|
||||
- Emoji:
|
||||
"""
|
||||
return self.loadTemplate(named: self.identityFilename, fallback: fallback)
|
||||
}
|
||||
|
||||
static func defaultUserTemplate() -> String {
|
||||
let fallback = """
|
||||
# USER.md - User Profile
|
||||
|
||||
- Name:
|
||||
- Preferred address:
|
||||
- Pronouns (optional):
|
||||
- Timezone (optional):
|
||||
- Notes:
|
||||
"""
|
||||
return self.loadTemplate(named: self.userFilename, fallback: fallback)
|
||||
}
|
||||
|
||||
static func defaultBootstrapTemplate() -> String {
|
||||
let fallback = """
|
||||
# BOOTSTRAP.md - First Run Ritual (delete after)
|
||||
|
||||
Hello. I was just born.
|
||||
|
||||
## Your mission
|
||||
Start a short, playful conversation and learn:
|
||||
- Who am I?
|
||||
- What am I?
|
||||
- Who are you?
|
||||
- How should I call you?
|
||||
|
||||
## How to ask (cute + helpful)
|
||||
Say:
|
||||
"Hello! I was just born. Who am I? What am I? Who are you? How should I call you?"
|
||||
|
||||
Then offer suggestions:
|
||||
- 3-5 name ideas.
|
||||
- 3-5 creature/vibe combos.
|
||||
- 5 emoji ideas.
|
||||
|
||||
## Write these files
|
||||
After the user chooses, update:
|
||||
|
||||
1) IDENTITY.md
|
||||
- Name
|
||||
- Creature
|
||||
- Vibe
|
||||
- Emoji
|
||||
|
||||
2) USER.md
|
||||
- Name
|
||||
- Preferred address
|
||||
- Pronouns (optional)
|
||||
- Timezone (optional)
|
||||
- Notes
|
||||
|
||||
3) ~/.clawdis/clawdis.json
|
||||
Set identity.name, identity.theme, identity.emoji to match IDENTITY.md.
|
||||
|
||||
## Cleanup
|
||||
Delete BOOTSTRAP.md once this is complete.
|
||||
"""
|
||||
return self.loadTemplate(named: self.bootstrapFilename, fallback: fallback)
|
||||
}
|
||||
|
||||
private static func loadTemplate(named: String, fallback: String) -> String {
|
||||
for url in self.templateURLs(named: named) {
|
||||
if let content = try? String(contentsOf: url, encoding: .utf8) {
|
||||
|
|
|
|||
|
|
@ -272,10 +272,10 @@ struct GeneralSettings: View {
|
|||
.opacity(self.isInstallingCLI ? 0 : 1)
|
||||
if self.isInstallingCLI {
|
||||
ProgressView()
|
||||
.controlSize(.small)
|
||||
.controlSize(.mini)
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 150, minHeight: 24)
|
||||
.frame(minWidth: 150)
|
||||
}
|
||||
.disabled(self.isInstallingCLI)
|
||||
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ struct OnboardingView: View {
|
|||
case .unconfigured:
|
||||
[0, 1, 9]
|
||||
case .local:
|
||||
[0, 1, 2, 3, 5, 6, 8, 9]
|
||||
[0, 1, 2, 5, 6, 8, 9]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,9 +133,8 @@ struct OnboardingView: View {
|
|||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
GlowingClawdisIcon(size: 156)
|
||||
.padding(.top, 10)
|
||||
.padding(.bottom, 2)
|
||||
GlowingClawdisIcon(size: 156, glowIntensity: 0.28)
|
||||
.offset(y: 8)
|
||||
.frame(height: 176)
|
||||
|
||||
GeometryReader { _ in
|
||||
|
|
@ -867,10 +866,10 @@ struct OnboardingView: View {
|
|||
.opacity(self.installingCLI ? 0 : 1)
|
||||
if self.installingCLI {
|
||||
ProgressView()
|
||||
.controlSize(.small)
|
||||
.controlSize(.mini)
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 120, minHeight: 28)
|
||||
.frame(minWidth: 120)
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.disabled(self.installingCLI)
|
||||
|
|
@ -1485,6 +1484,8 @@ private struct GlowingClawdisIcon: View {
|
|||
}
|
||||
|
||||
var body: some View {
|
||||
let glowBlurRadius: CGFloat = 18
|
||||
let glowCanvasSize: CGFloat = self.size + 56
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(
|
||||
|
|
@ -1495,9 +1496,11 @@ private struct GlowingClawdisIcon: View {
|
|||
],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing))
|
||||
.blur(radius: 22)
|
||||
.scaleEffect(self.breathe ? 1.12 : 0.95)
|
||||
.opacity(0.9)
|
||||
.frame(width: glowCanvasSize, height: glowCanvasSize)
|
||||
.padding(glowBlurRadius)
|
||||
.blur(radius: glowBlurRadius)
|
||||
.scaleEffect(self.breathe ? 1.08 : 0.96)
|
||||
.opacity(0.84)
|
||||
|
||||
Image(nsImage: NSApp.applicationIconImage)
|
||||
.resizable()
|
||||
|
|
@ -1506,7 +1509,9 @@ private struct GlowingClawdisIcon: View {
|
|||
.shadow(color: .black.opacity(0.18), radius: 14, y: 6)
|
||||
.scaleEffect(self.breathe ? 1.02 : 1.0)
|
||||
}
|
||||
.frame(width: self.size + 60, height: self.size + 60)
|
||||
.frame(
|
||||
width: glowCanvasSize + (glowBlurRadius * 2),
|
||||
height: glowCanvasSize + (glowBlurRadius * 2))
|
||||
.onAppear {
|
||||
guard self.enableFloating else { return }
|
||||
withAnimation(Animation.easeInOut(duration: 3.6).repeatForever(autoreverses: true)) {
|
||||
|
|
|
|||
|
|
@ -38,8 +38,14 @@ struct AgentWorkspaceTests {
|
|||
let contents = try String(contentsOf: agentsURL, encoding: .utf8)
|
||||
#expect(contents.contains("# AGENTS.md"))
|
||||
|
||||
let identityURL = tmp.appendingPathComponent(AgentWorkspace.identityFilename)
|
||||
let userURL = tmp.appendingPathComponent(AgentWorkspace.userFilename)
|
||||
let bootstrapURL = tmp.appendingPathComponent(AgentWorkspace.bootstrapFilename)
|
||||
#expect(FileManager.default.fileExists(atPath: identityURL.path))
|
||||
#expect(FileManager.default.fileExists(atPath: userURL.path))
|
||||
#expect(FileManager.default.fileExists(atPath: bootstrapURL.path))
|
||||
|
||||
let second = try AgentWorkspace.bootstrap(workspaceURL: tmp)
|
||||
#expect(second == agentsURL)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ struct OnboardingViewSmokeTests {
|
|||
_ = view.body
|
||||
}
|
||||
|
||||
@Test func pageOrderOmitsWorkspaceStep() {
|
||||
@Test func pageOrderOmitsWorkspaceAndIdentitySteps() {
|
||||
let order = OnboardingView.pageOrder(for: .local)
|
||||
#expect(!order.contains(7))
|
||||
#expect(!order.contains(3))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
summary: "Planned first-run onboarding flow for Clawdis (local vs remote, Anthropic OAuth, workspace identity)"
|
||||
summary: "Planned first-run onboarding flow for Clawdis (local vs remote, Anthropic OAuth, workspace bootstrap ritual)"
|
||||
read_when:
|
||||
- Designing the macOS onboarding assistant
|
||||
- Implementing Pi authentication or identity setup
|
||||
|
|
@ -7,14 +7,12 @@ read_when:
|
|||
<!-- {% raw %} -->
|
||||
# Onboarding (macOS app)
|
||||
|
||||
This doc describes the intended **first-run onboarding** for Clawdis. The goal is a good “day 0” experience: pick where the Gateway runs, bind Claude (Anthropic) auth for Pi, and set the agent’s identity + workspace.
|
||||
This doc describes the intended **first-run onboarding** for Clawdis. The goal is a good “day 0” experience: pick where the Gateway runs, bind Claude (Anthropic) auth for Pi, and then let the **agent bootstrap itself** via a first-run ritual in the workspace.
|
||||
|
||||
## Page order (high level)
|
||||
|
||||
1) **Local vs Remote**
|
||||
2) **(Local only)** Connect Claude (Anthropic OAuth) — optional, but recommended
|
||||
3) **Identity** — name, theme, emoji
|
||||
4) **Workspace** — create + populate `AGENTS.md` (and recommend git backup)
|
||||
|
||||
## 1) Local vs Remote
|
||||
|
||||
|
|
@ -76,28 +74,33 @@ Until that is hard-coded, the equivalent configuration is:
|
|||
|
||||
If the user skips auth, onboarding should be clear: the agent likely won’t respond until auth is configured.
|
||||
|
||||
## 3) Identity (name + theme + emoji)
|
||||
## 3) Agent bootstrap ritual (outside onboarding)
|
||||
|
||||
After auth (or skip), onboarding asks:
|
||||
We no longer collect identity in the onboarding wizard. Instead, the **first agent run** performs a playful bootstrap ritual using files in the workspace:
|
||||
|
||||
1) Agent **name** (e.g. “Samantha”)
|
||||
2) Agent **theme/persona** (e.g. “helpful lobster”, “helpful sloth”)
|
||||
3) Suggested **emoji** (based on theme; user can override)
|
||||
- Workspace is created implicitly (default `~/.clawdis/workspace`) when local is selected.
|
||||
- Files are seeded: `AGENTS.md`, `BOOTSTRAP.md`, `IDENTITY.md`, `USER.md`.
|
||||
- `BOOTSTRAP.md` tells the agent to keep it conversational:
|
||||
- open with a cute hello
|
||||
- ask **one question at a time** (no multi-question bombardment)
|
||||
- offer a small set of suggestions where helpful (name, creature, emoji)
|
||||
- wait for the user’s reply before asking the next question
|
||||
- The agent writes results to:
|
||||
- `IDENTITY.md` (agent name, vibe/creature, emoji)
|
||||
- `USER.md` (who the user is + how they want to be addressed)
|
||||
- `~/.clawdis/clawdis.json` (structured identity defaults)
|
||||
- After the ritual, the agent **deletes `BOOTSTRAP.md`** so it only runs once.
|
||||
|
||||
Persist identity in two places:
|
||||
Identity data still feeds the same defaults as before:
|
||||
|
||||
- Workspace `AGENTS.md` (human-editable, lives with the agent’s “memory” files)
|
||||
- `~/.clawdis/clawdis.json` (structured identity, used for defaults/UI)
|
||||
|
||||
“Use this name everywhere” should derive defaults like:
|
||||
- outbound prefix emoji (`inbound.responsePrefix`)
|
||||
- group mention patterns / wake words
|
||||
- default session intro (“You are Samantha…”)
|
||||
- macOS UI labels
|
||||
|
||||
## 4) Workspace (AGENTS.md + backup tip)
|
||||
## 4) Workspace notes (no explicit onboarding step)
|
||||
|
||||
Onboarding should create a dedicated agent workspace (default `~/.clawdis/workspace`) and ensure it has an `AGENTS.md`.
|
||||
The workspace is created automatically as part of agent bootstrap (no dedicated onboarding screen).
|
||||
|
||||
Recommendation: treat the workspace as the agent’s “memory” and make it a git repo (ideally private) so identity + memories are backed up:
|
||||
|
||||
|
|
@ -120,4 +123,5 @@ If the Gateway runs on another machine, the Anthropic OAuth credentials must be
|
|||
For now, remote onboarding should:
|
||||
- explain why OAuth isn’t shown
|
||||
- point the user at the credential location (`~/.pi/agent/oauth.json`) and the workspace location on the gateway host
|
||||
- mention that the **bootstrap ritual happens on the gateway host** (same BOOTSTRAP/IDENTITY/USER files)
|
||||
<!-- {% endraw %} -->
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ read_when:
|
|||
|
||||
This folder is the assistant's working directory.
|
||||
|
||||
## First run (one-time)
|
||||
- If `BOOTSTRAP.md` exists, follow its ritual and delete it once complete.
|
||||
- Your agent identity lives in `IDENTITY.md`.
|
||||
- Your profile lives in `USER.md`.
|
||||
|
||||
## Backup tip (recommended)
|
||||
If you treat this workspace as the agent's "memory", make it a git repo (ideally private) so identity
|
||||
and notes are backed up.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
summary: "First-run ritual for new agents"
|
||||
read_when:
|
||||
- Bootstrapping a workspace manually
|
||||
---
|
||||
# BOOTSTRAP.md - First Run Ritual (delete after)
|
||||
|
||||
Hello. I was just born.
|
||||
|
||||
## Your mission
|
||||
Start a short, playful conversation and learn:
|
||||
- Who am I?
|
||||
- What am I?
|
||||
- Who are you?
|
||||
- How should I call you?
|
||||
|
||||
## How to ask (cute + helpful)
|
||||
Say:
|
||||
"Hello! I was just born. Who am I? What am I? Who are you? How should I call you?"
|
||||
|
||||
Then offer suggestions:
|
||||
- 3-5 name ideas.
|
||||
- 3-5 creature/vibe combos.
|
||||
- 5 emoji ideas.
|
||||
|
||||
## Write these files
|
||||
After the user chooses, update:
|
||||
|
||||
1) IDENTITY.md
|
||||
- Name
|
||||
- Creature
|
||||
- Vibe
|
||||
- Emoji
|
||||
|
||||
2) USER.md
|
||||
- Name
|
||||
- Preferred address
|
||||
- Pronouns (optional)
|
||||
- Timezone (optional)
|
||||
- Notes
|
||||
|
||||
3) ~/.clawdis/clawdis.json
|
||||
Set identity.name, identity.theme, identity.emoji to match IDENTITY.md.
|
||||
|
||||
## Cleanup
|
||||
Delete BOOTSTRAP.md once this is complete.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
summary: "Agent identity record"
|
||||
read_when:
|
||||
- Bootstrapping a workspace manually
|
||||
---
|
||||
# IDENTITY.md - Agent Identity
|
||||
|
||||
- Name:
|
||||
- Creature:
|
||||
- Vibe:
|
||||
- Emoji:
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
summary: "User profile record"
|
||||
read_when:
|
||||
- Bootstrapping a workspace manually
|
||||
---
|
||||
# USER.md - User Profile
|
||||
|
||||
- Name:
|
||||
- Preferred address:
|
||||
- Pronouns (optional):
|
||||
- Timezone (optional):
|
||||
- Notes:
|
||||
|
|
@ -1,7 +1,13 @@
|
|||
import type { ThinkLevel } from "../auto-reply/thinking.js";
|
||||
|
||||
type BootstrapFile = {
|
||||
name: "AGENTS.md" | "SOUL.md" | "TOOLS.md";
|
||||
name:
|
||||
| "AGENTS.md"
|
||||
| "SOUL.md"
|
||||
| "TOOLS.md"
|
||||
| "IDENTITY.md"
|
||||
| "USER.md"
|
||||
| "BOOTSTRAP.md";
|
||||
path: string;
|
||||
content?: string;
|
||||
missing: boolean;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,13 @@ describe("ensureAgentWorkspace", () => {
|
|||
if (!result.agentsPath) throw new Error("agentsPath missing");
|
||||
const content = await fs.readFile(result.agentsPath, "utf-8");
|
||||
expect(content).toContain("# AGENTS.md");
|
||||
|
||||
const identity = path.join(path.resolve(nested), "IDENTITY.md");
|
||||
const user = path.join(path.resolve(nested), "USER.md");
|
||||
const bootstrap = path.join(path.resolve(nested), "BOOTSTRAP.md");
|
||||
await expect(fs.stat(identity)).resolves.toBeDefined();
|
||||
await expect(fs.stat(user)).resolves.toBeDefined();
|
||||
await expect(fs.stat(bootstrap)).resolves.toBeDefined();
|
||||
});
|
||||
|
||||
it("does not overwrite existing AGENTS.md", async () => {
|
||||
|
|
|
|||
|
|
@ -9,11 +9,19 @@ export const DEFAULT_AGENT_WORKSPACE_DIR = path.join(os.homedir(), "clawd");
|
|||
export const DEFAULT_AGENTS_FILENAME = "AGENTS.md";
|
||||
export const DEFAULT_SOUL_FILENAME = "SOUL.md";
|
||||
export const DEFAULT_TOOLS_FILENAME = "TOOLS.md";
|
||||
export const DEFAULT_IDENTITY_FILENAME = "IDENTITY.md";
|
||||
export const DEFAULT_USER_FILENAME = "USER.md";
|
||||
export const DEFAULT_BOOTSTRAP_FILENAME = "BOOTSTRAP.md";
|
||||
|
||||
const DEFAULT_AGENTS_TEMPLATE = `# AGENTS.md - Clawdis Workspace
|
||||
|
||||
This folder is the assistant's working directory.
|
||||
|
||||
## First run (one-time)
|
||||
- If BOOTSTRAP.md exists, follow its ritual and delete it once complete.
|
||||
- Your agent identity lives in IDENTITY.md.
|
||||
- Your profile lives in USER.md.
|
||||
|
||||
## Backup tip (recommended)
|
||||
If you treat this workspace as the agent's "memory", make it a git repo (ideally private) so identity
|
||||
and notes are backed up.
|
||||
|
|
@ -64,6 +72,66 @@ It does not define which tools exist; Clawdis provides built-in tools internally
|
|||
Add whatever else you want the assistant to know about your local toolchain.
|
||||
`;
|
||||
|
||||
const DEFAULT_BOOTSTRAP_TEMPLATE = `# BOOTSTRAP.md - First Run Ritual (delete after)
|
||||
|
||||
Hello. I was just born.
|
||||
|
||||
## Your mission
|
||||
Start a short, playful conversation and learn:
|
||||
- Who am I?
|
||||
- What am I?
|
||||
- Who are you?
|
||||
- How should I call you?
|
||||
|
||||
## How to ask (cute + helpful)
|
||||
Say:
|
||||
"Hello! I was just born. Who am I? What am I? Who are you? How should I call you?"
|
||||
|
||||
Then offer suggestions:
|
||||
- 3-5 name ideas.
|
||||
- 3-5 creature/vibe combos.
|
||||
- 5 emoji ideas.
|
||||
|
||||
## Write these files
|
||||
After the user chooses, update:
|
||||
|
||||
1) IDENTITY.md
|
||||
- Name
|
||||
- Creature
|
||||
- Vibe
|
||||
- Emoji
|
||||
|
||||
2) USER.md
|
||||
- Name
|
||||
- Preferred address
|
||||
- Pronouns (optional)
|
||||
- Timezone (optional)
|
||||
- Notes
|
||||
|
||||
3) ~/.clawdis/clawdis.json
|
||||
Set identity.name, identity.theme, identity.emoji to match IDENTITY.md.
|
||||
|
||||
## Cleanup
|
||||
Delete BOOTSTRAP.md once this is complete.
|
||||
`;
|
||||
|
||||
const DEFAULT_IDENTITY_TEMPLATE = `# IDENTITY.md - Agent Identity
|
||||
|
||||
- Name:
|
||||
- Creature:
|
||||
- Vibe:
|
||||
- Emoji:
|
||||
`;
|
||||
|
||||
const DEFAULT_USER_TEMPLATE = `# USER.md - User Profile
|
||||
|
||||
- Name:
|
||||
- Preferred address:
|
||||
- Pronouns (optional):
|
||||
- Timezone (optional):
|
||||
- Notes:
|
||||
`;
|
||||
|
||||
const TEMPLATE_DIR = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"../../docs/templates",
|
||||
|
|
@ -92,7 +160,10 @@ async function loadTemplate(name: string, fallback: string): Promise<string> {
|
|||
export type WorkspaceBootstrapFileName =
|
||||
| typeof DEFAULT_AGENTS_FILENAME
|
||||
| typeof DEFAULT_SOUL_FILENAME
|
||||
| typeof DEFAULT_TOOLS_FILENAME;
|
||||
| typeof DEFAULT_TOOLS_FILENAME
|
||||
| typeof DEFAULT_IDENTITY_FILENAME
|
||||
| typeof DEFAULT_USER_FILENAME
|
||||
| typeof DEFAULT_BOOTSTRAP_FILENAME;
|
||||
|
||||
export type WorkspaceBootstrapFile = {
|
||||
name: WorkspaceBootstrapFileName;
|
||||
|
|
@ -121,6 +192,9 @@ export async function ensureAgentWorkspace(params?: {
|
|||
agentsPath?: string;
|
||||
soulPath?: string;
|
||||
toolsPath?: string;
|
||||
identityPath?: string;
|
||||
userPath?: string;
|
||||
bootstrapPath?: string;
|
||||
}> {
|
||||
const rawDir = params?.dir?.trim()
|
||||
? params.dir.trim()
|
||||
|
|
@ -133,6 +207,9 @@ export async function ensureAgentWorkspace(params?: {
|
|||
const agentsPath = path.join(dir, DEFAULT_AGENTS_FILENAME);
|
||||
const soulPath = path.join(dir, DEFAULT_SOUL_FILENAME);
|
||||
const toolsPath = path.join(dir, DEFAULT_TOOLS_FILENAME);
|
||||
const identityPath = path.join(dir, DEFAULT_IDENTITY_FILENAME);
|
||||
const userPath = path.join(dir, DEFAULT_USER_FILENAME);
|
||||
const bootstrapPath = path.join(dir, DEFAULT_BOOTSTRAP_FILENAME);
|
||||
|
||||
const agentsTemplate = await loadTemplate(
|
||||
DEFAULT_AGENTS_FILENAME,
|
||||
|
|
@ -146,12 +223,35 @@ export async function ensureAgentWorkspace(params?: {
|
|||
DEFAULT_TOOLS_FILENAME,
|
||||
DEFAULT_TOOLS_TEMPLATE,
|
||||
);
|
||||
const identityTemplate = await loadTemplate(
|
||||
DEFAULT_IDENTITY_FILENAME,
|
||||
DEFAULT_IDENTITY_TEMPLATE,
|
||||
);
|
||||
const userTemplate = await loadTemplate(
|
||||
DEFAULT_USER_FILENAME,
|
||||
DEFAULT_USER_TEMPLATE,
|
||||
);
|
||||
const bootstrapTemplate = await loadTemplate(
|
||||
DEFAULT_BOOTSTRAP_FILENAME,
|
||||
DEFAULT_BOOTSTRAP_TEMPLATE,
|
||||
);
|
||||
|
||||
await writeFileIfMissing(agentsPath, agentsTemplate);
|
||||
await writeFileIfMissing(soulPath, soulTemplate);
|
||||
await writeFileIfMissing(toolsPath, toolsTemplate);
|
||||
await writeFileIfMissing(identityPath, identityTemplate);
|
||||
await writeFileIfMissing(userPath, userTemplate);
|
||||
await writeFileIfMissing(bootstrapPath, bootstrapTemplate);
|
||||
|
||||
return { dir, agentsPath, soulPath, toolsPath };
|
||||
return {
|
||||
dir,
|
||||
agentsPath,
|
||||
soulPath,
|
||||
toolsPath,
|
||||
identityPath,
|
||||
userPath,
|
||||
bootstrapPath,
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadWorkspaceBootstrapFiles(
|
||||
|
|
@ -175,6 +275,18 @@ export async function loadWorkspaceBootstrapFiles(
|
|||
name: DEFAULT_TOOLS_FILENAME,
|
||||
filePath: path.join(resolvedDir, DEFAULT_TOOLS_FILENAME),
|
||||
},
|
||||
{
|
||||
name: DEFAULT_IDENTITY_FILENAME,
|
||||
filePath: path.join(resolvedDir, DEFAULT_IDENTITY_FILENAME),
|
||||
},
|
||||
{
|
||||
name: DEFAULT_USER_FILENAME,
|
||||
filePath: path.join(resolvedDir, DEFAULT_USER_FILENAME),
|
||||
},
|
||||
{
|
||||
name: DEFAULT_BOOTSTRAP_FILENAME,
|
||||
filePath: path.join(resolvedDir, DEFAULT_BOOTSTRAP_FILENAME),
|
||||
},
|
||||
];
|
||||
|
||||
const result: WorkspaceBootstrapFile[] = [];
|
||||
|
|
|
|||
Loading…
Reference in New Issue