tissues 0.3.0 → 0.4.0
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 +50 -49
- package/package.json +7 -8
- package/src/cli.js +2 -1
- 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,19 @@
|
|
|
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
|
|
|
14
15
|
```bash
|
|
15
|
-
npm install -g
|
|
16
|
+
npm install -g tissues
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
Requires Node.js >= 18.
|
|
@@ -23,45 +24,45 @@ Requires Node.js >= 18.
|
|
|
23
24
|
|
|
24
25
|
```bash
|
|
25
26
|
# Authenticate (auto-detects gh CLI token)
|
|
26
|
-
|
|
27
|
+
tissues auth
|
|
27
28
|
|
|
28
29
|
# Set your active repo
|
|
29
|
-
|
|
30
|
+
tissues open
|
|
30
31
|
|
|
31
32
|
# Create an issue interactively
|
|
32
|
-
|
|
33
|
+
tissues create
|
|
33
34
|
|
|
34
35
|
# Check safety status before running in CI
|
|
35
|
-
|
|
36
|
+
tissues status
|
|
36
37
|
```
|
|
37
38
|
|
|
38
39
|
---
|
|
39
40
|
|
|
40
41
|
## Commands
|
|
41
42
|
|
|
42
|
-
### `
|
|
43
|
+
### `tissues auth`
|
|
43
44
|
|
|
44
45
|
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
46
|
|
|
46
47
|
```bash
|
|
47
|
-
|
|
48
|
+
tissues auth
|
|
48
49
|
```
|
|
49
50
|
|
|
50
|
-
### `
|
|
51
|
+
### `tissues open`
|
|
51
52
|
|
|
52
53
|
Set the active repository context. All subsequent commands use this repo unless overridden with `--repo`.
|
|
53
54
|
|
|
54
55
|
```bash
|
|
55
|
-
|
|
56
|
+
tissues open
|
|
56
57
|
# prompts: pick a repo from your GitHub account
|
|
57
58
|
```
|
|
58
59
|
|
|
59
|
-
### `
|
|
60
|
+
### `tissues create`
|
|
60
61
|
|
|
61
62
|
Create a new GitHub issue. Runs dedup checks and safety gates before creating anything.
|
|
62
63
|
|
|
63
64
|
```bash
|
|
64
|
-
|
|
65
|
+
tissues create [options]
|
|
65
66
|
```
|
|
66
67
|
|
|
67
68
|
**Options:**
|
|
@@ -82,10 +83,10 @@ ghissue create [options]
|
|
|
82
83
|
|
|
83
84
|
```bash
|
|
84
85
|
# Interactive
|
|
85
|
-
|
|
86
|
+
tissues create
|
|
86
87
|
|
|
87
88
|
# Fully scripted (no prompts)
|
|
88
|
-
|
|
89
|
+
tissues create \
|
|
89
90
|
--repo owner/my-repo \
|
|
90
91
|
--template bug \
|
|
91
92
|
--title "Login fails on Safari 17" \
|
|
@@ -93,7 +94,7 @@ ghissue create \
|
|
|
93
94
|
--labels "bug,P1"
|
|
94
95
|
|
|
95
96
|
# From an AI agent
|
|
96
|
-
|
|
97
|
+
tissues create \
|
|
97
98
|
--agent claude-opus-4-6 \
|
|
98
99
|
--session abc123 \
|
|
99
100
|
--template feature \
|
|
@@ -101,23 +102,23 @@ ghissue create \
|
|
|
101
102
|
--dry-run
|
|
102
103
|
```
|
|
103
104
|
|
|
104
|
-
### `
|
|
105
|
+
### `tissues list`
|
|
105
106
|
|
|
106
107
|
Browse open issues for the active repo.
|
|
107
108
|
|
|
108
109
|
```bash
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
tissues list
|
|
111
|
+
tissues list --repo owner/other-repo
|
|
111
112
|
```
|
|
112
113
|
|
|
113
|
-
### `
|
|
114
|
+
### `tissues status`
|
|
114
115
|
|
|
115
116
|
Show circuit breaker state and rate limit usage for the active repo.
|
|
116
117
|
|
|
117
118
|
```bash
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
tissues status
|
|
120
|
+
tissues status --agent claude-opus-4-6
|
|
121
|
+
tissues status --reset # force-reset circuit breaker to closed
|
|
121
122
|
```
|
|
122
123
|
|
|
123
124
|
Output:
|
|
@@ -136,7 +137,7 @@ Fingerprints stored: 47
|
|
|
136
137
|
|
|
137
138
|
## Safety Features
|
|
138
139
|
|
|
139
|
-
|
|
140
|
+
tissues is designed to be called from AI agent loops. The safety infrastructure prevents one misconfigured agent from flooding a repo with issues.
|
|
140
141
|
|
|
141
142
|
### Circuit Breaker
|
|
142
143
|
|
|
@@ -151,8 +152,8 @@ The circuit trips after 3 blocked attempts (dedup blocks count as failures). If
|
|
|
151
152
|
To inspect or reset:
|
|
152
153
|
|
|
153
154
|
```bash
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
tissues status
|
|
156
|
+
tissues status --reset
|
|
156
157
|
```
|
|
157
158
|
|
|
158
159
|
### Rate Limiting
|
|
@@ -165,7 +166,7 @@ Three independent rate limits, all checked before creating:
|
|
|
165
166
|
| Per-agent burst | 5 issues / 5 minutes | per repo + agent |
|
|
166
167
|
| Global hourly | 30 issues/hour | per repo, all agents combined |
|
|
167
168
|
|
|
168
|
-
All limits are configurable in `.
|
|
169
|
+
All limits are configurable in `.tissues/config.json`.
|
|
169
170
|
|
|
170
171
|
### Deduplication (3 layers)
|
|
171
172
|
|
|
@@ -179,7 +180,7 @@ A `block` always prevents creation. A `warn` prompts interactively (or is skippe
|
|
|
179
180
|
|
|
180
181
|
### Attribution Tracking
|
|
181
182
|
|
|
182
|
-
Every issue created by
|
|
183
|
+
Every issue created by tissues includes a machine-readable `<!-- tissues-meta -->` block. See [Attribution](#attribution) below.
|
|
183
184
|
|
|
184
185
|
---
|
|
185
186
|
|
|
@@ -188,11 +189,11 @@ Every issue created by ghissue includes a machine-readable `<!-- gitissues-meta
|
|
|
188
189
|
Configuration is loaded and merged from four sources in ascending priority order:
|
|
189
190
|
|
|
190
191
|
1. Built-in defaults
|
|
191
|
-
2. User-level config: `~/.config/
|
|
192
|
-
3. Repo-level config: `.
|
|
193
|
-
4. Environment variables: `
|
|
192
|
+
2. User-level config: `~/.config/tissues/config.json`
|
|
193
|
+
3. Repo-level config: `.tissues/config.json`
|
|
194
|
+
4. Environment variables: `TISSUES_*`
|
|
194
195
|
|
|
195
|
-
### Example `.
|
|
196
|
+
### Example `.tissues/config.json`
|
|
196
197
|
|
|
197
198
|
```json
|
|
198
199
|
{
|
|
@@ -214,7 +215,7 @@ Configuration is loaded and merged from four sources in ascending priority order
|
|
|
214
215
|
"defaultAgent": "human"
|
|
215
216
|
},
|
|
216
217
|
"templates": {
|
|
217
|
-
"dir": ".
|
|
218
|
+
"dir": ".tissues/templates",
|
|
218
219
|
"default": "bug"
|
|
219
220
|
},
|
|
220
221
|
"hooks": {
|
|
@@ -225,13 +226,13 @@ Configuration is loaded and merged from four sources in ascending priority order
|
|
|
225
226
|
|
|
226
227
|
### Environment Variables
|
|
227
228
|
|
|
228
|
-
Environment variables follow the pattern `
|
|
229
|
+
Environment variables follow the pattern `TISSUES_<SECTION>_<KEY>`:
|
|
229
230
|
|
|
230
231
|
```bash
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
232
|
+
TISSUES_SAFETY_MAX_PER_HOUR=5
|
|
233
|
+
TISSUES_SAFETY_BURST_LIMIT=3
|
|
234
|
+
TISSUES_SAFETY_GLOBAL_MAX_PER_HOUR=20
|
|
235
|
+
TISSUES_ATTRIBUTION_DEFAULT_AGENT=my-agent
|
|
235
236
|
```
|
|
236
237
|
|
|
237
238
|
### Hooks
|
|
@@ -239,9 +240,9 @@ GITISSUES_ATTRIBUTION_DEFAULT_AGENT=my-agent
|
|
|
239
240
|
`postCreate` runs a shell command after every successful issue creation. Three environment variables are injected:
|
|
240
241
|
|
|
241
242
|
```bash
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
TISSUES_REPO # owner/repo
|
|
244
|
+
TISSUES_ISSUE_NUMBER # 142
|
|
245
|
+
TISSUES_ISSUE_URL # https://github.com/owner/repo/issues/142
|
|
245
246
|
```
|
|
246
247
|
|
|
247
248
|
---
|
|
@@ -261,7 +262,7 @@ GITISSUES_ISSUE_URL # https://github.com/owner/repo/issues/142
|
|
|
261
262
|
|
|
262
263
|
### Custom Templates
|
|
263
264
|
|
|
264
|
-
Place `.md` files in `.
|
|
265
|
+
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
266
|
|
|
266
267
|
Template files support `{{variable}}` substitution:
|
|
267
268
|
|
|
@@ -274,7 +275,7 @@ Template files support `{{variable}}` substitution:
|
|
|
274
275
|
| `{{date}}` | ISO date (YYYY-MM-DD) |
|
|
275
276
|
| `{{repo}}` | Repository (owner/name) |
|
|
276
277
|
|
|
277
|
-
**Example** `.
|
|
278
|
+
**Example** `.tissues/templates/incident.md`:
|
|
278
279
|
|
|
279
280
|
```markdown
|
|
280
281
|
name: Incident Report
|
|
@@ -303,21 +304,21 @@ Use it with `--template incident`.
|
|
|
303
304
|
|
|
304
305
|
## Attribution
|
|
305
306
|
|
|
306
|
-
Every issue created by
|
|
307
|
+
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
308
|
|
|
308
309
|
```html
|
|
309
|
-
<!--
|
|
310
|
+
<!-- tissues-meta
|
|
310
311
|
agent: claude-opus-4-6
|
|
311
312
|
session: abc123
|
|
312
313
|
pid: 84921
|
|
313
314
|
trigger: cli-create
|
|
314
315
|
fingerprint: sha256:3f2a1b...
|
|
315
316
|
created_at: 2026-02-19T15:30:00.000Z
|
|
316
|
-
created_via:
|
|
317
|
+
created_via: tissues-cli/0.3.0
|
|
317
318
|
-->
|
|
318
319
|
```
|
|
319
320
|
|
|
320
|
-
The block is used by
|
|
321
|
+
The block is used by tissues to:
|
|
321
322
|
|
|
322
323
|
- Power content fingerprint dedup (prevents re-creating identical issues)
|
|
323
324
|
- Track which agent created which issue
|
|
@@ -329,10 +330,10 @@ You can pass additional fields via `--agent`, `--session`, and the programmatic
|
|
|
329
330
|
|
|
330
331
|
## State Database
|
|
331
332
|
|
|
332
|
-
|
|
333
|
+
tissues stores local state in a SQLite database at:
|
|
333
334
|
|
|
334
335
|
```
|
|
335
|
-
.
|
|
336
|
+
.tissues/data/tissues.db
|
|
336
337
|
```
|
|
337
338
|
|
|
338
339
|
**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.0",
|
|
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
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
|