@11agents/cli 0.1.8 → 0.1.9

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 CHANGED
@@ -27,7 +27,7 @@ export ELEVENAGENTS_SERVER="http://localhost:8082"
27
27
 
28
28
  On startup, the CLI prints its current version and target server to stderr. It also checks npm for a newer `@11agents/cli` package and prints an upgrade command when one is available.
29
29
 
30
- Project-scoped MCP and knowledge-base sync tokens can be stored in `~/.11agents/credentials`:
30
+ Project-scoped MCP, knowledge-base sync, and database sync tokens can be stored in `~/.11agents/credentials`:
31
31
 
32
32
  ```yaml
33
33
  # tokens:
@@ -35,7 +35,24 @@ flatkey: gtm_xxxxxxxxxxxxxxxxxxxx
35
35
  voc-ai: gtm_yyyyyyyyyyyyyyyyyyyy
36
36
  ```
37
37
 
38
- When syncing a project knowledge base, the CLI chooses tokens in this order: explicit tool/command token, matching project token from `~/.11agents/credentials`, `GTM_SWARM_TOKEN`, then the daemon control token as a compatibility fallback.
38
+ When syncing project data, the CLI chooses tokens in this order: explicit tool/command token, matching project token from `~/.11agents/credentials`, `GTM_SWARM_TOKEN`, then the daemon control token as a compatibility fallback.
39
+
40
+ For runtime tasks, token lookup tries the cloud `workspace.slug`, cloud `workspace.name`, `--project`, and `--workspace`. Use the project slug when you know it; adding the display name is also safe when the slug/name differ:
41
+
42
+ ```yaml
43
+ # tokens:
44
+ flatkey-prod: gtm_xxxxxxxxxxxxxxxxxxxx
45
+ Flat Key: gtm_xxxxxxxxxxxxxxxxxxxx
46
+ ```
47
+
48
+ The credentials file must live under the same `HOME` used by the daemon process. If the daemon is launched by a service manager or SSH session under a different user, confirm with `11agents daemon status` and check that user has the file at `~/.11agents/credentials`. After changing credentials or upgrading the CLI, restart the daemon:
49
+
50
+ ```bash
51
+ npm install -g @11agents/cli@latest
52
+ 11agents --version
53
+ 11agents daemon stop
54
+ 11agents daemon start --background
55
+ ```
39
56
 
40
57
  ## Runtime Pool
41
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@11agents/cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "11agents local runtime and telemetry CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -106,6 +106,27 @@ async function runWithDaemonRetry(label, operation, deps, retryState) {
106
106
  }
107
107
  }
108
108
 
