@mindfoldhq/trellis 0.4.0-beta.9 → 0.4.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +3 -3
  2. package/dist/cli/index.js +1 -0
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/init.d.ts +1 -0
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +132 -4
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/update.d.ts.map +1 -1
  9. package/dist/commands/update.js +14 -2
  10. package/dist/commands/update.js.map +1 -1
  11. package/dist/configurators/droid.d.ts +5 -0
  12. package/dist/configurators/droid.d.ts.map +1 -0
  13. package/dist/configurators/droid.js +48 -0
  14. package/dist/configurators/droid.js.map +1 -0
  15. package/dist/configurators/index.d.ts.map +1 -1
  16. package/dist/configurators/index.js +13 -0
  17. package/dist/configurators/index.js.map +1 -1
  18. package/dist/migrations/manifests/0.4.0-beta.10.json +9 -0
  19. package/dist/migrations/manifests/0.4.0-rc.0.json +9 -0
  20. package/dist/migrations/manifests/0.4.0-rc.1.json +9 -0
  21. package/dist/templates/claude/hooks/ralph-loop.py +10 -9
  22. package/dist/templates/claude/hooks/session-start.py +29 -12
  23. package/dist/templates/claude/hooks/statusline.py +7 -0
  24. package/dist/templates/codex/hooks/session-start.py +29 -14
  25. package/dist/templates/copilot/hooks/session-start.py +29 -4
  26. package/dist/templates/droid/commands/trellis/before-dev.md +33 -0
  27. package/dist/templates/droid/commands/trellis/brainstorm.md +491 -0
  28. package/dist/templates/droid/commands/trellis/break-loop.md +111 -0
  29. package/dist/templates/droid/commands/trellis/check-cross-layer.md +157 -0
  30. package/dist/templates/droid/commands/trellis/check.md +29 -0
  31. package/dist/templates/droid/commands/trellis/create-command.md +158 -0
  32. package/dist/templates/droid/commands/trellis/finish-work.md +147 -0
  33. package/dist/templates/droid/commands/trellis/integrate-skill.md +223 -0
  34. package/dist/templates/droid/commands/trellis/onboard.md +362 -0
  35. package/dist/templates/droid/commands/trellis/record-session.md +66 -0
  36. package/dist/templates/droid/commands/trellis/start.md +377 -0
  37. package/dist/templates/droid/commands/trellis/update-spec.md +358 -0
  38. package/dist/templates/droid/index.d.ts +27 -0
  39. package/dist/templates/droid/index.d.ts.map +1 -0
  40. package/dist/templates/droid/index.js +47 -0
  41. package/dist/templates/droid/index.js.map +1 -0
  42. package/dist/templates/extract.d.ts +11 -0
  43. package/dist/templates/extract.d.ts.map +1 -1
  44. package/dist/templates/extract.js +19 -0
  45. package/dist/templates/extract.js.map +1 -1
  46. package/dist/templates/iflow/hooks/session-start.py +29 -12
  47. package/dist/templates/opencode/lib/trellis-context.js +4 -248
  48. package/dist/templates/opencode/plugins/inject-subagent-context.js +71 -121
  49. package/dist/templates/opencode/plugins/session-start.js +143 -119
  50. package/dist/templates/trellis/scripts/common/cli_adapter.py +27 -2
  51. package/dist/templates/trellis/workflow.md +17 -4
  52. package/dist/types/ai-tools.d.ts +3 -3
  53. package/dist/types/ai-tools.d.ts.map +1 -1
  54. package/dist/types/ai-tools.js +9 -1
  55. package/dist/types/ai-tools.js.map +1 -1
  56. package/package.json +1 -1
@@ -3,11 +3,7 @@
3
3
  * Trellis Session Start Plugin
4
4
  *
5
5
  * Injects context when user sends the first message in a session.
6
- * Uses OpenCode's chat.message + experimental.chat.messages.transform hooks.
7
- *
8
- * Compatibility:
9
- * - If oh-my-opencode handles via .claude/hooks/, this plugin skips
10
- * - Otherwise, this plugin handles injection
6
+ * Uses OpenCode's chat.message hook directly so the context persists in history.
11
7
  */
12
8
 
