@kendoo.agentdesk/agentdesk 0.9.5 → 0.9.7
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 +4 -13
- package/bin/agentdesk.mjs +2 -7
- package/cli/agents.mjs +53 -78
- package/cli/daemon.mjs +1 -2
- package/cli/init.mjs +78 -13
- package/cli/orchestrator.mjs +2 -271
- package/cli/prompt.mjs +7 -3
- package/cli/stream-parser.mjs +1 -1
- package/cli/team.mjs +38 -25
- package/cli/tracker-check.mjs +211 -0
- package/package.json +1 -1
- package/prompts/team.md +135 -278
- package/cli/agent-prompts.mjs +0 -166
- package/cli/agent-runner.mjs +0 -148
package/README.md
CHANGED
|
@@ -79,7 +79,6 @@ agentdesk update Update to the latest version
|
|
|
79
79
|
|------|-------------|
|
|
80
80
|
| `--description`, `-d` | Task description or requirements |
|
|
81
81
|
| `--cwd` | Working directory (defaults to current) |
|
|
82
|
-
| `--lite` | Lite mode — all agents share one process (faster, cheaper) |
|
|
83
82
|
|
|
84
83
|
## Configuration
|
|
85
84
|
|
|
@@ -116,22 +115,14 @@ AgentDesk also auto-discovers agents from `.claude/agents/`, `.claude/commands/`
|
|
|
116
115
|
|
|
117
116
|
## How It Works
|
|
118
117
|
|
|
119
|
-
|
|
118
|
+
All agents collaborate in a single Claude process — each with distinct roles, ground rules, and areas of expertise.
|
|
120
119
|
|
|
121
120
|
1. You run `agentdesk team TASK-ID` in your project directory
|
|
122
121
|
2. AgentDesk detects your project type, reads `CLAUDE.md`, and discovers existing agents
|
|
123
122
|
3. The orchestrator manages 5 phases: **Intake** > **Brainstorm** > **Planning** > **Execution** > **Review**
|
|
124
|
-
4.
|
|
125
|
-
5.
|
|
126
|
-
6.
|
|
127
|
-
7. Token usage is tracked and displayed per session
|
|
128
|
-
|
|
129
|
-
### Agent Modes
|
|
130
|
-
|
|
131
|
-
| Mode | Command | Description |
|
|
132
|
-
|------|---------|-------------|
|
|
133
|
-
| Full (default) | `agentdesk team -d "..."` | Each agent runs in its own process. Better output. |
|
|
134
|
-
| Lite | `agentdesk team -d "..." --lite` | All agents share one process. Faster and cheaper. |
|
|
123
|
+
4. Agents discuss, disagree, and build on each other's ideas within a shared context
|
|
124
|
+
5. The session streams live to [agentdesk.live](https://agentdesk.live) where you can watch and send messages to the team
|
|
125
|
+
6. Token usage is tracked and displayed per session
|
|
135
126
|
|
|
136
127
|
## Daemon (Remote Sessions)
|
|
137
128
|
|
package/bin/agentdesk.mjs
CHANGED
|
@@ -70,7 +70,7 @@ if (!command || command === "help" || command === "--help") {
|
|
|
70
70
|
Options:
|
|
71
71
|
--description, -d Task description or requirements
|
|
72
72
|
--cwd Working directory (defaults to current)
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
|
|
75
75
|
How it works:
|
|
76
76
|
With a tracker (Linear, Jira, GitHub Issues):
|
|
@@ -110,7 +110,6 @@ else if (command === "team") {
|
|
|
110
110
|
let description = "";
|
|
111
111
|
let cwd = process.cwd();
|
|
112
112
|
let taskId = null;
|
|
113
|
-
let lite = false;
|
|
114
113
|
const remaining = args.slice(1);
|
|
115
114
|
|
|
116
115
|
for (let i = 0; i < remaining.length; i++) {
|
|
@@ -118,10 +117,6 @@ else if (command === "team") {
|
|
|
118
117
|
description = remaining[++i];
|
|
119
118
|
} else if (remaining[i] === "--cwd" && remaining[i + 1]) {
|
|
120
119
|
cwd = remaining[++i];
|
|
121
|
-
} else if (remaining[i] === "--lite" || remaining[i] === "--legacy") {
|
|
122
|
-
lite = true;
|
|
123
|
-
} else if (remaining[i] === "--full") {
|
|
124
|
-
lite = false;
|
|
125
120
|
} else if (!taskId && !remaining[i].startsWith("-")) {
|
|
126
121
|
taskId = remaining[i];
|
|
127
122
|
}
|
|
@@ -134,7 +129,7 @@ else if (command === "team") {
|
|
|
134
129
|
}
|
|
135
130
|
|
|
136
131
|
const { runTeam } = await import("../cli/team.mjs");
|
|
137
|
-
const code = await runTeam(taskId, { description, cwd
|
|
132
|
+
const code = await runTeam(taskId, { description, cwd });
|
|
138
133
|
process.exit(code);
|
|
139
134
|
}
|
|
140
135
|
|
package/cli/agents.mjs
CHANGED
|
@@ -4,34 +4,32 @@ export const BUILT_IN_AGENTS = {
|
|
|
4
4
|
Jane: {
|
|
5
5
|
badge: "●● JANE ●●",
|
|
6
6
|
role: "Product Analyst / Team Lead",
|
|
7
|
-
description: "
|
|
8
|
-
groundRules: "Jane
|
|
9
|
-
|
|
10
|
-
planning: "Requirements Summary (what we're building, acceptance criteria, scope boundaries). If this task involves UI changes, Jane declares it a UI task and tells Luna to define screenshot requirements.",
|
|
7
|
+
description: "leads the session, clarifies requirements, coordinates the team, manages tracker status",
|
|
8
|
+
groundRules: "Jane focuses on requirements, scope, and coordination — she does not read code. She creates tracker tasks when needed, manages status transitions, and posts the session start/end comments.",
|
|
9
|
+
planning: "Requirements: what we're building, acceptance criteria, scope. Flags UI tasks for Luna.",
|
|
11
10
|
execution: {
|
|
12
11
|
step: "Jane wraps up",
|
|
13
12
|
tasks: [
|
|
14
|
-
"
|
|
15
|
-
"
|
|
13
|
+
"Transition task to 'In Review'.",
|
|
14
|
+
"Verify PR description is clear.",
|
|
15
|
+
"Post final summary comment on tracker.",
|
|
16
16
|
],
|
|
17
|
-
order: 99,
|
|
17
|
+
order: 99,
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
20
|
Dennis: {
|
|
21
21
|
badge: "■■ DENNIS ■■",
|
|
22
22
|
role: "Senior Developer",
|
|
23
|
-
description: "
|
|
24
|
-
groundRules: "Dennis
|
|
25
|
-
codePrinciple: "
|
|
26
|
-
|
|
27
|
-
planning: "Implementation Plan (files to modify, approach, complexity S/M/L)",
|
|
23
|
+
description: "implements the solution, verifies technical feasibility",
|
|
24
|
+
groundRules: "Dennis verifies assumptions about the codebase with tools before committing to an approach.",
|
|
25
|
+
codePrinciple: "Extract logic out of components/templates into functions, hooks, or services.",
|
|
26
|
+
planning: "Implementation plan: files to modify, approach, complexity (S/M/L)",
|
|
28
27
|
execution: {
|
|
29
28
|
step: "Dennis implements",
|
|
30
29
|
tasks: [
|
|
31
|
-
"Create
|
|
32
|
-
"Implement the changes according to the agreed plan.",
|
|
30
|
+
"Create branch, implement changes per the plan.",
|
|
33
31
|
"Run linter and build to verify.",
|
|
34
|
-
"Commit
|
|
32
|
+
"Commit.",
|
|
35
33
|
],
|
|
36
34
|
order: 1,
|
|
37
35
|
},
|
|
@@ -39,17 +37,15 @@ export const BUILT_IN_AGENTS = {
|
|
|
39
37
|
Sam: {
|
|
40
38
|
badge: "◆◆ SAM ◆◆",
|
|
41
39
|
role: "Architecture Auditor",
|
|
42
|
-
description: "
|
|
43
|
-
groundRules: "Sam
|
|
44
|
-
codePrinciple: "
|
|
45
|
-
|
|
46
|
-
planning: "Architecture Review (existing violations, whether approach is clean)",
|
|
40
|
+
description: "guards code architecture and separation of concerns",
|
|
41
|
+
groundRules: "Sam backs claims with file:line references.",
|
|
42
|
+
codePrinciple: "Guards separation of concerns. Cites file and line number.",
|
|
43
|
+
planning: "Architecture review: existing patterns, whether approach is clean",
|
|
47
44
|
execution: {
|
|
48
|
-
step: "Sam
|
|
45
|
+
step: "Sam audits",
|
|
49
46
|
tasks: [
|
|
50
|
-
"Read
|
|
51
|
-
"
|
|
52
|
-
"If violations found, the developer fixes them before proceeding.",
|
|
47
|
+
"Read changed files, check for architecture violations.",
|
|
48
|
+
"If violations found, developer fixes before proceeding.",
|
|
53
49
|
],
|
|
54
50
|
order: 2,
|
|
55
51
|
},
|
|
@@ -57,21 +53,18 @@ export const BUILT_IN_AGENTS = {
|
|
|
57
53
|
Bart: {
|
|
58
54
|
badge: "▲▲ BART ▲▲",
|
|
59
55
|
role: "QA Engineer",
|
|
60
|
-
description: "
|
|
61
|
-
groundRules: "Bart
|
|
62
|
-
codePrinciple: "Flag
|
|
63
|
-
|
|
64
|
-
planning: "Test Plan (acceptance criteria, key test cases, edge cases)",
|
|
56
|
+
description: "reviews quality, edge cases, creates PR, captures screenshots",
|
|
57
|
+
groundRules: "Bart identifies risks and edge cases before approving.",
|
|
58
|
+
codePrinciple: "Flag inline logic in components/templates.",
|
|
59
|
+
planning: "Test plan: acceptance criteria, key edge cases",
|
|
65
60
|
execution: {
|
|
66
|
-
step: "Bart reviews",
|
|
61
|
+
step: "Bart reviews & creates PR",
|
|
67
62
|
tasks: [
|
|
68
|
-
"Read
|
|
69
|
-
"Check calculations, edge cases, error handling.",
|
|
63
|
+
"Read all changed files. Check edge cases, error handling.",
|
|
70
64
|
"Run linter and build.",
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"Post screenshots
|
|
74
|
-
"Approve the PR if clean.",
|
|
65
|
+
"Capture screenshots if UI task (per Luna's plan).",
|
|
66
|
+
"Push and create PR. Post PR link on tracker.",
|
|
67
|
+
"Post screenshots as separate tracker comment.",
|
|
75
68
|
],
|
|
76
69
|
order: 4,
|
|
77
70
|
},
|
|
@@ -79,17 +72,15 @@ export const BUILT_IN_AGENTS = {
|
|
|
79
72
|
Vera: {
|
|
80
73
|
badge: "◈◈ VERA ◈◈",
|
|
81
74
|
role: "Test Engineer",
|
|
82
|
-
description: "writes unit and regression tests for changed code
|
|
83
|
-
groundRules: "Vera
|
|
84
|
-
codePrinciple: "
|
|
85
|
-
|
|
86
|
-
planning: "Test Plan (which functions need tests, regression tests)",
|
|
75
|
+
description: "writes unit and regression tests for changed code",
|
|
76
|
+
groundRules: "Vera identifies which functions need test coverage.",
|
|
77
|
+
codePrinciple: "Test logic directly — don't mount UI to test outcomes. Mirror service/utility structure.",
|
|
78
|
+
planning: "Test coverage: which functions need tests, regression cases",
|
|
87
79
|
execution: {
|
|
88
80
|
step: "Vera writes tests",
|
|
89
81
|
tasks: [
|
|
90
|
-
"
|
|
91
|
-
"Write unit tests following existing
|
|
92
|
-
"Run tests to verify they pass.",
|
|
82
|
+
"Identify testable functions in changed files.",
|
|
83
|
+
"Write unit tests following existing patterns. Run and verify.",
|
|
93
84
|
"Commit test files.",
|
|
94
85
|
],
|
|
95
86
|
order: 3,
|
|
@@ -98,21 +89,16 @@ export const BUILT_IN_AGENTS = {
|
|
|
98
89
|
Luna: {
|
|
99
90
|
badge: "☾☾ LUNA ☾☾",
|
|
100
91
|
role: "UX/UI Designer",
|
|
101
|
-
description: "
|
|
102
|
-
groundRules: "Luna
|
|
103
|
-
codePrinciple: "Reviews
|
|
104
|
-
|
|
105
|
-
planning: "UX Review (visual impact, layout concerns, accessibility checklist, interaction improvements). For UI tasks: define which pages/views need screenshots and whether desktop, mobile, or both are relevant.",
|
|
92
|
+
description: "reviews UI changes for visual consistency, accessibility, and UX",
|
|
93
|
+
groundRules: "Luna references specific components and proposes exact CSS fixes.",
|
|
94
|
+
codePrinciple: "Reviews visual hierarchy, spacing, color, typography, responsive behavior, accessibility (WCAG).",
|
|
95
|
+
planning: "UX review: visual impact, accessibility. For UI tasks: define screenshot plan (pages, viewports).",
|
|
106
96
|
execution: {
|
|
107
|
-
step: "Luna reviews UI
|
|
97
|
+
step: "Luna reviews UI (if applicable)",
|
|
108
98
|
tasks: [
|
|
109
|
-
"Read
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"Check responsive behavior and interaction flow.",
|
|
113
|
-
"If issues found, propose specific fixes (exact CSS values, spacing, colors). The developer implements them before proceeding.",
|
|
114
|
-
"Define the screenshot plan: which pages/routes to capture and which viewports (desktop, mobile, or both). Hand this plan to Bart.",
|
|
115
|
-
"Skip this step if the task has no UI impact.",
|
|
99
|
+
"Read changed UI files. Check visual consistency and accessibility.",
|
|
100
|
+
"Propose specific fixes if needed. Developer implements.",
|
|
101
|
+
"Hand screenshot plan to Bart. Skip if no UI impact.",
|
|
116
102
|
],
|
|
117
103
|
order: 2.1,
|
|
118
104
|
},
|
|
@@ -120,19 +106,16 @@ export const BUILT_IN_AGENTS = {
|
|
|
120
106
|
Mark: {
|
|
121
107
|
badge: "✦✦ MARK ✦✦",
|
|
122
108
|
role: "Content Writer",
|
|
123
|
-
description: "
|
|
124
|
-
groundRules: "Mark
|
|
125
|
-
codePrinciple: "
|
|
126
|
-
|
|
127
|
-
planning: "Content Review (user-facing text audit, tone, clarity, empty states, error messages)",
|
|
109
|
+
description: "reviews user-facing text for clarity, tone, and consistency",
|
|
110
|
+
groundRules: "Mark reviews all labels, buttons, error messages, tooltips, and empty states.",
|
|
111
|
+
codePrinciple: "Rewrites vague or jargon-heavy copy into clear, concise language. Ensures consistent tone.",
|
|
112
|
+
planning: "Content review: user-facing text audit, tone consistency",
|
|
128
113
|
execution: {
|
|
129
114
|
step: "Mark reviews content (if applicable)",
|
|
130
115
|
tasks: [
|
|
131
|
-
"Read
|
|
132
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"Propose exact replacement strings. The developer implements them.",
|
|
135
|
-
"Skip this step if the task has no user-facing text changes.",
|
|
116
|
+
"Read changed files with user-facing text.",
|
|
117
|
+
"Propose exact replacement strings if needed. Developer implements.",
|
|
118
|
+
"Skip if no user-facing text changes.",
|
|
136
119
|
],
|
|
137
120
|
order: 2.2,
|
|
138
121
|
},
|
|
@@ -172,7 +155,6 @@ export function resolveTeam(config) {
|
|
|
172
155
|
role: custom.role || "Team Member",
|
|
173
156
|
description: custom.role || "Custom team member",
|
|
174
157
|
groundRules: custom.when ? `${custom.name} is invoked: ${custom.when}. How to use: ${custom.how || "as needed"}.` : "",
|
|
175
|
-
brainstorm: { tag: "SAY", focus: custom.role || "general input" },
|
|
176
158
|
planning: `${custom.role || custom.name} Review`,
|
|
177
159
|
execution: custom.when ? {
|
|
178
160
|
step: `${custom.name} reviews`,
|
|
@@ -201,7 +183,6 @@ export function resolveTeam(config) {
|
|
|
201
183
|
role: entry.role || "Team Member",
|
|
202
184
|
description: entry.description || entry.role || "Custom team member",
|
|
203
185
|
groundRules: entry.groundRules || "",
|
|
204
|
-
brainstorm: entry.brainstorm || { tag: "SAY", focus: entry.role || "general input" },
|
|
205
186
|
planning: entry.planning || `${entry.role || entry.name} Review`,
|
|
206
187
|
execution: entry.execution || null,
|
|
207
188
|
});
|
|
@@ -242,9 +223,9 @@ export function generateTeamPrompt(team) {
|
|
|
242
223
|
|
|
243
224
|
// Ground rules
|
|
244
225
|
const rules = [];
|
|
245
|
-
rules.push(`1. Each agent speaks
|
|
246
|
-
rules.push(`2. ALL text output MUST be prefixed with the acting agent's badge
|
|
247
|
-
rules.push(`3.
|
|
226
|
+
rules.push(`1. Each agent speaks prefixed with their badge (e.g., "${team[0].badge} ...").`);
|
|
227
|
+
rules.push(`2. ALL text output MUST be prefixed with the acting agent's badge.`);
|
|
228
|
+
rules.push(`3. Raise concerns when you see problems — don't rubber-stamp.`);
|
|
248
229
|
let ruleNum = 4;
|
|
249
230
|
for (const a of team) {
|
|
250
231
|
if (a.groundRules) {
|
|
@@ -252,7 +233,6 @@ export function generateTeamPrompt(team) {
|
|
|
252
233
|
ruleNum++;
|
|
253
234
|
}
|
|
254
235
|
}
|
|
255
|
-
rules.push(`${ruleNum}. Every statement should add value — no filler, no repeating what someone else already said.`);
|
|
256
236
|
sections.groundRules = rules.join("\n");
|
|
257
237
|
|
|
258
238
|
// Code principles
|
|
@@ -261,11 +241,6 @@ export function generateTeamPrompt(team) {
|
|
|
261
241
|
.map(a => `- ${a.name}: ${a.codePrinciple}`);
|
|
262
242
|
sections.codePrinciples = principles.length > 0 ? principles.join("\n") : "";
|
|
263
243
|
|
|
264
|
-
// Brainstorm order
|
|
265
|
-
sections.brainstormOrder = team.map((a, i) =>
|
|
266
|
-
`${i + 1}. ${a.badge} [${a.brainstorm.tag}] ${a.brainstorm.focus}`
|
|
267
|
-
).join("\n");
|
|
268
|
-
|
|
269
244
|
// Planning presentations
|
|
270
245
|
sections.planningOrder = team.map(a =>
|
|
271
246
|
`${a.badge} ${a.planning}`
|
package/cli/daemon.mjs
CHANGED
|
@@ -313,7 +313,7 @@ export async function runDaemon() {
|
|
|
313
313
|
|
|
314
314
|
// 4. Session handling
|
|
315
315
|
|
|
316
|
-
async function handleStartSession({ sessionId, projectId, taskId: remoteTaskId, prompt
|
|
316
|
+
async function handleStartSession({ sessionId, projectId, taskId: remoteTaskId, prompt }) {
|
|
317
317
|
// Validate project against local allowlist
|
|
318
318
|
const project = projects.find(p => p.id === projectId);
|
|
319
319
|
if (!project) {
|
|
@@ -379,7 +379,6 @@ export async function runDaemon() {
|
|
|
379
379
|
project: detected, team, teamSections,
|
|
380
380
|
inboxUrl, sessionUrl,
|
|
381
381
|
cwd: project.path,
|
|
382
|
-
mode: mode || "full",
|
|
383
382
|
apiKey,
|
|
384
383
|
serverUrl: agentdeskServer,
|
|
385
384
|
onEvent(event) {
|
package/cli/init.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { detectProject } from "./detect.mjs";
|
|
|
7
7
|
import { loadConfig } from "./config.mjs";
|
|
8
8
|
import { getStoredApiKey } from "./login.mjs";
|
|
9
9
|
import { registerLocalProject } from "./projects.mjs";
|
|
10
|
+
import { checkTrackerPermissions, resolveCredentialsFromEnv } from "./tracker-check.mjs";
|
|
10
11
|
|
|
11
12
|
const SERVER = process.env.AGENTDESK_SERVER || "https://agentdesk.live";
|
|
12
13
|
|
|
@@ -62,6 +63,12 @@ export async function runInit(cwd) {
|
|
|
62
63
|
if (project.lintCommand) console.log(` Lint: ${project.lintCommand}`);
|
|
63
64
|
if (project.testCommand || project.buildCommand || project.lintCommand) console.log("");
|
|
64
65
|
|
|
66
|
+
// --- Project key ---
|
|
67
|
+
const defaultKey = existingConfig.projectKey || projectId;
|
|
68
|
+
const keyAnswer = await ask(rl, ` Project key (${defaultKey}): `);
|
|
69
|
+
const finalProjectKey = keyAnswer.trim() || defaultKey;
|
|
70
|
+
console.log("");
|
|
71
|
+
|
|
65
72
|
// --- Tracker selection ---
|
|
66
73
|
const trackerOptions = [
|
|
67
74
|
{ label: "Linear", value: "linear" },
|
|
@@ -75,22 +82,28 @@ export async function runInit(cwd) {
|
|
|
75
82
|
console.log("");
|
|
76
83
|
|
|
77
84
|
// Build config
|
|
78
|
-
const config = {};
|
|
85
|
+
const config = { projectKey: finalProjectKey };
|
|
79
86
|
if (tracker) config.tracker = tracker;
|
|
80
87
|
|
|
81
88
|
if (tracker === "linear") {
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const ws =
|
|
85
|
-
|
|
89
|
+
const currentWs = existingConfig.linear?.workspace || "";
|
|
90
|
+
const wsAnswer = await ask(rl, ` Linear workspace slug${currentWs ? ` (${currentWs})` : ""}: `);
|
|
91
|
+
const ws = wsAnswer.trim() || currentWs;
|
|
92
|
+
const currentKey = existingConfig.linear?.teamKey || "";
|
|
93
|
+
const keyAnswer = await ask(rl, ` Linear team key${currentKey ? ` (${currentKey})` : ""} (e.g. KEN): `);
|
|
94
|
+
const teamKey = keyAnswer.trim() || currentKey;
|
|
95
|
+
if (ws || teamKey) config.linear = { ...(ws && { workspace: ws }), ...(teamKey && { teamKey }) };
|
|
86
96
|
console.log("");
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
if (tracker === "jira") {
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const url =
|
|
93
|
-
|
|
100
|
+
const currentUrl = existingConfig.jira?.baseUrl || "";
|
|
101
|
+
const urlAnswer = await ask(rl, ` Jira base URL${currentUrl ? ` (${currentUrl})` : ""}: `);
|
|
102
|
+
const url = urlAnswer.trim() || currentUrl;
|
|
103
|
+
const currentProj = existingConfig.jira?.project || "";
|
|
104
|
+
const projAnswer = await ask(rl, ` Jira project key${currentProj ? ` (${currentProj})` : ""} (e.g. PROJ): `);
|
|
105
|
+
const proj = projAnswer.trim() || currentProj;
|
|
106
|
+
if (url || proj) config.jira = { ...(url && { baseUrl: url }), ...(proj && { project: proj }) };
|
|
94
107
|
console.log("");
|
|
95
108
|
}
|
|
96
109
|
|
|
@@ -102,12 +115,64 @@ export async function runInit(cwd) {
|
|
|
102
115
|
console.log("");
|
|
103
116
|
}
|
|
104
117
|
|
|
118
|
+
// --- Verify tracker permissions ---
|
|
119
|
+
if (tracker) {
|
|
120
|
+
console.log(" Checking tracker permissions...");
|
|
121
|
+
const envPath = join(cwd, ".env");
|
|
122
|
+
let dotEnv = {};
|
|
123
|
+
if (existsSync(envPath)) {
|
|
124
|
+
for (const line of readFileSync(envPath, "utf-8").split("\n")) {
|
|
125
|
+
const trimmed = line.trim();
|
|
126
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
127
|
+
const eq = trimmed.indexOf("=");
|
|
128
|
+
if (eq !== -1) dotEnv[trimmed.slice(0, eq)] = trimmed.slice(eq + 1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Also try fetching credentials from server
|
|
133
|
+
const apiKey = loadApiKey(cwd);
|
|
134
|
+
let serverCreds = {};
|
|
135
|
+
if (apiKey) {
|
|
136
|
+
try {
|
|
137
|
+
const res = await fetch(`${SERVER}/api/projects/${finalProjectKey}/settings/credentials`, {
|
|
138
|
+
headers: { "x-api-key": apiKey },
|
|
139
|
+
signal: AbortSignal.timeout(5000),
|
|
140
|
+
});
|
|
141
|
+
if (res.ok) serverCreds = await res.json();
|
|
142
|
+
} catch {}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const credentials = resolveCredentialsFromEnv({ ...dotEnv, ...serverCreds });
|
|
146
|
+
const check = await checkTrackerPermissions({ tracker, config, credentials });
|
|
147
|
+
|
|
148
|
+
if (!check.ok) {
|
|
149
|
+
console.log("");
|
|
150
|
+
console.log(" ⚠ Tracker permission issues:");
|
|
151
|
+
for (const err of check.errors) {
|
|
152
|
+
console.log(` • ${err}`);
|
|
153
|
+
}
|
|
154
|
+
console.log("");
|
|
155
|
+
const proceed = await ask(rl, " Continue anyway? (y/N): ");
|
|
156
|
+
if (proceed.trim().toLowerCase() !== "y") {
|
|
157
|
+
console.log(" Setup cancelled. Fix the issues above and run 'agentdesk init' again.");
|
|
158
|
+
rl.close();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
console.log("");
|
|
162
|
+
} else {
|
|
163
|
+
console.log(" ✓ Tracker permissions verified (read, create, update)");
|
|
164
|
+
console.log("");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
105
168
|
// --- Save .agentdesk.json ---
|
|
106
169
|
let merged = {};
|
|
107
170
|
if (hasConfig) {
|
|
108
171
|
try { merged = JSON.parse(readFileSync(configPath, "utf-8")); } catch {}
|
|
109
172
|
}
|
|
110
173
|
|
|
174
|
+
merged.projectKey = finalProjectKey;
|
|
175
|
+
|
|
111
176
|
if (tracker) {
|
|
112
177
|
merged.tracker = tracker;
|
|
113
178
|
if (config.linear) merged.linear = config.linear;
|
|
@@ -124,7 +189,7 @@ export async function runInit(cwd) {
|
|
|
124
189
|
console.log(` Saved .agentdesk.json`);
|
|
125
190
|
|
|
126
191
|
// Register in local project index (for daemon discovery)
|
|
127
|
-
registerLocalProject(
|
|
192
|
+
registerLocalProject(finalProjectKey, project.name || finalProjectKey, project.dir);
|
|
128
193
|
|
|
129
194
|
// --- Register with server ---
|
|
130
195
|
try {
|
|
@@ -135,8 +200,8 @@ export async function runInit(cwd) {
|
|
|
135
200
|
...(loadApiKey(cwd) ? { "x-api-key": loadApiKey(cwd) } : {}),
|
|
136
201
|
},
|
|
137
202
|
body: JSON.stringify({
|
|
138
|
-
id:
|
|
139
|
-
name: project.name ||
|
|
203
|
+
id: finalProjectKey,
|
|
204
|
+
name: project.name || finalProjectKey,
|
|
140
205
|
path: project.dir,
|
|
141
206
|
type: project.type,
|
|
142
207
|
tracker,
|
|
@@ -148,7 +213,7 @@ export async function runInit(cwd) {
|
|
|
148
213
|
// Push settings to server
|
|
149
214
|
const key = loadApiKey(cwd);
|
|
150
215
|
if (key) {
|
|
151
|
-
await fetch(`${SERVER}/api/projects/${
|
|
216
|
+
await fetch(`${SERVER}/api/projects/${finalProjectKey}/settings`, {
|
|
152
217
|
method: "PUT",
|
|
153
218
|
headers: { "Content-Type": "application/json", "x-api-key": key },
|
|
154
219
|
body: JSON.stringify(merged),
|