create-claude-workspace 1.1.131 → 1.1.132

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.
@@ -1,13 +1,10 @@
1
1
  // ─── Terminal UI for autonomous loop ───
2
- // Log lines scroll naturally. Status bar + input are the last line,
3
- // erased and redrawn with \r\x1b[2K (single line, no cursor-up tricks).
4
- // Agent names shown as colored prefix on every log line.
2
+ // Events push lines to a buffer. Render loop (4 fps) draws pending lines
3
+ // + status bar. No cursor tricks just append lines and \r overwrite status.
5
4
  import { appendFileSync } from 'node:fs';
6
5
  // ─── ANSI ───
7
6
  const a = {
8
- reset: '\x1b[0m',
9
- bold: '\x1b[1m',
10
- dim: '\x1b[2m',
7
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
11
8
  fg: {
12
9
  red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m',
13
10
  blue: '\x1b[34m', magenta: '\x1b[35m', cyan: '\x1b[36m', white: '\x1b[37m',
@@ -17,7 +14,7 @@ const a = {
17
14
  },
18
15
  bg: { gray: '\x1b[48;5;236m' },
19
16
  };
20
- // ─── Agent colors ───
17
+ // ─── Shared utilities ───
21
18
  const AGENT_PALETTE = [a.fg.brightCyan, a.fg.brightMagenta, a.fg.brightGreen, a.fg.brightYellow, a.fg.brightBlue, a.fg.brightRed];
22
19
  const agentColors = new Map();
23
20
  let nextColor = 0;
@@ -26,13 +23,10 @@ function agentColor(name) {
26
23
  agentColors.set(name, AGENT_PALETTE[nextColor++ % AGENT_PALETTE.length]);
27
24
  return agentColors.get(name);
28
25
  }
29
- // ─── Tool icons ───
30
26
  const ICONS = {
31
27
  Bash: '⚡', Read: '📖', Write: '✏️ ', Edit: '🔧', Glob: '🔍', Grep: '🔎',
32
- Agent: '🤖', TodoRead: '📋', TodoWrite: '📝', WebSearch: '🌐', WebFetch: '🌐',
33
- AskUserQuestion: '❓',
28
+ Agent: '🤖', TodoRead: '📋', TodoWrite: '📝', WebSearch: '🌐', WebFetch: '🌐', AskUserQuestion: '❓',
34
29
  };
35
- // ─── Helpers ───
36
30
  function ts() { return new Date().toLocaleTimeString('en-GB', { hour12: false }); }
37
31
  function strip(s) { return s.replace(/\x1b\[[0-9;]*m/g, ''); }
38
32
  function trunc(s, n) { const c = s.replace(/\n/g, ' ').trim(); return c.length > n ? c.slice(0, n) + '…' : c; }
@@ -46,6 +40,10 @@ export class TUI {
46
40
  onInput = null;
47
41
  onHotkey = null;
48
42
  paused_ = false;
43
+ // Render state
44
+ pendingLines = [];
45
+ renderTimer = null;
46
+ lastStatusLen = 0;
49
47
  // Stats
50
48
  loopStart = Date.now();
51
49
  iteration_ = 0;
@@ -58,17 +56,60 @@ export class TUI {
58
56
  iterStart_ = 0;
59
57
  agents = [];
60
58
  inputBuf = '';
61
- ticker = null;
62
- statusVisible = false; // track if status line is currently on screen
63
59
  constructor(logFile, interactive = false) {
64
60
  this.logFile = logFile;
65
61
  this.interactive = interactive && process.stdin.isTTY === true;
66
62
  if (this.interactive) {
67
63
  this.setupInput();
68
- this.ticker = setInterval(() => this.tickStatus(), 1000);
64
+ // 4 fps render loop
65
+ this.renderTimer = setInterval(() => this.render(), 250);
69
66
  }
70
67
  }
71
- // ─── Input handling ───
68
+ // ─── Render loop ───
69
+ render() {
70
+ const out = process.stdout;
71
+ // Clear current status line
72
+ if (this.lastStatusLen > 0) {
73
+ out.write('\r\x1b[2K');
74
+ }
75
+ // Flush pending log lines
76
+ for (const line of this.pendingLines) {
77
+ out.write(line + '\n');
78
+ }
79
+ this.pendingLines = [];
80
+ // Draw status bar (single line, no \n — stays overwritable)
81
+ const status = this.buildStatus();
82
+ out.write(status);
83
+ this.lastStatusLen = strip(status).length;
84
+ }
85
+ buildStatus() {
86
+ const elapsed = fmtDur(Date.now() - this.loopStart);
87
+ const iterTime = this.iterStart_ ? fmtDur(Date.now() - this.iterStart_) : '—';
88
+ const pct = this.maxIter > 0 ? Math.round((this.iteration_ / this.maxIter) * 100) : 0;
89
+ const tok = fmtTok(this.tokens_.input + this.tokens_.output);
90
+ const parts = [
91
+ `${a.fg.white}${elapsed}`,
92
+ `Iter ${this.iteration_}/${this.maxIter} ${bar(pct, 8)}`,
93
+ `${iterTime}`,
94
+ `${a.fg.cyan}${this.tools}${a.reset} tools`,
95
+ `${a.fg.yellow}${tok}${a.reset} tok`,
96
+ ];
97
+ if (this.agents.length > 0) {
98
+ const cur = this.agents[this.agents.length - 1];
99
+ parts.push(`${agentColor(cur)}${cur}${a.reset}`);
100
+ }
101
+ if (this.taskName_)
102
+ parts.push(`${a.fg.cyan}${trunc(this.taskName_, 20)}${a.reset}`);
103
+ if (this.paused_)
104
+ parts.push(`${a.fg.yellow}⏸ PAUSED${a.reset}`);
105
+ const info = parts.join(`${a.fg.gray} │ ${a.reset}`);
106
+ const input = this.inputBuf ? ` ${a.fg.gray}›${a.reset} ${this.inputBuf}` : '';
107
+ const cols = process.stdout.columns || 120;
108
+ const line = ` ${info}${input} `;
109
+ const pad = Math.max(0, cols - strip(line).length);
110
+ return `\r${a.bg.gray}${line}${' '.repeat(pad)}${a.reset}`;
111
+ }
112
+ // ─── Input ───
72
113
  setupInput() {
73
114
  if (!process.stdin.isTTY)
74
115
  return;
@@ -83,7 +124,6 @@ export class TUI {
83
124
  if (key === '\x1a') {
84
125
  this.paused_ = !this.paused_;
85
126
  this.onHotkey?.(this.paused_ ? 'pause' : 'resume');
86
- this.tickStatus();
87
127
  return;
88
128
  }
89
129
  if (key === '\x13') {
@@ -94,68 +134,37 @@ export class TUI {
94
134
  if (this.inputBuf.trim())
95
135
  this.onInput?.(this.inputBuf.trim());
96
136
  this.inputBuf = '';
97
- this.tickStatus();
98
137
  return;
99
138
  }
100
139
  if (key === '\x7f' || key === '\b') {
101
140
  this.inputBuf = this.inputBuf.slice(0, -1);
102
- this.tickStatus();
103
141
  return;
104
142
  }
105
143
  if (key.length === 1 && key >= ' ') {
106
144
  this.inputBuf += key;
107
- this.tickStatus();
108
145
  }
109
146
  });
110
147
  }
111
148
  destroy() {
112
- if (this.ticker) {
113
- clearInterval(this.ticker);
114
- this.ticker = null;
149
+ if (this.renderTimer) {
150
+ clearInterval(this.renderTimer);
151
+ this.renderTimer = null;
115
152
  }
116
- if (this.interactive && process.stdin.isTTY) {
117
- process.stdin.setRawMode(false);
118
- this.clearStatus();
153
+ if (this.interactive) {
154
+ // Final flush
155
+ this.render();
119
156
  process.stdout.write('\n');
157
+ if (process.stdin.isTTY)
158
+ process.stdin.setRawMode(false);
120
159
  }
121
160
  }
122
161
  setInputHandler(handler) { this.onInput = handler; }
123
162
  setHotkeyHandler(handler) { this.onHotkey = handler; }
124
163
  isPaused() { return this.paused_; }
125
- // ─── Status line (single line, rewritten in place) ───
126
- buildStatusLine() {
127
- const elapsed = fmtDur(Date.now() - this.loopStart);
128
- const iterTime = this.iterStart_ ? fmtDur(Date.now() - this.iterStart_) : '—';
129
- const pct = this.maxIter > 0 ? Math.round((this.iteration_ / this.maxIter) * 100) : 0;
130
- const tok = fmtTok(this.tokens_.input + this.tokens_.output);
131
- const agent = this.agents.length > 0 ? ` │ ${agentColor(this.agents[this.agents.length - 1])}${this.agents[this.agents.length - 1]}${a.reset}` : '';
132
- const task = this.taskName_ ? ` │ ${a.fg.cyan}${trunc(this.taskName_, 25)}${a.reset}` : '';
133
- const pause = this.paused_ ? ` │ ${a.fg.yellow}⏸ PAUSED${a.reset}` : '';
134
- const input = this.inputBuf ? ` › ${this.inputBuf}` : '';
135
- return `${a.bg.gray} ${elapsed} │ Iter ${this.iteration_}/${this.maxIter} ${bar(pct, 8)} │ ${iterTime} │ ${this.tools} tools │ ${tok} tok${agent}${task}${pause}${input} ${a.reset}`;
136
- }
137
- tickStatus() {
138
- if (!this.interactive)
139
- return;
140
- // Overwrite current status line in place
141
- const line = this.buildStatusLine();
142
- const cols = process.stdout.columns || 120;
143
- const padded = line + ' '.repeat(Math.max(0, cols - strip(line).length));
144
- process.stdout.write(`\r\x1b[2K${padded}`);
145
- this.statusVisible = true;
146
- }
147
- clearStatus() {
148
- if (this.statusVisible) {
149
- process.stdout.write('\r\x1b[2K');
150
- this.statusVisible = false;
151
- }
152
- }
153
- // ─── Output ───
154
- out(formatted, raw) {
164
+ // ─── Output (buffered) ───
165
+ push(formatted, raw) {
155
166
  if (this.interactive) {
156
- this.clearStatus(); // erase status line
157
- console.log(formatted); // print log line (scrolls naturally)
158
- this.tickStatus(); // redraw status line after log
167
+ this.pendingLines.push(formatted);
159
168
  }
160
169
  else {
161
170
  console.log(formatted);
@@ -176,13 +185,13 @@ export class TUI {
176
185
  catch { /* */ }
177
186
  }
178
187
  }
179
- // ─── Agent label (shown as prefix on log lines) ───
188
+ // ─── Agent prefix ───
180
189
  agentPrefix() {
181
190
  if (this.agents.length === 0)
182
191
  return '';
183
- const current = this.agents[this.agents.length - 1];
184
- const col = agentColor(current);
185
- const short = current.length > 12 ? current.slice(0, 12) : current;
192
+ const cur = this.agents[this.agents.length - 1];
193
+ const col = agentColor(cur);
194
+ const short = cur.length > 14 ? cur.slice(0, 14) : cur;
186
195
  return `${col}${short}${a.reset} `;
187
196
  }
188
197
  indent() {
@@ -192,21 +201,21 @@ export class TUI {
192
201
  const pipes = this.agents.slice(0, -1).map(n => `${agentColor(n)}│${a.reset}`).join('');
193
202
  return ` ${pipes} ${prefix}`;
194
203
  }
195
- // ─── Public methods ───
204
+ // ─── Public API ───
196
205
  banner() {
197
- this.out('');
198
- this.out(` ${a.fg.cyan}${a.bold}╔══════════════════════════════════════════════╗${a.reset}`);
199
- this.out(` ${a.fg.cyan}${a.bold}║ ${a.fg.white}Claude Starter Kit — Autonomous Loop${a.fg.cyan} ║${a.reset}`);
200
- this.out(` ${a.fg.cyan}${a.bold}╚══════════════════════════════════════════════╝${a.reset}`);
206
+ this.push('');
207
+ this.push(` ${a.fg.cyan}${a.bold}╔══════════════════════════════════════════════╗${a.reset}`);
208
+ this.push(` ${a.fg.cyan}${a.bold}║ ${a.fg.white}Claude Starter Kit — Autonomous Loop${a.fg.cyan} ║${a.reset}`);
209
+ this.push(` ${a.fg.cyan}${a.bold}╚══════════════════════════════════════════════╝${a.reset}`);
201
210
  if (this.interactive) {
202
- this.out(` ${a.fg.gray} Ctrl+Z pause │ Ctrl+S stop │ Ctrl+C quit │ Type to send input${a.reset}`);
211
+ this.push(` ${a.fg.gray} Ctrl+Z pause │ Ctrl+S stop │ Ctrl+C quit │ Type to send input${a.reset}`);
203
212
  }
204
- this.out('');
213
+ this.push('');
205
214
  }
206
- info(msg) { this.out(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.blue}ℹ${a.reset} ${msg}`); }
207
- warn(msg) { this.out(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.yellow}⚠${a.reset} ${a.fg.yellow}${msg}${a.reset}`); }
208
- error(msg) { this.out(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.red}✗${a.reset} ${a.fg.red}${msg}${a.reset}`); }
209
- success(msg) { this.out(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.green}✓${a.reset} ${a.fg.green}${msg}${a.reset}`); }
215
+ info(msg) { this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.blue}ℹ${a.reset} ${msg}`); }
216
+ warn(msg) { this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.yellow}⚠${a.reset} ${a.fg.yellow}${msg}${a.reset}`); }
217
+ error(msg) { this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.red}✗${a.reset} ${a.fg.red}${msg}${a.reset}`); }
218
+ success(msg) { this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.green}✓${a.reset} ${a.fg.green}${msg}${a.reset}`); }
210
219
  setIteration(i, max) {
211
220
  this.iteration_ = i;
212
221
  this.maxIter = max;
@@ -216,13 +225,13 @@ export class TUI {
216
225
  this.agents = [];
217
226
  const pct = Math.round((i / max) * 100);
218
227
  const elapsed = fmtDur(Date.now() - this.loopStart);
219
- this.out('');
220
- this.out(` ${a.bold}${a.fg.white}━━━ Iteration ${i}/${max} ${bar(pct, 20)} ${pct}% ${a.fg.gray}│ ${elapsed} elapsed${a.fg.white} ━━━${a.reset}`);
228
+ this.push('');
229
+ this.push(` ${a.bold}${a.fg.white}━━━ Iteration ${i}/${max} ${bar(pct, 20)} ${pct}% ${a.fg.gray}│ ${elapsed} elapsed${a.fg.white} ━━━${a.reset}`);
221
230
  if (this.taskName_) {
222
231
  const tPct = this.tasksTotal_ > 0 ? Math.round((this.tasksDone_ / this.tasksTotal_) * 100) : 0;
223
- this.out(` ${a.fg.cyan}📋 ${this.taskName_}${a.reset} ${a.fg.gray}(${this.tasksDone_}/${this.tasksTotal_} tasks ${bar(tPct, 10)} ${tPct}%)${a.reset}`);
232
+ this.push(` ${a.fg.cyan}📋 ${this.taskName_}${a.reset} ${a.fg.gray}(${this.tasksDone_}/${this.tasksTotal_} tasks ${bar(tPct, 10)} ${tPct}%)${a.reset}`);
224
233
  }
225
- this.out('');
234
+ this.push('');
226
235
  }
227
236
  setTask(name, done, total) {
228
237
  this.taskName_ = name;
@@ -233,8 +242,8 @@ export class TUI {
233
242
  const iterElapsed = fmtDur(Date.now() - this.iterStart_);
234
243
  const totalElapsed = fmtDur(Date.now() - this.loopStart);
235
244
  const tok = fmtTok(this.tokens_.input + this.tokens_.output);
236
- this.out('');
237
- this.out(` ${a.fg.gray}━━━━ ${iterElapsed} (iter) │ ${totalElapsed} (total) │ ${this.tools} tools │ ${tok} tokens ━━━━${a.reset}`);
245
+ this.push('');
246
+ this.push(` ${a.fg.gray}━━━━ ${iterElapsed} (iter) │ ${totalElapsed} (total) │ ${this.tools} tools │ ${tok} tokens ━━━━${a.reset}`);
238
247
  }
239
248
  // ─── SDK message handler ───
240
249
  handleMessage(message) {
@@ -263,7 +272,7 @@ export class TUI {
263
272
  }
264
273
  for (const block of content) {
265
274
  if (block.type === 'text' && block.text?.trim()) {
266
- this.out(`${this.indent()}${a.fg.white}${trunc(block.text, 300)}${a.reset}`, `TEXT: ${trunc(block.text, 300)}`);
275
+ this.push(`${this.indent()}${a.fg.white}${trunc(block.text, 300)}${a.reset}`, `TEXT: ${trunc(block.text, 300)}`);
267
276
  }
268
277
  if (block.type === 'tool_use')
269
278
  this.onToolUse(block);
@@ -276,14 +285,13 @@ export class TUI {
276
285
  const input = block.input || {};
277
286
  const pre = this.indent();
278
287
  const time = `${a.fg.gray}${ts()}${a.reset}`;
279
- // Agent delegation via tool
280
288
  if (name === 'Agent') {
281
289
  const type = input.subagent_type || input.type || 'agent';
282
290
  const model = input.model ? ` ${a.fg.gray}(${input.model})${a.reset}` : '';
283
291
  const desc = trunc(input.description || input.prompt || '', 50);
284
292
  const col = agentColor(type);
285
293
  this.agents.push(type);
286
- this.out(`${pre}${time} ${icon} ${col}${a.bold}${type}${a.reset}${model} ${a.fg.gray}${desc}${a.reset}`, `AGENT: ${type} ${input.model || ''} — ${desc}`);
294
+ this.push(`${pre}${time} ${icon} ${col}${a.bold}${type}${a.reset}${model} ${a.fg.gray}${desc}${a.reset}`, `AGENT: ${type} ${input.model || ''} — ${desc}`);
287
295
  return;
288
296
  }
289
297
  let detail;
@@ -306,7 +314,7 @@ export class TUI {
306
314
  break;
307
315
  default: detail = `${a.fg.gray}${trunc(JSON.stringify(input), 60)}${a.reset}`;
308
316
  }
309
- this.out(`${pre}${time} ${icon} ${a.bold}${name}${a.reset} ${detail}`, `TOOL: ${name} ${JSON.stringify(input).slice(0, 200)}`);
317
+ this.push(`${pre}${time} ${icon} ${a.bold}${name}${a.reset} ${detail}`, `TOOL: ${name} ${JSON.stringify(input).slice(0, 200)}`);
310
318
  }
311
319
  onToolResult(msg) {
312
320
  const content = msg.message?.content;
@@ -320,30 +328,36 @@ export class TUI {
320
328
  continue;
321
329
  const pre = this.indent();
322
330
  if (block.is_error) {
323
- this.out(`${pre} ${a.fg.red}✗ ${trunc(output, 100)}${a.reset}`, `ERROR: ${trunc(output, 100)}`);
331
+ this.push(`${pre} ${a.fg.red}✗ ${trunc(output, 100)}${a.reset}`, `ERROR: ${trunc(output, 100)}`);
324
332
  }
325
333
  else if (output.length < 150) {
326
- this.out(`${pre} ${a.fg.green}✓${a.reset} ${a.fg.gray}${trunc(output, 100)}${a.reset}`, `OK: ${trunc(output, 100)}`);
334
+ this.push(`${pre} ${a.fg.green}✓${a.reset} ${a.fg.gray}${trunc(output, 100)}${a.reset}`, `OK: ${trunc(output, 100)}`);
327
335
  }
328
336
  else {
329
337
  const n = output.split('\n').length;
330
- this.out(`${pre} ${a.fg.green}✓${a.reset} ${a.fg.gray}${n} lines${a.reset}`, `OK: (${n} lines)`);
338
+ this.push(`${pre} ${a.fg.green}✓${a.reset} ${a.fg.gray}${n} lines${a.reset}`, `OK: (${n} lines)`);
331
339
  }
332
340
  }
333
341
  }
334
342
  onSystem(msg) {
335
343
  if (msg.subtype === 'init') {
336
- const model = msg.model || 'unknown';
337
- const agents = msg.agents?.length ? msg.agents.join(', ') : 'none';
344
+ const model = msg.model || '?';
338
345
  const ver = msg.claude_code_version || '';
339
- this.out(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.cyan}⚙${a.reset} Claude Code ${ver} │ Model: ${a.bold}${model}${a.reset} Agents: ${a.fg.cyan}${agents}${a.reset}`);
346
+ const agentList = msg.agents?.join(', ') || 'none';
347
+ this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.cyan}⚙${a.reset} Claude Code ${ver} │ Model: ${a.bold}${model}${a.reset} │ Agents: ${a.fg.cyan}${agentList}${a.reset}`);
348
+ // Set top-level agent name from permissionMode or first agent
349
+ if (this.agents.length === 0 && msg.agents?.length) {
350
+ const topAgent = msg.agents[0];
351
+ this.agents.push(topAgent);
352
+ this.push(` ${a.fg.gray}${ts()}${a.reset} 🤖 ${agentColor(topAgent)}${a.bold}${topAgent}${a.reset} ${a.fg.gray}(${model})${a.reset}`, `AGENT_START: ${topAgent} (${model})`);
353
+ }
340
354
  return;
341
355
  }
342
356
  if (msg.subtype === 'task_started') {
343
357
  const desc = msg.description || '';
344
358
  const col = agentColor(desc);
345
359
  this.agents.push(desc);
346
- this.out(` ${a.fg.gray}${ts()}${a.reset} 🤖 ${col}${a.bold}${desc}${a.reset}`, `AGENT_START: ${desc}`);
360
+ this.push(` ${a.fg.gray}${ts()}${a.reset} 🤖 ${col}${a.bold}${desc}${a.reset}`, `AGENT_START: ${desc}`);
347
361
  return;
348
362
  }
349
363
  if (msg.subtype === 'task_notification') {
@@ -352,15 +366,15 @@ export class TUI {
352
366
  const col = agentColor(name);
353
367
  const icon = status === 'completed' ? `${a.fg.green}✓` : `${a.fg.red}✗`;
354
368
  const summary = msg.summary ? ` ${a.fg.gray}${trunc(msg.summary, 60)}${a.reset}` : '';
355
- this.out(` ${a.fg.gray}${ts()}${a.reset} ${icon}${a.reset} ${col}${name}${a.reset} ${status}${summary}`, `AGENT_END: ${name} ${status}`);
369
+ this.push(` ${a.fg.gray}${ts()}${a.reset} ${icon}${a.reset} ${col}${name}${a.reset} ${status}${summary}`, `AGENT_END: ${name} ${status}`);
356
370
  return;
357
371
  }
358
372
  if (msg.subtype === 'task_progress' && msg.description) {
359
- this.out(`${this.indent()} ${a.dim}${trunc(msg.description, 70)}${a.reset}`, `PROGRESS: ${msg.description}`);
373
+ this.push(`${this.indent()} ${a.dim}${trunc(msg.description, 70)}${a.reset}`, `PROGRESS: ${msg.description}`);
360
374
  return;
361
375
  }
362
376
  if (msg.subtype === 'api_retry') {
363
- this.out(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.yellow}↻${a.reset} ${a.fg.yellow}API retry ${msg.attempt}/${msg.max_retries} (${msg.error || ''})${a.reset}`);
377
+ this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.yellow}↻${a.reset} ${a.fg.yellow}API retry ${msg.attempt}/${msg.max_retries} (${msg.error || ''})${a.reset}`);
364
378
  return;
365
379
  }
366
380
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-workspace",
3
- "version": "1.1.131",
3
+ "version": "1.1.132",
4
4
  "author": "",
5
5
  "repository": {
6
6
  "type": "git",