13
9
  import { existsSync, readFileSync, readdirSync, statSync } from "fs"
@@ -29,14 +25,12 @@ function getTaskStatus(ctx) {
29
25
  return "Status: NO ACTIVE TASK\nNext: Describe what you want to work on"
30
26
  }
31
27
 
32
- // Resolve task directory
33
28
  const taskDir = ctx.resolveTaskDir(taskRef)
34
29
 
35
30
  if (!taskDir || !existsSync(taskDir)) {
36
31
  return `Status: STALE POINTER\nTask: ${taskRef}\nNext: Task directory not found. Run: python3 ./.trellis/scripts/task.py finish`
37
32
  }
38
33
 
39
- // Read task.json
40
34
  let taskData = {}
41
35
  const taskJsonPath = join(taskDir, "task.json")
42
36
  if (existsSync(taskJsonPath)) {
@@ -55,7 +49,6 @@ function getTaskStatus(ctx) {
55
49
  return `Status: COMPLETED\nTask: ${taskTitle}\nNext: Archive with \`python3 ./.trellis/scripts/task.py archive ${dirName}\` or start a new task`
56
50
  }
57
51
 
58
- // Check if context is configured (jsonl files exist and non-empty)
59
52
  let hasContext = false
60
53
  for (const jsonlName of ["implement.jsonl", "check.jsonl", "spec.jsonl"]) {
61
54
  const jsonlPath = join(taskDir, jsonlName)
@@ -105,7 +98,6 @@ function loadTrellisConfig(directory) {
105
98
  if (data.mode !== "monorepo") {
106
99
  return { isMonorepo: false, packages: {}, specScope: null, activeTaskPackage: null, defaultPackage: null }
107
100
  }
108
- // Convert packages array to dict keyed by name
109
101
  const pkgDict = {}
110
102
  for (const pkg of (data.packages || [])) {
111
103
  pkgDict[pkg.name] = pkg
@@ -135,7 +127,6 @@ function checkLegacySpec(directory, config) {
135
127
  const specDir = join(directory, ".trellis", "spec")
136
128
  if (!existsSync(specDir)) return null
137
129
 
138
- // Check for legacy flat spec dirs
139
130
  let hasLegacy = false
140
131
  for (const name of ["backend", "frontend"]) {
141
132
  if (existsSync(join(specDir, name, "index.md"))) {
@@ -145,7 +136,6 @@ function checkLegacySpec(directory, config) {
145
136
  }
146
137
  if (!hasLegacy) return null
147
138
 
148
- // Check which packages are missing spec/<pkg>/ directory
149
139
  const pkgNames = Object.keys(config.packages).sort()
150
140
  const missing = pkgNames.filter(name => !existsSync(join(specDir, name)))
151
141
 
@@ -193,7 +183,6 @@ function resolveSpecScope(config) {
193
183
  }
194
184
  }
195
185
  if (valid.size > 0) return valid
196
- // All invalid: fallback
197
186
  if (activeTaskPackage && activeTaskPackage in packages) return new Set([activeTaskPackage])
198
187
  if (defaultPackage && defaultPackage in packages) return new Set([defaultPackage])
199
188
  return null
@@ -209,10 +198,7 @@ function resolveSpecScope(config) {
209
198
  function buildSessionContext(ctx) {
210
199
  const directory = ctx.directory
211
200
  const trellisDir = join(directory, ".trellis")
212
- const claudeDir = join(directory, ".claude")
213
- const opencodeDir = join(directory, ".opencode")
214
201
 
215
- // Load config for scope filtering and legacy detection
216
202
  const config = loadTrellisConfig(directory)
217
203
  const allowedPkgs = resolveSpecScope(config)
218
204
 
@@ -241,11 +227,20 @@ Read and follow all instructions below carefully.
241
227
  }
242
228
  }
243
229
 
244
- // 3. Workflow Guide
245
- const workflow = ctx.readProjectFile(".trellis/workflow.md")
246
- if (workflow) {
230
+ // 3. Workflow Guide (ToC only — lazy-load the full file on demand)
231
+ const workflowContent = ctx.readProjectFile(".trellis/workflow.md")
232
+ if (workflowContent) {
233
+ const tocLines = [
234
+ "# Development Workflow — Section Index",
235
+ "Full guide: .trellis/workflow.md (read on demand)",
236
+ "",
237
+ ]
238
+ for (const line of workflowContent.split("\n")) {
239
+ if (line.startsWith("## ")) tocLines.push(line)
240
+ }
241
+ tocLines.push("", "To read a section: use the Read tool on .trellis/workflow.md")
247
242
  parts.push("<workflow>")
248
- parts.push(workflow)
243
+ parts.push(tocLines.join("\n"))
249
244
  parts.push("</workflow>")
250
245
  }
251
246
 
@@ -267,7 +262,6 @@ Read and follow all instructions below carefully.
267
262
  }).sort()
268
263
 
269
264
  for (const sub of subs) {
270
- // Always include guides/ regardless of scope
271
265
  if (sub === "guides") {
272
266
  const indexFile = join(specDir, sub, "index.md")
273
267
  if (existsSync(indexFile)) {
@@ -281,14 +275,11 @@ Read and follow all instructions below carefully.
281
275
 
282
276
  const indexFile = join(specDir, sub, "index.md")
283
277
  if (existsSync(indexFile)) {
284
- // Flat spec dir: spec/<layer>/index.md (single-repo)
285
278
  const content = ctx.readFile(indexFile)
286
279
  if (content) {
287
280
  parts.push(`## ${sub}\n${content}\n`)
288
281
  }
289
282
  } else {
290
- // Nested package dirs (monorepo): spec/<pkg>/<layer>/index.md
291
- // Apply scope filter
292
283
  if (allowedPkgs !== null && !allowedPkgs.has(sub)) {
293
284
  continue
294
285
  }
@@ -322,131 +313,164 @@ Read and follow all instructions below carefully.
322
313
 
323
314
  parts.push("</guidelines>")
324
315
 
325
- // 5. Session Instructions - try both .claude and .opencode
326
- let startMd = ctx.readFile(join(claudeDir, "commands", "trellis", "start.md"))
327
- if (!startMd) {
328
- startMd = ctx.readFile(join(opencodeDir, "commands", "trellis", "start.md"))
329
- }
330
- if (startMd) {
331
- parts.push("<instructions>")
332
- parts.push(startMd)
333
- parts.push("</instructions>")
334
- }
335
-
336
- // 6. Task status (R2: check task state for session resume)
316
+ // 6. Task status
337
317
  const taskStatus = getTaskStatus(ctx)
338
318
  parts.push(`<task-status>\n${taskStatus}\n</task-status>`)
339
319
 
340
- // 7. Final directive (R3: active, not passive)
320
+ // 7. Final directive
341
321
  parts.push(`<ready>
342
- Context loaded. Steps 1-3 (workflow, context, guidelines) are already injected above — do NOT re-read them.
343
- Start from Step 4. Wait for user's first message, then follow <instructions> to handle their request.
322
+ Context loaded. Workflow index, project state, and guidelines are already injected above — do NOT re-read them.
323
+ Wait for the user's first message, then handle it following the workflow guide.
344
324
  If there is an active task, ask whether to continue it.
345
325
  </ready>`)
346
326
 
347
327
  return parts.join("\n\n")
348
328
  }
349
329
 
350
- export default async ({ directory }) => {
351
- const ctx = new TrellisContext(directory)
352
- debugLog("session", "Plugin loaded, directory:", directory)
330
+ function getTrellisMetadata(metadata) {
331
+ if (!metadata || typeof metadata !== "object") {
332
+ return {}
333
+ }
353
334
 
354
- return {
355
- // chat.message - triggered when user sends a message
356
- "chat.message": async (input) => {
357
- try {
358
- const sessionID = input.sessionID
359
- const agent = input.agent || "unknown"
360
- debugLog("session", "chat.message called, sessionID:", sessionID, "agent:", agent)
361
-
362
- // Skip in non-interactive mode
363
- if (process.env.OPENCODE_NON_INTERACTIVE === "1") {
364
- debugLog("session", "Skipping - non-interactive mode")
365
- return
366
- }
335
+ const trellis = metadata.trellis
336
+ if (!trellis || typeof trellis !== "object") {
337
+ return {}
338
+ }
367
339
 
368
- // Check if we should skip (omo will handle)
369
- if (ctx.shouldSkipHook("session-start")) {
370
- debugLog("session", "Skipping - omo will handle via .claude/hooks/")
371
- return
372
- }
340
+ return trellis
341
+ }
373
342
 
374
- // Only inject on first message
375
- if (contextCollector.isProcessed(sessionID)) {
376
- debugLog("session", "Skipping - session already processed")
377
- return
378
- }
343
+ function markPartAsSessionStart(part) {
344
+ const metadata = part.metadata && typeof part.metadata === "object"
345
+ ? part.metadata
346
+ : {}
347
+ part.metadata = {
348
+ ...metadata,
349
+ trellis: {
350
+ ...getTrellisMetadata(metadata),
351
+ sessionStart: true,
352
+ },
353
+ }
354
+ }
379
355
 
380
- // Mark session as processed
381
- contextCollector.markProcessed(sessionID)
356
+ function hasSessionStartMarker(part) {
357
+ if (!part || part.type !== "text" || typeof part.text !== "string") {
358
+ return false
359
+ }
382
360
 
383
- // Build and store context
384
- const context = buildSessionContext(ctx)
385
- debugLog("session", "Built context, length:", context.length)
361
+ return getTrellisMetadata(part.metadata).sessionStart === true
362
+ }
386
363
 
387
- contextCollector.store(sessionID, context)
388
- debugLog("session", "Context stored for session:", sessionID)
364
+ export function hasInjectedTrellisContext(messages) {
365
+ if (!Array.isArray(messages)) {
366
+ return false
367
+ }
389
368
 
390
- } catch (error) {
391
- debugLog("session", "Error in chat.message:", error.message, error.stack)
392
- }
393
- },
369
+ return messages.some(message => {
370
+ if (!message?.info || message.info.role !== "user" || !Array.isArray(message.parts)) {
371
+ return false
372
+ }
394
373
 
395
- // experimental.chat.messages.transform - modify messages before sending to AI
396
- "experimental.chat.messages.transform": async (input, output) => {
397
- try {
398
- const { messages } = output
399
- debugLog("session", "messages.transform called, messageCount:", messages?.length)
374
+ return message.parts.some(hasSessionStartMarker)
375
+ })
376
+ }
400
377
 
401
- if (!messages || messages.length === 0) {
402
- return
403
- }
378
+ async function hasPersistedInjectedContext(client, directory, sessionID) {
379
+ try {
380
+ const response = await client.session.messages({
381
+ path: { id: sessionID },
382
+ query: { directory },
383
+ throwOnError: true,
384
+ })
385
+ return hasInjectedTrellisContext(response.data || [])
386
+ } catch (error) {
387
+ debugLog(
388
+ "session",
389
+ "Failed to read session history for dedupe:",
390
+ error instanceof Error ? error.message : String(error),
391
+ )
392
+ return false
393
+ }
394
+ }
404
395
 
405
- // Find last user message
406
- let lastUserMessageIndex = -1
407
- for (let i = messages.length - 1; i >= 0; i--) {
408
- if (messages[i].info?.role === "user") {
409
- lastUserMessageIndex = i
410
- break
411
- }
412
- }
396
+ export default {
397
+ id: "trellis.session-start",
398
+ server: async ({ directory, client }) => {
399
+ const ctx = new TrellisContext(directory)
400
+ debugLog("session", "Plugin loaded, directory:", directory)
413
401
 
414
- if (lastUserMessageIndex === -1) {
415
- debugLog("session", "No user message found")
416
- return
402
+ return {
403
+ // Clear in-memory dedupe after compaction so context can be re-injected.
404
+ event: ({ event }) => {
405
+ try {
406
+ if (event?.type === "session.compacted" && event?.properties?.sessionID) {
407
+ const sessionID = event.properties.sessionID
408
+ contextCollector.clear(sessionID)
409
+ debugLog("session", "Cleared processed flag after compaction for session:", sessionID)
410
+ }
411
+ } catch (error) {
412
+ debugLog(
413
+ "session",
414
+ "Error in event hook:",
415
+ error instanceof Error ? error.message : String(error),
416
+ )
417
417
  }
418
+ },
418
419
 
419
- const lastUserMessage = messages[lastUserMessageIndex]
420
- const sessionID = lastUserMessage.info?.sessionID
420
+ // chat.message - triggered when user sends a message.
421
+ // Modify the message in-place so the context is persisted with updateMessage/updatePart.
422
+ "chat.message": async (input, output) => {
423
+ try {
424
+ const sessionID = input.sessionID
425
+ const agent = input.agent || "unknown"
426
+ debugLog("session", "chat.message called, sessionID:", sessionID, "agent:", agent)
427
+
428
+ // Skip in non-interactive mode
429
+ if (process.env.OPENCODE_NON_INTERACTIVE === "1") {
430
+ debugLog("session", "Skipping - non-interactive mode")
431
+ return
432
+ }
421
433
 
422
- debugLog("session", "Found user message, sessionID:", sessionID)
434
+ // Only inject on first message
435
+ if (contextCollector.isProcessed(sessionID)) {
436
+ debugLog("session", "Skipping - session already processed")
437
+ return
438
+ }
423
439
 
424
- if (!sessionID || !contextCollector.hasPending(sessionID)) {
425
- debugLog("session", "No pending context for session")
426
- return
427
- }
440
+ if (await hasPersistedInjectedContext(client, ctx.directory, sessionID)) {
441
+ contextCollector.markProcessed(sessionID)
442
+ debugLog("session", "Skipping - session already contains persisted Trellis context")
443
+ return
444
+ }
428
445
 
429
- // Get and consume pending context
430
- const pending = contextCollector.consume(sessionID)
446
+ // Build context
447
+ const context = buildSessionContext(ctx)
448
+ debugLog("session", "Built context, length:", context.length)
449
+
450
+ // Inject context directly into output.parts so it gets persisted by updatePart
451
+ const parts = output?.parts || []
452
+ const textPartIndex = parts.findIndex(
453
+ p => p.type === "text" && p.text !== undefined
454
+ )
455
+
456
+ if (textPartIndex !== -1) {
457
+ const originalText = parts[textPartIndex].text || ""
458
+ parts[textPartIndex].text = `${context}\n\n---\n\n${originalText}`
459
+ markPartAsSessionStart(parts[textPartIndex])
460
+ debugLog("session", "Injected context into chat.message text part, length:", context.length)
461
+ } else {
462
+ // No existing text part: prepend a new one
463
+ const injectedPart = { type: "text", text: context }
464
+ markPartAsSessionStart(injectedPart)
465
+ parts.unshift(injectedPart)
466
+ debugLog("session", "Prepended new text part with context, length:", context.length)
467
+ }
431
468
 
432
- // Find first text part
433
- const textPartIndex = lastUserMessage.parts?.findIndex(
434
- p => p.type === "text" && p.text !== undefined
435
- )
469
+ contextCollector.markProcessed(sessionID)
436
470
 
437
- if (textPartIndex === -1) {
438
- debugLog("session", "No text part found in user message")
439
- return
471
+ } catch (error) {
472
+ debugLog("session", "Error in chat.message:", error.message, error.stack)
440
473
  }
441
-
442
- // Prepend context to the text part (same approach as omo)
443
- const originalText = lastUserMessage.parts[textPartIndex].text || ""
444
- lastUserMessage.parts[textPartIndex].text = `${pending.content}\n\n---\n\n${originalText}`
445
-
446
- debugLog("session", "Injected context by prepending to text, length:", pending.content.length)
447
-
448
- } catch (error) {
449
- debugLog("session", "Error in messages.transform:", error.message, error.stack)
450
474
  }
451
475
  }
452
476
  }
@@ -1,7 +1,7 @@
1
1
  """
2
2
  CLI Adapter for Multi-Platform Support.
3
3
 
4
- Abstracts differences between Claude Code, OpenCode, Cursor, iFlow, Codex, Kilo, Kiro Code, Gemini CLI, Antigravity, Windsurf, Qoder, CodeBuddy, and GitHub Copilot interfaces.
4
+ Abstracts differences between Claude Code, OpenCode, Cursor, iFlow, Codex, Kilo, Kiro Code, Gemini CLI, Antigravity, Windsurf, Qoder, CodeBuddy, GitHub Copilot, and Factory Droid interfaces.
5
5
 
6
6
  Supported platforms:
7
7
  - claude: Claude Code (default)
@@ -17,6 +17,7 @@ Supported platforms:
17
17
  - qoder: Qoder
18
18
  - codebuddy: CodeBuddy
19
19
  - copilot: GitHub Copilot (VS Code)
20
+ - droid: Factory Droid (commands-based)
20
21
 
21
22
  Usage:
22
23
  from common.cli_adapter import CLIAdapter
@@ -49,6 +50,7 @@ Platform = Literal[
49
50
  "qoder",
50
51
  "codebuddy",
51
52
  "copilot",
53
+ "droid",
52
54
  ]
53
55
 
54
56
 
@@ -119,6 +121,8 @@ class CLIAdapter:
119
121
  return ".codebuddy"
120
122
  elif self.platform == "copilot":
121
123
  return ".github/copilot"
124
+ elif self.platform == "droid":
125
+ return ".factory"
122
126
  else:
123
127
  return ".claude"
124
128
 
@@ -241,6 +245,8 @@ class CLIAdapter:
241
245
  return f".kilocode/workflows/{name}.md"
242
246
  elif self.platform == "copilot":
243
247
  return f".github/prompts/{name}.prompt.md"
248
+ elif self.platform == "droid":
249
+ return f".factory/commands/trellis/{name}.md"
244
250
  else:
245
251
  return f"{self.config_dir_name}/commands/trellis/{name}.md"
246
252
 
@@ -274,6 +280,8 @@ class CLIAdapter:
274
280
  return {}
275
281
  elif self.platform == "copilot":
276
282
  return {}
283
+ elif self.platform == "droid":
284
+ return {}
277
285
  else:
278
286
  return {"CLAUDE_NON_INTERACTIVE": "1"}
279
287
 
@@ -353,6 +361,10 @@ class CLIAdapter:
353
361
  raise ValueError(
354
362
  "GitHub Copilot is IDE-only; CLI agent run is not supported."
355
363
  )
364
+ elif self.platform == "droid":
365
+ raise ValueError(
366
+ "Factory Droid CLI agent run is not yet integrated with Trellis multi-agent."
367
+ )
356
368
 
357
369
  else: # claude
358
370
  cmd = ["claude", "-p"]
@@ -413,6 +425,10 @@ class CLIAdapter:
413
425
  raise ValueError(
414
426
  "GitHub Copilot is IDE-only; CLI resume is not supported."
415
427
  )
428
+ elif self.platform == "droid":
429
+ raise ValueError(
430
+ "Factory Droid CLI resume is not yet integrated with Trellis multi-agent."
431
+ )
416
432
  else:
417
433
  return ["claude", "--resume", session_id]
418
434
 
@@ -483,6 +499,8 @@ class CLIAdapter:
483
499
  return "codebuddy"
484
500
  elif self.platform == "copilot":
485
501
  return "copilot"
502
+ elif self.platform == "droid":
503
+ return "droid"
486
504
  else:
487
505
  return "claude"
488
506
 
@@ -569,9 +587,10 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
569
587
  "qoder",
570
588
  "codebuddy",
571
589
  "copilot",
590
+ "droid",
572
591
  ):
573
592
  raise ValueError(
574
- f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', 'codebuddy', or 'copilot')"
593
+ f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', 'codebuddy', 'copilot', or 'droid')"
575
594
  )
576
595
 
577
596
  return CLIAdapter(platform=platform) # type: ignore
@@ -592,6 +611,7 @@ _ALL_PLATFORM_CONFIG_DIRS = (
592
611
  ".qoder",
593
612
  ".codebuddy",
594
613
  ".github/copilot",
614
+ ".factory",
595
615
  )
596
616
  """All platform config directory names (used by detect_platform exclusion checks)."""
597
617
 
@@ -647,6 +667,7 @@ def detect_platform(project_root: Path) -> Platform:
647
667
  "qoder",
648
668
  "codebuddy",
649
669
  "copilot",
670
+ "droid",
650
671
  ):
651
672
  return env_platform # type: ignore
652
673
 
@@ -712,6 +733,10 @@ def detect_platform(project_root: Path) -> Platform:
712
733
  if (project_root / ".github" / "copilot").is_dir():
713
734
  return "copilot"
714
735
 
736
+ # Check for .factory directory (Factory Droid-specific)
737
+ if (project_root / ".factory").is_dir():
738
+ return "droid"
739
+
715
740
  return "claude"
716
741
 
717
742
 
@@ -196,21 +196,30 @@ python3 ./.trellis/scripts/task.py create "<title>" --slug <task-name>
196
196
  1. Create or select task
197
197
  --> python3 ./.trellis/scripts/task.py create "<title>" --slug <name> or list
198
198
 
199
- 2. Write code according to guidelines
199
+ 2. Start task (mark as current)
200
+ --> python3 ./.trellis/scripts/task.py start <name>
201
+ --> Writes .trellis/.current-task; future sessions see it in <current-state>
202
+
203
+ 3. Write code according to guidelines
200
204
  --> Read .trellis/spec/ docs relevant to your task
201
205
  --> For cross-layer: read .trellis/spec/guides/
202
206
 
203
- 3. Self-test
207
+ 4. Self-test
204
208
  --> Run project's lint/test commands (see spec docs)
205
209
  --> Manual feature testing
206
210
 
207
- 4. Commit code
211
+ 5. Commit code
208
212
  --> git add <files>
209
213
  --> git commit -m "type(scope): description"
210
214
  Format: feat/fix/docs/refactor/test/chore
211
215
 
212
- 5. Record session (one command)
216
+ 6. Record session (one command)
213
217
  --> python3 ./.trellis/scripts/add_session.py --title "Title" --commit "hash"
218
+
219
+ 7. Finish task (clear current)
220
+ --> python3 ./.trellis/scripts/task.py finish
221
+ --> Only when the task is fully done; otherwise leave it set so the
222
+ next session resumes where you left off
214
223
  ```
215
224
 
216
225
  ### Code Quality Checklist
@@ -315,11 +324,15 @@ tasks/
315
324
  **Commands**:
316
325
  ```bash
317
326
  python3 ./.trellis/scripts/task.py create "<title>" [--slug <name>] # Create task directory
327
+ python3 ./.trellis/scripts/task.py start <name> # Set as current task (writes .current-task, triggers after_start hooks)
328
+ python3 ./.trellis/scripts/task.py finish # Clear current task (triggers after_finish hooks)
318
329
  python3 ./.trellis/scripts/task.py archive <name> # Archive to archive/{year-month}/
319
330
  python3 ./.trellis/scripts/task.py list # List active tasks
320
331
  python3 ./.trellis/scripts/task.py list-archive # List archived tasks
321
332
  ```
322
333
 
334
+ **Current task mechanism**: `task.py start <name>` writes the selected task path to `.trellis/.current-task`. The SessionStart hook reads this file to inject `## CURRENT TASK` into every new session's context, so the AI immediately knows what you're working on without being told. Run `task.py finish` when you're done — subsequent sessions will show `(none)` until you start another task.
335
+
323
336
  ---
324
337
 
325
338
  ## Best Practices
@@ -6,16 +6,16 @@
6
6
  /**
7
7
  * Supported AI coding tools
8
8
  */
9
- export type AITool = "claude-code" | "cursor" | "opencode" | "iflow" | "codex" | "kilo" | "kiro" | "gemini" | "antigravity" | "windsurf" | "qoder" | "codebuddy" | "copilot";
9
+ export type AITool = "claude-code" | "cursor" | "opencode" | "iflow" | "codex" | "kilo" | "kiro" | "gemini" | "antigravity" | "windsurf" | "qoder" | "codebuddy" | "copilot" | "droid";
10
10
  /**
11
11
  * Template directory categories
12
12
  */
13
- export type TemplateDir = "common" | "claude" | "cursor" | "opencode" | "iflow" | "codex" | "kilo" | "kiro" | "gemini" | "antigravity" | "windsurf" | "qoder" | "codebuddy" | "copilot";
13
+ export type TemplateDir = "common" | "claude" | "cursor" | "opencode" | "iflow" | "codex" | "kilo" | "kiro" | "gemini" | "antigravity" | "windsurf" | "qoder" | "codebuddy" | "copilot" | "droid";
14
14
  /**
15
15
  * CLI flag names for platform selection (e.g., --claude, --cursor, --kilo, --kiro, --gemini, --antigravity)
16
16
  * Must match keys in InitOptions (src/commands/init.ts)
17
17
  */
18
- export type CliFlag = "claude" | "cursor" | "opencode" | "iflow" | "codex" | "kilo" | "kiro" | "gemini" | "antigravity" | "windsurf" | "qoder" | "codebuddy" | "copilot";
18
+ export type CliFlag = "claude" | "cursor" | "opencode" | "iflow" | "codex" | "kilo" | "kiro" | "gemini" | "antigravity" | "windsurf" | "qoder" | "codebuddy" | "copilot" | "droid";
19
19
  /**
20
20
  * Configuration for an AI tool
21
21
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ai-tools.d.ts","sourceRoot":"","sources":["../../src/types/ai-tools.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,MAAM,GACd,aAAa,GACb,QAAQ,GACR,UAAU,GACV,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,QAAQ,GACR,aAAa,GACb,UAAU,GACV,OAAO,GACP,WAAW,GACX,SAAS,CAAC;AAEd;;GAEG;AACH,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,QAAQ,GACR,aAAa,GACb,UAAU,GACV,OAAO,GACP,WAAW,GACX,SAAS,CAAC;AAEd;;;GAGG;AACH,MAAM,MAAM,OAAO,GACf,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,QAAQ,GACR,aAAa,GACb,UAAU,GACV,OAAO,GACP,WAAW,GACX,SAAS,CAAC;AAEd;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,qEAAqE;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;IACxB,+EAA+E;IAC/E,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CA2GjD,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAExD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAUtD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAE3D"}
1
+ {"version":3,"file":"ai-tools.d.ts","sourceRoot":"","sources":["../../src/types/ai-tools.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,MAAM,GACd,aAAa,GACb,QAAQ,GACR,UAAU,GACV,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,QAAQ,GACR,aAAa,GACb,UAAU,GACV,OAAO,GACP,WAAW,GACX,SAAS,GACT,OAAO,CAAC;AAEZ;;GAEG;AACH,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,QAAQ,GACR,aAAa,GACb,UAAU,GACV,OAAO,GACP,WAAW,GACX,SAAS,GACT,OAAO,CAAC;AAEZ;;;GAGG;AACH,MAAM,MAAM,OAAO,GACf,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,QAAQ,GACR,aAAa,GACb,UAAU,GACV,OAAO,GACP,WAAW,GACX,SAAS,GACT,OAAO,CAAC;AAEZ;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,qEAAqE;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;IACxB,+EAA+E;IAC/E,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAmHjD,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAExD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAUtD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAE3D"}
@@ -48,7 +48,7 @@ export const AI_TOOLS = {
48
48
  hasPythonHooks: true,
49
49
  },
50
50
  codex: {
51
- name: "Codex",
51
+ name: "Codex (also writes .agents/skills/ — read by Cursor, Gemini CLI, GitHub Copilot, Amp, Kimi Code)",
52
52
  templateDirs: ["common", "codex"],
53
53
  configDir: ".codex",
54
54
  supportsAgentSkills: true,
@@ -121,6 +121,14 @@ export const AI_TOOLS = {
121
121
  defaultChecked: false,
122
122
  hasPythonHooks: true,
123
123
  },
124
+ droid: {
125
+ name: "Factory Droid",
126
+ templateDirs: ["common", "droid"],
127
+ configDir: ".factory",
128
+ cliFlag: "droid",
129
+ defaultChecked: false,
130
+ hasPythonHooks: false,
131
+ },
124
132
  };
125
133
  /**
126
134
  * Get the configuration for a specific AI tool
@@ -1 +1 @@
1
- {"version":3,"file":"ai-tools.js","sourceRoot":"","sources":["../../src/types/ai-tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ai-tools.js","sourceRoot":"","sources":["../../src/types/ai-tools.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindfoldhq/trellis",
3
- "version": "0.4.0-beta.9",
3
+ "version": "0.4.0-rc.1",
4
4
  "description": "AI capabilities grow like ivy — Trellis provides the structure to guide them along a disciplined path",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",