tissues 0.3.0 → 0.4.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.
- package/README.md +56 -49
- package/package.json +7 -8
- package/src/cli.js +5 -2
- package/src/commands/create.js +4 -4
- package/src/commands/status.js +1 -1
- package/src/lib/attribution.js +8 -8
- package/src/lib/config.js +3 -3
- package/src/lib/db.js +5 -5
- package/src/lib/defaults.js +9 -9
- package/src/lib/safety.js +1 -1
- package/src/lib/templates.js +2 -2
- /package/bin/{gitissues.js → tissues.js} +0 -0
package/README.md
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
#
|
|
1
|
+
# tissues
|
|
2
2
|
|
|
3
3
|
AI-enhanced GitHub issue creation with built-in safety guardrails.
|
|
4
4
|
|
|
5
5
|
Create structured, deduplicated GitHub issues from the command line — with circuit breakers and rate limiting to prevent runaway issue creation when used inside AI agent workflows.
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.com/package/tissues)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://tissues.cc)
|
|
9
10
|
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
## Install
|
|
13
14
|
|
|
15
|
+
**Homebrew (recommended):**
|
|
14
16
|
```bash
|
|
15
|
-
|
|
17
|
+
brew install calebogden/tap/tissues
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**npm:**
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g tissues
|
|
16
23
|
```
|
|
17
24
|
|
|
18
25
|
Requires Node.js >= 18.
|
|
@@ -23,45 +30,45 @@ Requires Node.js >= 18.
|
|
|
23
30
|
|
|
24
31
|
```bash
|
|
25
32
|
# Authenticate (auto-detects gh CLI token)
|
|
26
|
-
|
|
33
|
+
tissues auth
|
|
27
34
|
|
|
28
35
|
# Set your active repo
|
|
29
|
-
|
|
36
|
+
tissues open
|
|
30
37
|
|
|
31
38
|
# Create an issue interactively
|
|
32
|
-
|
|
39
|
+
tissues create
|
|
33
40
|
|
|
34
41
|
# Check safety status before running in CI
|
|
35
|
-
|
|
42
|
+
tissues status
|
|
36
43
|
```
|
|
37
44
|
|
|
38
45
|
---
|
|
39
46
|
|
|
40
47
|
## Commands
|
|
41
48
|
|
|
42
|
-
### `
|
|
49
|
+
### `tissues auth`
|
|
43
50
|
|
|
44
51
|
Authenticate with GitHub. Auto-detects your `gh` CLI token if you're already logged in via `gh`. Falls back to GitHub OAuth Device Flow if not.
|
|
45
52
|
|
|
46
53
|
```bash
|
|
47
|
-
|
|
54
|
+
tissues auth
|
|
48
55
|
```
|
|
49
56
|
|
|
50
|
-
### `
|
|
57
|
+
### `tissues open`
|
|
51
58
|
|
|
52
59
|
Set the active repository context. All subsequent commands use this repo unless overridden with `--repo`.
|
|
53
60
|
|
|
54
61
|
```bash
|
|
55
|
-
|
|
62
|
+
tissues open
|
|
56
63
|
# prompts: pick a repo from your GitHub account
|
|
57
64
|
```
|
|
58
65
|
|
|
59
|
-
### `
|
|
66
|
+
### `tissues create`
|
|
60
67
|
|
|
61
68
|
Create a new GitHub issue. Runs dedup checks and safety gates before creating anything.
|
|
62
69
|
|
|
63
70
|
```bash
|
|
64
|
-
|
|
71
|
+
tissues create [options]
|
|
65
72
|
```
|
|
66
73
|
|
|
67
74
|
**Options:**
|
|
@@ -82,10 +89,10 @@ ghissue create [options]
|
|
|
82
89
|
|
|
83
90
|
```bash
|
|
84
91
|
# Interactive
|
|
85
|
-
|
|
92
|
+
tissues create
|
|
86
93
|
|
|
87
94
|
# Fully scripted (no prompts)
|
|
88
|
-
|
|
95
|
+
tissues create \
|
|
89
96
|
--repo owner/my-repo \
|
|
90
97
|
--template bug \
|
|
91
98
|
--title "Login fails on Safari 17" \
|
|
@@ -93,7 +100,7 @@ ghissue create \
|
|
|
93
100
|
--labels "bug,P1"
|
|
94
101
|
|
|
95
102
|
# From an AI agent
|
|
96
|
-
|
|
103
|
+
tissues create \
|
|
97
104
|
--agent claude-opus-4-6 \
|
|
98
105
|
--session abc123 \
|
|
99
106
|
--template feature \
|
|
@@ -101,23 +108,23 @@ ghissue create \
|
|
|
101
108
|
--dry-run
|
|
102
109
|
```
|
|
103
110
|
|
|
104
|
-
### `
|
|
111
|
+
### `tissues list`
|
|
105
112
|
|
|
106
113
|
Browse open issues for the active repo.
|
|
107
114
|
|
|
108
115
|
```bash
|
|
109
|
-
|
|
110
|
-
|
|
116
|
+
tissues list
|
|
117
|
+
tissues list --repo owner/other-repo
|
|
111
118
|
```
|
|
112
119
|
|
|
113
|
-
### `
|
|
120
|
+
### `tissues status`
|
|
114
121
|
|
|
115
122
|
Show circuit breaker state and rate limit usage for the active repo.
|
|
116
123
|
|
|
117
124
|
```bash
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
125
|
+
tissues status
|
|
126
|
+
tissues status --agent claude-opus-4-6
|
|
127
|
+
tissues status --reset # force-reset circuit breaker to closed
|
|
121
128
|
```
|
|
122
129
|
|
|
123
130
|
Output:
|
|
@@ -136,7 +143,7 @@ Fingerprints stored: 47
|
|
|
136
143
|
|
|
137
144
|
## Safety Features
|
|
138
145
|
|
|
139
|
-
|
|
146
|
+
tissues is designed to be called from AI agent loops. The safety infrastructure prevents one misconfigured agent from flooding a repo with issues.
|
|
140
147
|
|
|
141
148
|
### Circuit Breaker
|
|
142
149
|
|
|
@@ -151,8 +158,8 @@ The circuit trips after 3 blocked attempts (dedup blocks count as failures). If
|
|
|
151
158
|
To inspect or reset:
|
|
152
159
|
|
|
153
160
|
```bash
|
|
154
|
-
|
|
155
|
-
|
|
161
|
+
tissues status
|
|
162
|
+
tissues status --reset
|
|
156
163
|
```
|
|
157
164
|
|
|
158
165
|
### Rate Limiting
|
|
@@ -165,7 +172,7 @@ Three independent rate limits, all checked before creating:
|
|
|
165
172
|
| Per-agent burst | 5 issues / 5 minutes | per repo + agent |
|
|
166
173
|
| Global hourly | 30 issues/hour | per repo, all agents combined |
|
|
167
174
|
|
|
168
|
-
All limits are configurable in `.
|
|
175
|
+
All limits are configurable in `.tissues/config.json`.
|
|
169
176
|
|
|
170
177
|
### Deduplication (3 layers)
|
|
171
178
|
|
|
@@ -179,7 +186,7 @@ A `block` always prevents creation. A `warn` prompts interactively (or is skippe
|
|
|
179
186
|
|
|
180
187
|
### Attribution Tracking
|
|
181
188
|
|
|
182
|
-
Every issue created by
|
|
189
|
+
Every issue created by tissues includes a machine-readable `<!-- tissues-meta -->` block. See [Attribution](#attribution) below.
|
|
183
190
|
|
|
184
191
|
---
|
|
185
192
|
|
|
@@ -188,11 +195,11 @@ Every issue created by ghissue includes a machine-readable `<!-- gitissues-meta
|
|
|
188
195
|
Configuration is loaded and merged from four sources in ascending priority order:
|
|
189
196
|
|
|
190
197
|
1. Built-in defaults
|
|
191
|
-
2. User-level config: `~/.config/
|
|
192
|
-
3. Repo-level config: `.
|
|
193
|
-
4. Environment variables: `
|
|
198
|
+
2. User-level config: `~/.config/tissues/config.json`
|
|
199
|
+
3. Repo-level config: `.tissues/config.json`
|
|
200
|
+
4. Environment variables: `TISSUES_*`
|
|
194
201
|
|
|
195
|
-
### Example `.
|
|
202
|
+
### Example `.tissues/config.json`
|
|
196
203
|
|
|
197
204
|
```json
|
|
198
205
|
{
|
|
@@ -214,7 +221,7 @@ Configuration is loaded and merged from four sources in ascending priority order
|
|
|
214
221
|
"defaultAgent": "human"
|
|
215
222
|
},
|
|
216
223
|
"templates": {
|
|
217
|
-
"dir": ".
|
|
224
|
+
"dir": ".tissues/templates",
|
|
218
225
|
"default": "bug"
|
|
219
226
|
},
|
|
220
227
|
"hooks": {
|
|
@@ -225,13 +232,13 @@ Configuration is loaded and merged from four sources in ascending priority order
|
|
|
225
232
|
|
|
226
233
|
### Environment Variables
|
|
227
234
|
|
|
228
|
-
Environment variables follow the pattern `
|
|
235
|
+
Environment variables follow the pattern `TISSUES_<SECTION>_<KEY>`:
|
|
229
236
|
|
|
230
237
|
```bash
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
238
|
+
TISSUES_SAFETY_MAX_PER_HOUR=5
|
|
239
|
+
TISSUES_SAFETY_BURST_LIMIT=3
|
|
240
|
+
TISSUES_SAFETY_GLOBAL_MAX_PER_HOUR=20
|
|
241
|
+
TISSUES_ATTRIBUTION_DEFAULT_AGENT=my-agent
|
|
235
242
|
```
|
|
236
243
|
|
|
237
244
|
### Hooks
|
|
@@ -239,9 +246,9 @@ GITISSUES_ATTRIBUTION_DEFAULT_AGENT=my-agent
|
|
|
239
246
|
`postCreate` runs a shell command after every successful issue creation. Three environment variables are injected:
|
|
240
247
|
|
|
241
248
|
```bash
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
249
|
+
TISSUES_REPO # owner/repo
|
|
250
|
+
TISSUES_ISSUE_NUMBER # 142
|
|
251
|
+
TISSUES_ISSUE_URL # https://github.com/owner/repo/issues/142
|
|
245
252
|
```
|
|
246
253
|
|
|
247
254
|
---
|
|
@@ -261,7 +268,7 @@ GITISSUES_ISSUE_URL # https://github.com/owner/repo/issues/142
|
|
|
261
268
|
|
|
262
269
|
### Custom Templates
|
|
263
270
|
|
|
264
|
-
Place `.md` files in `.
|
|
271
|
+
Place `.md` files in `.tissues/templates/` (repo-level) or `~/.config/tissues/templates/` (user-level). Repo templates take priority over user templates, which take priority over built-ins.
|
|
265
272
|
|
|
266
273
|
Template files support `{{variable}}` substitution:
|
|
267
274
|
|
|
@@ -274,7 +281,7 @@ Template files support `{{variable}}` substitution:
|
|
|
274
281
|
| `{{date}}` | ISO date (YYYY-MM-DD) |
|
|
275
282
|
| `{{repo}}` | Repository (owner/name) |
|
|
276
283
|
|
|
277
|
-
**Example** `.
|
|
284
|
+
**Example** `.tissues/templates/incident.md`:
|
|
278
285
|
|
|
279
286
|
```markdown
|
|
280
287
|
name: Incident Report
|
|
@@ -303,21 +310,21 @@ Use it with `--template incident`.
|
|
|
303
310
|
|
|
304
311
|
## Attribution
|
|
305
312
|
|
|
306
|
-
Every issue created by
|
|
313
|
+
Every issue created by tissues includes a machine-readable HTML comment at the bottom of the body. GitHub renders it as invisible text; it does not appear in the rendered issue.
|
|
307
314
|
|
|
308
315
|
```html
|
|
309
|
-
<!--
|
|
316
|
+
<!-- tissues-meta
|
|
310
317
|
agent: claude-opus-4-6
|
|
311
318
|
session: abc123
|
|
312
319
|
pid: 84921
|
|
313
320
|
trigger: cli-create
|
|
314
321
|
fingerprint: sha256:3f2a1b...
|
|
315
322
|
created_at: 2026-02-19T15:30:00.000Z
|
|
316
|
-
created_via:
|
|
323
|
+
created_via: tissues-cli/0.3.0
|
|
317
324
|
-->
|
|
318
325
|
```
|
|
319
326
|
|
|
320
|
-
The block is used by
|
|
327
|
+
The block is used by tissues to:
|
|
321
328
|
|
|
322
329
|
- Power content fingerprint dedup (prevents re-creating identical issues)
|
|
323
330
|
- Track which agent created which issue
|
|
@@ -329,10 +336,10 @@ You can pass additional fields via `--agent`, `--session`, and the programmatic
|
|
|
329
336
|
|
|
330
337
|
## State Database
|
|
331
338
|
|
|
332
|
-
|
|
339
|
+
tissues stores local state in a SQLite database at:
|
|
333
340
|
|
|
334
341
|
```
|
|
335
|
-
.
|
|
342
|
+
.tissues/data/tissues.db
|
|
336
343
|
```
|
|
337
344
|
|
|
338
345
|
**No separate SQLite install.** The CLI uses [better-sqlite3](https://github.com/WiseLibs/better-sqlite3), which compiles SQLite into the Node addon at `npm install` time. Users do not need to install SQLite on their system. SQLite is a common choice for CLI tool state when you need queryable, structured data (rate limits, dedup history, circuit breaker); simpler config lives in JSON (e.g. `conf` store).
|
package/package.json
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tissues",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "AI-enhanced GitHub issue creation with built-in safety guardrails. Wraps gh CLI with circuit breakers, rate limiting, dedup, and templates.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"tissues": "./bin/
|
|
7
|
+
"tissues": "./bin/tissues.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"dev": "node bin/
|
|
11
|
-
"watch": "node --watch bin/
|
|
10
|
+
"dev": "node bin/tissues.js",
|
|
11
|
+
"watch": "node --watch bin/tissues.js --help",
|
|
12
12
|
"test": "node --test src/**/*.test.js",
|
|
13
13
|
"audit": "node scripts/audit.js",
|
|
14
14
|
"build": "node scripts/build.js",
|
|
15
|
-
"prepublishOnly": "node scripts/audit.js",
|
|
16
15
|
"dev:install": "node scripts/dev-install.js",
|
|
17
16
|
"dev:uninstall": "node scripts/dev-install.js --uninstall"
|
|
18
17
|
},
|
|
@@ -28,13 +27,13 @@
|
|
|
28
27
|
],
|
|
29
28
|
"author": "Caleb Ogden",
|
|
30
29
|
"license": "MIT",
|
|
31
|
-
"homepage": "https://
|
|
30
|
+
"homepage": "https://tissues.cc",
|
|
32
31
|
"repository": {
|
|
33
32
|
"type": "git",
|
|
34
|
-
"url": "https://github.com/calebogden/
|
|
33
|
+
"url": "https://github.com/calebogden/tissues.git"
|
|
35
34
|
},
|
|
36
35
|
"bugs": {
|
|
37
|
-
"url": "https://github.com/calebogden/
|
|
36
|
+
"url": "https://github.com/calebogden/tissues/issues"
|
|
38
37
|
},
|
|
39
38
|
"files": [
|
|
40
39
|
"bin",
|
package/src/cli.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander'
|
|
2
|
+
import { createRequire } from 'module'
|
|
3
|
+
const { version } = createRequire(import.meta.url)('../package.json')
|
|
2
4
|
import { authCommand } from './commands/auth.js'
|
|
3
5
|
import { openCommand } from './commands/open.js'
|
|
4
6
|
import { createCommand } from './commands/create.js'
|
|
@@ -26,7 +28,7 @@ function authDescription() {
|
|
|
26
28
|
program
|
|
27
29
|
.name('tissues')
|
|
28
30
|
.description('AI-enhanced GitHub issue creation from the command line.')
|
|
29
|
-
.version(
|
|
31
|
+
.version(version)
|
|
30
32
|
.hook('preAction', (_thisCommand, actionCommand) => {
|
|
31
33
|
const name = actionCommand.name()
|
|
32
34
|
|
|
@@ -49,7 +51,8 @@ program.addHelpText(
|
|
|
49
51
|
'after',
|
|
50
52
|
`
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
Web: https://tissues.cc
|
|
55
|
+
Pkg: https://www.npmjs.com/package/tissues
|
|
53
56
|
`,
|
|
54
57
|
)
|
|
55
58
|
|
package/src/commands/create.js
CHANGED
|
@@ -29,7 +29,7 @@ function warnIfCircuitNotClosed(circuitState) {
|
|
|
29
29
|
chalk.yellow('[safety]') +
|
|
30
30
|
' Circuit breaker is ' +
|
|
31
31
|
stateColor(circuitState.toUpperCase()) +
|
|
32
|
-
'. Use `
|
|
32
|
+
'. Use `tissues status` to view details.',
|
|
33
33
|
)
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -62,9 +62,9 @@ function runPostCreateHook(hookCmd, ctx) {
|
|
|
62
62
|
execSync(hookCmd, {
|
|
63
63
|
env: {
|
|
64
64
|
...process.env,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
TISSUES_REPO: ctx.repo,
|
|
66
|
+
TISSUES_ISSUE_NUMBER: String(ctx.issueNumber),
|
|
67
|
+
TISSUES_ISSUE_URL: ctx.issueUrl,
|
|
68
68
|
},
|
|
69
69
|
stdio: 'inherit',
|
|
70
70
|
})
|
package/src/commands/status.js
CHANGED
package/src/lib/attribution.js
CHANGED
|
@@ -23,7 +23,7 @@ function resolvePackageVersion() {
|
|
|
23
23
|
const pkgPath = path.join(dir, 'package.json')
|
|
24
24
|
if (fs.existsSync(pkgPath)) {
|
|
25
25
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
|
26
|
-
if (
|
|
26
|
+
if (pkg.name === 'tissues' && pkg.version) return pkg.version
|
|
27
27
|
}
|
|
28
28
|
dir = path.dirname(dir)
|
|
29
29
|
}
|
|
@@ -120,7 +120,7 @@ export function buildAttribution(opts = {}) {
|
|
|
120
120
|
|
|
121
121
|
// Timestamps / versioning
|
|
122
122
|
meta.created_at = createdAt ?? nowISO()
|
|
123
|
-
meta.created_via = `
|
|
123
|
+
meta.created_via = `tissues-cli/${PKG_VERSION}`
|
|
124
124
|
|
|
125
125
|
return meta
|
|
126
126
|
}
|
|
@@ -130,18 +130,18 @@ export function buildAttribution(opts = {}) {
|
|
|
130
130
|
* bottom of a GitHub issue body.
|
|
131
131
|
*
|
|
132
132
|
* The format is a YAML-like key: value listing inside an HTML comment, which
|
|
133
|
-
* GitHub renders as invisible text. Other tools (including
|
|
133
|
+
* GitHub renders as invisible text. Other tools (including tissues itself)
|
|
134
134
|
* can parse it back via `parseAttribution`.
|
|
135
135
|
*
|
|
136
136
|
* @example
|
|
137
|
-
* <!--
|
|
137
|
+
* <!-- tissues-meta
|
|
138
138
|
* agent: claude-opus-4-6
|
|
139
139
|
* session: abc123
|
|
140
140
|
* pid: 12345
|
|
141
141
|
* trigger: cli-create
|
|
142
142
|
* fingerprint: sha256:deadbeef
|
|
143
143
|
* created_at: 2026-02-19T15:30:00Z
|
|
144
|
-
* created_via:
|
|
144
|
+
* created_via: tissues-cli/0.1.0
|
|
145
145
|
* -->
|
|
146
146
|
*
|
|
147
147
|
* @param {AttributionOpts} opts
|
|
@@ -149,7 +149,7 @@ export function buildAttribution(opts = {}) {
|
|
|
149
149
|
*/
|
|
150
150
|
export function renderAttribution(opts = {}) {
|
|
151
151
|
const meta = buildAttribution(opts)
|
|
152
|
-
const lines = ['<!--
|
|
152
|
+
const lines = ['<!-- tissues-meta']
|
|
153
153
|
|
|
154
154
|
for (const [key, value] of Object.entries(meta)) {
|
|
155
155
|
if (Array.isArray(value)) {
|
|
@@ -165,7 +165,7 @@ export function renderAttribution(opts = {}) {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
/**
|
|
168
|
-
* Parse a `<!--
|
|
168
|
+
* Parse a `<!-- tissues-meta ... -->` attribution block from an issue body.
|
|
169
169
|
*
|
|
170
170
|
* Returns the parsed key/value pairs as a plain object, or `null` if no
|
|
171
171
|
* attribution block is present in `issueBody`.
|
|
@@ -180,7 +180,7 @@ export function parseAttribution(issueBody) {
|
|
|
180
180
|
if (!issueBody) return null
|
|
181
181
|
|
|
182
182
|
// Match the comment block (non-greedy, handle CRLF)
|
|
183
|
-
const match = issueBody.match(/<!--\s*
|
|
183
|
+
const match = issueBody.match(/<!--\s*tissues-meta\s*([\s\S]*?)-->/m)
|
|
184
184
|
if (!match) return null
|
|
185
185
|
|
|
186
186
|
const block = match[1]
|
package/src/lib/config.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import Conf from 'conf'
|
|
2
2
|
|
|
3
3
|
// Conf handles OS-appropriate storage path + creates dir automatically
|
|
4
|
-
// macOS: ~/Library/Preferences/
|
|
5
|
-
// Linux: ~/.config/
|
|
6
|
-
export const store = new Conf({ projectName: '
|
|
4
|
+
// macOS: ~/Library/Preferences/tissues-nodejs/config.json
|
|
5
|
+
// Linux: ~/.config/tissues-nodejs/config.json
|
|
6
|
+
export const store = new Conf({ projectName: 'tissues' })
|
|
7
7
|
|
|
8
8
|
export function getConfig() {
|
|
9
9
|
return store.store
|
package/src/lib/db.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SQLite state database for
|
|
2
|
+
* SQLite state database for tissues CLI.
|
|
3
3
|
*
|
|
4
4
|
* Stores fingerprints (deduplication), idempotency keys, circuit breaker
|
|
5
5
|
* state, and rate-limit event log. Uses better-sqlite3 (synchronous API).
|
|
6
6
|
*
|
|
7
|
-
* Database path: .
|
|
7
|
+
* Database path: .tissues/data/tissues.db (relative to repo root).
|
|
8
8
|
* Created automatically on first use. Users can .gitignore the data dir
|
|
9
9
|
* for personal-only state, or commit it for shared team dedup history.
|
|
10
10
|
*/
|
|
@@ -14,7 +14,7 @@ import path from 'path'
|
|
|
14
14
|
import fs from 'fs'
|
|
15
15
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
|
-
// Path resolution — repo-local .
|
|
17
|
+
// Path resolution — repo-local .tissues/data/tissues.db
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -35,11 +35,11 @@ function findRepoRootForDb(startDir) {
|
|
|
35
35
|
function getDbPath() {
|
|
36
36
|
const repoRoot = findRepoRootForDb()
|
|
37
37
|
if (repoRoot) {
|
|
38
|
-
return path.join(repoRoot, '.
|
|
38
|
+
return path.join(repoRoot, '.tissues', 'data', 'tissues.db')
|
|
39
39
|
}
|
|
40
40
|
// Fallback: user home config dir (outside a git repo)
|
|
41
41
|
const home = process.env.HOME || process.env.USERPROFILE || ''
|
|
42
|
-
return path.join(home, '.config', '
|
|
42
|
+
return path.join(home, '.config', 'tissues', 'tissues.db')
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const DB_PATH = getDbPath()
|
package/src/lib/defaults.js
CHANGED
|
@@ -32,7 +32,7 @@ const BUILT_IN_DEFAULTS = {
|
|
|
32
32
|
|
|
33
33
|
// Templates
|
|
34
34
|
templates: {
|
|
35
|
-
dir: '.
|
|
35
|
+
dir: '.tissues/templates', // relative to repo root, or absolute
|
|
36
36
|
default: 'default', // default template name
|
|
37
37
|
},
|
|
38
38
|
|
|
@@ -54,7 +54,7 @@ const BUILT_IN_DEFAULTS = {
|
|
|
54
54
|
// Env var prefix
|
|
55
55
|
// ---------------------------------------------------------------------------
|
|
56
56
|
|
|
57
|
-
const ENV_PREFIX = '
|
|
57
|
+
const ENV_PREFIX = 'TISSUES_'
|
|
58
58
|
|
|
59
59
|
// ---------------------------------------------------------------------------
|
|
60
60
|
// Helpers
|
|
@@ -114,15 +114,15 @@ function readJsonFile(filePath) {
|
|
|
114
114
|
* @returns {string}
|
|
115
115
|
*/
|
|
116
116
|
function userConfigPath() {
|
|
117
|
-
return path.join(os.homedir(), '.config', '
|
|
117
|
+
return path.join(os.homedir(), '.config', 'tissues', 'config.json')
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
|
-
* Convert a flat `
|
|
121
|
+
* Convert a flat `TISSUES_SECTION_KEY=value` environment variable map into
|
|
122
122
|
* a nested object matching the config shape.
|
|
123
123
|
*
|
|
124
124
|
* Variable names are lowercased and split on `_` to build the path:
|
|
125
|
-
*
|
|
125
|
+
* TISSUES_SAFETY_MAX_PER_HOUR=20 → { safety: { maxPerHour: 20 } }
|
|
126
126
|
*
|
|
127
127
|
* Simple camelCase reconstruction: after stripping the prefix and splitting on
|
|
128
128
|
* `_`, the first segment is the section; the remaining segments are joined in
|
|
@@ -198,9 +198,9 @@ export function findRepoRoot(startDir) {
|
|
|
198
198
|
/**
|
|
199
199
|
* Load merged configuration from all sources in ascending priority order:
|
|
200
200
|
* 1. Built-in defaults (lowest)
|
|
201
|
-
* 2. User-level config (~/.config/
|
|
202
|
-
* 3. Repo-level config (<repoRoot>/.
|
|
203
|
-
* 4. Environment vars (
|
|
201
|
+
* 2. User-level config (~/.config/tissues/config.json)
|
|
202
|
+
* 3. Repo-level config (<repoRoot>/.tissues/config.json)
|
|
203
|
+
* 4. Environment vars (TISSUES_*)
|
|
204
204
|
* 5. CLI flags (passed as `cliOverrides`, highest)
|
|
205
205
|
*
|
|
206
206
|
* @param {string} [repoRoot] - path to the repo root; auto-detected if omitted
|
|
@@ -219,7 +219,7 @@ export function loadConfig(repoRoot, cliOverrides) {
|
|
|
219
219
|
|
|
220
220
|
// 3. Repo-level config
|
|
221
221
|
if (root) {
|
|
222
|
-
const repoCfg = readJsonFile(path.join(root, '.
|
|
222
|
+
const repoCfg = readJsonFile(path.join(root, '.tissues', 'config.json'))
|
|
223
223
|
if (repoCfg) merged = deepMerge(merged, repoCfg)
|
|
224
224
|
}
|
|
225
225
|
|
package/src/lib/safety.js
CHANGED
package/src/lib/templates.js
CHANGED
|
@@ -86,10 +86,10 @@ function readTemplatesFromDir(dir, source) {
|
|
|
86
86
|
*/
|
|
87
87
|
function resolveTemplateDir(source, repoRoot, cfg) {
|
|
88
88
|
if (source === 'user') {
|
|
89
|
-
return path.join(os.homedir(), '.config', '
|
|
89
|
+
return path.join(os.homedir(), '.config', 'tissues', 'templates')
|
|
90
90
|
}
|
|
91
91
|
// repo source
|
|
92
|
-
const templateDir = cfg.templates?.dir ?? '.
|
|
92
|
+
const templateDir = cfg.templates?.dir ?? '.tissues/templates'
|
|
93
93
|
if (path.isAbsolute(templateDir)) return templateDir
|
|
94
94
|
if (repoRoot) return path.join(repoRoot, templateDir)
|
|
95
95
|
return path.resolve(templateDir)
|
|
File without changes
|