109
+ async function runWithTaskRetry(label, operation, deps, { maxAttempts = 3 } = {}) {
110
+ let failures = 0
111
+ while (true) {
112
+ try {
113
+ return await operation()
114
+ } catch (error) {
115
+ failures += 1
116
+ if (failures >= maxAttempts) throw error
117
+ const delay = retryDelayMs(failures)
118
+ deps.log(JSON.stringify({
119
+ retrying: label,
120
+ error: errorMessage(error),
121
+ consecutive_failures: failures,
122
+ max_attempts: maxAttempts,
123
+ next_retry_ms: delay,
124
+ }, null, 2))
125
+ await deps.sleep(delay)
126
+ }
127
+ }
128
+ }
129
+
109
130
  export async function scanRuntime(flags = {}) {
110
131
  const scan = await buildRuntimeScan({ env: scanEnvWithOverrides(flags), cliVersion: CLI_VERSION })
111
132
  console.log(JSON.stringify(scan, null, 2))
@@ -183,7 +204,7 @@ async function syncRuntimeProjectMetadataBestEffort(flags, deps) {
183
204
 
184
205
  function normalizeTaskCompletion(task, completion) {
185
206
  const result = completion && typeof completion === 'object' ? completion : {}
186
- return {
207
+ const body = {
187
208
  task_id: task.id,
188
209
  runtime_id: task.runtime_id,
189
210
  machine_key: task.runtime?.machine_key || '',
@@ -191,6 +212,26 @@ function normalizeTaskCompletion(task, completion) {
191
212
  memory_delta: String(result.memory_delta || result.memoryDelta || ''),
192
213
  status: result.status ? String(result.status) : null,
193
214
  }
215
+ if (result.knowledge_snapshot || result.knowledgeSnapshot) {
216
+ body.knowledge_snapshot = result.knowledge_snapshot || result.knowledgeSnapshot
217
+ }
218
+ return body
219
+ }
220
+
221
+ function extractJsonObject(text) {
222
+ const source = String(text || '').trim()
223
+ if (!source) return null
224
+ const fenced = source.match(/```(?:json)?\s*([\s\S]*?)```/i)
225
+ const candidates = fenced ? [fenced[1].trim(), source] : [source]
226
+ for (const candidate of candidates) {
227
+ try {
228
+ const parsed = JSON.parse(candidate)
229
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed
230
+ } catch {
231
+ // Try the next representation.
232
+ }
233
+ }
234
+ return null
194
235
  }
195
236
 
196
237
  async function loadTaskHandler(handlerPath, deps) {
@@ -571,24 +612,18 @@ function buildCodexPrompt(task) {
571
612
  source_url: deepOrganize.source_url,
572
613
  source_type: deepOrganize.source_type || (deepOrganize.source_url.includes('github.com') ? 'github' : 'url'),
573
614
  output_dir: task.execution_context?.workdir ? `${task.execution_context.workdir}/knowledge_base` : './knowledge_base',
574
- callback: 'After organizing files, call the 11agents MCP knowledge_sync tool with mode=push so cloud receives the finished wiki.',
615
+ callback: 'Prefer the 11agents MCP knowledge_sync tool with mode=push. If MCP/local writes are unavailable, include a knowledge_snapshot object in the final task completion JSON so the platform writes cloud knowledge during task completion.',
575
616
  }),
576
617
  '',
577
- 'Build the knowledge base using the standard llms.txt / LLM Wiki pattern popularized for LLM-readable documentation:',
578
- '- Create or update ./knowledge_base/llms.txt with one H1 project title, a blockquote summary, concise context, and H2 sections containing Markdown links to the most important files.',
579
- '- Create or update ./knowledge_base/llms-full.txt as a fuller single-file context pack when useful.',
580
- '- Create or update ./knowledge_base/README.md, ./knowledge_base/index.md, and ./knowledge_base/CLAUDE.md as the human entry point, node map, and agent operating guide.',
581
- '- Treat the finished result as an agent skill graph like a strong hand-built knowledge base: every durable file should tell the next agent exactly when to read it and how to act on it.',
582
- '- Do not stop at project-profile.md / knowledge-map.md placeholders. Create concrete markdown files with actionable content.',
583
- '- For product, website, or business sources, use semantic folders such as information/, voice/, audience/, marketing/, product-design/, resources/, and decisions/. Create files like information/what-we-do/product-overview.md, voice/brand-voice.md, audience/README.md, and marketing/README.md when the source supports them.',
584
- '- For GitHub sources, use semantic folders such as codebase/, architecture/, api/, operations/, examples/, resources/, and agent-playbook/. Create files like codebase/README.md, architecture/overview.md, operations/setup.md, api/README.md, and examples/README.md when the repo supports them.',
585
- '- README.md should include a "Where to Start" table and a directory overview.',
586
- '- index.md should include Identity, Node Map, and Execution Instructions sections with wiki-style references to important files.',
587
- '- CLAUDE.md should include the exact read order and output/update rules for runtime agents.',
588
- '- If source_type is github, inspect README, docs, package manifests, examples, and source tree. Prefer concise durable explanations over raw dumps.',
589
- '- If source_type is url, read the page and prefer /llms.txt or /llms-full.txt from that origin when available.',
590
- '- Keep file paths stable and descriptive. Do not write temporary research into the durable wiki.',
591
- '- When done, use MCP to push the local knowledge_base back to cloud.',
618
+ 'Build the knowledge base using https://github.com/lewislulu/llm-wiki-skill:',
619
+ '- Install or inspect llm-wiki-skill when possible, then follow its compile/ingest/query/lint/audit workflow.',
620
+ '- Use MCP knowledge_sync mode=pull first when available so existing cloud knowledge is included.',
621
+ '- Use MCP knowledge_sync mode=push after organizing when available.',
622
+ '- If shell, apply_patch, local file writes, skill installation, or MCP push are unavailable, still produce the organized result as a knowledge_snapshot in the task completion payload.',
623
+ '- knowledge_snapshot must contain nodes and edges arrays compatible with 11agents knowledge sync. Each node should include id, kind, title, body, and local_path.',
624
+ '- When using the built-in Codex worker, finish with a JSON object or fenced ```json block containing comment, memory_delta, status, and knowledge_snapshot.',
625
+ '- Do not report blocked only because shell, apply_patch, local file writes, or MCP push are unavailable. Report blocked only if source research and a structured knowledge_snapshot are also impossible.',
626
+ '- Preserve useful existing knowledge, remove duplication, and make the wiki directly usable by future runtime agents.',
592
627
  ].join('\n') : '',
593
628
  '',
594
629
  'Work in this repository and make the needed changes. When finished, respond with a concise summary for the task thread.',
@@ -630,6 +665,17 @@ async function runCodex({ task, prompt, flags = {}, deps }) {
630
665
  status: 'failed',
631
666
  }
632
667
  }
668
+ if (knowledgeDeepOrganizeSpec(task)) {
669
+ const structured = extractJsonObject(output)
670
+ if (structured?.knowledge_snapshot || structured?.knowledgeSnapshot) {
671
+ return {
672
+ ...structured,
673
+ comment: String(structured.comment || structured.summary || output || `Codex completed task ${task.id}.`),
674
+ memory_delta: String(structured.memory_delta || structured.memoryDelta || `Codex completed task ${task.id}.`),
675
+ status: structured.status ? String(structured.status) : 'in_review',
676
+ }
677
+ }
678
+ }
633
679
  return {
634
680
  comment: output || `Codex completed task ${task.id}.`,
635
681
  memory_delta: `Codex completed task ${task.id}.`,
@@ -687,51 +733,86 @@ async function claimAndRunRuntimeTasks(registration, flags, deps, handlerModule,
687
733
  }
688
734
 
689
735
  deps.log(JSON.stringify({ claimed: runtimeTask.id, runtime_id: runtime.id }, null, 2))
736
+ let completion = null
737
+ let executionContext = null
690
738
  if (runtimeTask.workspace?.slug) {
691
739
  const token = await projectSyncToken(projectTokenCandidatesForTask(runtimeTask, flags), flags, deps)
692
740
  const syncConfig = { ...config, token }
693
- await runWithDaemonRetry('sync knowledge base', () => (
694
- deps.syncKnowledge({
695
- project: runtimeTask.workspace.slug,
696
- mode: 'pull',
697
- server: flags.server,
698
- token,
699
- }, {
700
- requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
701
- log: () => {},
702
- })
703
- ), deps, retryState)
741
+ try {
742
+ await runWithTaskRetry('sync knowledge base', () => (
743
+ deps.syncKnowledge({
744
+ project: runtimeTask.workspace.slug,
745
+ mode: 'pull',
746
+ server: flags.server,
747
+ token,
748
+ }, {
749
+ requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
750
+ log: () => {},
751
+ })
752
+ ), deps)
753
+ } catch (error) {
754
+ completion = {
755
+ comment: `Knowledge sync pull failed before task execution: ${errorMessage(error)}`,
756
+ status: 'failed',
757
+ }
758
+ }
704
759
  }
705
- const executionContext = await prepareRuntimeTask(runtimeTask, flags, deps, config)
706
- runtimeTask.execution_context = executionContext
707
- let completion
708
- try {
709
- completion = await handlerModule.handleRuntimeTask(runtimeTask)
710
- } catch (error) {
711
- completion = {
712
- comment: error instanceof Error ? error.message : String(error),
713
- status: 'failed',
760
+ if (!completion) {
761
+ executionContext = await prepareRuntimeTask(runtimeTask, flags, deps, config)
762
+ runtimeTask.execution_context = executionContext
763
+ try {
764
+ completion = await handlerModule.handleRuntimeTask(runtimeTask)
765
+ } catch (error) {
766
+ completion = {
767
+ comment: error instanceof Error ? error.message : String(error),
768
+ status: 'failed',
769
+ }
770
+ } finally {
771
+ await rm(executionContext.tmp_dir, { recursive: true, force: true })
714
772
  }
715
- } finally {
716
- await rm(executionContext.tmp_dir, { recursive: true, force: true })
717
773
  }
718
- await appendTaskMemoryDelta({ task: runtimeTask, completion, workdir: executionContext.workdir })
774
+ if (executionContext) {
775
+ await appendTaskMemoryDelta({ task: runtimeTask, completion, workdir: executionContext.workdir })
776
+ }
719
777
 
720
- if (runtimeTask.workspace?.slug) {
721
- const syncBack = knowledgeDeepOrganizeSpec(runtimeTask) ? deps.mcpKnowledgeSync : deps.syncKnowledge
778
+ if (executionContext && runtimeTask.workspace?.slug) {
779
+ const isDeepOrganize = Boolean(knowledgeDeepOrganizeSpec(runtimeTask))
780
+ const syncBack = isDeepOrganize ? deps.mcpKnowledgeSync : deps.syncKnowledge
722
781
  const token = await projectSyncToken(projectTokenCandidatesForTask(runtimeTask, flags), flags, deps)
723
782
  const syncConfig = { ...config, token }
724
- await runWithDaemonRetry('sync knowledge base back to cloud', () => (
725
- syncBack({
726
- project: runtimeTask.workspace.slug,
727
- mode: 'push',
728
- server: flags.server,
729
- token,
730
- }, {
731
- requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
732
- log: () => {},
733
- })
734
- ), deps, retryState)
783
+ const pushKnowledge = () => syncBack({
784
+ project: runtimeTask.workspace.slug,
785
+ mode: 'push',
786
+ server: flags.server,
787
+ token,
788
+ }, {
789
+ requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
790
+ log: () => {},
791
+ })
792
+ if (isDeepOrganize) {
793
+ try {
794
+ await pushKnowledge()
795
+ } catch (error) {
796
+ const message = `Knowledge sync push failed before task completion: ${errorMessage(error)}. If a knowledge_snapshot is included in completion, the platform will write it directly.`
797
+ completion = {
798
+ ...(completion && typeof completion === 'object' ? completion : {}),
799
+ comment: [String(completion?.comment || completion?.summary || '').trim(), message].filter(Boolean).join('\n\n'),
800
+ memory_delta: [String(completion?.memory_delta || completion?.memoryDelta || '').trim(), message].filter(Boolean).join('\n'),
801
+ }
802
+ }
803
+ } else {
804
+ try {
805
+ await runWithTaskRetry('sync knowledge base back to cloud', pushKnowledge, deps)
806
+ } catch (error) {
807
+ const message = `Knowledge sync push failed before task completion: ${errorMessage(error)}`
808
+ completion = {
809
+ ...(completion && typeof completion === 'object' ? completion : {}),
810
+ comment: [String(completion?.comment || completion?.summary || '').trim(), message].filter(Boolean).join('\n\n'),
811
+ memory_delta: [String(completion?.memory_delta || completion?.memoryDelta || '').trim(), message].filter(Boolean).join('\n'),
812
+ status: 'failed',
813
+ }
814
+ }
815
+ }
735
816
  }
736
817
 
737
818
  const body = normalizeTaskCompletion(runtimeTask, completion)